diff options
Diffstat (limited to 'examples/multimedia/video/qmlvideo/qmlvideo')
50 files changed, 2012 insertions, 0 deletions
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt b/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt new file mode 100644 index 000000000..84a65710e --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt @@ -0,0 +1,81 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + message(FATAL_ERROR "This module is part of the 'qmlvideo' example, and should not be built independently.") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/qmlvideo/qmlvideo") + +qt_add_qml_module(qmlvideo + URI qmlvideo + SOURCES + videosingleton.cpp + videosingleton.h + qmlvideo_global.h + QML_FILES + "CameraBasic.qml" + "CameraDrag.qml" + "CameraDummy.qml" + "CameraFullScreen.qml" + "CameraFullScreenInverted.qml" + "CameraItem.qml" + "CameraMove.qml" + "CameraOverlay.qml" + "CameraResize.qml" + "CameraRotate.qml" + "CameraSpin.qml" + "Content.qml" + "ErrorDialog.qml" + "Main.qml" + "Scene.qml" + "SceneBasic.qml" + "SceneDrag.qml" + "SceneFullScreen.qml" + "SceneFullScreenInverted.qml" + "SceneMove.qml" + "SceneMulti.qml" + "SceneOverlay.qml" + "SceneResize.qml" + "SceneRotate.qml" + "SceneSelectionPanel.qml" + "SceneSpin.qml" + "SeekControl.qml" + "VideoBasic.qml" + "VideoDrag.qml" + "VideoDummy.qml" + "VideoFillMode.qml" + "VideoFullScreen.qml" + "VideoFullScreenInverted.qml" + "VideoItem.qml" + "VideoMetadata.qml" + "VideoMove.qml" + "VideoOverlay.qml" + "VideoPlaybackRate.qml" + "VideoResize.qml" + "VideoRotate.qml" + "VideoSeek.qml" + "VideoSpin.qml" + RESOURCES + "images/folder.png" + "images/leaves.jpg" + "images/up.png" +) + +target_compile_definitions(qmlvideo PRIVATE QMLVIDEO_LIB) + +target_link_libraries(qmlvideo PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Quick +) + +install(TARGETS qmlvideo + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir + DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml new file mode 100644 index 000000000..77072c6c9 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml @@ -0,0 +1,7 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneBasic { + contentType: "camera" + started: true +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml new file mode 100644 index 000000000..2ea3672c6 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneDrag { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml new file mode 100644 index 000000000..c9d14e595 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +// Item which is loaded by CameraItem if Qt Multimedia is not available +Rectangle { + id: root + color: "grey" + height: width + + signal fatalError + signal sizeChanged + signal framePainted + + Label { + anchors.fill: parent + anchors.margins: 10 + horizontalAlignment: Text.AlignHCenter + text: qsTr("Failed to create Camera item\n\nCheck that Qt Multimedia is installed") + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } + + onWidthChanged: height = width + onHeightChanged: root.sizeChanged() + + function start() { } + function stop() { } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml new file mode 100644 index 000000000..b204e74ab --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml @@ -0,0 +1,7 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneFullScreen { + contentType: "camera" +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml new file mode 100644 index 000000000..ff7a40253 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml @@ -0,0 +1,7 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneFullScreenInverted { + contentType: "camera" +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml new file mode 100644 index 000000000..d6e4b85b5 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtMultimedia + +Item { + id: root + height: width + + signal fatalError + signal sizeChanged + + onHeightChanged: root.sizeChanged() + + CaptureSession { + camera: Camera { + id: camera + + onErrorOccurred: function(error, errorString) { + if (Camera.NoError !== error) { + console.log("[qmlvideo] CameraItem.onError error " + error + " errorString " + errorString) + root.fatalError() + } + } + } + imageCapture: ImageCapture { + id: imageCapture + } + + recorder: MediaRecorder { + id: recorder +// resolution: "640x480" +// frameRate: 30 + } + videoOutput: videoOutput + } + + VideoOutput { + id: videoOutput + anchors.fill: parent + } + + + function start() { camera.start() } + function stop() { camera.stop() } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml new file mode 100644 index 000000000..c4c5da914 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneMove { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml new file mode 100644 index 000000000..13136be00 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneOverlay { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml new file mode 100644 index 000000000..d89eef3ad --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneResize { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml new file mode 100644 index 000000000..8394fb6fd --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneRotate { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml new file mode 100644 index 000000000..642a0edfd --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneSpin { + contentType: "camera" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml new file mode 100644 index 000000000..b08d19325 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml @@ -0,0 +1,125 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import frequencymonitor + +Rectangle { + id: root + border.color: palette.window + border.width: showBorder ? 1 : 0 + color: "transparent" + property string contentType // "camera" or "video" + property string source + property real volume + property bool dummy: false + property bool autoStart: true + property bool started: false + property bool showFrameRate: false + property bool showBorder: false + property alias contentItem: contentLoader.item + + signal initialized + signal error + signal videoFramePainted + + Loader { + id: contentLoader + } + + Connections { + id: framePaintedConnection + function onFramePainted() { + (frameRateLoader.item as FrequencyItem)?.notify() + root.videoFramePainted() + } + ignoreUnknownSignals: true + } + + Connections { + id: errorConnection + function onFatalError() { + console.log("[qmlvideo] Content.onFatalError") + root.stop() + root.error() + } + ignoreUnknownSignals: true + } + + Component { + id: frequencyItem + FrequencyItem {} + } + + Loader { + id: frameRateLoader + sourceComponent: root.showFrameRate ? frequencyItem : undefined + onLoaded: { + item.parent = root + item.anchors.top = root.top + item.anchors.right = root.right + item.anchors.margins = 10 + } + } + + onWidthChanged: { + if (root.contentItem) + root.contentItem.width = width + } + + onHeightChanged: { + if (root.contentItem) + root.contentItem.height = height + } + + function initialize() { + if ("video" == contentType) { + contentLoader.source = "VideoItem.qml" + if (Loader.Error == contentLoader.status) { + contentLoader.source = "VideoDummy.qml" + dummy = true + } + contentLoader.item.volume = volume + } else if ("camera" == contentType) { + contentLoader.source = "CameraItem.qml" + if (Loader.Error == contentLoader.status) { + contentLoader.source = "CameraDummy.qml" + dummy = true + } + } else { + console.log("[qmlvideo] Content.initialize: error: invalid contentType") + } + if (contentLoader.item) { + contentLoader.item.sizeChanged.connect(updateRootSize) + contentLoader.item.parent = root + contentLoader.item.width = root.width + framePaintedConnection.target = contentLoader.item + errorConnection.target = contentLoader.item + if (root.autoStart) + root.start() + } + root.initialized() + } + + function start() { + if (contentLoader.item) { + if (root.contentType == "video") + contentLoader.item.mediaSource = root.source + contentLoader.item.start() + root.started = true + } + } + + // qmllint disable + function stop() { + if (contentLoader.item) { + contentLoader.item.stop() + if (root.contentType == "video") + contentLoader.item.mediaSource = "" + root.started = false + } + } + // qmllint enable + + function updateRootSize() { root.height = (root.contentItem as Item).height } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml b/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml new file mode 100644 index 000000000..f06079a35 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml @@ -0,0 +1,70 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: root + color: "transparent" + opacity: 0.0 + property alias enabled: mouseArea.enabled + property int dialogWidth: 300 + property int dialogHeight: 200 + state: enabled ? "on" : "baseState" + + states: [ + State { + name: "on" + PropertyChanges { + root.opacity: 1.0 + } + } + ] + + transitions: [ + Transition { + from: "*" + to: "*" + NumberAnimation { + properties: "opacity" + easing.type: Easing.OutQuart + duration: 500 + } + } + ] + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.75 + } + + Rectangle { + anchors.centerIn: parent + width: root.dialogWidth + height: root.dialogHeight + radius: 5 + color: "white" + + Text { + id: text + anchors.fill: parent + anchors.margins: 10 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "black" + wrapMode: Text.WordWrap + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.enabled = false + } + + function show(msg) { + text.text = "<b>Error</b><br><br>" + msg + root.enabled = true + } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml new file mode 100644 index 000000000..49b94590c --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml @@ -0,0 +1,188 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs +import performancemonitor + +Rectangle { + id: root + anchors.fill: parent + color: palette.window + + property bool perfMonitorsLogging: false + property bool perfMonitorsVisible: false + + Loader { + id: performanceLoader + + Connections { + target: columnLayout + function onVisibleChanged() { + if (performanceLoader.item) + performanceLoader.item.enabled = !columnLayout.visible + } + ignoreUnknownSignals: true + } + + Component { + id: performanceItem + PerformanceItem {} + } + + function init() { + var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible + sourceComponent = enabled ? performanceItem : undefined + } + + onLoaded: { + item.parent = root + item.anchors.fill = root + item.logging = root.perfMonitorsLogging + item.displayed = root.perfMonitorsVisible + item.enabled = false + item.init() + } + } + + ColumnLayout { + id: columnLayout + anchors.fill: parent + spacing: 5 + + Button { + id: openFile1Button + text: (VideoSingleton.source1 == '') ? qsTr("Select file 1") : VideoSingleton.source1 + Component.onCompleted: console.log("source1: " + VideoSingleton.source1) + onClicked: { + fileDialog.setFirstSource = true + fileDialog.open() + } + + Layout.fillWidth: true + } + + Button { + id: openFile2Button + text: (VideoSingleton.source2 == '') ? qsTr("Select file 2") : VideoSingleton.source2 + Component.onCompleted: console.log("source2: " + VideoSingleton.source2) + onClicked: { + fileDialog.setFirstSource = false + fileDialog.open() + } + + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Label { + text: qsTr("Video Modes") + + horizontalAlignment: Qt.AlignHCenter + Layout.preferredWidth: 50 + Layout.fillWidth: true + } + Label { + text: qsTr("Camera Modes") + + horizontalAlignment: Qt.AlignHCenter + Layout.preferredWidth: 50 + Layout.fillWidth: true + } + } + + SceneSelectionPanel { + id: sceneSelectionPanel + itemHeight: Math.min(width / 10, height / 10) + color: palette.dark + radius: 0 + onSceneSourceChanged: { + sceneLoader.source = sceneSource + var scene = null + var innerVisible = true + if (sceneSource == "") { + if (performanceLoader.item) + performanceLoader.item.videoActive = false + } else { + scene = sceneLoader.item + if (scene) { + if (scene.contentType === "video" && VideoSingleton.source1 === "") { + errorDialog.show(qsTr("You must first select a video file")) + sceneSource = "" + } else { + scene.parent = root + scene.color = root.palette.window + scene.source1 = VideoSingleton.source1 + scene.source2 = VideoSingleton.source2 + scene.volume = VideoSingleton.volume + scene.anchors.fill = root + scene.close.connect(closeScene) + scene.content.initialize() + innerVisible = false + } + } + } + videoFramePaintedConnection.target = scene + columnLayout.visible = innerVisible + } + + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + Loader { + id: sceneLoader + } + + Connections { + id: videoFramePaintedConnection + // qmllint disable + function onVideoFramePainted() { + if (performanceLoader.item) + performanceLoader.item.videoFramePainted() + } + // qmllint enable + ignoreUnknownSignals: true + } + + FileDialog { + id: fileDialog + property bool setFirstSource + onAccepted: function() { + if (setFirstSource) + VideoSingleton.source1 = selectedFile + else + VideoSingleton.source2 = selectedFile + } + } + + ErrorDialog { + id: errorDialog + anchors.fill: root + dialogWidth: Math.min(root.width, root.height) * 0.5 + dialogHeight: Math.min(root.width, root.height) * 0.3 + enabled: false + } + + // Called from main() once root properties have been set + function init() { + performanceLoader.init() + fileDialog.currentFolder = VideoSingleton.videoPath + } + + // qmllint disable + function qmlFramePainted() { + if (performanceLoader.item) + performanceLoader.item.qmlFramePainted() + } + // qmllint enable + + function closeScene() { + sceneSelectionPanel.sceneSource = "" + } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml new file mode 100644 index 000000000..e16478d41 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Rectangle { + id: root + color: palette.window + property string source1 + property string source2 + property int contentWidth: parent.width / 2 + property real volume: 0.25 + property int margins: 5 + property QtObject content + + signal close + signal videoFramePainted + + Button { + id: closeButton + anchors { + top: parent.top + right: parent.right + margins: root.margins + } + z: 2.0 + text: qsTr("Back") + onClicked: root.close() + } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml new file mode 100644 index 000000000..8ad6c99b8 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + property string contentType + property bool autoStart: false + property bool started: false + + Content { + id: content + autoStart: parent.autoStart + started: parent.started + anchors.fill: parent + width: parent.contentWidth + contentType: parent.contentType + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + 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 +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml new file mode 100644 index 000000000..99a7cb7f1 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property int margin: 20 + property string contentType + + Image { + id: background + source: "images/leaves.jpg" + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + Content { + id: content + anchors.centerIn: parent + width: root.contentWidth + contentType: root.contentType + source: root.source1 + volume: root.volume + onVideoFramePainted: root.videoFramePainted() + } + } + + MouseArea { + anchors.fill: parent + drag.target: background + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml new file mode 100644 index 000000000..ec8564311 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml @@ -0,0 +1,67 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + state: "left" + + states: [ + State { + name: "fullScreen" + PropertyChanges { + content.width: content.parent.width + content.height: content.parent.height + } + } + ] + + transitions: [ + Transition { + ParallelAnimation { + PropertyAnimation { + property: "width" + easing.type: Easing.Linear + duration: 250 + } + PropertyAnimation { + property: "height" + easing.type: Easing.Linear + duration: 250 + } + } + } + ] + + MouseArea { + anchors.fill: parent + onClicked: content.state = (content.state == "fullScreen") ? "baseState" : "fullScreen" + } + + onVideoFramePainted: root.videoFramePainted() + } + + Label { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: 20 + } + text: qsTr("Tap on the content to toggle full-screen mode") + z: 2.0 + } + + Component.onCompleted: root.content = content +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml new file mode 100644 index 000000000..cb96ac134 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml @@ -0,0 +1,70 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.width + height: parent.height + contentType: root.contentType + source: parent.source1 + volume: parent.volume + state: "left" + + states: [ + State { + name: "nonFullScreen" + PropertyChanges { content.width: root.contentWidth } + } + ] + + transitions: [ + Transition { + ParallelAnimation { + PropertyAnimation { + property: "width" + easing.type: Easing.Linear + duration: 250 + } + PropertyAnimation { + property: "height" + easing.type: Easing.Linear + duration: 250 + } + } + } + ] + + MouseArea { + anchors.fill: parent + onClicked: content.state = (content.state === "nonFullScreen") ? "baseState" : "nonFullScreen" + } + + onVideoFramePainted: root.videoFramePainted() + + onInitialized: { + width = parent.width + height = parent.height + } + } + + Label { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: 20 + } + text: qsTr("Tap on the content to toggle full-screen mode") + z: 2.0 + } + + Component.onCompleted: root.content = content +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml new file mode 100644 index 000000000..d1512831e --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml @@ -0,0 +1,49 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property int margin: 20 + property string contentType + + Content { + id: content + anchors.verticalCenter: parent.verticalCenter + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + + SequentialAnimation on x { + id: animation + loops: Animation.Infinite + property int from: root.margin + property int to: 100 + property int duration: 1500 + running: false + PropertyAnimation { + from: animation.from + to: animation.to + duration: animation.duration + easing.type: Easing.InOutCubic + } + PropertyAnimation { + from: animation.to + to: animation.from + duration: animation.duration + easing.type: Easing.InOutCubic + } + } + + onVideoFramePainted: root.videoFramePainted() + } + + onWidthChanged: { + animation.to = root.width - content.width - margin + animation.start() + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml new file mode 100644 index 000000000..f93dabcdd --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml @@ -0,0 +1,186 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + + property real itemWidth: (width / 3) - 40 + property real itemTopMargin: 50 + + QtObject { + id: contentProxy + function initialize() { + video1.initialize() + video2.initialize() + } + } + + Component { + id: startStopComponent + + Rectangle { + id: root + color: "transparent" + + signal start + signal stop + + Label { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: 20 + } + // qmllint disable + text: root.started ? qsTr("Tap to stop") : qsTr("Tap to start") + // qmllint enable + } + + MouseArea { + anchors.fill: parent + // qmllint disable + onClicked: { + if (root.started) + root.stop() + else + root.start() + } + // qmllint enable + } + } + } + + Content { + id: video1 + anchors { + left: parent.left + leftMargin: 10 + top: parent.top + topMargin: root.itemTopMargin + } + autoStart: false + contentType: "video" + showBorder: true + showFrameRate: started + source: parent.source1 + width: root.itemWidth + volume: parent.volume + + Loader { + id: video1StartStopLoader + + property bool started: parent.started + + onLoaded: { + item.parent = video1 + item.anchors.fill = video1 + item.start.connect(video1.start) + item.stop.connect(video1.stop) + } + } + + onInitialized: video1StartStopLoader.sourceComponent = startStopComponent + } + + Rectangle { + id: cameraHolder + + property bool started: false + + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: root.itemTopMargin + } + border.width: 1 + border.color: palette.base + color: "transparent" + width: root.itemWidth + height: width + + Loader { + id: cameraLoader + onLoaded: { + item.parent = cameraHolder + item.anchors.centerIn = cameraHolder + item.contentType = "camera" + item.showFrameRate = true + item.width = root.itemWidth + item.z = 1.0 + cameraErrorConnection.target = item + item.initialize() + } + } + + Loader { + id: cameraStartStopLoader + + property bool started: parent.started + + sourceComponent: startStopComponent + onLoaded: { + item.parent = cameraHolder + item.anchors.fill = cameraHolder + item.z = 2.0 + item.start.connect(cameraHolder.start) + item.stop.connect(cameraHolder.stop) + } + } + + Connections { + id: cameraErrorConnection + function onError() { + console.log("[qmlvideo] SceneMulti.camera.onError") + cameraHolder.stop() + } + ignoreUnknownSignals: true + } + + function start() { + cameraLoader.source = "Content.qml" + cameraHolder.started = true + } + + function stop() { + cameraLoader.source = "" + cameraHolder.started = false + } + } + + Content { + id: video2 + anchors { + right: parent.right + rightMargin: 10 + top: parent.top + topMargin: root.itemTopMargin + } + autoStart: false + contentType: "video" + showBorder: true + showFrameRate: started + source: parent.source2 + width: root.itemWidth + volume: parent.volume + + Loader { + id: video2StartStopLoader + + property bool started: parent.started + + onLoaded: { + item.parent = video2 + item.anchors.fill = video2 + item.start.connect(video2.start) + item.stop.connect(video2.stop) + } + } + + onInitialized: video2StartStopLoader.sourceComponent = startStopComponent + } + + Component.onCompleted: root.content = contentProxy +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml new file mode 100644 index 000000000..5863d618c --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml @@ -0,0 +1,83 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property int margin: 20 + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + Rectangle { + id: overlay + y: 0.5 * parent.height + width: content.width + height: content.height + color: "#e0e0e0" + opacity: 0.5 + + SequentialAnimation on x { + id: xAnimation + loops: Animation.Infinite + property int from: root.margin + property int to: 100 + property int duration: 1500 + running: false + PropertyAnimation { + from: xAnimation.from + to: xAnimation.to + duration: xAnimation.duration + easing.type: Easing.InOutCubic + } + PropertyAnimation { + from: xAnimation.to + to: xAnimation.from + duration: xAnimation.duration + easing.type: Easing.InOutCubic + } + } + + SequentialAnimation on y { + id: yAnimation + loops: Animation.Infinite + property int from: root.margin + property int to: 180 + property int duration: 1500 + running: false + PropertyAnimation { + from: yAnimation.from + to: yAnimation.to + duration: yAnimation.duration + easing.type: Easing.InOutCubic + } + PropertyAnimation { + from: yAnimation.to + to: yAnimation.from + duration: yAnimation.duration + easing.type: Easing.InOutCubic + } + } + } + + onWidthChanged: { + xAnimation.to = root.width - content.width - margin + xAnimation.start() + } + + onHeightChanged: { + //yAnimation.to = root.height - content.height - margin + yAnimation.start() + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml new file mode 100644 index 000000000..4e2f15588 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml @@ -0,0 +1,41 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + + SequentialAnimation on scale { + id: animation + loops: Animation.Infinite + property int duration: 1500 + running: true + PropertyAnimation { + from: 1.5 + to: 0.5 + duration: animation.duration + easing.type: Easing.InOutCubic + } + PropertyAnimation { + from: 0.5 + to: 1.5 + duration: animation.duration + easing.type: Easing.InOutCubic + } + } + + onVideoFramePainted: root.videoFramePainted() + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml new file mode 100644 index 000000000..1362522e6 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml @@ -0,0 +1,57 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + property int margin: 20 + property int delta: 30 + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + Button { + id: rotatePositiveButton + anchors { + right: parent.right + bottom: rotateNegativeButton.top + margins: parent.margins + } + text: qsTr("Rotate +%1").arg(root.delta) + onClicked: content.rotation = content.rotation + root.delta + } + + Button { + id: rotateNegativeButton + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + margins: parent.margins + } + text: qsTr("Rotate -%1").arg(root.delta) + onClicked: content.rotation = content.rotation - root.delta + } + + Button { + id: rotateValueButton + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + margins: parent.margins + } + enabled: false + text: content.rotation % 360 + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml new file mode 100644 index 000000000..91e7c6ad4 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml @@ -0,0 +1,88 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +pragma ComponentBehavior: Bound + +Rectangle { + id: root + property int itemHeight: 25 + property string sceneSource: "" + + ListModel { + id: videolist + ListElement { name: qsTr("Multi"); source: "SceneMulti.qml" } + ListElement { name: qsTr("Video"); source: "VideoBasic.qml" } + ListElement { name: qsTr("Drag"); source: "VideoDrag.qml" } + ListElement { name: qsTr("Fillmode"); source: "VideoFillMode.qml" } + ListElement { name: qsTr("Fullscreen"); source: "VideoFullScreen.qml" } + ListElement { name: qsTr("Fullscreen-inverted"); source: "VideoFullScreenInverted.qml" } + ListElement { name: qsTr("Metadata"); source: "VideoMetadata.qml" } + ListElement { name: qsTr("Move"); source: "VideoMove.qml" } + ListElement { name: qsTr("Overlay"); source: "VideoOverlay.qml" } + ListElement { name: qsTr("Playback Rate"); source: "VideoPlaybackRate.qml" } + ListElement { name: qsTr("Resize"); source: "VideoResize.qml" } + ListElement { name: qsTr("Rotate"); source: "VideoRotate.qml" } + ListElement { name: qsTr("Spin"); source: "VideoSpin.qml" } + ListElement { name: qsTr("Seek"); source: "VideoSeek.qml" } + } + + ListModel { + id: cameralist + ListElement { name: qsTr("Camera"); source: "CameraBasic.qml" } + ListElement { name: qsTr("Drag"); source: "CameraDrag.qml" } + ListElement { name: qsTr("Fullscreen"); source: "CameraFullScreen.qml" } + ListElement { name: qsTr("Fullscreen-inverted"); source: "CameraFullScreenInverted.qml" } + ListElement { name: qsTr("Move"); source: "CameraMove.qml" } + ListElement { name: qsTr("Overlay"); source: "CameraOverlay.qml" } + ListElement { name: qsTr("Resize"); source: "CameraResize.qml" } + ListElement { name: qsTr("Rotate"); source: "CameraRotate.qml" } + ListElement { name: qsTr("Spin"); source: "CameraSpin.qml" } + } + + Component { + id: buttonDelegate + Button { + required property string name + required property string source + + width: root.width / 2 + height: root.itemHeight + + text: name + padding: 3 + onClicked: root.sceneSource = source + } + } + + Flickable { + anchors.fill: parent + contentHeight: (root.itemHeight * videolist.count) + 10 + clip: true + + Row { + id: layout + anchors { + fill: parent + topMargin: 5 + bottomMargin: 5 + } + + Column { + Repeater { + model: videolist + delegate: buttonDelegate + } + } + + Column { + Repeater { + model: cameralist + delegate: buttonDelegate + } + } + } + } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml new file mode 100644 index 000000000..3e280d3b1 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property int margin: 20 + property string contentType + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: root.contentType + source: parent.source1 + volume: parent.volume + + PropertyAnimation on rotation { + id: animation + loops: Animation.Infinite + running: true + from: 0 + to: 360 + duration: 3000 + easing.type: Easing.Linear + } + + onVideoFramePainted: root.videoFramePainted() + } + + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml new file mode 100644 index 000000000..646e5e8ef --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml @@ -0,0 +1,105 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: seekControl + height: Math.min(parent.width, parent.height) / 20 + property int duration: 0 + property int playPosition: 0 + property int seekPosition: 0 + property bool enabled: true + property bool seeking: false + + Rectangle { + id: background + anchors.fill: parent + color: palette.base + opacity: 0.3 + radius: parent.height / 15 + } + + Rectangle { + id: progressBar + anchors { left: parent.left; top: parent.top; bottom: parent.bottom } + width: seekControl.duration == 0 ? 0 : background.width * seekControl.playPosition / seekControl.duration + color: palette.highlight + opacity: 0.7 + } + + Text { + width: 90 + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; leftMargin: 10 } + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + color: palette.windowText + smooth: true + text: seekControl.formatTime(seekControl.playPosition) + } + + Text { + width: 90 + anchors { right: parent.right; top: parent.top; bottom: parent.bottom; rightMargin: 10 } + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: palette.windowText + smooth: true + text: seekControl.formatTime(seekControl.duration) + } + + Rectangle { + id: progressHandle + height: parent.height + width: parent.height / 2 + color: palette.accent + opacity: 0.5 + anchors.verticalCenter: progressBar.verticalCenter + x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * background.width + + MouseArea { + id: mouseArea + anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom } + height: parent.height + width: parent.height * 2 + enabled: seekControl.enabled + drag { + target: progressHandle + axis: Drag.XAxis + minimumX: 0 + maximumX: background.width + } + onPressed: { + seekControl.seeking = true; + } + onCanceled: { + seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width + seekControl.seeking = false + } + onReleased: (mouse) => { + seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width + seekControl.seeking = false + mouse.accepted = true + } + } + } + + Timer { // Update position also while user is dragging the progress handle + id: seekTimer + repeat: true + interval: 300 + running: seekControl.seeking + onTriggered: { + seekControl.seekPosition = progressHandle.x*seekControl.duration / background.width + } + } + + function formatTime(timeInMs) { + if (!timeInMs || timeInMs <= 0) return "0:00" + let seconds = timeInMs / 1000; + let minutes = Math.floor(seconds / 60) + seconds = Math.floor(seconds % 60) + if (seconds < 10) seconds = "0" + seconds; + return minutes + ":" + seconds + } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml new file mode 100644 index 000000000..15c25c978 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneBasic { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml new file mode 100644 index 000000000..4a5c5d49b --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneDrag { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml new file mode 100644 index 000000000..a30b9ebf6 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +// Item which is loaded by VideoItem if Qt Multimedia is not available +Rectangle { + id: root + color: "grey" + height: width + property int duration: 0 + property int position: 0 + property string source + property real volume: 1.0 + property real playbackRate: 1.0 + + signal fatalError + signal sizeChanged + signal framePainted + + Label { + anchors.fill: parent + anchors.margins: 10 + horizontalAlignment: Text.AlignHCenter + text: qsTr("Failed to create Video item\n\nCheck that Qt Multimedia is installed") + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } + + onWidthChanged: height = width + onHeightChanged: root.sizeChanged() + + function start() { } + function stop() { } + function seek() { } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml new file mode 100644 index 000000000..af950c735 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml @@ -0,0 +1,49 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtMultimedia + +Scene { + id: root + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: "video" + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + Button { + id: button + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + margins: parent.margins + } + text: qsTr("PreserveAspectFit") + // qmllint disable + onClicked: { + if (!content.dummy) { + let video = content.contentItem + if (video.fillMode === VideoOutput.Stretch) { + video.fillMode = VideoOutput.PreserveAspectFit + text = qsTr("PreserveAspectFit") + } else if (video.fillMode === VideoOutput.PreserveAspectFit) { + video.fillMode = VideoOutput.PreserveAspectCrop + text = qsTr("PreserveAspectCrop") + } else { + video.fillMode = VideoOutput.Stretch + text = qsTr("Stretch") + } + } + } + // qmllint enable + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml new file mode 100644 index 000000000..e49faadb6 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml @@ -0,0 +1,7 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneFullScreen { + contentType: "video" +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml new file mode 100644 index 000000000..0bf82941e --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml @@ -0,0 +1,7 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneFullScreenInverted { + contentType: "video" +} + diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml new file mode 100644 index 000000000..2ea796de8 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml @@ -0,0 +1,42 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtMultimedia + +VideoOutput { + id: root + height: width + + property alias duration: mediaPlayer.duration + property alias mediaSource: mediaPlayer.source + property alias metaData: mediaPlayer.metaData + property alias playbackRate: mediaPlayer.playbackRate + property alias position: mediaPlayer.position + property alias seekable: mediaPlayer.seekable + property alias volume: audioOutput.volume + + signal sizeChanged + signal fatalError + + onHeightChanged: root.sizeChanged() + + MediaPlayer { + id: mediaPlayer + videoOutput: root; + audioOutput: AudioOutput { + id: audioOutput + } + + onErrorOccurred: function(error, errorString) { + if (MediaPlayer.NoError !== error) { + console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString) + root.fatalError() + } + } + } + + function start() { mediaPlayer.play() } + function stop() { mediaPlayer.stop() } + function seek(position) { mediaPlayer.setPosition(position); } +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml new file mode 100644 index 000000000..f83cbffe9 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml @@ -0,0 +1,80 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtMultimedia + +pragma ComponentBehavior: Bound + +Scene { + id: root + property string contentType: "video" + + Content { + id: videoContent + anchors.centerIn: parent + width: parent.contentWidth + contentType: "video" + source: parent.source1 + volume: parent.volume + onInitialized: { + if (!dummy) + metadata.createObject(root) + } + onVideoFramePainted: root.videoFramePainted() + } + + Component { + id: metadata + Column { + anchors.fill: parent + // qmllint disable + property var videoMetaData: videoContent.contentItem?.metaData + // qmllint enable + Label { + text: qsTr("Title: %1").arg(parent.videoMetaData?.value(MediaMetaData.Title) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Resolution: %1").arg(parent.videoMetaData?.value(MediaMetaData.Resolution) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Media type: %1").arg(parent.videoMetaData?.value(MediaMetaData.MediaType) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Video codec: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoCodec) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Video bit rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoBitRate) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Video frame rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoFrameRate) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Audio codec: %1").arg(parent.videoMetaData?.value(MediaMetaData.AudioCodec) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Audio bit rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.AudioBitRate) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Date: %1").arg(parent.videoMetaData?.value(MediaMetaData.Date) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Description: %1").arg(parent.videoMetaData?.value(MediaMetaData.Description) ?? qsTr("Unknown")) + } + Label { + text: qsTr("Copyright: %1").arg(parent.videoMetaData?.value(MediaMetaData.Copyright) ?? qsTr("Unknown")) + } + Label { + // qmllint disable + text: qsTr("Seekable: %1").arg(videoContent.contentItem?.seekable ?? qsTr("Unknown")) + // qmllint enable + } + Label { + text: qsTr("Orientation: %1").arg(parent.videoMetaData?.value(MediaMetaData.Orientation) ?? qsTr("Unknown")) + } + } + } + + Component.onCompleted: root.content = videoContent +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml new file mode 100644 index 000000000..2b9230f3c --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneMove { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml new file mode 100644 index 000000000..576b33ff9 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneOverlay { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml new file mode 100644 index 000000000..55e38ef19 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml @@ -0,0 +1,65 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls + +Scene { + id: root + property int margin: 20 + property real delta: 0.1 + property string contentType: "video" + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: "video" + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + Button { + id: increaseButton + anchors { + right: parent.right + bottom: decreaseButton.top + margins: parent.margins + } + text: qsTr("Increase") + onClicked: { + let video = (content.contentItem as VideoItem) + video.playbackRate += root.delta + } + } + + Button { + id: decreaseButton + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + margins: parent.margins + } + text: qsTr("Decrease") + onClicked: { + let video = (content.contentItem as VideoItem) + video.playbackRate -= root.delta + } + } + + Button { + id: valueButton + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + margins: parent.margins + } + enabled: false + // qmllint disable + text: Math.round(10 * content.contentItem?.playbackRate ?? 1) / 10 + // qmllint enable + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml new file mode 100644 index 000000000..88fe7a2d2 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneResize { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml new file mode 100644 index 000000000..d429ec413 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneRotate { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml new file mode 100644 index 000000000..5e2584fb6 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml @@ -0,0 +1,36 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Scene { + id: root + property string contentType: "video" + contentWidth: parent.width + + Content { + id: content + anchors.centerIn: parent + width: parent.contentWidth + contentType: "video" + source: parent.source1 + volume: parent.volume + onVideoFramePainted: root.videoFramePainted() + } + + SeekControl { + anchors { + left: parent.left + right: parent.right + margins: 10 + bottom: parent.bottom + } + // qmllint disable + duration: content.contentItem?.duration ?? 0 + playPosition: content.contentItem?.position ?? 0 + onSeekPositionChanged: content.contentItem?.seek(seekPosition); + // qmllint enable + } + + Component.onCompleted: root.content = content +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml new file mode 100644 index 000000000..7c365ddc7 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +SceneSpin { + contentType: "video" +} diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png b/examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png Binary files differnew file mode 100644 index 000000000..62d97004f --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg b/examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg Binary files differnew file mode 100644 index 000000000..66533b34a --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/images/up.png b/examples/multimedia/video/qmlvideo/qmlvideo/images/up.png Binary files differnew file mode 100644 index 000000000..6823de004 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/up.png diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/qmldir b/examples/multimedia/video/qmlvideo/qmlvideo/qmldir new file mode 100644 index 000000000..1cba13d95 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/qmldir @@ -0,0 +1,44 @@ +module qmlvideo + +CameraBasic 1.0 CameraBasic.qml +CameraDrag 1.0 CameraDrag.qml +CameraDummy 1.0 CameraDummy.qml +CameraFullScreen 1.0 CameraFullScreen.qml +CameraFullScreenInverted 1.0 CameraFullScreenInverted.qml +CameraItem 1.0 CameraItem.qml +CameraMove 1.0 CameraMove.qml +CameraOverlay 1.0 CameraOverlay.qml +CameraResize 1.0 CameraResize.qml +CameraRotate 1.0 CameraRotate.qml +CameraSpin 1.0 CameraSpin.qml +CameraContent 1.0 Content.qml +ErrorDialog 1.0 ErrorDialog.qml +Scene 1.0 Scene.qml +SceneBasic 1.0 SceneBasic.qml +SceneDrag 1.0 SceneDrag.qml +SceneFullScreen 1.0 SceneFullScreen.qml +SceneFullScreeninverted 1.0 SceneFullScreenInverted.qml +SceneMoved 1.0 SceneMove.qml +SceneMulti 1.0 SceneMulti.qml +SceneOverlay 1.0 SceneOverlay.qml +SceneResize 1.0 SceneResize.qml +SceneRotate 1.0 SceneRotate.qml +SceneSelectionPanel 1.0 SceneSelectionPanel.qml +SceneSpin 1.0 SceneSpin.qml +SeekControl 1.0 SeekControl.qml +VideoBasic 1.0 VideoBasic.qml +VideoDrag 1.0 VideoDrag.qml +VideoDummy 1.0 VideoDummy.qml +VideoFillMode 1.0 VideoFillMode.qml +VideoFullScreen 1.0 VideoFullScreen.qml +VideoFullScreenInverted 1.0 VideoFullScreenInverted.qml +VideoItem 1.0 VideoItem.qml +VideoMetadata 1.0 VideoMetadata.qml +VideoMove 1.0 VideoMove.qml +VideoOverlay 1.0 VideoOverlay.qml +VideoPlaybackRate 1.0 VideoPlaybackRate.qml +VideoResize 1.0 VideoResize.qml +VideoRotate 1.0 VideoRotate.qml +VideoSeek 1.0 VideoSeek.qml +VideoSpin 1.0 VideoSpin.qml +Main 1.0 Main.qml diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h b/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h new file mode 100644 index 000000000..8df5b5b64 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h @@ -0,0 +1,10 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtCore/QtGlobal> + +#if defined(QMLVIDEO_LIB) +#define QMLVIDEO_LIB_EXPORT Q_DECL_EXPORT +#else +#define QMLVIDEO_LIB_EXPORT Q_DECL_IMPORT +#endif diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp new file mode 100644 index 000000000..2b02fa505 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "videosingleton.h" + +VideoSingleton::VideoSingleton(QObject * parent) : QObject(parent) +{ } + +QUrl VideoSingleton::source1() const +{ + return m_source1; +} +void VideoSingleton::setSource1(const QUrl &source1) +{ + if (source1 == m_source1) + return; + m_source1 = source1; + emit source1Changed(); +} +QUrl VideoSingleton::source2() const +{ + return m_source2; +} +void VideoSingleton::setSource2(const QUrl &source2) +{ + if (source2 == m_source2) + return; + m_source2 = source2; + emit source2Changed(); +} +qreal VideoSingleton::volume() const +{ + return m_volume; +} +void VideoSingleton::setVolume(qreal volume) +{ + if (volume == m_volume) + return; + m_volume = volume; + emit volumeChanged(); +} +QUrl VideoSingleton::videoPath() const +{ + return m_videoPath; +} +void VideoSingleton::setVideoPath(const QUrl &videoPath) +{ + if (m_videoPath == videoPath) + return; + m_videoPath = videoPath; + emit videoPathChanged(); +} + +#include "moc_videosingleton.cpp" diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h new file mode 100644 index 000000000..3e0d56247 --- /dev/null +++ b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h @@ -0,0 +1,48 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef QMLVIDEOSINGLETON_H +#define QMLVIDEOSINGLETON_H + +#include "qmlvideo_global.h" + +#include <QtQml/qqml.h> + +class QMLVIDEO_LIB_EXPORT VideoSingleton : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl source1 READ source1 WRITE setSource1 NOTIFY source1Changed FINAL) + Q_PROPERTY(QUrl source2 READ source2 WRITE setSource2 NOTIFY source2Changed FINAL) + Q_PROPERTY(QUrl videoPath READ videoPath WRITE setVideoPath NOTIFY videoPathChanged FINAL) + Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged FINAL) + QML_SINGLETON + QML_ELEMENT + +public: + explicit VideoSingleton(QObject *parent = nullptr); + + QUrl source1() const; + void setSource1(const QUrl &source1); + QUrl source2() const; + void setSource2(const QUrl &source2); + QUrl videoPath() const; + void setVideoPath(const QUrl &videoPath); + qreal volume() const; + void setVolume(qreal volume); + +Q_SIGNALS: + void source1Changed(); + void source2Changed(); + void volumeChanged(); + void videoPathChanged(); + +private: + QUrl m_source1; + QUrl m_source2; + QUrl m_videoPath; + qreal m_volume = 0.5; +}; + +QML_DECLARE_TYPE(VideoSingleton); + +#endif // QMLVIDEOSINGLETON_H |