QML 视频录制器

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

QML Recorder demonstrates a simple application that can record audio and video separate or together, using a microphone, a camera, or with screen capturing.

运行范例

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

概述

At its core, this is a QML application, see 采用 Qt Quick 快速入门编程 . This documentation is focused on how this example uses the Qt Multimedia QML 类型 .

The example uses the QML Camera and AudioInput types connected to a CaptureSession MediaRecorder object is then used to record the captured audio and video.

除了 QtMultimedia , features of Qt Quick Windows, Controls, and Layouts are used to implement the graphic user interface and functionality. Playback won't be covered here, for that see the QML 媒体播放器范例 .

The example demonstrates the following:

  • Input devices can be selected.
  • An input type switched off.
  • Settings for capturing such as quality, codec choice, file format, and assigning metadata.
  • Captured files are stored and can be played back.

Recording

The application implements recording.

captureSession

main.qml , captureSession is declared like so:

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

main.qml MediaRecorder recorder handles recording media as well as capturing a thumbnail for the file and appending it to a 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(); }
    }
					

mediaList is declared in the Frame 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

These are defined in Controls.qml and declared in main.qml.

Its root is a Row 包含 Columns inputControls , recordButton , optionButtons , each defined in their own .qml files.

Selecting a video source

Defined in VideoSourceSelect.qml , VideoSourceSlect is comprised of a Switch ComboBox and enables the user to select from available cameras.

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 , declared in the above snippet, assigns the current video source.

    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
					
Selecting an audio input

Implemented in the same way as Selecting a video source and defined in 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