多媒体(Multimedia)

在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

×

喜欢就点赞,疼爱就打赏