在QtMultimedia模块中的multimedia元素可以播放和记录媒体资源,例如声音,视频,或者图片。解码和编码的操作由特定的后台完成。例如在Linux上的gstreamer框架,Windows上的DirectShow,和OS X上的QuickTime。
multimedia元素不是QtQuick核心的接口。它的接口通过导入QtMultimedia 5.0来加入,如下所示:
import QtMultimedia 5.0
媒体播放(Playing Media)
在QML应用程序中,最基本的媒体应用是播放媒体。使用MediaPlayer元素可以完成它,如果源是一个图片或者视频,可以选择结合VideoOutput元素。MediaPlayer元素有一个source属性指向需要播放的媒体。当媒体源被绑定后,简单的调用play函数就可以开始播放。
如果你想播放一个可视化的媒体,例如图片或者视频等,你需要配置一个VideoOutput元素。MediaPlayer播放通过source属性与视频输出绑定。
在下面的例子中,给MediaPlayer元素一个视频文件作为source。一个VideoOutput被创建和绑定到媒体播放器上。一旦主要部件完全初始化,例如在Component.onCompleted中,播放器的play函数被调用。
import QtQuick 2.0
import QtMultimedia 5.0
import QtSystemInfo 5.0
Item {
width: 1024
height: 600
MediaPlayer {
id: player
source: "trailer_400p.ogg"
}
VideoOutput {
anchors.fill: parent
source: player
}
Component.onCompleted: {
player.play();
}
ScreenSaver {
screenSaverEnabled: false;
}
}
// M1>>
除了上面介绍的视频播放,这个例子也包括了一小段代码用于禁止屏幕保护。这将阻止视频被中断。通过设置ScreenSaver元素的screenSaverEnabled属性为false来完成。通过导入QtSystemInfo 5.0可以使用ScreenSaver元素。
基础操作例如当播放媒体时可以通过MediaPlayer元素的volume属性来控制音量。还有一些其它有用的属性。例如,duration与position属性可以用来创建一个进度条。如果seekable属性为true,当拨动进度条时可以更新position属性。下面这个例子展示了在上面的例子基础上如何添加基础播放。
Rectangle {
id: progressBar
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 100
height: 30
color: "lightGray"
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: player.duration>0?parent.width*player.position/player.duration:0
color: "darkGray"
}
MouseArea {
anchors.fill: parent
onClicked: {
if (player.seekable)
player.position = player.duration * mouse.x/width;
}
}
}
默认情况下position属性每秒更新一次。这意味着进度条将只会在大跨度下的时间周期下才会更新,需要媒体持续时间足够长,进度条像素足够宽。然而,这个可以通过mediaObject属性的notifyInterval属性改变。它可以设置每个position之间更新的毫秒数,增加用户界面的平滑度。
Connections {
target: player
onMediaObjectChanged: {
if (player.mediaObject)
player.mediaObject.notifyInterval = 50;
}
}
当使用MediaPlayer创建一个媒体播放器时,最好使用status属性来监听播放器。这个属性是一个枚举,它枚举了播放器可能出现的状态,从MediaPlayer.Buffered到MediaPlayer.InvalidMedia。下面是这些状态值的总结:
MediaPlayer.UnknownStatus - 未知状态
MediaPlayer.NoMedia - 播放器没有指定媒体资源,播放停止
MediaPlayer.Loading - 播放器正在加载媒体
MediaPlayer.Loaded - 媒体已经加载完毕,播放停止
MediaPlayer.Stalled - 加载媒体已经停止
MediaPlayer.Buffering - 媒体正在缓冲
MediaPlayer.Buffered - 媒体缓冲完成
MediaPlayer.EndOfMedia - 媒体播放完毕,播放停止
MediaPlayer.InvalidMedia - 无法播放媒体,播放停止
正如上面提到的这些枚举项,播放状态会随着时间变化。调用play,pause或者stop将会切换状态,但由于媒体的原因也会影响这些状态。例如,媒体播放完毕,它将会无效,导致播放停止。当前的播放状态可以使用playbackState属性跟踪。这个值可能是MediaPlayer.PlayingState,MediaPlayer.PasuedState或者MediaPlayer.StoppedState。
使用autoPlay属性,MediaPlayer在source属性改变时将会尝试进入播放状态。类似的属性autoLoad将会导致播放器在source属性改变时尝试加载媒体。默认下autoLoad是被允许的。
当然也可以让MediaPlayer循环播放一个媒体项。loops属性控制source将会被重复播放多少次。设置属性为MediaPlayer.Infinite将会导致不停的重播。非常适合持续的动画或者一个重复的背景音乐。
声音效果(Sounds Effects)
当播放声音效果时,从请求播放到真实响应播放的响应时间非常重要。在这种情况下,SoundEffect元素将会派上用场。设置source属性,一个简单调用play函数会直接开始播放。
当敲击屏幕时,可以使用它来完成音效反馈,如下所示:
SoundEffect {
id: beep
source: "beep.wav"
}
Rectangle {
id: button
anchors.centerIn: parent
width: 200
height: 100
color: "red"
MouseArea {
anchors.fill: parent
onClicked: beep.play()
}
}
这个元素也可以用来完成一个配有音效的转换。为了从转换触发,使用ScriptAction元素。
SoundEffect {
id: swosh
source: "swosh.wav"
}
transitions: [
Transition {
ParallelAnimation {
ScriptAction { script: swosh.play(); }
PropertyAnimation { properties: "rotation"; duration: 200; }
}
}
]
除了调用play函数,在MediaPlayer中类似属性也可以使用。比如volume和loops。loops可以设置为SoundEffect.Infinite来提供无限重复播放。停止播放调用stop函数。
注意
当后台使用PulseAudio时,stop将不会立即停止,但会阻止继续循环。这是由于底层API的限制造成的。
视频流(Video Streams)
VideoOutput元素不被限制与MediaPlayer元素绑定使用的。它也可以直接用来加载实时视频资源显示一个流媒体。应用程序使用Camera元素作为资源。来自Camera的视频流给用户提供了一个实时流媒体。
import QtQuick 2.0
import QtMultimedia 5.0
Item {
width: 1024
height: 600
VideoOutput {
anchors.fill: parent
source: camera
}
Camera {
id: camera
}
}
捕捉图像(Capturing Images)
Camera元素一个关键特性就是可以用来拍照。我们将在一个简单的定格动画程序中使用到它。在这章中,你将学习如何显示一个视图查找器,截图和追踪拍摄的图片。
用户界面如下所示。它由三部分组成,背景是一个视图查找器,右边有一列按钮,底部有一连串拍摄的图片。我们想要拍摄一系列的图片,然后点击Play Sequence按钮。这将回放图片,并创建一个简单的定格电影。
相机的视图查找器部分是在VideoOutput中使用一个简单的Camera元素作为资源。这将给用户显示一个来自相机的流媒体视频。
VideoOutput {
anchors.fill: parent
source: camera
}
Camera {
id: camera
}
使用一个水平放置的ListView显示来自ListModel的图片,这个部件叫做imagePaths。在背景中使用一个半透明的Rectangle。
ListModel {
id: imagePaths
}
ListView {
id: listView
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
height: 100
orientation: ListView.Horizontal
spacing: 10
model: imagePaths
delegate: Image { source: path; fillMode: Image.PreserveAspectFit; height: 100; }
Rectangle {
anchors.fill: parent
anchors.topMargin: -10
color: "black"
opacity: 0.5
}
}
为了拍摄图像,你需要知道Camera元素包含了一组子对象用来完成各种工作。使用Camera.imageCapture用来捕捉图像。当你调用capture方法时,一张图片就被拍摄下来了。Camera.imageCapture的结果将会发送imageCaptured信号,接着发送imageSaved信号。
Button {
id: shotButton
width: 200
height: 75
text: "Take Photo"
onClicked: {
camera.imageCapture.capture();
}
}
为了拦截子元素的信号,需要一个Connections元素。在这个例子中,我们不需要显示预览图片,仅仅只是将结果图片加入底部的ListView中。就如下面的例子展示的一样,图片保存的路径由信号的path参数提供。
Connections {
target: camera.imageCapture
onImageSaved: {
imagePaths.append({"path": path})
listView.positionViewAtEnd();
}
}
为了显示预览,连接imageCaptured信号,并且使用preview信号参数作为Image元素的source。requestId信号参数与imageCaptured和imageSaved一起发送。这个值由capture方法返回。这样,就可以完整的跟踪拍摄的图片了。预览的图片首先被使用,然后替换为保存的图片。然而在这个例子中我们不需要这样做。
最后是自动回放的部分。使用Timer元素来驱动它,并且加上一些JavaScript。_imageIndex变量被用来跟踪当前显示的图片。当最后一张图片被显示时,回放停止。在例子中,当播放序列时,root.state被用来隐藏用户界面。
property int _imageIndex: -1
function startPlayback()
{
root.state = "playing";
setImageIndex(0);
playTimer.start();
}
function setImageIndex(i)
{
_imageIndex = i;
if (_imageIndex >= 0 && _imageIndex < imagePaths.count)
image.source = imagePaths.get(_imageIndex).path;
else
image.source = "";
}
Timer {
id: playTimer
interval: 200
repeat: false
onTriggered: {
if (_imageIndex + 1 < imagePaths.count)
{
setImageIndex(_imageIndex + 1);
playTimer.start();
}
else
{
setImageIndex(-1);
root.state = "";
}
}
}
高级用法(Advanced Techniques)
10.5.1 实现一个播放列表(Implementing a Playlist)
Qt 5 multimedia接口没有提供播放列表。幸好,它非常容易实现。通过设置模型子项与MediaPlayer元素可以实现它,如下所示。当playstate通过player控制时,Playlist元素负责设置MediaPlayer的source。
Playlist {
id: playlist
mediaPlayer: player
items: ListModel {
ListElement { source: "trailer_400p.ogg" }
ListElement { source: "trailer_400p.ogg" }
ListElement { source: "trailer_400p.ogg" }
}
}
MediaPlayer {
id: player
}
Playlist元素的第一部分如下,注意使用setIndex函数来设置source元素的索引值。我们也实现了next与previous函数来操作链表。
Item {
id: root
property int index: 0
property MediaPlayer mediaPlayer
property ListModel items: ListModel {}
function setIndex(i)
{
console.log("setting index to: " + i);
index = i;
if (index < 0 || index >= items.count)
{
index = -1;
mediaPlayer.source = "";
}
else
mediaPlayer.source = items.get(index).source;
}
function next()
{
setIndex(index + 1);
}
function previous()
{
setIndex(index + 1);
}
让播放列表自动播放下一个元素的诀窍是使用MediaPlayer的status属性。当得到MediaPlayer.EndOfMedia状态时,索引值增加,恢复播放,或者当列表达到最后时,停止播放。
Connections {
target: root.mediaPlayer
onStopped: {
if (root.mediaPlayer.status == MediaPlayer.EndOfMedia)
{
root.next();
if (root.index == -1)
root.mediaPlayer.stop();
else
root.mediaPlayer.play();
}
}
}
总结(Summary)
Qt的媒体应用程序接口提供了播放和捕捉视频和音频的机制。通过VideoOutput元素,视频源能够在我们的用户界面上显示。通过MediaPlayer元素,可以操作大多数的播放,SoundEffect被用于低延迟的声音。Camera元素被用来截图或者显示一个实时的视频流。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2291184112@qq.com