QML 视频范例

Transforming video and camera viewfinder content.

QML Video demonstrates the various transformations (move; resize; rotate; change aspect ratio) that can be applied to QML VideoOutput and Camera 类型。

It also shows how native code can be combined with QML to implement more advanced functionality - in this case, C++ code is used to calculate the QML frame rate. This value is rendered in QML in a semi-transparent item overlaid on the video content.

The following image shows the application executing the video-overlay scene, which creates a dummy overlay item (just a semi-transparent Rectangle ), which moves across the VideoOutput 项。

运行范例

要运行范例从 Qt Creator ,打开 欢迎 模式,然后选择范例从 范例 。更多信息,拜访 构建和运行范例 .

应用程序结构

The Main.qml file creates a UI which includes the following items:

  • Two Button instances, each of which displays a filename, and can be used to launch a FileDialog .
  • An exit Button .
  • A SceneSelectionPanel , which is a flickable list displaying the available scenes.
  • At the lower left, an item which displays the QML repainting rate - the upper number is the instantaneous frame rate and the lower number is the average over the past second.

Each scene in the flickable list is implemented in its own QML file - for example the video-basic scene (which just displays a static VideoOutput in the center of the screen) is implemented in the VideoBasic.qml file. As you can see from the code, this makes use of a type of inheritance; a VideoBasic item ...

SceneBasic {
    contentType: "video"
}
					

... is of type SceneBasic ...

import QtQuick
import QtQuick.Controls
Scene {
    id: root
    property string contentType
    ...
    Content {
        id: content
    ...
    }
    Label {
        anchors {
            horizontalCenter: parent.horizontalCenter
            bottom: parent.bottom
            margins: 20
        }
        text: content.started ? qsTr("Tap the screen to stop content")
                              : qsTr("Tap the screen to start content")
        z: 2.0
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            if (content.started)
                content.stop()
            else
                content.start()
        }
    }
    Component.onCompleted: root.content = content
}
					

... which itself is a Scene :

import QtQuick
import QtQuick.Controls
Rectangle {
    id: root
    ...
    property QtObject content
    ...
    Button {
        id: closeButton
        anchors {
            top: parent.top
            right: parent.right
            margins: root.margins
        }
        z: 2.0
        text: qsTr("Back")
        onClicked: root.close()
    }
}
					

SceneBasic describes the structure and behavior of the scene, but is agnostic of the type of content which will be displayed - this is abstracted by 内容 .

This pattern allows us to define a particular use case (in this case, simply display a static piece of content), and then instantiate that use case for both video content ( VideoBasic ) and camera content ({CameraBasic}) . This approach is used to implement many of the other scenes - for example, "repeatedly slide the content from left to right and back again" is implemented by SceneMove , on which VideoMove and CameraMove are based.

Depending on the value of the contentType property in the top-level scene instance, the embedded 内容 item creates either a MediaPlayer Camera 项。

计算和显示 QML 描绘率

The QML painting rate is calculated by the FrequencyMonitor class, which turns a stream of events (received via the notify() slot), into an instantaneous and an averaged frequency:

class FrequencyMonitor : public QObject
{
    Q_OBJECT
    Q_PROPERTY(qreal instantaneousFrequency READ instantaneousFrequency NOTIFY
                       instantaneousFrequencyChanged)
    Q_PROPERTY(qreal averageFrequency READ averageFrequency NOTIFY averageFrequencyChanged)
public:
    ...
    static void qmlRegisterType();
public slots:
    Q_INVOKABLE void notify();
};
					

The FrequencyMonitor class is exposed to QML like this

void FrequencyMonitor::qmlRegisterType()
{
    ::qmlRegisterType<FrequencyMonitor>("FrequencyMonitor", 1, 0, "FrequencyMonitor");
}
					

and its data is displayed by defining a QML item called FrequencyItem, like this:

import FrequencyMonitor 1.0
Rectangle {
    id: root
    ...
    function notify() {
        monitor.notify()
    }
    FrequencyMonitor {
        id: monitor
        onAverageFrequencyChanged: {
            averageFrequencyText.text = monitor.averageFrequency.toFixed(2)
        }
    }
    Text {
        id: labelText
        anchors {
            left: parent.left
            top: parent.top
            margins: 10
        }
        color: root.textColor
        font.pixelSize: 0.6 * root.textSize
        text: root.label
        width: root.width - 2*anchors.margins
        elide: Text.ElideRight
    }
    Text {
        id: averageFrequencyText
        anchors {
            right: parent.right
            bottom: parent.bottom
            margins: 10
        }
        color: root.textColor
        font.pixelSize: root.textSize
    }
}
					

The result looks like this:

All that remains is to connect the afterRendering() signal of the QQuickView object to a JavaScript function, which will eventually call frequencyItem.notify() :

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    ...
    QQuickView viewer;
    ...
    QQuickItem *rootObject = viewer.rootObject();
    ...
    QObject::connect(&viewer, SIGNAL(afterRendering()), rootObject, SLOT(qmlFramePainted()));
					

范例工程 @ code.qt.io