16. 媒体录制
一、媒体捕获
多媒体设备是指本机中的音频输入设备(如麦克风)、音频输出设备(如音箱、头戴耳机)和视频输入设备(如摄像头)。多媒体设备 通过 MediaDevices 类提供的方法来获取,音频输入输出设备类 是 AudioDevice,视频输入设备类 是 CameraDevice。
我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
MediaDevices 类型用于 提供可用多媒体设备和系统默认值的信息,它主要监控音频输入设备(麦克风)、音频输出设备(扬声器、耳机)和视频输入设备(摄像头)。该类型的常用属性如下:
audioInputs : list<audioDevice> // 获取相应音频输入设备组的列表
defaultAudioInput : audioDevice // 获取相应音频输入设备组的系统默认设备
audioOutputs : list<audioDevice> // 获取相应音频输出设备组的列表
defaultAudioOutput : audioDevice // 获取相应音频输出设备组的系统默认设备
videoInputs : list<cameraDevice> // 获取相应视频输入设备组的列表
defaultVideoInput : cameraDevice // 获取相应视频输入设备组的系统默认设备
CaptureSession 是 管理本地设备上媒体捕获的中心类型,用于捕获的输入输出设备通过该类型进行统一管理,例如可以将相机和音频输入对象绑定到 CaptureSession。该类型的常用属性如下:
audioInput : AudioInput // 音频输入对象
audioOutput : AudioOutput // 音频输出对象
videoOutput : VideoOutput // 视频输出对象
camera : Camera // 相机
imageCapture : ImageCapture // 图形捕获对象
recorder : MediaRecorder // 音视频录制对象
screenCapture : ScreenCapture // 捕捉屏幕画面的设备
windowCapture : WindowCapture // 用于遮挡窗口的物体
二、图像捕获
Camera 类型可以在 CaptureSession 中用于 视频录制和图像拍摄。可以使用 MediaDevices 获得可用的相机并指定要使用的相机,然后调用 start() 来开启相机,调用 stop() 来关闭相机。
Camera 对象用来访问和控制系统的物理设备,完成拍照功能,它提供了多个属性来控制拍摄和图像的处理。
active : bool // 当前相机是否已激活
cameraDevice : cameraDevice // 当前激活的摄像头设备
cameraFormat : cameraFormat // 当前激活的相机格式
supportedFeatures : Features // 只读属性,当前相机支持的特色
whiteBalanceMode : WhiteBalanceMode // 白平衡模式
focusDistance : real // 聚焦距离
focusMode : enumeration // 聚焦方式
focusPoint : point // 当前自动对焦系统所选定用于对焦的点
customFocusPoint : point // 自定义聚焦位置
zoomFactor : real // 数字变焦倍数
minimumZoomFactor : real // 最小数字变焦倍数
maximumZoomFactor : real // 最大数字变焦倍数
exposureMode : ExposureMode // 曝光模式
exposureCompensation : real // 曝光补偿值
exposureTime : real // 曝光时间
manualExposureTime : real // 手动曝光时间
isoSensitivity : int // ISO感光度
manualIsoSensitivity : int // 手动ISO感光度
flashReady : bool // 闪光灯是否就绪
flashMode : enumeration // 闪光灯模式
torchMode : Camera::TorchMode // 手电筒模式
error : enumeration // 错误类型
errorString : string // 错误描述字符串
我们可以使用 Camera 对象的 whiteBalanceMode 属性 设置白平衡模式,它是一个枚举值,可以取值如下:
Camera.WhiteBalanceAuto // 自动白平衡模式
Camera.WhiteBalanceManual // 手动白平衡模式
Camera.WhiteBalanceSunlight // 日光白平衡模式
Camera.WhiteBalanceCloudy // 多云白平衡模式
Camera.WhiteBalanceShade // 阴影白平衡模式
Camera.WhiteBalanceTungsten // 钨灯白平衡模式
Camera.WhiteBalanceFluorescent // 荧光灯白平衡模式
Camera.WhiteBalanceFlash // 闪光灯白平衡模式
Camera.WhiteBalanceSunset // 日落白平衡模式
我们可以使用 Camera 对象的 focusMode 属性 设置聚焦方式,它是一个枚举值,可以取值如下:
Camera.FocusModeAuto // 连续自动对焦模式
Camera.FocusModeAutoNear // 持续自动对焦,更倾向于聚焦于靠近相机的物体
Camera.FocusModeAutoFar // 持续自动对焦,更倾向于聚焦于离相机较远的物体
Camera.FocusModeHyperfocal // 超焦距模式,将焦距调整至超焦距位置,从而实现最大的景深效果
Camera.FocusModeInfinity // 无限焦距模式,将焦距调整至无限大
Camera.FocusModeManual // 手动聚焦模式,镜头的聚焦距离被设定为由focusDistance指定的值
我们可以使用 Camera 对象的 exposureMode 属性 设置曝光模式,它是一个枚举值,可以取值如下:
Camera.ExposureAuto // 自动曝光模式
Camera.ExposureManual // 手动曝光模式
Camera.ExposurePortrait // 人像模式
Camera.ExposureNight // 夜景模式
Camera.ExposureSports // 运动模式
Camera.ExposureSnow // 雪景模式
Camera.ExposureBeach // 海滩模式
Camera.ExposureAction // 动作模式
Camera.ExposureLandscape // 风景模式
Camera.ExposureNightPortrait // 夜景人像模式
Camera.ExposureTheatre // 剧院模式
Camera.ExposureSunset // 日落模式
Camera.ExposureSteadyPhoto // 稳定拍摄模式
Camera.ExposureFireworks // 烟花模式
Camera.ExposureParty // 派对模式
Camera.ExposureCandlelight // 烛光模式
Camera.ExposureBarcode // 条形码模式
我们可以使用 Camera 对象的 flashMode 属性 设置闪光灯模式,它是一个枚举值,可以取值如下:
Camera.FlashOff // 关闭闪光灯
Camera.FlashOn // 开启闪光灯
Camera.FlashAuto // 自动
我们可以使用 torchMode 属性 设置手电筒模式,它是一个枚举值,可以取值如下:
Camera.TorchOff // 关闭手电筒
Camera.TorchOn // 开启手电筒
Camera.TorchAuto // 自动
我们可以使用 error 属性 获取错误类型,它是一个枚举值,可能的取值如下:
Camera.NoError // 无错误
Camera.CameraError // 相机错误
如果我们想要 拍摄静态照片,则可以使用 ImageCapture 类型,它的常用属性如下:
fileFormat : enumeration // 图像文件格式
supportedFormats : list<FileFormat> // 支持的图像文件格式列表
metaData : mediaMetaData // 嵌入图像的元数据
preview : string // 只读属性,最新抓取图像的URL
quality : enumeration // 图像质量
readyForCapture : bool // 指示是否准备好捕获图像
我们可以使用 ImageCapture 类型的 fileFormat 属性用来 指定图像的保存格式,它是一个枚举值,目前可以支持的格式如下:
ImageCapture.UnspecifiedFormat // 未指定格式
ImageCapture.JPEG // .jpg或.jpeg格式
ImageCapture.PNG // .png格式
ImageCapture.WebP // .webp格式
ImageCapture.Tiff // .tiff格式
我们可以使用 ImageCapture 类型的 quality 属性 设置保存的图像质量,它是一个枚举值,可以取值如下:
ImageCapture.VeryLowQuality // 非常低质量
ImageCapture.LowQuality // 低质量
ImageCapture.NormalQuality // 正常质量
ImageCapture.HighQuality // 高质量
ImageCapture.VeryHighQuality // 非常高质量
如果我们要使用 ImageCapture ,需要先在 CaptureSession 中进行绑定,然后调用 capture() 来 捕获图像,一旦捕获成功,就可以使用 preview 属性 获取捕获图像的路径并通过 Image 进行预览,或者通过 saveToFile(location) 方法 保存到指定的位置。另外,也可以通过 captureToFile(location) 函数 直接完成捕获和保存操作。当图像捕获成功或者保存成功时,会分别发射 imageCaptured() 和 imageSaved() 信号。如果出现问题,会发射 errorOccurred() 信号。
新建 template.py 文件。
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtQml import QQmlApplicationEngine
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
engine = QQmlApplicationEngine() # 2.创建QML引擎对象
engine.load("template.qml") # 3.加载QML文件
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
新建 template.qml 文件。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtMultimedia
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
ColumnLayout {
anchors.fill: parent
spacing: 10
Row {
topPadding: 10
leftPadding: 10
spacing: 10
ToolButton {
id: saveFolderButtonId
width: 90
text: "选择保存位置"
onClicked: {
folderDialogId.open() // 打开文件对话框
// 如果用户选择了文件夹,则将其设置为当前文件夹
if (folderDialogId.selectedFolder) {
folderDialogId.currentFolder = folderDialogId.selectedFolder
}
}
}
Text {
id: folderPathTextId
topPadding: (saveFolderButtonId.height - font.pointSize) / 2
text: folderDialogId.currentFolder.toString().replace("file:///", "")
}
}
ToolBar {
Row {
ToolButton {
width: 90
text: "打开摄像头"
onClicked: {
if (text === "打开摄像头") {
captureSessionId.camera.start() // 打开摄像头
text = "关闭摄像头"
takePhotoButtonId.enabled = true
} else {
captureSessionId.camera.stop() // 关闭摄像头
text = "打开摄像头"
takePhotoButtonId.enabled = false
}
}
}
ToolButton {
id: takePhotoButtonId
width: 90
text: "拍照"
enabled: false
onClicked: {
if (folderPathTextId.text) {
var fileName = folderPathTextId.text + "/" + Date.now() + ".jpeg"
captureSessionId.imageCapture.captureToFile(fileName) // 拍照并保存到文件
}
}
}
}
}
VideoOutput {
id: videoOutputId
Layout.fillWidth: true
Layout.fillHeight: true
}
}
CaptureSession {
id: captureSessionId
camera: cameraId // 相机
imageCapture: imageCaptureId // 图形捕获对象
videoOutput: videoOutputId // 视频输出对象
}
Camera {
id: cameraId
}
ImageCapture {
id: imageCaptureId
fileFormat: ImageCapture.JPEG // 图像文件格式
quality: ImageCapture.HighQuality // 图像质量
}
FolderDialog {
id: folderDialogId
title: "选择保存位置"
onAccepted: {
folderPathTextId.text = folderDialogId.currentFolder.toString().replace("file:///", "")
folderPathTextId.text = folderPathTextId.text
}
}
}
三、媒体录制
我们可以使用 MediaRecorder 对相机和麦克风捕获的视频和音频进行录制,它需要绑定到 CaptureSession 中。它的常用属性如下:
encodingMode : enumeration // 编码模式
recorderState : enumeration // 录像状态
quality : enumeration // 录像质量
duration : qint64 // 录制时长,单位是毫秒
metaData : mediaMetaData // 元数据
mediaFormat : mediaFormat // 记录器当前的媒体格式
audioBitRate : int // 压缩音频流的比特率
audioChannelCount : int // 音频通道数
audioSampleRate : int // 音频的采样率
videoBitRate : int // 压缩视频流的比特率
videoFrameRate : int // 视频的帧率
videoResolution : Size // 视频的分辨率
outputLocation : url // 输出路径
actualLocation : url // 只读属性,最后的媒体内容的实际存放位置
error : enumeration // 记录器当前的错误状态
errorString : string // 记录器当前的错误字符串
我们可以调用 record() 函数 开始录制,调用 pause() 函数 暂停录制,调用 stop() 函数 停止录制,每当调用一个方法,就会发射 recorderStateChanged() 信号,可以通过 recorderState 属性 获取录制状态。我们可以通过 duration 属性 获取录制时长,单位是毫秒,每当 duration 的值改变时,都会发射 durationChanged() 信号。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtMultimedia
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 800 // 窗口的宽度
height: 600 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
ColumnLayout {
anchors.fill: parent
spacing: 10
Row {
topPadding: 10
leftPadding: 10
spacing: 10
ToolButton {
id: saveFolderButtonId
width: 90
text: "选择保存位置"
onClicked: {
folderDialogId.open() // 打开文件对话框
// 如果用户选择了文件夹,则将其设置为当前文件夹
if (folderDialogId.selectedFolder) {
folderDialogId.currentFolder = folderDialogId.selectedFolder
}
}
}
Text {
id: folderPathTextId
topPadding: (saveFolderButtonId.height - font.pointSize) / 2
text: folderDialogId.currentFolder.toString().replace("file:///", "")
}
}
ToolBar {
Row {
ToolButton {
width: 90
text: "打开摄像头"
onClicked: {
if (text === "打开摄像头") {
cameraId.start() // 打开摄像头
text = "关闭摄像头"
takePhotoButtonId.enabled = true
} else {
cameraId.stop() // 关闭摄像头
mediaRecorderId.stop() // 停止录制
text = "打开摄像头"
takePhotoButtonId.enabled = false
stopPhotoButtonId.enabled = false
}
}
}
ToolButton {
id: takePhotoButtonId
width: 90
text: "开始录像"
enabled: false
onClicked: {
if (text === "开始录像" || text === "继续录像") {
mediaRecorderId.record() // 开始录制
text = "暂停录像"
stopPhotoButtonId.enabled = true
} else if (text == "暂停录像"){
mediaRecorderId.pause() // 暂停录制
text = "继续录像"
}
}
}
ToolButton {
id: stopPhotoButtonId
width: 90
text: "停止录像"
enabled: false
onClicked: {
mediaRecorderId.stop() // 停止录制
takePhotoButtonId.text = "开始录像"
}
}
}
}
VideoOutput {
id: videoOutputId
Layout.fillWidth: true
Layout.fillHeight: true
}
}
CaptureSession {
id: captureSessionId
camera: cameraId // 相机
audioInput: audioInputId // 音频输入对象
recorder: mediaRecorderId // 音视频录制对象
videoOutput: videoOutputId // 视频输出对象
}
Camera {
id: cameraId
}
AudioInput {
id: audioInputId
}
MediaRecorder {
id: mediaRecorderId
outputLocation: folderPathTextId.text + "/" + Date.now() + ".mp4"
}
FolderDialog {
id: folderDialogId
title: "选择保存位置"
onAccepted: {
folderPathTextId.text = folderDialogId.currentFolder.toString().replace("file:///", "")
folderPathTextId.text = folderPathTextId.text
}
}
}

浙公网安备 33010602011771号