ソースを参照

添加第十章 multimedia

cwc 10 年 前
コミット
bab28bd217

+ 7 - 0
SUMMARY.md

@@ -66,4 +66,11 @@
    * [顶点着色器(Vertex Shader)](shader_effect/vertex_shader.md)
    * [顶点着色器(Vertex Shader)](shader_effect/vertex_shader.md)
    * [剧幕效果(Curtain Effect)](shader_effect/curtain_effect.md)
    * [剧幕效果(Curtain Effect)](shader_effect/curtain_effect.md)
    * [Qt图像效果库(Qt GraphicsEffect Library)](shader_effect/qtqt_graphicseffect_library.md)
    * [Qt图像效果库(Qt GraphicsEffect Library)](shader_effect/qtqt_graphicseffect_library.md)
+* [Multimedia](multimedia/README.md)
+   * [媒体播放(Playing Media)](multimedia/playing_media.md)
+   * [声音效果(Sounds Effects)](multimedia/sounds_effects.md)
+   * [视频流(Video Streams)](multimedia/video_streams.md)
+   * [捕捉图像(Capturing Images)](multimedia/capturing_images.md)
+   * [高级用法(Advanced Techniques)](multimedia/advanced_techniques.md)
+   * [总结(Summary)](multimedia/summary.md)
 
 

+ 8 - 0
multimedia/README.md

@@ -0,0 +1,8 @@
+# Multimedia
+
+在QtMultimedia模块中的multimedia元素可以播放和记录媒体资源,例如声音,视频,或者图片。解码和编码的操作由特定的后台完成。例如在Linux上的gstreamer框架,Windows上的DirectShow,和OS X上的QuickTime。
+multimedia元素不是QtQuick核心的接口。它的接口通过导入QtMultimedia 5.0来加入,如下所示:
+
+```
+import QtMultimedia 5.0
+```

+ 78 - 0
multimedia/advanced_techniques.md

@@ -0,0 +1,78 @@
+# 高级用法(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();
+            }
+        }
+    }
+```

+ 128 - 0
multimedia/capturing_images.md

@@ -0,0 +1,128 @@
+# 捕捉图像(Capturing Images)
+
+Camera元素一个关键特性就是可以用来拍照。我们将在一个简单的定格动画程序中使用到它。在这章中,你将学习如何显示一个视图查找器,截图和追踪拍摄的图片。
+
+用户界面如下所示。它由三部分组成,背景是一个视图查找器,右边有一列按钮,底部有一连串拍摄的图片。我们想要拍摄一系列的图片,然后点击Play Sequence按钮。这将回放图片,并创建一个简单的定格电影。
+
+![](http://qmlbook.org/_images/camera-ui.png)
+
+相机的视图查找器部分是在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 = "";
+            }
+        }
+    }
+```

+ 113 - 0
multimedia/playing_media.md

@@ -0,0 +1,113 @@
+# 媒体播放(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将会导致不停的重播。非常适合持续的动画或者一个重复的背景音乐。

+ 52 - 0
multimedia/sounds_effects.md

@@ -0,0 +1,52 @@
+# 声音效果(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的限制造成的。**

+ 3 - 0
multimedia/summary.md

@@ -0,0 +1,3 @@
+# 总结(Summary)
+
+Qt的媒体应用程序接口提供了播放和捕捉视频和音频的机制。通过VideoOutput元素,视频源能够在我们的用户界面上显示。通过MediaPlayer元素,可以操作大多数的播放,SoundEffect被用于低延迟的声音。Camera元素被用来截图或者显示一个实时的视频流。

+ 22 - 0
multimedia/video_streams.md

@@ -0,0 +1,22 @@
+# 视频流(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
+    }
+}
+```