QML 비디오 레코더

Qt Quick 을 사용하여 오디오 및 동영상 녹화하기.

QML 레코더는 마이크, 카메라 또는 화면 캡처를 사용하여 오디오와 비디오를 개별적으로 또는 함께 녹화할 수 있는 간단한 애플리케이션을 보여줍니다.

예제 실행하기

에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 예제 만들기 및 실행하기를 참조하세요.

개요

이 예제의 핵심은 QML 애플리케이션입니다( Qt Quick 으로 프로그래밍 시작하기 참조). 이 문서는 이 예제에서 어떻게 Qt Multimedia QML Types.

이 예에서는 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.qml 에 정의되어 있고 main.qml 에 선언되어 있습니다.

루트는 Columns inputControls , recordButton, optionButtons, 각각 자체 .qml 파일에 정의된 것을 포함하는 Row 입니다.

비디오 소스 선택

VideoSourceSelect.qml 에 정의된 VideoSourceSlectSwitchComboBox 로 구성되어 있으며 사용자가 사용 가능한 카메라 중에서 선택할 수 있습니다.

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

© 2025 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.