# 播放媒体
在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
组件提供了几个有用的属性。 例如,duration
和position
属性可用于构建进度条。 如果将seekable
属性设置为true
,甚至可以在点击进度条时更新position
。
还可以利用AudioOutput
和VideoOutput
属性来自定义体验并提供功能,例如音量控制。
以下示例为播放添加自定义控件:
- 音量滑块
- 播放/暂停按钮
- 进度滑块
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.Buffered
到MediaPlayer.InvalidMedia
可能的状态,在下面的列表中总结了可能的值:
MediaPlayer.NoMedia
(没有媒体):还没有设置媒体,回放被停止。MediaPlayer.Loading
(正在加载):媒体当前被加载。MediaPlayer.Loaded
(已加载):媒体已经被加载,回放被停止。MediaPlayer.Buffering
(缓存中):媒体正在缓存数据。MediaPlayer.Stalled
(拖延住):当媒体缓存数据的时候回放被中断。MediaPlayer.Buffered
(已缓存):媒体已经被缓存,这意味着播放器可以开始播放媒体。MediaPlayer.EndOfMedia
(媒体结尾):到达了媒体的结尾处,回放被停止。MediaPlayer.InvalidMedia
(无效状态):媒体不能被播放,回放被停止。MediaPlayer.UnknownStatus
(未知状态):媒体的状态是未知状态。
正如上面的列表中提到的,播放状态会随着时间的推移而变化。 调用play
、pause
或stop
会改变状态,但有问题的媒体也会这些状态产生影响,例如:可以到达结尾、也可以无效、导致播放停止。
提示
也可以让MediaPlayer
循环播放一个媒体项。 loops
属性控制要播放source
的次数,将其属性设置为MediaPlayer.Infinite
会导致无限循环,这非常适合连续动画或循环的背景歌曲。