QML 媒体播放器示例

使用 QML MediaPlayer 类型播放音频和视频。

此示例演示了一个简单的多媒体播放器,可以使用各种编解码器播放音频和视频文件。

运行示例

要从 Qt Creator 运行示例,请打开 欢迎模式 并从 示例 中选择示例。有关更多信息,请访问 构建和运行示例

实例化 MediaPlayer

此示例中 QML 代码的入口点是 Main.qml。在这里创建了一个 ApplicationWindow 并设置了如 idtitlewidthheight 等属性。

ApplicationWindow {
    id: root
    title: qsTr("Multimedia Player")
    width: 1280
    height: 720

接下来创建了 MediaPlayer 并定义了负责视频和音频输出的两个属性。首先,是 videoOutput 用于渲染视频查看器,其次 audioOutput 为播放器提供音频输出。

MediaPlayer {
    id: mediaPlayer
    ...
videoOutput: videoOutput
audioOutput: AudioOutput {
    id: audio
    muted: playbackController.muted
    volume: playbackController.volume
}
    ...
VideoOutput {
    id: videoOutput
    anchors.fill: parent
    visible: mediaPlayer.mediaStatus > 0

    TapHandler {
        onDoubleTapped: {
            root.fullScreen ?  root.showNormal() : root.showFullScreen()
            root.fullScreen = !root.fullScreen
        }
    }
}

MediaPlayermediaStatus 属性大于 0 时,VideoOutput 类型的 visible 属性设置为 truemediaStatus 是枚举类型,等于 0 时表示 没有设置媒体,否则大于 0。因此,当设置了媒体时,VideoOutput 才可见。

MediaPlayer 类型有一个名为 onErrorOccurred 的信号属性,可以特定地重写以处理错误。在这种情况下,该信号使用 open() 方法打开一个 MessageDialog 并将其 text 属性设置为名为 errorStringMediaPlayer 属性。

onErrorOccurred: {
    mediaError.open()
    mediaError.text = mediaPlayer.errorString
}

播放控制

为了有一个可用的媒体播放器,需要有一个用于控制播放的接口。这在一个单独的组件文件 PlaybackControl.qml 中创建,并在 Main.qml 中实例化。

PlaybackControl {
    id: playbackController
    ...
onTracksChanged: {
    audioTracksInfo.read(mediaPlayer.audioTracks)
    videoTracksInfo.read(mediaPlayer.videoTracks)
    subtitleTracksInfo.read(mediaPlayer.subtitleTracks, 6) /* QMediaMetaData::Language = 6 */
    updateMetadata()
    mediaPlayer.play()
}

创建时,会将诸如曲目信息、元数据信息以及 MediaPlayer 对象本身之类的对象转发到此类型。在 PlaybackControl.qml 中,这些对象中的每一个都有一个 required property,这意味着在创建 PlaybackControl 对象时必须设置这些属性。

Item {
    id: playbackController

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

这些播放控制可以分为几个部分。在面板的左上角有一组按钮,用于打开文件,无论是通过从文件资源管理器中选择文件还是输入 URL。通过设置 source 属性将文件加载到 MediaPlayer 中。这两个按钮都是使用 CustomButton 自定义组件 实例化的。

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

在此面板上创建了三个按钮,分别用于播放、暂停以及向后或向前搜索十秒钟。播放和暂停通过分别调用`play()`和`pause()`方法实现。为了知道何时绘制播放或暂停按钮,通过查询`playbackState`属性来得到信息。例如,当它等于枚举值`MediaPlayer.PlayingState`时,则绘制暂停按钮。

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

要向前或向后导航十秒钟,通过将`MediaPlayer`类型的`position`增加10,000毫秒并使用`setPosition()`方法来设置它。

CustomRoundButton {
    id: forward10Button
    icon.source: "../images/forward10.svg"
    onClicked: {
        const pos = Math.min(playbackController.mediaPlayer.duration,
                           playbackController.mediaPlayer.position + 10000)
        playbackController.mediaPlayer.setPosition(pos)
    }
}

播放搜索和音频

在`PlaybackControl.qml`中,实例化了`AudioControl`和`PlaybackSeekControl`类型。这些都在各自的组件文件中定义,分别负责音量和播放搜索。`AudioControl`类型定义了一个静音按钮和一个来自`QtQuick Controls`的`Slider`来设置播放器的音量。这两个属性通过定义`mute`和`volume`属性来公开,并通过在`Main.qml`的`AudioOutput`定义中访问它们。

property alias muted: muteButton.checked
property real volume: slider.value

`PlaybackSeekControl`使用一个包含一个`Slider`和一个位于其两侧的`Text`项的`RowLayout`。这两个`Text`项显示正在播放的媒体当前时间和剩余时间。这些都是使用`MediaPlayer`类型的两个属性计算的,即`position`(以毫秒为单位给出当前播放位置)和`duration`(以毫秒为单位给出媒体的持续时间)。

Text {
    id: currentTime
    Layout.preferredWidth: 45
    text: seekController.formatToMinutes(seekController.mediaPlayer.position)
    horizontalAlignment: Text.AlignLeft
    font.pixelSize: 11
}
    ...
Text {
    id: remainingTime
    Layout.preferredWidth: 45
    text: seekController.formatToMinutes(seekController.mediaPlayer.duration - seekController.mediaPlayer.position)
    horizontalAlignment: Text.AlignRight
    font.pixelSize: 11
}

当媒体播放器可搜索且不是如直播媒体时,才启用`Slider`。`MediaPlayer`类型有一个名为`seekable`的属性来表示这一点。使用`position`和`duration`属性计算`Slider`的`value`。

enabled: seekController.mediaPlayer.seekable
value: seekController.mediaPlayer.position / seekController.mediaPlayer.duration

元数据和轨道信息

`PlaybackControl`类型实例化了一个包含有关当前载入媒体元数据和轨道选择信息,以及更新播放速率能力的`SettingsPopup`。此`Popup`在`SettingsPopup.qml`中定义。

元数据包含在其自己的组件文件`MetadataInfo.qml`中。它包含一个`ListModel`,一个清除它的函数`clear()`,以及一个填充它的函数`read(MediaMetadata metadata)`。`read(MediaMetadata metadata)`函数接受一个类型为`MediaMetaData`的对象作为参数,并遍历其键值结构,将其数据提取到`ListView`的`model`中。用于此目的的方法是`keys()`,它返回所有`MediaMetaData`的键,以及`{stringValue(Key key)}`,它返回特定`key`的值。

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
}

然后,在`SettingsPopup.qml`中使用`ListView`类型显示这些数据。此`ListView`的`delegate`是一行两个`Text`项,对应于从`MediaMetaData`项抽象出来的键值对。

在`Popup`的另一边是播放速率控制和音频、视频和字幕的轨道选择。播放速率从`ComboBox`中选择并使用`playbackRate`属性设置。

settingsController.mediaPlayer.playbackRate = (currentIndex + 1) * 0.25

名为 TracksInfo 的类型,定义在 TracksInfo.qml 中,包含有关轨道的数据。更具体地说,是一个包含轨道标题的 ListModel,或对于特定于字幕的语言。此信息通过在 Main.qml 中调用在 TracksInfo 类型中定义的 read(MediaMetadata mediaMetadata) 函数来填充。

onTracksChanged: {
    audioTracksInfo.read(mediaPlayer.audioTracks)
    videoTracksInfo.read(mediaPlayer.videoTracks)
    subtitleTracksInfo.read(mediaPlayer.subtitleTracks, 6) /* QMediaMetaData::Language = 6 */
    updateMetadata()
    mediaPlayer.play()
}

TracksInfo 中定义的 model 然后将在 SettingsPopup 中的 ComboBox 中查询以选择当前轨道。

settingsController.mediaPlayer.pause()
tracksInfo.selectedTrack = currentIndex
settingsController.mediaPlayer.play()

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 本文档中的文档贡献享有各自所有者的版权。本文档根据自由软件基金会(Free Software Foundation)发布的 GNU 自由文档许可证版本 1.3 的条款提供。Qt 和相应的徽标是 The Qt Company Ltd. 在芬兰和/或世界上其他国家的商标。所有其他商标均为其所有者财产。