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號