blob: 47eda92d916300a7862b3249816ecbec7087d9d7 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
// 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()
focusPolicy: Qt.NoFocus
}
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)
}
}
}
}
}
|