summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Cooke <ed.cooke@qt.io>2024-01-03 14:11:55 +0100
committerEd Cooke <ed.cooke@qt.io>2024-04-24 13:59:05 +0200
commit5aa9f991c22f580d3d937b77cded6f6b2f3ffd08 (patch)
tree4b13fab968fc05f851ff44f49b06d5c732e0a985
parent4e475802fc32775af6f41449129bb96b76ad9c42 (diff)
Update QML Media Player
Rewrite the QML media player with a new UI. Changes include: * Remove the menu bar and replace it with popups for file importing and track selection. * Replace the playback rate slider with +/- 10 second buttons and a playback rate combo box. * Add a timer to hide the playback controls when the mouse has been idle for 3 seconds. * Add a button to toggle between single play or infinite loop. * Add a layout for mobile using responsive layouts. Fixes: QTBUG-118474 Pick-to: 6.6 6.7 Change-Id: I0496766a79287e9eabd6f7aff8392a8ea0706920 Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
-rw-r--r--examples/multimedia/video/mediaplayer/AudioControl.qml45
-rw-r--r--examples/multimedia/video/mediaplayer/CMakeLists.txt56
-rw-r--r--examples/multimedia/video/mediaplayer/Main.qml178
-rw-r--r--examples/multimedia/video/mediaplayer/MetadataInfo.qml69
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackControl.qml150
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackRateControl.qml33
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml42
-rw-r--r--examples/multimedia/video/mediaplayer/PlayerMenuBar.qml124
-rw-r--r--examples/multimedia/video/mediaplayer/TracksInfo.qml75
-rw-r--r--examples/multimedia/video/mediaplayer/controls/AudioControl.qml50
-rw-r--r--examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml30
-rw-r--r--examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml318
-rw-r--r--examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml56
-rw-r--r--examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml204
-rw-r--r--examples/multimedia/video/mediaplayer/controls/TracksInfo.qml25
-rw-r--r--examples/multimedia/video/mediaplayer/controls/UrlPopup.qml54
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpgbin181335 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gifbin65177 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gifbin222405 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/audio-control.gifbin150039 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/mediaplayer.pngbin0 -> 49506 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/meta-data.pngbin66670 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpgbin84480 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gifbin188432 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gifbin179494 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpgbin67156 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/settings.pngbin0 -> 33705 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpgbin96819 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/url.pngbin68983 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf4
-rw-r--r--examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc296
-rw-r--r--examples/multimedia/video/mediaplayer/images/backward10.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/ff.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/forward10.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/link.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/loop.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/more.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/mute.svg (renamed from examples/multimedia/video/mediaplayer/Mute_Icon.svg)0
-rw-r--r--examples/multimedia/video/mediaplayer/images/open_new.svg6
-rw-r--r--examples/multimedia/video/mediaplayer/images/pause_symbol.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/play_symbol.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/rewind.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/settings.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/speaker.svg (renamed from examples/multimedia/video/mediaplayer/Speaker_Icon.svg)0
-rw-r--r--examples/multimedia/video/mediaplayer/images/stop_symbol.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/url.svg6
-rw-r--r--examples/multimedia/video/mediaplayer/images/volume.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/volume_mute.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/zoom_maximize.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/zoom_minimize.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/main.cpp6
-rw-r--r--examples/multimedia/video/mediaplayer/main.qml153
-rw-r--r--src/multimedia/doc/qtmultimedia.qdocconf1
53 files changed, 1158 insertions, 882 deletions
diff --git a/examples/multimedia/video/mediaplayer/AudioControl.qml b/examples/multimedia/video/mediaplayer/AudioControl.qml
deleted file mode 100644
index e37ae032a..000000000
--- a/examples/multimedia/video/mediaplayer/AudioControl.qml
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- property bool muted: false
- property real volume: volumeSlider.value/100.
-
- implicitHeight: buttons.height
-
- RowLayout {
- anchors.fill: parent
-
- Item {
- id: buttons
-
- width: muteButton.implicitWidth
- height: muteButton.implicitHeight
-
- RoundButton {
- id: muteButton
- radius: 50.0
- icon.source: muted ? "qrc:///Mute_Icon.svg" : "qrc:///Speaker_Icon.svg"
- onClicked: { muted = !muted }
- }
- }
-
- Slider {
- id: volumeSlider
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignVCenter
-
- enabled: true
- to: 100.0
- value: 100.0
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/CMakeLists.txt b/examples/multimedia/video/mediaplayer/CMakeLists.txt
index 9688b0b0b..e33a20ad4 100644
--- a/examples/multimedia/video/mediaplayer/CMakeLists.txt
+++ b/examples/multimedia/video/mediaplayer/CMakeLists.txt
@@ -13,40 +13,56 @@ endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/mediaplayer")
-find_package(Qt6 REQUIRED COMPONENTS Core Multimedia Quick QuickControls2 Svg)
+find_package(Qt6 REQUIRED COMPONENTS Multimedia Core Quick QuickControls2 Svg)
-qt_add_executable(mediaplayer
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(mediaplayerexample
main.cpp
)
-set(resource_files
- "main.qml"
- "PlaybackControl.qml"
- "MetadataInfo.qml"
- "AudioControl.qml"
- "PlaybackSeekControl.qml"
- "PlaybackRateControl.qml"
- "PlayerMenuBar.qml"
- "TracksInfo.qml"
- "Mute_Icon.svg"
- "Speaker_Icon.svg"
+set_target_properties(mediaplayerexample PROPERTIES
+ MACOSX_BUNDLE TRUE
)
-qt_add_resources(mediaplayer "mediaplayer"
- PREFIX
- "/"
- FILES
- ${resource_files}
+qt_add_qml_module(mediaplayerexample
+ URI mediaplayer
+ QML_FILES
+ "Main.qml"
+ "controls/PlaybackControl.qml"
+ "controls/AudioControl.qml"
+ "controls/PlaybackSeekControl.qml"
+ "controls/SettingsPopup.qml"
+ "controls/UrlPopup.qml"
+ "controls/MetadataInfo.qml"
+ "controls/TracksInfo.qml"
+ RESOURCES
+ "images/backward10.svg"
+ "images/mute.svg"
+ "images/open_new.svg"
+ "images/pause_symbol.svg"
+ "images/play_symbol.svg"
+ "images/forward10.svg"
+ "images/more.svg"
+ "images/speaker.svg"
+ "images/stop_symbol.svg"
+ "images/volume.svg"
+ "images/volume_mute.svg"
+ "images/zoom_maximize.svg"
+ "images/zoom_minimize.svg"
+ "images/link.svg"
+ "images/loop.svg"
)
-target_link_libraries(mediaplayer PRIVATE
+target_link_libraries(mediaplayerexample PRIVATE
Qt6::Core
Qt6::Multimedia
Qt6::Svg
Qt6::Quick
+ Qt6::Multimedia
)
-install(TARGETS mediaplayer
+install(TARGETS mediaplayerexample
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
diff --git a/examples/multimedia/video/mediaplayer/Main.qml b/examples/multimedia/video/mediaplayer/Main.qml
new file mode 100644
index 000000000..9ee9cf6c3
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/Main.qml
@@ -0,0 +1,178 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtMultimedia
+import "controls"
+
+//! [0]
+ApplicationWindow {
+ id: root
+ title: qsTr("Multimedia Player")
+ width: 1280
+ height: 720
+ //! [0]
+ minimumWidth: 960
+ minimumHeight: 540
+ visible: true
+ color: "black"
+
+ property alias source: mediaPlayer.source
+ property alias playbackRate: mediaPlayer.playbackRate
+ property bool fullScreen: false
+
+ MessageDialog {
+ id: mediaError
+ buttons: MessageDialog.Ok
+ }
+
+ MouseArea {
+ // an activity listener to hide the playback contols when idle
+ id: activityListener
+ anchors.fill: parent
+ z: 1
+ propagateComposedEvents: true
+ hoverEnabled: true
+
+ property bool inactiveMouse: false
+
+ Timer {
+ id: timer
+ interval: 3000 // milliseconds
+ onTriggered: activityListener.inactiveMouse = true
+ }
+
+ function activityHandler(mouse) {
+ if (activityListener.inactiveMouse)
+ activityListener.inactiveMouse = false
+ timer.restart()
+ timer.start()
+ mouse.accepted = false
+ }
+
+ onPositionChanged: mouse => activityHandler(mouse)
+ onPressed: mouse => activityHandler(mouse)
+ onDoubleClicked: mouse => mouse.accepted = false
+ }
+
+ MetadataInfo {
+ id: metadataInfo
+ }
+
+ TracksInfo {
+ id: audioTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeAudioTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ TracksInfo {
+ id: videoTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeVideoTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ TracksInfo {
+ id: subtitleTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeSubtitleTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ //! [1]
+ MediaPlayer {
+ id: mediaPlayer
+ //! [1]
+ function updateMetadata() {
+ metadataInfo.clear()
+ metadataInfo.read(mediaPlayer.metaData)
+ metadataInfo.read(mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack])
+ metadataInfo.read(mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack])
+ metadataInfo.read(mediaPlayer.subtitleTracks[mediaPlayer.activeSubtitleTrack])
+ }
+ //! [2]
+ videoOutput: videoOutput
+ audioOutput: AudioOutput {
+ id: audio
+ muted: playbackController.muted
+ volume: playbackController.volume
+ }
+ //! [2]
+ //! [4]
+ onErrorOccurred: {
+ mediaError.open()
+ mediaError.text = mediaPlayer.errorString
+ }
+ //! [4]
+ onMetaDataChanged: { updateMetadata() }
+ //! [6]
+ onTracksChanged: {
+ audioTracksInfo.read(mediaPlayer.audioTracks)
+ videoTracksInfo.read(mediaPlayer.videoTracks)
+ subtitleTracksInfo.read(mediaPlayer.subtitleTracks, 6) /* QMediaMetaData::Language = 6 */
+ updateMetadata()
+ mediaPlayer.play()
+ }
+ //! [6]
+ source: new URL("https://download.qt.io/learning/videos/media-player-example/Qt_LogoMergeEffect.mp4")
+ }
+
+ //! [3]
+ VideoOutput {
+ id: videoOutput
+ anchors.fill: parent
+ visible: mediaPlayer.mediaStatus > 0
+
+ TapHandler {
+ onDoubleTapped: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+ }
+ //! [3]
+
+ Rectangle {
+ anchors.fill: parent
+ visible: mediaPlayer.mediaStatus === 0
+ color: "black"
+
+ TapHandler {
+ onDoubleTapped: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+ }
+
+ //! [5]
+ PlaybackControl {
+ id: playbackController
+ //! [5]
+
+ property bool showControls: !activityListener.inactiveMouse || busy
+ opacity: showControls
+ // onOpacityChanged can't be used as it is animated and therefore not immediate
+ onShowControlsChanged: activityListener.cursorShape = showControls ?
+ Qt.ArrowCursor : Qt.BlankCursor
+
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ //! [6]
+ mediaPlayer: mediaPlayer
+ audioTracksInfo: audioTracksInfo
+ videoTracksInfo: videoTracksInfo
+ subtitleTracksInfo: subtitleTracksInfo
+ metadataInfo: metadataInfo
+ }
+ //! [6]
+}
diff --git a/examples/multimedia/video/mediaplayer/MetadataInfo.qml b/examples/multimedia/video/mediaplayer/MetadataInfo.qml
deleted file mode 100644
index 2ac4bc02d..000000000
--- a/examples/multimedia/video/mediaplayer/MetadataInfo.qml
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
- implicitWidth: 200
-
- function clear() {
- elements.clear();
- }
-
- function read(metadata) {
- if (metadata) {
- for (var key of metadata.keys()) {
- if (metadata.stringValue(key)) {
- elements.append(
- { name: metadata.metaDataKeyToString(key)
- , value: metadata.stringValue(key)
- })
- }
- }
- }
- }
-
- ListModel {
- id: elements
- }
-
- Frame {
- anchors.fill: parent
- padding: 15
-
- background: Rectangle {
- color: "lightgray"
- opacity: 0.7
- }
-
- ListView {
- id: metadataList
- visible: elements.count > 0
- anchors.fill: parent
- model: elements
- delegate: RowLayout {
- width: metadataList.width
- Text {
- Layout.preferredWidth: 90
- text: model.name + ":"
- horizontalAlignment: Text.AlignRight
- }
- Text {
- Layout.fillWidth: true
- text: model.value
- wrapMode: Text.WrapAnywhere
- }
- }
- }
-
- Text {
- id: metadataNoList
- visible: elements.count === 0
- text: qsTr("No metadata present")
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlaybackControl.qml b/examples/multimedia/video/mediaplayer/PlaybackControl.qml
deleted file mode 100644
index 386599bad..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackControl.qml
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- property int mediaPlayerState: mediaPlayer.playbackState
- property alias muted: audio.muted
- property alias volume: audio.volume
-
- height: frame.height
-
- opacity: 1
-
- Behavior on opacity { NumberAnimation { duration: 300 }}
-
- function updateOpacity() {
- //hover is not usable in mobile platforms
- if (Qt.platform.os == "android" || Qt.platform.os == "ios")
- return;
-
- if (playbackControlHover.hovered || mediaPlayerState != MediaPlayer.PlayingState || !mediaPlayer.hasVideo)
- root.opacity = 1;
- else
- root.opacity = 1; // 0; TODO: enable opacity change when HoverHandle is fixed
- }
-
- Connections {
- target: mediaPlayer
- function onPlaybackStateChanged() { updateOpacity() }
- function onHasVideoChanged() { updateOpacity() }
- }
-
- HoverHandler {
- id: playbackControlHover
- margin: 50
- onHoveredChanged: updateOpacity()
- }
-
- Frame {
- id: frame
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
-
- background: Rectangle {
- color: "white"
- }
-
- ColumnLayout {
- id: playbackControlPanel
- anchors.fill: parent
- anchors.leftMargin: 10
- anchors.rightMargin: 10
-
- PlaybackSeekControl {
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
-
- RowLayout {
- id: playerButtons
-
- Layout.fillWidth: true
-
- PlaybackRateControl {
- Layout.minimumWidth: 100
- Layout.maximumWidth: 150
- Layout.fillHeight: true
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- RowLayout {
- Layout.alignment: Qt.AlignCenter
- id: controlButtons
-
- RoundButton {
- id: pauseButton
- radius: 50.0
- text: "\u2016";
- onClicked: mediaPlayer.pause()
- }
-
- RoundButton {
- id: playButton
- radius: 50.0
- text: "\u25B6";
- onClicked: mediaPlayer.play()
- }
-
- RoundButton {
- id: stopButton
- radius: 50.0
- text: "\u25A0";
- onClicked: mediaPlayer.stop()
- }
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- AudioControl {
- id: audio
- Layout.minimumWidth: 100
- Layout.maximumWidth: 150
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
- }
- }
- }
-
- states: [
- State {
- name: "playing"
- when: mediaPlayerState == MediaPlayer.PlayingState
- PropertyChanges { target: pauseButton; visible: true}
- PropertyChanges { target: playButton; visible: false}
- PropertyChanges { target: stopButton; visible: true}
- },
- State {
- name: "stopped"
- when: mediaPlayerState == MediaPlayer.StoppedState
- PropertyChanges { target: pauseButton; visible: false}
- PropertyChanges { target: playButton; visible: true}
- PropertyChanges { target: stopButton; visible: false}
- },
- State {
- name: "paused"
- when: mediaPlayerState == MediaPlayer.PausedState
- PropertyChanges { target: pauseButton; visible: false}
- PropertyChanges { target: playButton; visible: true}
- PropertyChanges { target: stopButton; visible: true}
- }
- ]
-
-}
-
diff --git a/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml b/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml
deleted file mode 100644
index 131ee17c4..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
-
- RowLayout {
- anchors.fill: parent
-
- Slider {
- id: slider
- Layout.fillWidth: true
- snapMode: Slider.SnapOnRelease
- enabled: true
- from: 0.5
- to: 2.5
- stepSize: 0.5
- value: 1.0
-
- onMoved: { mediaPlayer.setPlaybackRate(value) }
- }
- Text {
- text: "Rate " + mediaPlayer.playbackRate + "x"
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml b/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml
deleted file mode 100644
index ad583c79a..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
-
- implicitHeight: 20
-
-
- RowLayout {
- anchors.fill: parent
-
- Text {
- id: mediaTime
- Layout.minimumWidth: 50
- Layout.minimumHeight: 18
- horizontalAlignment: Text.AlignRight
- text: {
- var m = Math.floor(mediaPlayer.position / 60000)
- var ms = (mediaPlayer.position / 1000 - m * 60).toFixed(1)
- return `${m}:${ms.padStart(4, 0)}`
- }
- }
-
- Slider {
- id: mediaSlider
- Layout.fillWidth: true
- enabled: mediaPlayer.seekable
- to: 1.0
- value: mediaPlayer.position / mediaPlayer.duration
-
- onMoved: mediaPlayer.setPosition(value * mediaPlayer.duration)
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml b/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml
deleted file mode 100644
index 822f9519b..000000000
--- a/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Dialogs
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- required property VideoOutput videoOutput
- required property MetadataInfo metadataInfo
- required property TracksInfo audioTracksInfo
- required property TracksInfo videoTracksInfo
- required property TracksInfo subtitleTracksInfo
-
- height: menuBar.height
-
- signal closePlayer
-
- function loadUrl(url) {
- mediaPlayer.stop()
- mediaPlayer.source = url
- mediaPlayer.play()
- }
-
- function closeOverlays(){
- metadataInfo.visible = false;
- audioTracksInfo.visible = false;
- videoTracksInfo.visible = false;
- subtitleTracksInfo.visible = false;
- }
-
- function showOverlay(overlay){
- closeOverlays();
- overlay.visible = true;
- }
-
- Popup {
- id: urlPopup
- anchors.centerIn: Overlay.overlay
-
- RowLayout {
- id: rowOpenUrl
- Label {
- text: qsTr("URL:");
- }
-
- TextInput {
- id: urlText
- focus: true
- Layout.minimumWidth: 400
- wrapMode: TextInput.WrapAnywhere
- Keys.onReturnPressed: { loadUrl(text); urlText.text = ""; urlPopup.close() }
- }
-
- Button {
- text: "Load"
- onClicked: { loadUrl(urlText.text); urlText.text = ""; urlPopup.close() }
- }
- }
- onOpened: { urlPopup.forceActiveFocus() }
- }
-
- FileDialog {
- id: fileDialog
- title: "Please choose a file"
- onAccepted: {
- mediaPlayer.stop()
- mediaPlayer.source = fileDialog.currentFile
- mediaPlayer.play()
- }
- }
-
- MenuBar {
- id: menuBar
- anchors.left: parent.left
- anchors.right: parent.right
-
- Menu {
- title: qsTr("&File")
- Action {
- text: qsTr("&Open")
- onTriggered: fileDialog.open()
- }
- Action {
- text: qsTr("&URL");
- onTriggered: urlPopup.open()
- }
-
- Action {
- text: qsTr("&Exit");
- onTriggered: closePlayer()
- }
- }
-
- Menu {
- title: qsTr("&View")
- Action {
- text: qsTr("Metadata")
- onTriggered: showOverlay(metadataInfo)
- }
- }
-
- Menu {
- title: qsTr("&Tracks")
- Action {
- text: qsTr("Audio")
- onTriggered: showOverlay(audioTracksInfo)
- }
- Action {
- text: qsTr("Video")
- onTriggered: showOverlay(videoTracksInfo)
- }
- Action {
- text: qsTr("Subtitles")
- onTriggered: showOverlay(subtitleTracksInfo)
- }
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/TracksInfo.qml b/examples/multimedia/video/mediaplayer/TracksInfo.qml
deleted file mode 100644
index ee6273d25..000000000
--- a/examples/multimedia/video/mediaplayer/TracksInfo.qml
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
- implicitWidth: 200
-
- property int selectedTrack: 0
-
- function read(metadataList) {
- var LanguageKey = 6;
-
- elements.clear()
-
- elements.append(
- { language: "No Selected Track"
- , trackNumber: -1
- })
-
- if (!metadataList)
- return;
-
- metadataList.forEach(function (metadata, index) {
- var language = metadata.stringValue(LanguageKey);
- var label = language ? metadata.stringValue(LanguageKey) : "track " + (index + 1)
- elements.append(
- { language: label
- , trackNumber: index
- })
- });
- }
-
- ListModel {
- id: elements
- }
-
- Frame {
- anchors.fill: parent
- padding: 15
-
- background: Rectangle {
- color: "lightgray"
- opacity: 0.7
- }
-
- ButtonGroup {id:group; }
-
- ListView {
- id: trackList
- visible: elements.count > 0
- anchors.fill: parent
- model: elements
- delegate: RowLayout {
- width: trackList.width
- RadioButton {
- checked: model.trackNumber === selectedTrack
- text: model.language
- ButtonGroup.group: group
- onClicked: selectedTrack = model.trackNumber
- }
- }
- }
-
- Text {
- id: metadataNoList
- visible: elements.count === 0
- text: qsTr("No tracks present")
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/controls/AudioControl.qml b/examples/multimedia/video/mediaplayer/controls/AudioControl.qml
new file mode 100644
index 000000000..81ebe7f10
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/AudioControl.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: audioController
+
+ property alias busy: slider.pressed
+ //! [0]
+ property alias muted: muteButton.checked
+ property real volume: slider.value
+ //! [0]
+ property alias showSlider: slider.visible
+ property int iconDimension: 24
+
+ implicitHeight: 46
+ implicitWidth: mainLayout.width
+
+ RowLayout {
+ id: mainLayout
+ spacing: 10
+ anchors.verticalCenter: parent.verticalCenter
+
+ RoundButton {
+ id: muteButton
+ implicitHeight: 40
+ implicitWidth: 40
+ radius: 4
+ icon.source: audioController.muted ? "../images/volume_mute.svg" : "../images/volume.svg"
+ icon.width: audioController.iconDimension
+ icon.height: audioController.iconDimension
+ flat: true
+ checkable: true
+ }
+
+ Slider {
+ id: slider
+ visible: !audioController.showSlider
+ implicitWidth: 136
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+
+ enabled: !audioController.muted
+ value: 1
+ }
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml b/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml
new file mode 100644
index 000000000..3e68faa8e
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ property alias metadata: listModel
+ property alias count: listModel.count
+
+ function clear() {
+ listModel.clear()
+ }
+
+ //! [0]
+ function read(metadata) {
+ if (!metadata)
+ return
+ for (const key of metadata.keys())
+ if (metadata.stringValue(key))
+ listModel.append({
+ name: metadata.metaDataKeyToString(key),
+ value: metadata.stringValue(key)
+ })
+ }
+
+ ListModel {
+ id: listModel
+ }
+ //! [0]
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml b/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml
new file mode 100644
index 000000000..056cf50d0
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml
@@ -0,0 +1,318 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtMultimedia
+import QtQuick.Dialogs
+
+//! [0]
+Item {
+ id: playbackController
+
+ required property MediaPlayer mediaPlayer
+ required property MetadataInfo metadataInfo
+ required property TracksInfo audioTracksInfo
+ required property TracksInfo videoTracksInfo
+ required property TracksInfo subtitleTracksInfo
+ //! [0]
+
+ property alias muted: audioControl.muted
+ property alias volume: audioControl.volume
+
+ property bool landscapePlaybackControls: root.width >= 668
+ property bool busy: fileDialog.visible
+ || urlPopup.visible
+ || settingsPopup.visible
+ || audioControl.busy
+ || playbackSeekControl.busy
+ || !playbackController.mediaPlayer.playing
+
+ implicitHeight: landscapePlaybackControls ? 168 : 208
+
+ Behavior on opacity { NumberAnimation { duration: 300 } }
+
+ FileDialog {
+ id: fileDialog
+ title: "Please choose a file"
+ onAccepted: {
+ playbackController.mediaPlayer.stop()
+ playbackController.mediaPlayer.source = fileDialog.currentFile
+ playbackController.mediaPlayer.play()
+ }
+ }
+
+ UrlPopup {
+ id: urlPopup
+ anchors.centerIn: Overlay.overlay
+ mediaPlayer: playbackController.mediaPlayer
+ }
+
+ SettingsPopup {
+ id: settingsPopup
+ anchors.centerIn: Overlay.overlay
+
+ metadataInfo: playbackController.metadataInfo
+ mediaPlayer: playbackController.mediaPlayer
+ audioTracksInfo: playbackController.audioTracksInfo
+ videoTracksInfo: playbackController.videoTracksInfo
+ subtitleTracksInfo: playbackController.subtitleTracksInfo
+ }
+
+ component CustomButton: RoundButton {
+ implicitWidth: 40
+ implicitHeight: 40
+ radius: 4
+ icon.width: 24
+ icon.height: 24
+ flat: true
+ }
+
+ component CustomRoundButton: RoundButton {
+ property int diameter: 40
+ Layout.preferredWidth: diameter
+ Layout.preferredHeight: diameter
+ radius: diameter / 2
+ icon.width: 24
+ icon.height: 24
+ }
+
+ //! [1]
+ CustomButton {
+ id: fileDialogButton
+ icon.source: "../images/open_new.svg"
+ flat: false
+ onClicked: fileDialog.open()
+ }
+
+ CustomButton {
+ id: openUrlButton
+ icon.source: "../images/link.svg"
+ flat: false
+ onClicked: urlPopup.open()
+ }
+ //! [1]
+
+ CustomButton {
+ id: loopButton
+ icon.source: "../images/loop.svg"
+ icon.color: playbackController.mediaPlayer.loops === MediaPlayer.Once ? palette.buttonText : palette.accent
+ onClicked: playbackController.mediaPlayer.loops = playbackController.mediaPlayer.loops === MediaPlayer.Once
+ ? MediaPlayer.Infinite
+ : MediaPlayer.Once
+ }
+
+ CustomButton {
+ id: settingsButton
+ icon.source: "../images/more.svg"
+ onClicked: settingsPopup.open()
+ }
+
+ CustomButton {
+ id: fullScreenButton
+ icon.source: root.fullScreen ? "../images/zoom_minimize.svg"
+ : "../images/zoom_maximize.svg"
+ onClicked: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+
+ RowLayout {
+ id: controlButtons
+ spacing: 16
+
+ CustomRoundButton {
+ id: backward10Button
+ icon.source: "../images/backward10.svg"
+ onClicked: {
+ const pos = Math.max(0, playbackController.mediaPlayer.position - 10000)
+ playbackController.mediaPlayer.setPosition(pos)
+ }
+ }
+
+ //! [2]
+ CustomRoundButton {
+ id: playButton
+ visible: playbackController.mediaPlayer.playbackState !== MediaPlayer.PlayingState
+ icon.source: "../images/play_symbol.svg"
+ onClicked: playbackController.mediaPlayer.play()
+ }
+
+ CustomRoundButton {
+ id: pauseButton
+ visible: playbackController.mediaPlayer.playbackState === MediaPlayer.PlayingState
+ icon.source: "../images/pause_symbol.svg"
+ onClicked: playbackController.mediaPlayer.pause()
+ }
+ //! [2]
+
+ //! [3]
+ CustomRoundButton {
+ id: forward10Button
+ icon.source: "../images/forward10.svg"
+ onClicked: {
+ const pos = Math.min(playbackController.mediaPlayer.duration,
+ playbackController.mediaPlayer.position + 10000)
+ playbackController.mediaPlayer.setPosition(pos)
+ }
+ }
+ //! [3]
+ } // RowLayout controlButtons
+
+ AudioControl {
+ id: audioControl
+ showSlider: root.width >= 960
+ }
+
+ PlaybackSeekControl {
+ id: playbackSeekControl
+ Layout.fillWidth: true
+ mediaPlayer: playbackController.mediaPlayer
+ }
+
+ Frame {
+ id: landscapeLayout
+ anchors.fill: parent
+ padding: 32
+ topPadding: 28
+ visible: landscapePlaybackControls
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 16
+
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ id: fdbProxy
+ target: fileDialogButton
+ anchors.left: parent.left
+ }
+
+ LayoutItemProxy {
+ target: openUrlButton
+ anchors.left: fdbProxy.right
+ anchors.leftMargin: 12
+ }
+
+ LayoutItemProxy {
+ target: controlButtons
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ LayoutItemProxy {
+ target: loopButton
+ anchors.right: acProxy.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: acProxy
+ target: audioControl
+ anchors.right: sbProxy.left
+ anchors.rightMargin: 30
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ LayoutItemProxy {
+ id: sbProxy
+ target: settingsButton
+ anchors.right: fbProxy.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: fbProxy
+ target: fullScreenButton
+ anchors.right: parent.right
+ }
+ } // Item
+
+ LayoutItemProxy {
+ target: playbackSeekControl
+ Layout.topMargin: 16
+ Layout.bottomMargin: 16
+ }
+ }
+ } // Frame frame
+
+ Frame {
+ id: portraitLayout
+ anchors.fill: parent
+ padding: 32
+ topPadding: 28
+ visible: !landscapePlaybackControls
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 16
+
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ target: loopButton
+ anchors.right: cbProxy.left
+ anchors.rightMargin: 16
+ }
+
+ LayoutItemProxy {
+ id: cbProxy
+ target: controlButtons
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ LayoutItemProxy {
+ target: audioControl
+ anchors.left: cbProxy.right
+ anchors.leftMargin: 16
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ id: fdbProxy_
+ target: fileDialogButton
+ anchors.left: parent.left
+ }
+
+ LayoutItemProxy {
+ target: openUrlButton
+ anchors.left: fdbProxy_.right
+ anchors.leftMargin: 12
+ }
+
+ LayoutItemProxy {
+ target: settingsButton
+ anchors.right: fbProxy_.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: fbProxy_
+ target: fullScreenButton
+ anchors.right: parent.right
+ }
+ }
+
+ LayoutItemProxy {
+ target: playbackSeekControl
+ Layout.topMargin: 8
+ Layout.bottomMargin: 8
+ }
+ }
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml b/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml
new file mode 100644
index 000000000..c94d844f8
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtMultimedia
+
+Item {
+ id: seekController
+ required property MediaPlayer mediaPlayer
+ property alias busy: slider.pressed
+
+ implicitHeight: 20
+
+ function formatToMinutes(milliseconds) {
+ const min = Math.floor(milliseconds / 60000)
+ const sec = ((milliseconds - min * 60000) / 1000).toFixed(1)
+ return `${min}:${sec.padStart(4, 0)}`
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ spacing: 22
+
+ //! [0]
+ Text {
+ id: currentTime
+ Layout.preferredWidth: 45
+ text: seekController.formatToMinutes(seekController.mediaPlayer.position)
+ horizontalAlignment: Text.AlignLeft
+ font.pixelSize: 11
+ }
+ //! [0]
+
+ Slider {
+ id: slider
+ Layout.fillWidth: true
+ //! [2]
+ enabled: seekController.mediaPlayer.seekable
+ value: seekController.mediaPlayer.position / seekController.mediaPlayer.duration
+ //! [2]
+ onMoved: seekController.mediaPlayer.setPosition(value * seekController.mediaPlayer.duration)
+ }
+
+ //! [1]
+ Text {
+ id: remainingTime
+ Layout.preferredWidth: 45
+ text: seekController.formatToMinutes(seekController.mediaPlayer.duration - seekController.mediaPlayer.position)
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: 11
+ }
+ //! [1]
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml b/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml
new file mode 100644
index 000000000..0f24c8503
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml
@@ -0,0 +1,204 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtMultimedia
+
+Popup {
+ id: settingsController
+ focus: true
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
+
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+
+ required property MetadataInfo metadataInfo
+ required property MediaPlayer mediaPlayer
+ required property TracksInfo audioTracksInfo
+ required property TracksInfo videoTracksInfo
+ required property TracksInfo subtitleTracksInfo
+
+ property int vPadding: 20
+ property int hPadding: 26
+ property bool landscapeSettingsPopup: root.width >= settingsLayout.width + metadataLayout.width + 2 * hPadding + 20 + 24
+
+ padding: {
+ top: vPadding
+ bottom: vPadding
+ left: hPadding
+ right: hPadding
+ }
+
+ Flickable {
+ id: flickable
+ implicitWidth: mainLayout.width
+ implicitHeight: landscapeSettingsPopup ? 200 : 340
+ contentWidth: mainLayout.width
+ contentHeight: mainLayout.height
+ flickableDirection: Flickable.VerticalFlick
+ clip: true
+
+ GridLayout {
+ id: mainLayout
+
+ columns: landscapeSettingsPopup ? 2 : 1
+ columnSpacing: 24
+ rowSpacing: 24
+
+ ColumnLayout {
+ id: settingsLayout
+ spacing: 16
+ Layout.alignment: Qt.AlignTop
+
+ Label {
+ id: settingsLabel
+ text: qsTr("Settings")
+ font.pixelSize: 16
+ font.bold: true
+ }
+
+ GridLayout {
+ id: gridLayout
+ columns: 2
+ rowSpacing: 16
+ columnSpacing: 16
+
+ component CustomComboBox: ComboBox {
+ required property TracksInfo tracksInfo
+
+ model: tracksInfo.model
+ enabled: model.count > 0
+ textRole: "data"
+ currentIndex: model.count > 0 ? 0 : -1
+
+ onActivated: {
+ //! [1]
+ settingsController.mediaPlayer.pause()
+ tracksInfo.selectedTrack = currentIndex
+ settingsController.mediaPlayer.play()
+ //! [1]
+ }
+ }
+
+ Label {
+ text: qsTr("Playback Speed")
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ ComboBox {
+ id: rateCb
+ model: ["0.25", "0.5", "0.75", "Normal", "1.25", "1.5", "1.75", "2"]
+ currentIndex: 3
+
+ onCurrentIndexChanged: {
+ //! [0]
+ settingsController.mediaPlayer.playbackRate = (currentIndex + 1) * 0.25
+ //! [0]
+ }
+ }
+
+ Label {
+ text: qsTr("Audio Tracks")
+ enabled: audioCb.enabled
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: audioCb
+ tracksInfo: settingsController.audioTracksInfo
+
+ }
+
+ Label {
+ text: qsTr("Video Tracks")
+ enabled: videoCb.enabled
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: videoCb
+ tracksInfo: settingsController.videoTracksInfo
+ }
+
+ Label {
+ text: qsTr("Subtitle Tracks")
+ enabled: subtitlesCb.enabled
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: subtitlesCb
+ tracksInfo: settingsController.subtitleTracksInfo
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: metadataLayout
+ spacing: 16
+ Layout.alignment: Qt.AlignTop
+
+ Label {
+ id: metadataLabel
+ text: qsTr("Metadata")
+ font.pixelSize: 16
+ font.bold: true
+ }
+
+ Rectangle {
+ id: metadataRect
+ implicitWidth: 240
+ implicitHeight: metadataList.height
+ border.color: "#8E8E93"
+ radius: 6
+
+ Column {
+ id: metadataList
+ visible: settingsController.metadataInfo.count > 0
+
+ padding: 10
+ Repeater {
+ Row {
+ spacing: metadataList.padding
+ Text {
+ text: model.name
+ font.bold: true
+ width: (metadataRect.width - 3 * metadataList.padding) / 2
+ horizontalAlignment: Text.AlignRight
+ wrapMode: Text.WordWrap
+ font.pixelSize: 12
+ }
+
+ Text {
+ text: model.value
+ width: (metadataRect.width - 3 * metadataList.padding) / 2
+ horizontalAlignment: Text.AlignLeft
+ anchors.verticalCenter: parent.verticalCenter
+ wrapMode: Text.WrapAnywhere
+ font.pixelSize: 12
+ }
+ }
+ model: settingsController.metadataInfo.metadata
+ }
+ }
+
+ Text {
+ id: metadataNoList
+ visible: settingsController.metadataInfo.count === 0
+ anchors.centerIn: parent
+ text: qsTr("No metadata present")
+ font.pixelSize: 14
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml b/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml
new file mode 100644
index 000000000..a2ae96f02
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ property alias model: model
+ property int selectedTrack: 0
+
+ function read(tracks, key = 0) {
+ // language is the 6th index in the enum QMediaMetaData::Key
+ model.clear()
+
+ if (!tracks)
+ return
+
+ tracks.forEach((metadata, index) => {
+ const data = metadata.stringValue(key)
+ const label = data ? data : qsTr("track ") + (index + 1)
+ model.append({data: label, index: index})
+ })
+ }
+
+ ListModel { id: model }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml b/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml
new file mode 100644
index 000000000..5bc304178
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtMultimedia
+
+
+Popup {
+ id: popupController
+ width: Math.min(500, root.width - 40)
+
+ required property MediaPlayer mediaPlayer
+
+ function loadUrl(url) {
+ popupController.mediaPlayer.stop()
+ popupController.mediaPlayer.source = url
+ popupController.mediaPlayer.play()
+ }
+
+ RowLayout {
+ id: rowOpenUrl
+ anchors.fill: parent
+ Label {
+ text: qsTr("URL:");
+ }
+
+ TextField {
+ id: urlText
+ Layout.fillWidth: true
+ focus: true
+
+ placeholderText: qsTr("Enter text here...")
+ wrapMode: TextInput.WrapAnywhere
+
+ Keys.onReturnPressed: {
+ popupController.loadUrl(text)
+ urlText.text = ""
+ popupController.close()
+ }
+ }
+
+ Button {
+ text: qsTr("Load")
+ enabled: urlText.text !== ""
+ onClicked: {
+ popupController.loadUrl(urlText.text)
+ urlText.text = ""
+ popupController.close()
+ }
+ }
+ }
+ onOpened: { popupController.forceActiveFocus() }
+}
diff --git a/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg b/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg
deleted file mode 100644
index a53e85803..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif b/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif
deleted file mode 100644
index 779713332..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif b/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif
deleted file mode 100644
index b85ce61c5..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif b/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif
deleted file mode 100644
index 5ac07ccc2..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png b/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png
new file mode 100644
index 000000000..857e70abd
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/meta-data.png b/examples/multimedia/video/mediaplayer/doc/images/meta-data.png
deleted file mode 100644
index a96b8f7e1..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/meta-data.png
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg b/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg
deleted file mode 100644
index 2d530a46d..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif b/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif
deleted file mode 100644
index 355f7af86..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif b/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif
deleted file mode 100644
index c71e577d7..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg b/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg
deleted file mode 100644
index 94e9c04a0..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/settings.png b/examples/multimedia/video/mediaplayer/doc/images/settings.png
new file mode 100644
index 000000000..5cf175795
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/doc/images/settings.png
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg b/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg
deleted file mode 100644
index ab7342d5d..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/url.png b/examples/multimedia/video/mediaplayer/doc/images/url.png
deleted file mode 100644
index 7bd4ff9f4..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/url.png
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf b/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf
deleted file mode 100644
index 3faf2054f..000000000
--- a/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf
+++ /dev/null
@@ -1,4 +0,0 @@
-{HTML.extraimages,DocBook.extraFiles,qhp.QtMultimedia.extraFiles} += \
- images/OqosZsDqvzQ.jpg \
- images/sf_yv01UtIw.jpg \
- images/nHrBbW0H-pc.jpg
diff --git a/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc b/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
index cec04fad8..1cc681f8f 100644
--- a/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
+++ b/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
@@ -6,176 +6,146 @@
\title QML Media Player Example
\ingroup multimedia_examples
\ingroup video_examples_qml
- \examplecategory {Multimedia}
- \brief Playing audio and video using Qt Quick.
+ \examplecategory {Graphics & Multimedia}
+ \brief Playing audio and video using the QML \c MediaPlayer type.
\meta {tag} {quick}
\meta {tag} {player}
- \image qmlmediaplayer.jpg
+ \image mediaplayer.png
This example demonstrates a simple multimedia player that can play
audio and video files using various codecs.
\include examples-run.qdocinc
- \section1 Overview
- At its core this is a QML application, see
- \l{Getting Started Programming with Qt Quick} for information specific to
- that. This documentation is focused on how this example utilizes the
- \l{Qt Multimedia QML Types}.
-
- \image architecture-overview.gif
-
- \section1 Using MediaPlayer and VideoOutput
- In \c main.qml a MediaPlayer instance is connected to a VideoOutput to
- play back the video:
-
- \quotefromfile video/mediaplayer/main.qml
- \skipto MediaPlayer
- \printuntil videoOutput: videoOutput
-
- \c videoOutput is declared like so:
-
- \skipto VideoOutput {
- \printto MetadataInfo {
-
- \section1 PlayerMenuBar
- \image PlayerMenuBar.gif
- This QML type handles media selection from a url or local file, exiting the
- application, viewing meta data, and the selection of available video, audio
- or subtitle tracks.
-
- \quotefromfile video/mediaplayer/PlayerMenuBar.qml
- Accessing the mediaPlayer object is done through properties:
- \skipto required property
- \printuntil TracksInfo subtitleTracksInfo
-
- \section2 fileDialog
- A FileDialog, \c fileDialog, is created with an \c onAccepted function that
- will stop \c mediaPlayer, load the source by setting the
- \l{MediaPlayer::source}{source} property and then play it automatically:
- \skipto FileDialog
- \printto MenuBar
-
- This is triggered in the Menu \c File, which is a child of the MenuBar:
- \skipto MenuBar
- \printto }
-
- \section2 loadUrl
- \image url.png
- While \c urlPopup handles prompting and capturing a url, it is the \c loadUrl
- function that interacts with \c mediaPlayer like so:
- \quotefromfile video/mediaplayer/PlayerMenuBar.qml
- \skipto function loadUrl
- \printto function closeOverlays(){
-
- \section2 Getting meta data
- \image meta-data.png
-
- In the declaration of \c mediaPlayer, in \c main.qml, there is the function
- \c updateMetadata():
-
- \quotefromfile video/mediaplayer/main.qml
- \skipto function updateMetadata(
- \printto }
-
- It is called in the following places:
- \skipto onMetaDataChanged:
- \printto onActiveTracksChanged: { updateMetadata() }
-
- Reading MetaData is done by the \c MetadataInfo type's \c read() function
- \quotefromfile video/mediaplayer/MetadataInfo.qml
- \skipto function read(metadata) {
- \printto ListModel
-
- The information is displayed via an \l[QML]{Overlay} item.
-
- \section2 Tracks information and control
- \youtube OqosZsDqvzQ
- This is defined in \c TracksInfo.qml and reading available tracks is done in
- a similar way to \c MetadataInfo:
- \quotefromfile video/mediaplayer/TracksInfo.qml
- \skipto function read(metadata
- \printto ListModel
-
- To set a track, the property \c selectedTrack is set like so:
- \skipto ListView
- \printto Text
-
- The \c onSelectectedTrackChanged signal, in each relevant \c TracksInfo
- instance in \c main.qml, is what makes changes to \c mediaPlayer like so:
- \quotefromfile video/mediaplayer/main.qml
- \skipto id: audioTracksInfo
- \printuntil audioTracksInfo.selectedTrack
-
- \section1 playbackControlPanel
- \image playbackControlPanel.gif
-
- This item has controls for \l{Playback control}, \l{Play Pause Stop},
- \l{Playback rate control} and \l{Playback seek control}.
-
- \section2 Playback control
- This qml type handles media playback and interacts with the MediaPlayer in
- \c main.qml.
-
- Here are the property definitions.
- \quotefromfile video/mediaplayer/PlaybackControl.qml
- \skipto required property
- \printto property alias
-
- Connections:
- \skipuntil Connections
- \printto HoverHandler
-
- \section2 Play Pause Stop
- \image play-pause-stop.gif
- \l{MediaPlayer::play()}{Play}, \l{MediaPlayer::stop()}{stop} and
- \l{MediaPlayer::pause()}{pause} interactions with the MediaPlayer object
- are done like so:
- \skipto RoundButton
- \printto Item {
-
- Playback states done using \l{MediaPlayer::playbackState}{playbackstate}
- like so:
- \skipuntil states:
- \printuntil ]
-
-
- \section2 Playback seek control
-
- \youtube sf_yv01UtIw
-
- Defined in \c PlaybackSeekControl.qml, this component comprises of an item
- with a Text, \c mediaTime, and \l[QML]{Slider}, \c mediaSlider, in a RowLayout.
-
- \c mediaTime uses MediaPlayer's \l{MediaPlayer::position}{position} property
- like so:
- \quotefromfile video/mediaplayer/PlaybackSeekControl.qml
- \skipto Text {
- \printto Slider
-
- \c mediaSlider uses the MediaPlayer \l{MediaPlayer::seekable}{seekable},
- \l{MediaPlayer::duration}{duration}, and \l{MediaPlayer::position}{position}
- properties like so:
- \skipto Slider
- \printuntil }
-
- \section2 Playback rate control
- \youtube nHrBbW0H-pc
-
- This type is defined in \c PlaybackRateControl.qml like so:
-
- \quotefromfile video/mediaplayer/PlaybackRateControl.qml
- \skipto Slider
- \printuntil text:
+ \section1 Instantiating the MediaPlayer
+ The entry point for the QML code in this example is \c Main.qml. Here
+ an \c ApplicationWindow is created and properties such as the \c id, \c title,
+ \c width and \c height are set.
- \section2 Audio control
- \image audio-control.gif
- This type is defined in \c AudioControl.qml, and utilizes the
- \l{AudioOutput::muted}{muted} and \l{AudioOutput::volume}{volume} properties
- of the AudioOutput instantiated within the MediaPlayer, which is
- instantiated in \c{main.qml}.
-
- \quotefromfile video/mediaplayer/AudioControl.qml
- \skipto required
- \printuntil value: 100.0
+ \snippet video/mediaplayer/Main.qml 0
+
+ Next the \c MediaPlayer is created and the two properties that are responsible for the video and
+ audio output are defined.
+ Firstly, \c videoOutput which renders the video viewfinder and secondly \c audioOutput which provides
+ the audio output for the player.
+
+ \snippet video/mediaplayer/Main.qml 1
+ \dots
+ \snippet video/mediaplayer/Main.qml 2
+ \dots
+ \snippet video/mediaplayer/Main.qml 3
+
+ The \c visible property of the \c VideoOutput type is set to \c true when the \c mediaStatus
+ property of the \c MediaPlayer is greater than 0. \c mediaStatus is of enumeration type
+ and is equal to 0 when \e {No media has been set}, and greater than 0 otherwise. Therefore,
+ the \c VideoOutput is visible when media has been set.
+
+ The \c MediaPlayer type has a signal property called \c onErrorOccurred that can be
+ overridden specifically to handle errors. In this case the signal opens a \c MessageDialog using
+ the method \c {open()} and sets its \c text property to a \c MediaPlayer property called
+ \c errorString.
+
+ \snippet video/mediaplayer/Main.qml 4
+
+ \section1 Playback Controls
+
+ In order to have a useable media player, there needs to be an interface to control the playback.
+ This is created in its own component file, \c PlaybackControl.qml, and instantiated in
+ \c Main.qml.
+
+ \snippet video/mediaplayer/Main.qml 5
+ \dots
+ \snippet video/mediaplayer/Main.qml 6
+
+ When created, objects are forwarded to this type such as track information,
+ metadata information and the \c MediaPlayer object itself. In \c PlaybackControl.qml,
+ each one of these objects have a \c {required property}, meaning that these properties must
+ be set when the \c PlaybackControl object is created.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 0
+
+ These playback controls can be broken down into sections. In the top left of the panel lies
+ a collection of buttons used to open a file, either by selecting a file from a file explorer or
+ entering a URL. The file is loaded into the \c MediaPlayer by setting the \c source property.
+ Both buttons are instantiated using a \c CustomButton \c {custom component}.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 1
+
+ Three buttons are created and centered on this panel, handling play, pause and seeking ten
+ seconds backwards or forwards. The media is played and paused using the methods \c {play()}
+ and \c {pause()}, respectively. To know when to draw a play or pause button, the \c playbackState
+ property is queried. For example, when it is equal to the enum value \c MediaPlayer.PlayingState
+ then the pause button is drawn.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 2
+
+ To navigate ten seconds forward or backwards, the \c position of the \c MediaPlayer type is
+ incremented by 10,000 milliseconds and set using the method \c {setPosition()}.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 3
+
+ \section1 Playback Seeking and Audio
+
+ In \c {PlaybackControl.qml}, an \c AudioControl and a \c PlaybackSeekControl type are instantiated.
+ These are both defined in their own component file and are responsible for volume control and
+ playback seeking, respectively. The \c AudioControl type defines a button to mute and a \c Slider,
+ from \c {QtQuick Controls}, to set the volume of the player. Both of these attributes are exposed by
+ defining a \c mute and \c volume property and are accessed from the \c AudioOutput definition in
+ \c {Main.qml}.
+
+ \snippet video/mediaplayer/controls/AudioControl.qml 0
+
+ The \c PlaybackSeekControl uses a \c RowLayout containing a \c Slider with a \c Text item either side.
+ The two \c Text items display the current time and the remaining time of the media being played. These
+ are both calculated using two properties of the \c MediaPlayer type, \c {position}, which gives the
+ current playback position in milliseconds, and \c {duration}, which gives the duration of the media
+ in milliseconds.
+
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 0
+ \dots
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 1
+
+ The \c Slider is only enabled when the media player is seekable and not, for example, live media.
+ The \c MediaPlayer type has a property for this called \c seekable. The \c value of the \c Slider
+ is calculated using the \c position and \c duration properties of the \c MediaPlayer.
+
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 2
+
+ \section1 Metadata and Track Information
+
+ The \c PlaybackControl type instantiates a \c SettingsPopup, which contains information about the
+ metadata of the currently loaded media and track selection, as well as the ability to update the
+ playback rate. This \c Popup is defined in \c SettingsPopup.qml.
+
+ \image settings.png
+
+ The metadata is contained in its own component file, \c MetadataInfo.qml. It contains a \c ListModel,
+ a function to clear it, \c {clear()}, and a function to populate it, \c {read(MediaMetadata metadata)}.
+ The \c {read(MediaMetadata metadata)} function takes as a parameter an object of type \c MediaMetaData,
+ and navigates its key-value structure to extract its data into the \c model of the \c {ListView}. The
+ methods used to do this are \c {keys()}, which returns all the keys of the \c MediaMetaData, and
+ {stringValue(Key key)}, which returns the \c value for a given \c key.
+
+ \snippet video/mediaplayer/controls/MetadataInfo.qml 0
+
+ The data is then displayed in \c {SettingsPopup.qml} in a \c ListView type. The \c delegate of this
+ \c ListView is a row of two \c Text items, corresponding to the key-value pairs abstracted from the
+ \c MediaMetaData item.
+
+ On the other side of the \c Popup there is playback rate controls and track selection for audio, video and
+ subtitles. The playback rate is chosen from a \c ComboBox and set using the property \c playbackRate.
+
+ \snippet video/mediaplayer/controls/SettingsPopup.qml 0
+
+ The type called \c TracksInfo, defined in \c {TracksInfo.qml}, contains the data about the tracks.
+ More specifically, a \c ListModel containing the titles of the tracks, or for subtitles specifically, the
+ langauges. This information is populated in \c Main.qml by calling the \c {read(MediaMetadata mediaMetadata)}
+ function defined in the \c TracksInfo type.
+
+ \snippet video/mediaplayer/Main.qml 6
+
+ The \c model defined in \c TracksInfo is then queried in the \c {ComboBox}es in the
+ \c SettingsPopup to select the current track.
+
+ \snippet video/mediaplayer/controls/SettingsPopup.qml 1
*/
diff --git a/examples/multimedia/video/mediaplayer/images/backward10.svg b/examples/multimedia/video/mediaplayer/images/backward10.svg
new file mode 100644
index 000000000..9166343bd
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/backward10.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 13.2001C4 14.7428 4.46919 16.2508 5.34824 17.5335C6.22729 18.8162 7.47672 19.816 8.93853 20.4063C10.4003 20.9967 12.0089 21.1511 13.5607 20.8502C15.1126 20.5492 16.538 19.8063 17.6569 18.7155C18.7757 17.6247 19.5376 16.2348 19.8463 14.7218C20.155 13.2088 19.9965 11.6404 19.391 10.2152C18.7855 8.78993 17.7602 7.57175 16.4446 6.71468C15.129 5.85761 13.5822 5.40015 12 5.40015" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M12.6152 3L10.1537 5.39999L12.6152 7.79997" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.16406 16V11.4766H9.09375L7.71094 12.4375V11.375L9.16797 10.3633H10.3438V16H9.16406ZM14.0469 16.1445C13.5833 16.1445 13.1836 16.0247 12.8477 15.7852C12.5143 15.543 12.2578 15.2005 12.0781 14.7578C11.8984 14.3151 11.8086 13.7904 11.8086 13.1836V13.1758C11.8086 12.5664 11.8984 12.0417 12.0781 11.6016C12.2578 11.1589 12.5143 10.8177 12.8477 10.5781C13.1836 10.3385 13.5833 10.2188 14.0469 10.2188C14.513 10.2188 14.9128 10.3385 15.2461 10.5781C15.5794 10.8177 15.8359 11.1589 16.0156 11.6016C16.1953 12.0417 16.2852 12.5664 16.2852 13.1758V13.1836C16.2852 13.7904 16.1953 14.3151 16.0156 14.7578C15.8359 15.2005 15.5794 15.543 15.2461 15.7852C14.9128 16.0247 14.513 16.1445 14.0469 16.1445ZM14.0469 15.2109C14.2708 15.2109 14.4596 15.1302 14.6133 14.9688C14.7695 14.8073 14.888 14.5755 14.9688 14.2734C15.0521 13.9714 15.0938 13.6081 15.0938 13.1836V13.1758C15.0938 12.7487 15.0521 12.3854 14.9688 12.0859C14.888 11.7839 14.7695 11.5534 14.6133 11.3945C14.4596 11.2331 14.2708 11.1523 14.0469 11.1523C13.8255 11.1523 13.6367 11.2331 13.4805 11.3945C13.3268 11.5534 13.2083 11.7839 13.125 12.0859C13.0443 12.3854 13.0039 12.7487 13.0039 13.1758V13.1836C13.0039 13.6081 13.0443 13.9714 13.125 14.2734C13.2083 14.5755 13.3268 14.8073 13.4805 14.9688C13.6367 15.1302 13.8255 15.2109 14.0469 15.2109Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/ff.svg b/examples/multimedia/video/mediaplayer/images/ff.svg
new file mode 100644
index 000000000..7c907c4fa
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/ff.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.19465 16.8817C5.5198 17.0557 5.91435 17.0366 6.2212 16.832L12.2212 12.832C12.4994 12.6466 12.6665 12.3344 12.6665 12C12.6665 11.6656 12.4994 11.3534 12.2212 11.1679L6.2212 7.16795C5.91435 6.96338 5.5198 6.9443 5.19465 7.11832C4.86949 7.29234 4.6665 7.6312 4.6665 8V16C4.6665 16.3688 4.86949 16.7077 5.19465 16.8817ZM6.6665 14.1315V9.86852L9.86373 12L6.6665 14.1315Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1946 16.8817C13.5198 17.0557 13.9143 17.0366 14.2212 16.832L20.2212 12.832C20.4994 12.6466 20.6665 12.3344 20.6665 12C20.6665 11.6656 20.4994 11.3534 20.2212 11.1679L14.2212 7.16795C13.9143 6.96338 13.5198 6.9443 13.1946 7.11832C12.8695 7.29234 12.6665 7.6312 12.6665 8V16C12.6665 16.3688 12.8695 16.7077 13.1946 16.8817ZM14.6665 14.1315V9.86852L17.8637 12L14.6665 14.1315Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/forward10.svg b/examples/multimedia/video/mediaplayer/images/forward10.svg
new file mode 100644
index 000000000..190a5aaab
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/forward10.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M20 13.2001C20 14.7428 19.5308 16.2508 18.6518 17.5335C17.7727 18.8162 16.5233 19.816 15.0615 20.4063C13.5997 20.9967 11.9911 21.1511 10.4393 20.8502C8.88743 20.5492 7.46197 19.8063 6.34315 18.7155C5.22433 17.6247 4.4624 16.2348 4.15372 14.7218C3.84504 13.2088 4.00347 11.6404 4.60897 10.2152C5.21447 8.78993 6.23985 7.57175 7.55544 6.71468C8.87103 5.85761 10.4178 5.40015 12 5.40015" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M11.3838 3L13.8453 5.39999L11.3838 7.79997" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.16406 16V11.4766H9.09375L7.71094 12.4375V11.375L9.16797 10.3633H10.3438V16H9.16406ZM14.0469 16.1445C13.5833 16.1445 13.1836 16.0247 12.8477 15.7852C12.5143 15.543 12.2578 15.2005 12.0781 14.7578C11.8984 14.3151 11.8086 13.7904 11.8086 13.1836V13.1758C11.8086 12.5664 11.8984 12.0417 12.0781 11.6016C12.2578 11.1589 12.5143 10.8177 12.8477 10.5781C13.1836 10.3385 13.5833 10.2188 14.0469 10.2188C14.513 10.2188 14.9128 10.3385 15.2461 10.5781C15.5794 10.8177 15.8359 11.1589 16.0156 11.6016C16.1953 12.0417 16.2852 12.5664 16.2852 13.1758V13.1836C16.2852 13.7904 16.1953 14.3151 16.0156 14.7578C15.8359 15.2005 15.5794 15.543 15.2461 15.7852C14.9128 16.0247 14.513 16.1445 14.0469 16.1445ZM14.0469 15.2109C14.2708 15.2109 14.4596 15.1302 14.6133 14.9688C14.7695 14.8073 14.888 14.5755 14.9688 14.2734C15.0521 13.9714 15.0938 13.6081 15.0938 13.1836V13.1758C15.0938 12.7487 15.0521 12.3854 14.9688 12.0859C14.888 11.7839 14.7695 11.5534 14.6133 11.3945C14.4596 11.2331 14.2708 11.1523 14.0469 11.1523C13.8255 11.1523 13.6367 11.2331 13.4805 11.3945C13.3268 11.5534 13.2083 11.7839 13.125 12.0859C13.0443 12.3854 13.0039 12.7487 13.0039 13.1758V13.1836C13.0039 13.6081 13.0443 13.9714 13.125 14.2734C13.2083 14.5755 13.3268 14.8073 13.4805 14.9688C13.6367 15.1302 13.8255 15.2109 14.0469 15.2109Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/link.svg b/examples/multimedia/video/mediaplayer/images/link.svg
new file mode 100644
index 000000000..6ce1b983d
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/link.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 16C11 16.5523 10.5523 17 10 17H7C5.61667 17 4.4375 16.5125 3.4625 15.5375C2.4875 14.5625 2 13.3833 2 12C2 10.6167 2.4875 9.4375 3.4625 8.4625C4.4375 7.4875 5.61667 7 7 7H10C10.5523 7 11 7.44772 11 8C11 8.55228 10.5523 9 10 9H7C6.16667 9 5.45833 9.29167 4.875 9.875C4.29167 10.4583 4 11.1667 4 12C4 12.8333 4.29167 13.5417 4.875 14.125C5.45833 14.7083 6.16667 15 7 15H10C10.5523 15 11 15.4477 11 16ZM9 13C8.44772 13 8 12.5523 8 12C8 11.4477 8.44772 11 9 11H15C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13H9ZM14 17C13.4477 17 13 16.5523 13 16C13 15.4477 13.4477 15 14 15H17C17.8333 15 18.5417 14.7083 19.125 14.125C19.7083 13.5417 20 12.8333 20 12C20 11.1667 19.7083 10.4583 19.125 9.875C18.5417 9.29167 17.8333 9 17 9H14C13.4477 9 13 8.55228 13 8C13 7.44772 13.4477 7 14 7H17C18.3833 7 19.5625 7.4875 20.5375 8.4625C21.5125 9.4375 22 10.6167 22 12C22 13.3833 21.5125 14.5625 20.5375 15.5375C19.5625 16.5125 18.3833 17 17 17H14Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/loop.svg b/examples/multimedia/video/mediaplayer/images/loop.svg
new file mode 100644
index 000000000..8161ca867
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/loop.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 17.5C5 17.5 7 17.5 5 17.5C3 17.5 3 17.5 3 15.5C3 13.5 3 8 3 6C3 4 3 4 5 4C7 4 17 4 19 4C21 4 21 4 21 6C21 8 21 13.5 21 15.5C21 17.5 21 17.5 19 17.5C17 17.5 11 17.5 11 17.5M11 17.5L13 15M11 17.5L13 20" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/more.svg b/examples/multimedia/video/mediaplayer/images/more.svg
new file mode 100644
index 000000000..fa8bddec8
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/more.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z" fill="black"/>
+<path d="M22 12C22 13.1046 21.1046 14 20 14C18.8954 14 18 13.1046 18 12C18 10.8954 18.8954 10 20 10C21.1046 10 22 10.8954 22 12Z" fill="black"/>
+<path d="M6 12C6 13.1046 5.10457 14 4 14C2.89543 14 2 13.1046 2 12C2 10.8954 2.89543 10 4 10C5.10457 10 6 10.8954 6 12Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/Mute_Icon.svg b/examples/multimedia/video/mediaplayer/images/mute.svg
index b66b6d311..b66b6d311 100644
--- a/examples/multimedia/video/mediaplayer/Mute_Icon.svg
+++ b/examples/multimedia/video/mediaplayer/images/mute.svg
diff --git a/examples/multimedia/video/mediaplayer/images/open_new.svg b/examples/multimedia/video/mediaplayer/images/open_new.svg
new file mode 100644
index 000000000..6c62237f1
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/open_new.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="File /open_new">
+<path id="Layer01" d="M2 5C2 3.34315 3.34315 2 5 2H11C11.5523 2 12 2.44772 12 3C12 3.55228 11.5523 4 11 4H5C4.44772 4 4 4.44772 4 5V19C4 19.5523 4.44772 20 5 20H19C19.5523 20 20 19.5523 20 19V13.125C20 12.5727 20.4477 12.125 21 12.125C21.5523 12.125 22 12.5727 22 13.125V19C22 20.6569 20.6569 22 19 22H5C3.34315 22 2 20.6569 2 19V5Z" fill="#0D0D0D"/>
+<path id="Layer02" fill-rule="evenodd" clip-rule="evenodd" d="M15 4C14.4477 4 14 3.55228 14 3C14 2.44772 14.4477 2 15 2H21C21.5523 2 22 2.44772 22 3V9C22 9.55228 21.5523 10 21 10C20.4477 10 20 9.55228 20 9V5.41421L8.76961 16.6446C8.37908 17.0351 7.74592 17.0351 7.35539 16.6446C6.96487 16.2541 6.96487 15.6209 7.35539 15.2304L18.5858 4H15Z" fill="#0D0D0D"/>
+</g>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/pause_symbol.svg b/examples/multimedia/video/mediaplayer/images/pause_symbol.svg
new file mode 100644
index 000000000..bdf6a9047
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/pause_symbol.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.3335 7C8.3335 6.44772 8.66928 6 9.0835 6H10.5835C10.9977 6 11.3335 6.44772 11.3335 7V17C11.3335 17.5523 10.9977 18 10.5835 18H9.0835C8.66928 18 8.3335 17.5523 8.3335 17V7Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.3335 7C13.3335 6.44772 13.6693 6 14.0835 6H15.5835C15.9977 6 16.3335 6.44772 16.3335 7V17C16.3335 17.5523 15.9977 18 15.5835 18H14.0835C13.6693 18 13.3335 17.5523 13.3335 17V7Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/play_symbol.svg b/examples/multimedia/video/mediaplayer/images/play_symbol.svg
new file mode 100644
index 000000000..edb567226
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/play_symbol.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.8182 12L6.89091 6.14985L6.89091 17.8501L16.8182 12ZM18.2 13.4143C19.2667 12.7857 19.2667 11.2143 18.2 10.5857L7.4 4.22123C6.33333 3.59264 5 4.37838 5 5.63555L5 18.3644C5 19.6216 6.33333 20.4074 7.4 19.7788L18.2 13.4143Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/rewind.svg b/examples/multimedia/video/mediaplayer/images/rewind.svg
new file mode 100644
index 000000000..e9b34122c
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/rewind.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.4719 16.8817C19.1467 17.0557 18.7522 17.0366 18.4453 16.832L12.4453 12.832C12.1671 12.6466 12 12.3344 12 12C12 11.6656 12.1671 11.3534 12.4453 11.1679L18.4453 7.16795C18.7522 6.96338 19.1467 6.9443 19.4719 7.11832C19.797 7.29234 20 7.6312 20 8V16C20 16.3688 19.797 16.7077 19.4719 16.8817ZM18 14.1315V9.86852L14.8028 12L18 14.1315Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4719 16.8817C11.1467 17.0557 10.7522 17.0366 10.4453 16.832L4.4453 12.832C4.1671 12.6466 4 12.3344 4 12C4 11.6656 4.1671 11.3534 4.4453 11.1679L10.4453 7.16795C10.7522 6.96338 11.1467 6.9443 11.4719 7.11832C11.797 7.29234 12 7.6312 12 8V16C12 16.3688 11.797 16.7077 11.4719 16.8817ZM10 14.1315V9.86852L6.80278 12L10 14.1315Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/settings.svg b/examples/multimedia/video/mediaplayer/images/settings.svg
new file mode 100644
index 000000000..3a13ccae1
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/settings.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84998 4C8.84998 2.89543 9.74541 2 10.85 2H13.1222C14.2268 2 15.1222 2.89543 15.1222 4V4.59509C15.1229 4.59732 15.125 4.6023 15.1306 4.61031C15.1434 4.62838 15.1691 4.65242 15.2077 4.66935C15.852 4.95163 16.4515 5.31609 16.9931 5.74921C17.0261 5.77554 17.0594 5.787 17.0815 5.78982C17.0912 5.79106 17.0966 5.79055 17.0989 5.79008L17.9363 5.33741C18.8691 4.8332 20.0332 5.14455 20.5897 6.04706L21.6744 7.80604C22.2777 8.78435 21.9342 10.0686 20.9231 10.6152L19.9875 11.1209C19.9859 11.1226 19.9824 11.1269 19.9781 11.1358C19.9683 11.156 19.9595 11.1904 19.9636 11.2327C19.9877 11.4855 20 11.7414 20 12C20 12.2586 19.9877 12.5145 19.9636 12.7673C19.9595 12.8096 19.9683 12.844 19.9781 12.8642C19.9824 12.8732 19.9859 12.8774 19.9875 12.8791L20.923 13.3848C21.9341 13.9314 22.2776 15.2157 21.6743 16.194L20.5897 17.9529C20.0331 18.8554 18.869 19.1668 17.9362 18.6626L17.0989 18.2099C17.0966 18.2095 17.0912 18.209 17.0815 18.2102C17.0594 18.213 17.026 18.2245 16.9931 18.2508C16.4515 18.6839 15.852 19.0484 15.2077 19.3306C15.1691 19.3476 15.1434 19.3716 15.1306 19.3897C15.125 19.3977 15.1229 19.4027 15.1222 19.4049V20C15.1222 21.1046 14.2268 22 13.1222 22H10.85C9.74541 22 8.84998 21.1046 8.84998 20V19.3928C8.84931 19.3906 8.84725 19.3857 8.84163 19.3777C8.82894 19.3597 8.80351 19.3357 8.76508 19.3187C8.12675 19.0362 7.53272 18.6729 6.99586 18.242C6.96292 18.2156 6.92951 18.2041 6.90737 18.2012C6.89759 18.2 6.8922 18.2005 6.8899 18.201L6.03592 18.6626C5.10317 19.1668 3.93904 18.8554 3.38251 17.9529L2.29784 16.194C1.69457 15.2156 2.03805 13.9314 3.04914 13.3848L4.01099 12.8649C4.01265 12.8632 4.01607 12.859 4.02042 12.85C4.03023 12.8299 4.03897 12.7956 4.03503 12.7534C4.0118 12.5051 3.99995 12.2538 3.99995 12C3.99995 11.7462 4.0118 11.4949 4.03503 11.2466C4.03897 11.2045 4.03022 11.1701 4.02042 11.15C4.01607 11.1411 4.01265 11.1368 4.01098 11.1352L3.04909 10.6152C2.038 10.0686 1.69452 8.78435 2.29779 7.80604L3.38245 6.04707C3.93898 5.14456 5.10312 4.83322 6.03587 5.33742L6.88988 5.79907C6.89218 5.79953 6.89757 5.80005 6.90735 5.79879C6.92949 5.79594 6.9629 5.78441 6.99584 5.75797C7.53271 5.32709 8.12674 4.96379 8.76508 4.68128C8.80351 4.66427 8.82894 4.64029 8.84163 4.62231C8.84725 4.61434 8.84931 4.60938 8.84998 4.60716V4ZM8.85024 4.60578C8.85029 4.60578 8.85026 4.60619 8.85001 4.60703L8.85024 4.60578ZM6.88852 5.79864C6.88854 5.7986 6.88898 5.79869 6.88977 5.79904L6.88852 5.79864ZM4.00988 11.1343C4.00991 11.1342 4.01026 11.1344 4.0109 11.1351L4.00988 11.1343ZM4.00989 12.8658C4.00988 12.8657 4.01021 12.8654 4.01091 12.865L4.00989 12.8658ZM6.88854 18.2014C6.88851 18.2013 6.8889 18.2012 6.88979 18.201L6.88854 18.2014ZM19.9886 12.88C19.9885 12.88 19.9881 12.8797 19.9876 12.8792L19.9886 12.88ZM19.9886 11.12C19.9887 11.12 19.9883 11.1204 19.9876 11.1208L19.9886 11.12ZM17.1003 5.78965C17.1003 5.78968 17.0999 5.78986 17.099 5.79006L17.1003 5.78965ZM15.122 4.5937C15.122 4.59371 15.1221 4.59414 15.1222 4.59496L15.122 4.5937ZM13.1222 4L10.85 4V4.60808C10.85 5.49562 10.2774 6.19908 9.57449 6.51017C9.09654 6.7217 8.65096 6.99408 8.24769 7.31774C7.63839 7.80676 6.73261 7.98755 5.93958 7.55888L5.08481 7.09682L4.00015 8.8558L4.9628 9.37617C5.7538 9.80375 6.09888 10.6574 6.02633 11.4329C6.00889 11.6193 5.99995 11.8085 5.99995 12C5.99995 12.1915 6.00889 12.3807 6.02633 12.5671C6.09889 13.3426 5.75381 14.1963 4.9628 14.6239L4.0002 15.1442L5.08486 16.9032L5.9396 16.4411C6.73263 16.0125 7.63841 16.1933 8.2477 16.6823C8.65097 17.0059 9.09654 17.2783 9.57449 17.4898C10.2774 17.8009 10.85 18.5044 10.85 19.3919V20H13.1222V19.404C13.1222 18.5133 13.6987 17.8083 14.4051 17.4988C14.8875 17.2874 15.3372 17.0142 15.744 16.6889C16.3532 16.2017 17.2575 16.0222 18.0492 16.4501L18.8873 16.9032L19.972 15.1442L19.0357 14.6381C18.2434 14.2098 17.8986 13.3541 17.9726 12.5776C17.9907 12.3878 18 12.1951 18 12C18 11.8049 17.9907 11.6123 17.9726 11.4224C17.8986 10.6459 18.2434 9.79023 19.0357 9.36192L19.972 8.85579L18.8874 7.09681L18.0492 7.54989C17.2575 7.97786 16.3532 7.79836 15.744 7.31116C15.3372 6.98582 14.8875 6.71259 14.4051 6.50124C13.6987 6.19173 13.1222 5.48675 13.1222 4.596L13.1222 4Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/Speaker_Icon.svg b/examples/multimedia/video/mediaplayer/images/speaker.svg
index 723e48b66..723e48b66 100644
--- a/examples/multimedia/video/mediaplayer/Speaker_Icon.svg
+++ b/examples/multimedia/video/mediaplayer/images/speaker.svg
diff --git a/examples/multimedia/video/mediaplayer/images/stop_symbol.svg b/examples/multimedia/video/mediaplayer/images/stop_symbol.svg
new file mode 100644
index 000000000..99f00162f
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/stop_symbol.svg
@@ -0,0 +1,3 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6665 7H7.6665L7.6665 17H17.6665V7ZM7.6665 5C6.56193 5 5.6665 5.89543 5.6665 7V17C5.6665 18.1046 6.56193 19 7.6665 19H17.6665C18.7711 19 19.6665 18.1046 19.6665 17V7C19.6665 5.89543 18.7711 5 17.6665 5H7.6665Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/url.svg b/examples/multimedia/video/mediaplayer/images/url.svg
new file mode 100644
index 000000000..44942911f
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/url.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 10C7 10.5523 6.55228 11 6 11C5.44772 11 5 10.5523 5 10C5 9.44772 5.44772 9 6 9C6.55228 9 7 9.44772 7 10Z" fill="black"/>
+<path d="M7 14C7 14.5523 6.55228 15 6 15C5.44772 15 5 14.5523 5 14C5 13.4477 5.44772 13 6 13C6.55228 13 7 13.4477 7 14Z" fill="black"/>
+<path d="M13 6L9 18" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M18 6L14 18" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/volume.svg b/examples/multimedia/video/mediaplayer/images/volume.svg
new file mode 100644
index 000000000..2cdc4df6b
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/volume.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98759 5.1163C11.1732 3.91192 13.2226 4.75139 13.2226 6.44139V17.5587C13.2226 19.2487 11.1732 20.0882 9.98759 18.8838L6.74406 15.5888C6.72318 15.5675 6.69465 15.5556 6.66488 15.5556H4.33371C3.29051 15.5556 2.44482 14.7099 2.44482 13.6667L2.44482 10.3334C2.44482 9.29016 3.29051 8.44448 4.33371 8.44448H6.66488C6.69465 8.44448 6.72318 8.43253 6.74406 8.41131L9.98759 5.1163ZM11.3101 6.32964C11.2992 6.3316 11.2809 6.33666 11.2545 6.36344L8.011 9.65845C7.65595 10.0191 7.171 10.2223 6.66488 10.2223H4.33371C4.27235 10.2223 4.2226 10.272 4.2226 10.3334L4.2226 13.6667C4.2226 13.7281 4.27235 13.7778 4.33371 13.7778H6.66488C7.171 13.7778 7.65595 13.9809 8.011 14.3416L11.2545 17.6366C11.2809 17.6634 11.2992 17.6685 11.3101 17.6704C11.326 17.6733 11.3496 17.6723 11.3758 17.6615C11.4021 17.6507 11.4196 17.6349 11.4289 17.6218C11.4353 17.6127 11.4448 17.5963 11.4448 17.5587V6.44139C11.4448 6.40381 11.4353 6.38737 11.4289 6.37829C11.4196 6.36513 11.4021 6.34932 11.3758 6.33857C11.3496 6.32781 11.326 6.3268 11.3101 6.32964Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0329 4.37143C18.6857 4.0243 18.1229 4.0243 17.7758 4.37143C17.4287 4.71857 17.4287 5.28138 17.7758 5.62851C21.3339 9.18662 21.3339 14.9555 17.7758 18.5136C17.4287 18.8607 17.4287 19.4235 17.7758 19.7706C18.1229 20.1178 18.6857 20.1178 19.0329 19.7706C23.2852 15.5183 23.2852 8.62381 19.0329 4.37143ZM16.2044 7.37146C15.8573 7.02433 15.2945 7.02433 14.9474 7.37146C14.6002 7.71859 14.6002 8.28141 14.9474 8.62854C16.9434 10.6246 16.9434 13.8607 14.9474 15.8567C14.6002 16.2039 14.6002 16.7667 14.9474 17.1138C15.2945 17.461 15.8573 17.461 16.2044 17.1138C18.8947 14.4235 18.8947 10.0617 16.2044 7.37146Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/volume_mute.svg b/examples/multimedia/video/mediaplayer/images/volume_mute.svg
new file mode 100644
index 000000000..635c308e9
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/volume_mute.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.6541 5.1163C10.8397 3.91192 12.8891 4.75139 12.8891 6.44139V17.5587C12.8891 19.2487 10.8397 20.0882 9.6541 18.8838L6.41057 15.5888C6.38968 15.5675 6.36116 15.5556 6.33138 15.5556H4.00022C2.95701 15.5556 2.11133 14.7099 2.11133 13.6667L2.11133 10.3334C2.11133 9.29016 2.95701 8.44448 4.00022 8.44448H6.33138C6.36116 8.44448 6.38968 8.43253 6.41057 8.41131L9.6541 5.1163ZM10.9766 6.32964C10.9657 6.3316 10.9474 6.33666 10.921 6.36344L7.67751 9.65845C7.32245 10.0191 6.8375 10.2223 6.33138 10.2223H4.00022C3.93885 10.2223 3.88911 10.272 3.88911 10.3334L3.88911 13.6667C3.88911 13.7281 3.93885 13.7778 4.00022 13.7778H6.33138C6.8375 13.7778 7.32245 13.9809 7.67751 14.3416L10.921 17.6366C10.9474 17.6634 10.9657 17.6685 10.9766 17.6704C10.9925 17.6733 11.0161 17.6723 11.0423 17.6615C11.0686 17.6507 11.0861 17.6349 11.0954 17.6218C11.1018 17.6127 11.1113 17.5963 11.1113 17.5587V6.44139C11.1113 6.40381 11.1018 6.38737 11.0954 6.37829C11.0861 6.36513 11.0686 6.34932 11.0423 6.33857C11.0161 6.32781 10.9925 6.3268 10.9766 6.32964Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7071 15.7071C15.3166 16.0976 14.6834 16.0976 14.2929 15.7071C13.9024 15.3166 13.9024 14.6834 14.2929 14.2929L16.5858 12L14.2929 9.70711C13.9024 9.31658 13.9024 8.68342 14.2929 8.29289C14.6834 7.90237 15.3166 7.90237 15.7071 8.29289L18 10.5858L20.2929 8.29289C20.6834 7.90237 21.3166 7.90237 21.7071 8.29289C22.0976 8.68342 22.0976 9.31658 21.7071 9.70711L19.4142 12L21.7071 14.2929C22.0976 14.6834 22.0976 15.3166 21.7071 15.7071C21.3166 16.0976 20.6834 16.0976 20.2929 15.7071L18 13.4142L15.7071 15.7071Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg b/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg
new file mode 100644
index 000000000..9c7fb8ebb
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.071 3.92892C19.6233 3.92892 20.071 4.37664 20.071 4.92892V9.17157C20.071 9.72385 19.6233 10.1716 19.071 10.1716C18.5188 10.1716 18.071 9.72385 18.071 9.17157V7.34314L16.1716 9.24263C15.781 9.63315 15.1479 9.63315 14.7573 9.24263C14.3668 8.8521 14.3668 8.21894 14.7573 7.82841L16.6568 5.92892L14.8284 5.92892C14.2761 5.92892 13.8284 5.48121 13.8284 4.92892C13.8284 4.37664 14.2761 3.92892 14.8284 3.92892H19.071ZM4.92892 13.8284C5.48121 13.8284 5.92892 14.2761 5.92892 14.8284V16.6569L8.29289 14.2929C8.68342 13.9024 9.31658 13.9024 9.70711 14.2929C10.0976 14.6834 10.0976 15.3166 9.70711 15.7071L7.34314 18.0711L9.17157 18.0711C9.72385 18.0711 10.1716 18.5188 10.1716 19.0711C10.1716 19.6234 9.72385 20.0711 9.17157 20.0711H4.92892C4.37664 20.0711 3.92892 19.6234 3.92892 19.0711V14.8284C3.92892 14.2761 4.37664 13.8284 4.92892 13.8284Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1716 4.92892C10.1716 5.48121 9.72388 5.92892 9.1716 5.92892L7.34316 5.92892L9.24265 7.82841C9.63318 8.21894 9.63318 8.8521 9.24265 9.24263C8.85213 9.63315 8.21896 9.63315 7.82844 9.24263L5.92896 7.34314V9.17157C5.92896 9.72385 5.48124 10.1716 4.92896 10.1716C4.37667 10.1716 3.92896 9.72385 3.92896 9.17157V4.92892C3.92896 4.37664 4.37667 3.92892 4.92896 3.92892H9.1716C9.72388 3.92892 10.1716 4.37664 10.1716 4.92892ZM19.0711 13.8284C19.6234 13.8284 20.0711 14.2761 20.0711 14.8284V19.0711C20.0711 19.6234 19.6234 20.0711 19.0711 20.0711H14.8284C14.2761 20.0711 13.8284 19.6234 13.8284 19.0711C13.8284 18.5188 14.2761 18.0711 14.8284 18.0711H16.6569L14.2929 15.7071C13.9024 15.3166 13.9024 14.6834 14.2929 14.2929C14.6834 13.9024 15.3166 13.9024 15.7071 14.2929L18.0711 16.6569V14.8284C18.0711 14.2761 18.5188 13.8284 19.0711 13.8284Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg b/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg
new file mode 100644
index 000000000..3d6ae65aa
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.1213 10C14.502 10 14 9.49797 14 8.87868L14 4.12132C14 3.50203 14.502 3 15.1213 3C15.7406 3 16.2426 3.50203 16.2426 4.12132V6.17158L18.3726 4.04163C18.8105 3.60372 19.5205 3.60372 19.9584 4.04163C20.3963 4.47953 20.3963 5.18951 19.9584 5.62741L17.8284 7.75736L19.8787 7.75736C20.498 7.75736 21 8.25939 21 8.87868C21 9.49797 20.498 10 19.8787 10L15.1213 10ZM8.87868 21C8.25939 21 7.75736 20.498 7.75736 19.8787V17.8284L5.10659 20.4792C4.66869 20.9171 3.95871 20.9171 3.52081 20.4792C3.0829 20.0413 3.0829 19.3313 3.52081 18.8934L6.17157 16.2426H4.12132C3.50203 16.2426 3 15.7406 3 15.1213C3 14.502 3.50203 14 4.12132 14L8.87868 14C9.49797 14 10 14.502 10 15.1213L10 19.8787C10 20.498 9.49797 21 8.87868 21Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.00002 8.87867C3.00002 8.25939 3.50205 7.75735 4.12134 7.75735H6.1716L4.04166 5.62741C3.60375 5.1895 3.60375 4.47952 4.04166 4.04162C4.47956 3.60372 5.18954 3.60372 5.62744 4.04162L7.75737 6.17155V4.12131C7.75737 3.50203 8.25941 2.99999 8.87869 2.99999C9.49798 2.99999 10 3.50203 10 4.12131L10 8.87867C10 9.49796 9.49798 9.99999 8.87869 9.99999L4.12134 9.99999C3.50205 9.99999 3.00002 9.49796 3.00002 8.87867ZM15.1213 21C14.502 21 14 20.498 14 19.8787V15.1213C14 14.502 14.502 14 15.1213 14L19.8787 14C20.498 14 21 14.502 21 15.1213C21 15.7406 20.498 16.2426 19.8787 16.2426H17.8284L20.4792 18.8934C20.9171 19.3313 20.9171 20.0413 20.4792 20.4792C20.0413 20.9171 19.3313 20.9171 18.8934 20.4792L16.2426 17.8284V19.8787C16.2426 20.498 15.7406 21 15.1213 21Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/main.cpp b/examples/multimedia/video/mediaplayer/main.cpp
index 3a2dab71c..3df4d6027 100644
--- a/examples/multimedia/video/mediaplayer/main.cpp
+++ b/examples/multimedia/video/mediaplayer/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QCommandLineParser>
@@ -21,7 +21,7 @@ int main(int argc, char *argv[])
parser.process(app);
QQmlApplicationEngine engine;
- const QUrl url(QStringLiteral("qrc:/main.qml"));
+ const QUrl url(QStringLiteral("qrc:/Main.qml"));
if (!parser.positionalArguments().isEmpty()) {
QUrl source = QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath());
@@ -32,7 +32,7 @@ int main(int argc, char *argv[])
});
}
- engine.load(url);
+ engine.loadFromModule("mediaplayer", "Main");
return app.exec();
}
diff --git a/examples/multimedia/video/mediaplayer/main.qml b/examples/multimedia/video/mediaplayer/main.qml
deleted file mode 100644
index d1dc1f63a..000000000
--- a/examples/multimedia/video/mediaplayer/main.qml
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-
-Window {
- id: root
- width: 640
- height: 480
- visible: true
- title: qsTr("Multimedia Player")
- property alias source: mediaPlayer.source
-
- Popup {
- id: mediaError
- anchors.centerIn: Overlay.overlay
- Text {
- id: mediaErrorText
- }
- }
-
- MediaPlayer {
- id: mediaPlayer
-
- function updateMetadata() {
- metadataInfo.clear();
- metadataInfo.read(mediaPlayer.metaData);
- metadataInfo.read(mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack]);
- metadataInfo.read(mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack]);
- }
-
- videoOutput: videoOutput
- audioOutput: AudioOutput {
- id: audio
- muted: playbackControl.muted
- volume: playbackControl.volume
- }
-
- onErrorOccurred: { mediaErrorText.text = mediaPlayer.errorString; mediaError.open() }
- onMetaDataChanged: { updateMetadata() }
- onTracksChanged: {
- audioTracksInfo.read(mediaPlayer.audioTracks);
- audioTracksInfo.selectedTrack = mediaPlayer.activeAudioTrack;
- videoTracksInfo.read(mediaPlayer.videoTracks);
- videoTracksInfo.selectedTrack = mediaPlayer.activeVideoTrack;
- subtitleTracksInfo.read(mediaPlayer.subtitleTracks);
- subtitleTracksInfo.selectedTrack = mediaPlayer.activeSubtitleTrack;
- updateMetadata()
- }
- onActiveTracksChanged: { updateMetadata() }
- }
-
- PlayerMenuBar {
- id: menuBar
-
- anchors.left: parent.left
- anchors.right: parent.right
-
- visible: !videoOutput.fullScreen
-
- mediaPlayer: mediaPlayer
- videoOutput: videoOutput
- metadataInfo: metadataInfo
- audioTracksInfo: audioTracksInfo
- videoTracksInfo: videoTracksInfo
- subtitleTracksInfo: subtitleTracksInfo
-
- onClosePlayer: root.close()
- }
-
-
- VideoOutput {
- id: videoOutput
-
- property bool fullScreen: false
-
- anchors.top: fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.top
- anchors.left: parent.left
- anchors.right: parent.right
-
- TapHandler {
- onDoubleTapped: {
- parent.fullScreen ? showNormal() : showFullScreen()
- parent.fullScreen = !parent.fullScreen
- }
- onTapped: {
- metadataInfo.visible = false
- audioTracksInfo.visible = false
- videoTracksInfo.visible = false
- subtitleTracksInfo.visible = false
- }
- }
- }
-
- MetadataInfo {
- id: metadataInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- }
-
- TracksInfo {
- id: audioTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeAudioTrack = audioTracksInfo.selectedTrack
- }
-
- TracksInfo {
- id: videoTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeVideoTrack = videoTracksInfo.selectedTrack
- }
-
- TracksInfo {
- id: subtitleTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeSubtitleTrack = subtitleTracksInfo.selectedTrack
- }
-
- PlaybackControl {
- id: playbackControl
-
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
-
- mediaPlayer: mediaPlayer
- }
-}
diff --git a/src/multimedia/doc/qtmultimedia.qdocconf b/src/multimedia/doc/qtmultimedia.qdocconf
index 5a16bddec..97e6dc696 100644
--- a/src/multimedia/doc/qtmultimedia.qdocconf
+++ b/src/multimedia/doc/qtmultimedia.qdocconf
@@ -1,6 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
include($QT_INSTALL_DOCS/config/exampleurl-qtmultimedia.qdocconf)
-include(../../../examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf)
project = QtMultimedia
description = Qt Multimedia Documentation