Playing audio and video using Qt Quick.
This example demonstrates a simple multimedia player that can play audio and video files using various codecs.
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.
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.
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 } } }
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
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()
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() }
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.
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
This item has controls for Playback control, Play Pause Stop, Playback rate control and Playback seek 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, 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} } ]
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) }
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"
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