このページでは

QML ビデオレコーダー

Qt Quick を使用した音声および動画の録画。

ライブプレビューと録画操作機能を備えたメディアレコーダーインターフェース

QML レコーダーは、マイク、カメラ、または画面キャプチャを使用して、音声と動画を別々に、あるいは同時に記録できるシンプルなアプリケーションの例です。

例の動作

サンプルは以下の手順で実行できます。

概要

本質的には、これは QML アプリケーションです。「Qt Quick アプリケーションの入門」を参照してください。このドキュメントでは、この例が Qt Multimedia QML Typesを使用する方法に焦点を当てています。

このサンプルでは、CaptureSession に接続されたQMLのCamera およびAudioInput タイプを使用しています。その後、MediaRecorder オブジェクトを使用して、キャプチャされたオーディオとビデオを記録します。

QtMultimedia に加え、Qt Quick のウィンドウ、コントロール、レイアウトの機能を使用して、グラフィカルユーザーインターフェースと機能を実装しています。再生機能についてはここでは扱いません。それについては、「QML Media Player サンプル」を参照してください。

このサンプルでは、以下の機能を実演しています:

  • 入力デバイスを選択できます。
  • 入力タイプを切り替えることができます。
  • 画質、コーデックの選択、ファイル形式、メタデータの割り当てなど、キャプチャに関する設定が可能です。
  • キャプチャされたファイルは保存され、再生可能です。

録画

このアプリケーションは録画機能を実装しています。

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 = Screen.orientation;
                    videoOutput.grabToImage(function(res) { mediaList.mediaThumbnail = res.url })
                }
            }
        onActualLocationChanged: (url) => { mediaList.mediaUrl = url }
        onErrorOccurred: { recorderErrorText.text = recorder.errorString; recorderError.open(); }
    }

mediaList 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.qml で定義され、Main.qml で宣言されています。

そのルートは、Columns inputControls 、recordButton 、optionButtons を含むRow であり、これらはそれぞれ独自の .qml ファイルで定義されています。

動画ソースの選択

VideoSourceSelect.qml で定義されているVideoSourceSlect は、Switch とComboBox で構成されており、ユーザーが利用可能なカメラから選択できるようにします。

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:
            root.cameraAvailable
            && PermissionHelper.cameraStatus === Qt.PermissionStatus.Granted
    }

    ScreenCapture {
        id: screenCapture
        active: root.screenAvailable
    }

    WindowCapture {
        id: windowCapture
        active: root.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

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.