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:
Button
instances, each of which displays a filename, and can be used to launch a
FileDialog
.
Button
.
SceneSelectionPanel
, which is a flickable list displaying the available scenes.
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
项。
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()));