QML 录像机

使用 Qt Quick 录制音频和视频。

QML 录像机演示了一个简单的应用程序,可以单独或同时录制音频和视频,使用麦克风、相机或屏幕捕捉。

运行示例

要从 Qt Creator 运行示例,请打开欢迎模式并从示例中选择示例。有关更多信息,请访问构建和运行示例

概述

从核心来看,这是一个 QML 应用程序,请参阅使用 Qt Quick 开发程序入门。本文档着重介绍此示例如何使用 Qt Multimedia QML 类型

该示例使用连接到 CaptureSession 的 QML CameraAudioInput 类型。然后使用 MediaRecorder 对象来录制捕获的音频和视频。

除了 QtMultimedia 之外,还使用了 Qt Quick 窗口、控件和布局的功能来实现图形用户界面和功能。不会介绍播放功能,有关播放功能,请参阅QML 媒体播放器示例

该示例演示了以下内容

  • 可以选择输入设备。
  • 关闭了输入类型。
  • 捕获设置,如质量、编解码器选择、文件格式以及分配元数据。
  • 捕获的文件被存储并可播放。

录制

应用程序实现了录制功能。

captureSession

main.qml 中,captureSession 被声明如下

    CaptureSession {
        id: captureSession
        recorder: recorder
        audioInput: controls.audioInput
        camera: controls.camera
        screenCapture: controls.screenCapture
        windowCapture: controls.windowCapture
        videoOutput: videoOutput
    }
recorder

main.qml 中,MediaRecorder recorder 负责录制媒体以及捕获文件的缩略图并将其添加到 ListModel mediaList 中。

    MediaRecorder {
        id: recorder
        onRecorderStateChanged:
            (state) => {
                if (state === MediaRecorder.StoppedState) {
                    root.contentOrientation = Qt.PrimaryOrientation
                    mediaList.append()
                } else if (state === MediaRecorder.RecordingState && captureSession.camera) {
                    // lock orientation while recording and create a preview image
                    root.contentOrientation = root.screen.orientation;
                    videoOutput.grabToImage(function(res) { mediaList.mediaThumbnail = res.url })
                }
            }
        onActualLocationChanged: (url) => { mediaList.mediaUrl = url }
        onErrorOccurred: { recorderErrorText.text = recorder.errorString; recorderError.open(); }
    }

mediaListFrame mediaListFrame 中声明

    Frame {
        id: mediaListFrame
        height: 150
        width: parent.width
        anchors.bottom: controlsFrame.top
        x: controls.capturesVisible ? 0 : parent.width
        background: Rectangle {
            anchors.fill: parent
            color: palette.base
            opacity: 0.8
        }

        Behavior on x { NumberAnimation { duration: 200 } }

        MediaList {
            id: mediaList
            anchors.fill: parent
            playback: playback
controls

这些在 Controls.qml 中定义,并在 main.qml 中声明。

它的根是一个 Row,其中包含定义在各自的 .qml 文件中的 Columns inputControlsrecordButtonoptionButtons

选择视频源

定义在 VideoSourceSelect.qml 中,VideoSourceSelect 包含一个 开关 和一个 组合框,允许用户从可用的摄像头中选择。

Row {
    id: root
    height: Style.height
    property Camera selectedCamera: cameraAvailable ? camera : null
    property ScreenCapture selectedScreenCapture: screenAvailable ? screenCapture : null
    property WindowCapture selectedWindowCapture: windowAvailable ? windowCapture : null

    property bool sourceAvailable: typeof comboBox.currentValue !== 'undefined' &&
                                   comboBox.currentValue.type !== 'toggler' &&
                                   videoSourceSwitch.checked

    property bool cameraAvailable: sourceAvailable && comboBox.currentValue.type === 'camera'
    property bool screenAvailable: sourceAvailable && comboBox.currentValue.type === 'screen'
    property bool windowAvailable: sourceAvailable && comboBox.currentValue.type === 'window'

    Component.onCompleted: {
        videoSourceModel.populate()

        for (var i = 0; i < videoSourceModel.count; i++) {
            if (videoSourceModel.get(i).value.type !== 'toggler') {
                comboBox.currentIndex = i
                break
            }
        }
    }

    Camera {
        id: camera
        active: cameraAvailable
    }

    ScreenCapture {
        id: screenCapture
        active: screenAvailable
    }

    WindowCapture {
        id: windowCapture
        active: windowAvailable
    }

    MediaDevices { id: mediaDevices
        onVideoInputsChanged: {

            videoSourceModel.populate()

            for (var i = 0; i < videoSourceModel.count; i++) {
                if (videoSourceModel.get(i).value.type !== 'toggler') {
                    comboBox.currentIndex = i
                    break
                }
            }
        }
    }

    Switch {
        id: videoSourceSwitch
        anchors.verticalCenter: parent.verticalCenter
        checked: true
    }

    ListModel {
        id: videoSourceModel
        property var enabledSources: {
            'camera': true,
            'screen': true,
            'window': false
        }

        function toggleEnabledSource(type) {
            enabledSources[type] = !enabledSources[type]
            populate()
        }

        function appendItem(text, value) {
            append({ text: text, value: value})
        }

        function appendToggler(name, sourceType) {
            appendItem((enabledSources[sourceType] ? "- Hide " : "+ Show ") + name,
                       { type: 'toggler', 'sourceType': sourceType })
        }

        function populate() {
            clear()

            appendToggler('Cameras', 'camera')
            if (enabledSources['camera'])
                for (var camera of mediaDevices.videoInputs)
                    appendItem(camera.description, { type: 'camera', camera: camera })

            appendToggler('Screens', 'screen')
            if (enabledSources['screen'])
                for (var screen of Application.screens)
                    appendItem(screen.name, { type: 'screen', screen: screen })

            appendToggler('Windows', 'window')
            if (enabledSources['window'])
                for (var window of windowCapture.capturableWindows())
                    appendItem(window.description, { type: 'window', window: window })
        }
    }

在上面的代码段中声明的 comboBox 被赋值为当前视频源。

    ComboBox {
        id: comboBox
        width: Style.widthLong
        height: Style.height
        background: StyleRectangle { anchors.fill: parent }
        model: videoSourceModel
        displayText: typeof currentValue === 'undefined' ||
                     currentValue.type === 'toggler' ? "Unavailable" : currentText
        font.pointSize: Style.fontSize
        textRole: "text"
        valueRole: "value"
        onCurrentValueChanged: {
            if (typeof currentValue === 'undefined')
                return
            if (currentValue.type === 'screen')
                screenCapture.screen = currentValue.screen
            else if (currentValue.type === 'camera')
                camera.cameraDevice = currentValue.camera
选择音频输入

选择视频源 以相同方式实现,并在 AudioInputSelect.qml 中定义,如下所示

Row {
    id: root
    height: Style.height
    property AudioInput selected: available ? audioInput : null
    property bool available: (typeof comboBox.currentValue !== 'undefined') && audioSwitch.checked

    Component.onCompleted: {
        audioInputModel.populate()
        comboBox.currentIndex = 0
    }

    MediaDevices { id: mediaDevices }

    AudioInput { id: audioInput; muted: !audioSwitch.checked }

    Switch {
        id: audioSwitch;
        height: Style.height;
        checked: true
    }

    ListModel {
        id: audioInputModel
        property var audioInputs: mediaDevices.audioInputs

        function populate() {
            audioInputModel.clear()

            for (var audioDevice of audioInputs)
                audioInputModel.append({ text: audioDevice.description, value:
                                        { type: 'audioDevice', audioDevice: audioDevice } })
        }
    }
    ComboBox {
        id: comboBox
        width: Style.widthLong
        height: Style.height
        background: StyleRectangle { anchors.fill: parent }
        model: audioInputModel
        textRole: "text"
        font.pointSize: Style.fontSize
        displayText: typeof currentValue === 'undefined' ? "unavailable" : currentText
        valueRole: "value"

示例项目 @ code.qt.io

© 2024 Qt 公司有限公司。此处包含的文档贡献者是各自所有者的版权。所提供的文档是根据自由软件基金会发布的 GNU 自由文档许可版本 1.3 的条款许可的。Qt 及其相关标志是芬兰及其他国家的 Qt 公司商标。所有其他商标均为其各自所有者的财产。