diff options
Diffstat (limited to 'Video.qml')
| -rw-r--r-- | Video.qml | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/Video.qml b/Video.qml new file mode 100644 index 0000000..f63a7ce --- /dev/null +++ b/Video.qml @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: Unlicense + +import QtQuick 2.14 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.6 +import QtMultimedia 5.11 + +Page { + property bool loaded: + media.status !== MediaPlayer.NoMedia && + media.status !== MediaPlayer.InvalidMedia && + media.status !== MediaPlayer.UnknownStatus + property alias source: media.source + property alias time: media.position + + function pause(yes) { + if (yes === undefined) + yes = media.playbackState === MediaPlayer.PlayingState + if (yes) + media.pause() + else + media.play() + } + + function seek(offset, relative) { + if (relative) + offset += media.position + media.seek(offset) + } + + Keys.onPressed: { + switch (event.key) { + // (Un)pause video. + case Qt.Key_Space: + pause() + break + // Seek video. + case Qt.Key_Left: + seek(-500, true) + break + case Qt.Key_Right: + seek(500, true) + break + // Change playback rate. + case Qt.Key_Equal: + rate.reset() + break + case Qt.Key_Comma: + rate.decrease() + break + case Qt.Key_Period: + rate.increase() + break + default: + return // don’t accept the event + } + event.accepted = true + } + + // Video. + ColumnLayout { + spacing: 0 + anchors.fill: parent + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: 'black' + clip: true + + VideoOutput { + anchors.fill: parent + fillMode: VideoOutput.PreserveAspectFit + source: media + + transform: Scale { + id: zoom + property real scale: 1.0 + xScale: scale + yScale: scale + origin.x: wheel.point.position.x + origin.y: wheel.point.position.y + } + + MediaPlayer { + id: media + notifyInterval: 100 + playbackRate: Number.fromLocaleString(rate.displayText) + volume: QtMultimedia.convertVolume( + volume.value, + QtMultimedia.LogarithmicVolumeScale, + QtMultimedia.LinearVolumeScale) + } + + TapHandler { + acceptedButtons: Qt.RightButton + onTapped: pause() + } + + WheelHandler { + id: wheel + onWheel: zoom.scale = Math.max(1.0, (event.angleDelta.y > 0 ? 1.1 : 0.9) * zoom.scale) + } + } + } + + // Video controls. + RowLayout { + Layout.margins: 5 + + Button { + icon.name: 'media-playback-pause' + implicitWidth: implicitHeight + checkable: true + checked: media.playbackState !== MediaPlayer.PlayingState + onClicked: checked ? media.pause() : media.play() + } + Label { text: new Date(media.position).toISOString().substr(12, 9) } + Slider { + Layout.fillWidth: true + from: 0; to: media.duration + value: media.position + onMoved: media.seek(value) + } + Label { text: new Date(media.duration).toISOString().substr(12, 7) } + + Volume { + id: volume + muted: media.muted + focusPolicy: Qt.NoFocus + } + + // Playback speed control. + SpinBox { + id: rate + implicitWidth: 80 + focusPolicy: Qt.NoFocus + + from: 25; to: 250; stepSize: 25 + value: 100 + + function reset() { value = 100 } + + textFromValue: function (value, locale) { + return (value / 100).toLocaleString(locale, 'f', 2) + } + } + } + } +} |
