2021-06-14 19:09:53 +02:00
|
|
|
|
// 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()
|
2021-09-12 19:37:16 +02:00
|
|
|
|
focusPolicy: Qt.NoFocus
|
2021-06-14 19:09:53 +02:00
|
|
|
|
}
|
|
|
|
|
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)
|
2021-09-15 22:21:32 +02:00
|
|
|
|
focusPolicy: Qt.NoFocus
|
2021-06-14 19:09:53 +02:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
from: 25; to: 250; stepSize: 25
|
|
|
|
|
value: 100
|
|
|
|
|
|
|
|
|
|
function reset() { value = 100 }
|
|
|
|
|
|
|
|
|
|
textFromValue: function (value, locale) {
|
|
|
|
|
return (value / 100).toLocaleString(locale, 'f', 2)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|