150 lines
4.1 KiB
QML
150 lines
4.1 KiB
QML
// 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)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|