- Qt 6.5
- Qt Multimedia
- QML Media Player Example
- Qt 6.5.0 Reference Documentation

# QML Media Player Example

Playing audio and video using Qt Quick.

This example demonstrates a simple multimedia player that can play audio and video files using various codecs.

#### Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

#### Overview

At its core this is a QML application, see Getting Started Programming with Qt Quick for information specific to that. This documentation is focused on how this example utilizes the Qt Multimedia QML types.

#### Using MediaPlayer and VideoOutput

In `main.qml` a MediaPlayer instance is connected to a VideoOutput to play back the video:

         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

`videoOutput` is declared like so:

         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
                 }
             }
         }

#### PlayerMenuBar

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.

Accessing the mediaPlayer object is done through properties:

         required property MediaPlayer mediaPlayer
         required property VideoOutput videoOutput
         required property MetadataInfo metadataInfo
         required property TracksInfo audioTracksInfo
         required property TracksInfo videoTracksInfo
         required property TracksInfo subtitleTracksInfo

##### fileDialog

A FileDialog, `fileDialog`, is created with an `onAccepted` function that will stop `mediaPlayer`, load the source by setting the source property and then play it automatically:

         FileDialog {
             id: fileDialog
             title: "Please choose a file"
             onAccepted: {
                 mediaPlayer.stop()
                 mediaPlayer.source = fileDialog.currentFile
                 mediaPlayer.play()
             }
         }

This is triggered in the Menu `File`, which is a child of the MenuBar:

         MenuBar {
             id: menuBar
             anchors.left: parent.left
             anchors.right: parent.right

             Menu {
                 title: qsTr("&File")
                 Action {
                     text: qsTr("&Open")
                     onTriggered: fileDialog.open()

##### loadUrl

While `urlPopup` handles prompting and capturing a url, it is the `loadUrl` function that interacts with `mediaPlayer` like so:

         function loadUrl(url) {
             mediaPlayer.stop()
             mediaPlayer.source = url
             mediaPlayer.play()
         }

##### Getting meta data

In the declaration of `mediaPlayer`, in `main.qml`, there is the function `updateMetadata()`:

             function updateMetadata() {
                 metadataInfo.clear();
                 metadataInfo.read(mediaPlayer.metaData);
                 metadataInfo.read(mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack]);
                 metadataInfo.read(mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack]);

It is called in the following places:

             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()
             }

Reading MetaData is done by the `MetadataInfo` type's `read()` function

         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)
                                     })
                     }
                 }
             }
         }

The information is displayed via an Overlay item.

##### Tracks information and control

This is defined in `TracksInfo.qml` and reading available tracks is done in a similar way to `MetadataInfo`:

         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
                             })
             });
         }

To set a track, the property `selectedTrack` is set like so:

             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
                     }
                 }
             }

The `onSelectectedTrackChanged` signal, in each relevant `TracksInfo` instance in `main.qml`, is what makes changes to `mediaPlayer` like so:

             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

#### playbackControlPanel

This item has controls for Playback control, Play Pause Stop, Playback rate control and Playback seek control.

##### Playback control

This qml type handles media playback and interacts with the MediaPlayer in `main.qml`.

Here are the property definitions.

         required property MediaPlayer mediaPlayer
         property int mediaPlayerState: mediaPlayer.playbackState

Connections:

             target: mediaPlayer
             function onPlaybackStateChanged() { updateOpacity() }
             function onHasVideoChanged() { updateOpacity() }
         }

##### Play Pause Stop

Play, stop and pause interactions with the MediaPlayer object are done like so:

                         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()
                         }
                     }

Playback states done using playbackstate like so:

             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}
             }
         ]

##### Playback seek control

Defined in `PlaybackSeekControl.qml`, this component comprises of an item with a Text, `mediaTime`, and Slider, `mediaSlider`, in a RowLayout.

`mediaTime` uses MediaPlayer's position property like so:

             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)}`
                 }
             }

`mediaSlider` uses the MediaPlayer seekable, duration, and position properties like so:

             Slider {
                 id: mediaSlider
                 Layout.fillWidth: true
                 enabled: mediaPlayer.seekable
                 to: 1.0
                 value: mediaPlayer.position / mediaPlayer.duration

                 onMoved: mediaPlayer.setPosition(value * mediaPlayer.duration)
             }

##### Playback rate control

This type is defined in `PlaybackRateControl.qml` like so:

             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 " + slider.value + "x"

##### Audio control

This type is defined in `AudioControl.qml`, and utilizes the muted and volume properties of the AudioOutput instantiated within the MediaPlayer, which is instantiated in `main.qml`.

         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

Example project @ code.qt.io