# 播放媒体

在QML应用程序中,集成多媒体的最基本情况就是播放媒体。 QtMultimedia模块通过提供一个专用的QML组件MediaPlayer来支持这一点。

MediaPlayer组件是一个非可视项,它将媒体源连接到一个或多个输出通道。 根据媒体(即音频、图像或视频)的性质,可以配置各种输出通道。

# 播放音频

在以下示例中,MediaPlayer在空窗口中播放来自远程URL的mp3示例音频文件:

import QtQuick
import QtMultimedia

Window {
    width: 1024
    height: 768
    visible: true

    MediaPlayer {
        id: player
        source: "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_2MG.mp3"
        audioOutput: AudioOutput {}
    }

    Component.onCompleted: {
        player.play()
    }
}

在这个例子中,MediaPlayer定义了两个属性:

  • source(源): 它包含要播放媒体的 URL。 它可以是嵌入资源(qrc://)、本地文件(file://)或远程地址(https://)。
  • audioOutput(音频输出): 它包含一个音频输出通道,AudioOutput,连接到一个物理输出设备。 默认情况下,它将使用系统的默认音频输出设备。

一旦主组件被完全初始化,播放器的play函数就会被调用:

Component.onCompleted: {
    player.play()
}

# 播放视频

如果要播放图片或视频等视觉媒体,还必须定义VideoOutput元素以将生成的图像或视频放置到用户界面中。

在以下示例中,MediaPlayer播放来自远程URL的mp4示例视频文件并将视频内容置于窗口中心:

import QtQuick
import QtMultimedia

Window {
    width: 1920
    height: 1080
    visible: true

    MediaPlayer {
        id: player
        source: "https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1920_18MG.mp4"
        audioOutput: AudioOutput {}
        videoOutput: videoOutput
    }

    VideoOutput {
        id: videoOutput
        anchors.fill: parent
        anchors.margins: 20
    }

    Component.onCompleted: {
        player.play()
    }
}

在这个例子中,MediaPlayer定义了第三个属性:

  • videoOutput(视频输出): 它包含视频输出通道,VideoOutput,代表为在用户界面中显示视频而保留的视觉空间。

提示

请注意,VideoOutput组件是一个可视项。 因此,它必须在可视化组件层次结构内创建,而不是在MediaPlayer内创建。

# 控制播放

MediaPlayer组件提供了几个有用的属性。 例如,durationposition属性可用于构建进度条。 如果将seekable属性设置为true,甚至可以在点击进度条时更新position

还可以利用AudioOutputVideoOutput属性来自定义体验并提供功能,例如音量控制。

以下示例为播放添加自定义控件:

  • 音量滑块
  • 播放/暂停按钮
  • 进度滑块
import QtQuick
import QtQuick.Controls
import QtMultimedia

Window {
    id: root
    width: 1920
    height: 1080
    visible: true

    MediaPlayer {
        id: player
        source: Qt.resolvedUrl("sample-5s.mp4")
        audioOutput: audioOutput
        videoOutput: videoOutput
    }

    AudioOutput {
        id: audioOutput
        volume: volumeSlider.value
    }

    VideoOutput {
        id: videoOutput
        width: videoOutput.sourceRect.width
        height: videoOutput.sourceRect.height
        anchors.horizontalCenter: parent.horizontalCenter
    }

    Slider {
        id: volumeSlider
        anchors.top: parent.top
        anchors.right: parent.right
        anchors.margins: 20
        orientation: Qt.Vertical
        value: 0.5
    }

    Item {
        height: 50
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.margins: 20

        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: player.playbackState ===  MediaPlayer.PlayingState ? qsTr("Pause") : qsTr("Play")
            onClicked: {
                switch(player.playbackState) {
                    case MediaPlayer.PlayingState: player.pause(); break;
                    case MediaPlayer.PausedState: player.play(); break;
                    case MediaPlayer.StoppedState: player.play(); break;
                }
            }
        }

        Slider {
            id: progressSlider
            width: parent.width
            anchors.bottom: parent.bottom
            enabled: player.seekable
            value: player.duration > 0 ? player.position / player.duration : 0
            background: Rectangle {
                implicitHeight: 8
                color: "white"
                radius: 3
                Rectangle {
                    width: progressSlider.visualPosition * parent.width
                    height: parent.height
                    color: "#1D8BF8"
                    radius: 3
                }
            }
            handle: Item {}
            onMoved: function () {
                player.position = player.duration * progressSlider.position
            }
        }
    }

    Component.onCompleted: {
        player.play()
    }
}

# 声音滑动条

在窗口的右上角添加了一个垂直的Slider组件,允许用户控制媒体的音量:

Slider {
    id: volumeSlider
    anchors.top: parent.top
    anchors.right: parent.right
    anchors.margins: 20
    orientation: Qt.Vertical
    value: 0.5
}

然后,AudioOutput的音量属性被映射到滑块的值:

AudioOutput {
    id: audioOutput
    volume: volumeSlider.value
}

# 播放、暂停

一个Button组件反映媒体的播放状态并允许用户控制此状态:

Button {
    anchors.horizontalCenter: parent.horizontalCenter
    text: player.playbackState ===  MediaPlayer.PlayingState ? qsTr("Pause") : qsTr("Play")
    onClicked: {
        switch(player.playbackState) {
            case MediaPlayer.PlayingState: player.pause(); break;
            case MediaPlayer.PausedState: player.play(); break;
            case MediaPlayer.StoppedState: player.play(); break;
        }
    }
}

根据播放状态,按钮中将显示不同的文本。 单击时,将触发相应的操作并播放或暂停媒体。

提示

可能的播放状态罗列如下:

  • MediaPlayer.PlayingState(正在播放状态): 媒体当前正在播放。
  • MediaPlayer.PausedState(被暂停状态): 媒体播放已暂停。
  • MediaPlayer.StoppedState(已停止状态): 媒体尚未开始播放。

# 进度条交互

添加一个Slider(滑块)组件以反映播放的当前进度,它还允许用户控制播放的当前位置。





 
 












 
 
 

Slider {
    id: progressSlider
    width: parent.width
    anchors.bottom: parent.bottom
    enabled: player.seekable
    value: player.duration > 0 ? player.position / player.duration : 0
    background: Rectangle {
        implicitHeight: 8
        color: "white"
        radius: 3
        Rectangle {
            width: progressSlider.visualPosition * parent.width
            height: parent.height
            color: "#1D8BF8"
            radius: 3
        }
    }
    handle: Item {}
    onMoved: function () {
        player.position = player.duration * progressSlider.position
    }
}

关于这个示例有几点需要注意:

  • 仅当媒体为seekable时才会启用此滑块(第5行)
  • 滑块的值将设置成当前媒体的进度,即player.position / player.duration(第6行)
  • 当用户移动滑块时,媒体的位置也将更新(第19-21行)

# 媒体状态

当使用MediaPlayer构建媒体播放器时,最好监控播放器的status属性。 这里列举了从MediaPlayer.BufferedMediaPlayer.InvalidMedia可能的状态,在下面的列表中总结了可能的值:

  • MediaPlayer.NoMedia(没有媒体):还没有设置媒体,回放被停止。
  • MediaPlayer.Loading(正在加载):媒体当前被加载。
  • MediaPlayer.Loaded(已加载):媒体已经被加载,回放被停止。
  • MediaPlayer.Buffering(缓存中):媒体正在缓存数据。
  • MediaPlayer.Stalled(拖延住):当媒体缓存数据的时候回放被中断。
  • MediaPlayer.Buffered(已缓存):媒体已经被缓存,这意味着播放器可以开始播放媒体。
  • MediaPlayer.EndOfMedia(媒体结尾):到达了媒体的结尾处,回放被停止。
  • MediaPlayer.InvalidMedia(无效状态):媒体不能被播放,回放被停止。
  • MediaPlayer.UnknownStatus(未知状态):媒体的状态是未知状态。

正如上面的列表中提到的,播放状态会随着时间的推移而变化。 调用playpausestop会改变状态,但有问题的媒体也会这些状态产生影响,例如:可以到达结尾、也可以无效、导致播放停止。

提示

也可以让MediaPlayer循环播放一个媒体项。 loops属性控制要播放source的次数,将其属性设置为MediaPlayer.Infinite会导致无限循环,这非常适合连续动画或循环的背景歌曲。

最后更新: 12/28/2021, 12:13:07 AM