summaryrefslogtreecommitdiff
path: root/Video.qml
blob: f63a7ce46ee0cd97cce9ba2c97fb17a92ff5a6bd (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
// 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)
                }
            }
        }
    }
}