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

QML 录制器演示了一个简单的应用程序,该应用程序可单独或同时录制音频和视频,支持使用麦克风、摄像头或屏幕录制功能。
运行示例
您可以通过以下方式运行该示例:
- Qt Creator
打开Welcome 模式,并从Examples 中选择该示例。有关更多信息,请参阅Qt Creator :教程:构建和运行。
- Qt Extension for Visual Studio Code
在Command Palette 中运行Qt: Open Qt examples 命令,并从列表中选择该示例。有关更多信息,请参阅Qt Extension for Visual Studio Code :教程:构建和运行。
概述
从本质上讲,这是一个 QML 应用程序,请参阅《Qt Quick 应用程序入门》。本文档重点介绍该示例如何使用 Qt Multimedia QML Types。
该示例使用了连接到CaptureSession 的QMLCamera 和AudioInput 类型。随后,通过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 = 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 中声明。
其根节点是一个Row ,其中包含Columns 、inputControls 、recordButton 和optionButtons ,它们各自在各自的.qml文件中定义。
选择视频源
VideoSourceSlect 定义在VideoSourceSelect.qml 中,由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"© 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.