15. 媒体播放器
一、播放压缩音频
MediaPlayer 是 QML 提供的核心多媒体类,可以用来播放压缩音频或者视频。要使用 MediaPlayer,需要引入 QtMultimedia 模块,在 QML 文档的开始加入 import QtMultimedia 语句。
我们可以在终端中使用 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
MediaPlayer 类型可以用来播放压缩音频或者视频。要播放音频,需要为 MediaPlayer 的 audioOutput 属性设置一个 AudioOutput 对象,常用的属性如下:
device : AudioDevice // 此输出所连接的音频设备
volume : real // 音量的大小,取值范围是0.0~1.0,默认值是1.0。
muted : bool // 是否静音,默认值是false
MediaPlayer 常用属性如下:
audioOutput : AudioOutput // 音频输出设备
source : url // 音频文件的路径
autoPlay : bool // 是否自动播放,默认值是false
loops : int // 播放循环次数,默认值是1
duration : int // 音频的持续时间
position : int // 当前播放位置,单位是毫秒
playbackRate : real // 播放速率,默认值是1.0
playbackState : enumeration // 播放状态
metaData : mediaMetaData // 音频文件的元数据
我们可以通过 MediaPlayer 的 source 属性 指定音频文件的路径,它的类型是 url,它能接受绝对路径、相对路径、有效的 http 链接。通过 loops 属性可以 设置播放的循环次数,当设置为 0 或 1 时 只会播放一次,当设置为 MediaPlayer::Infinite 时会 无限循环播放,其默认值为 1。
我们可以使用 playbackState 属性 获取播放状态,它是一个枚举值,可以取值如下:
PlayingState // 正在播放状态
PausedState // 暂停状态
StoppedState // 停止状态
我们可以通过 MediaPlayer 的 play()、pause() 和 stop() 等方法进行 播放、暂停 和 停止 等操作。当进行完这些操作时会发射 playbackStateChanged() 信号,如果发生错误,会发射 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 QtMultimedia
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 400 // 窗口的宽度
height: 100 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
MediaPlayer {
id: mediaPlayerId
audioOutput: AudioOutput {
id: audioOutputId
volume: volumeSliderId.value / 100 // 音量范围0.0-1.0
muted: muteCheckBoxId.checked // 是否静音
}
}
Column {
anchors.centerIn: parent
padding: 10
spacing: 10
Text {
id: textId
text: "请选择文件"
}
Row {
spacing: 10
Text {
text: "音量"
}
Slider {
id: volumeSliderId
width: 300
from: 0.0
to: 100
stepSize: 1
value: 80
}
}
Row {
spacing: 10
Button {
width: 90
text: "选择音频文件"
onClicked: {
fileDialogId.open() // 打开文件对话框
}
}
Button {
id: playButtonId
width: 90
enabled: false
text: "开始播放"
onClicked: {
if (mediaPlayerId.source == "") {
textId.text = "请先选择音频文件"
return
} else if (playButtonId.text == "开始播放" || playButtonId.text == "继续播放") {
mediaPlayerId.play() // 播放音频
playButtonId.text = "暂停播放"
stopButtonId.enabled = true
} else if (playButtonId.text == "暂停播放") {
mediaPlayerId.pause() // 暂停播放
playButtonId.text = "继续播放"
}
}
}
Button {
id: stopButtonId
width: 90
enabled: false
text: "停止播放"
onClicked: {
if (mediaPlayerId.source == "") {
textId.text = "请先选择音频文件"
return
} else {
mediaPlayerId.stop() // 停止播放
playButtonId.text = "开始播放"
stopButtonId.enabled = false
}
}
}
CheckBox {
id: muteCheckBoxId
text: "是否静音"
}
}
}
// 文件对话框
FileDialog {
id: fileDialogId
title: "选择音频文件" // 文件对话框的标题
nameFilters: ["音频文件(*.wav *.flac *.mp3)"] // 文件对话框的文件类型过滤器
onAccepted: {
// FileDialog的selectedFile保存了用户选择的文件路径,如果是多个文件,则显示第一个
var names = selectedFile.toString().split("/")
var fileName = names[names.length - 1]
textId.text = "你选择了【" + fileName + "】音频文件"
mediaPlayerId.stop() // 停止播放
mediaPlayerId.source = selectedFile // 设置音频文件路径
playButtonId.text = "开始播放"
playButtonId.enabled = true
stopButtonId.enabled = false
}
}
}
二、播放无损音频
我们可以使用 SoundEffect 类型通过 低延迟 的方式播放 未压缩 的音频文件。如果不需要低延迟,那么建议使用 MediaPlayer 类型。一般 SoundEffect 类型播放的声音都是会被多次使用的。它允许提前进行解析并且准备完毕,在需要的时候只需要触发即可。SoundEffect 的常用属性如下:
source : url // 音频文件的路径
volume : real // 音量的大小,取值范围是0.0~1.0,默认值是1.0
playing : bool // 是否正在播放,默认值是false
muted : bool // 是否静音,默认值是false
loops : int // 播放循环次数,默认值是1
loopsRemaining : int // 播放循环次数剩余,默认值是loops
status : enumeration // 播放状态
我们可以通过 SoundEffect 的 play() 和 stop() 等方法进行 播放 和 停止 等操作。
修改 template.qml 文件的内容。
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtMultimedia
// Window控件表示一个顶级窗口
// 在QML中,元素是通过大括号{}内的属性来配置的。
Window {
width: 300 // 窗口的宽度
height: 100 // 窗口的高度
visible: true // 显示窗口
color: "lightgray" // 窗口的背景颜色
SoundEffect {
id: soundEffectId
muted: muteCheckBoxId.checked
volume: volumeSliderId.value / 100
}
Column {
anchors.centerIn: parent
padding: 10
spacing: 10
Text {
id: textId
text: "请选择文件"
}
Row {
spacing: 10
Text {
text: "音量"
}
Slider {
id: volumeSliderId
width: 240
from: 0.0
to: 100
stepSize: 1
value: 80
}
}
Row {
spacing: 10
Button {
width: 90
text: "选择音频文件"
onClicked: {
fileDialogId.open() // 打开文件对话框
}
}
Button {
id: playButtonId
width: 90
enabled: false
text: "开始播放"
onClicked: {
if (soundEffectId.source == "") {
textId.text = "请先选择音频文件"
return
} else if (playButtonId.text == "开始播放") {
soundEffectId.play() // 播放音频
playButtonId.text = "停止播放"
} else if (playButtonId.text == "停止播放") {
soundEffectId.stop() // 停止播放
playButtonId.text = "开始播放"
}
}
}
CheckBox {
id: muteCheckBoxId
text: "是否静音"
}
}
}
// 文件对话框
FileDialog {
id: fileDialogId
title: "选择音频文件" // 文件对话框的标题
nameFilters: ["音频文件(*.wav)"] // 文件对话框的文件类型过滤器
onAccepted: {
// FileDialog的selectedFile保存了用户选择的文件路径,如果是多个文件,则显示第一个
var names = selectedFile.toString().split("/")
var fileName = names[names.length - 1]
textId.text = "你选择了【" + fileName + "】音频文件"
soundEffectId.stop() // 停止播放
soundEffectId.source = selectedFile // 设置音频文件路径
playButtonId.text = "开始播放"
playButtonId.enabled = true
}
}
}
三、视频播放
要使用 MediaPlayer 播放视频,除了 指定音频输出,还需要使用 videoOutput 属性 指定视频输出对象 VideoOutput。VideoOutput 类型中有一个 orientation 属性,可以 设置视频的方向,通过指定一个度数(需要是 90 的倍数)来旋转视频,逆时针方向为正值。另外,该类型还包含一个 fillMode 属性,用来定义 视频如何缩放来适应窗口,它是一个枚举值,可以取值如下:
VideoOutput.PreserveAspectFit // 默认值,视频宽高按比例进行缩放,不会进行裁剪
VideoOutput.PreserveAspectCrop // 视频宽高按比例进行缩放,在必要时会进行裁剪
VideoOutput.Stretch // 视频进行缩放来适应窗口大小
修改 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" // 窗口的背景颜色
MediaPlayer {
id: mediaPlayerId
audioOutput: AudioOutput {
id: audioOutputId
volume: volumeSliderId.value / 100 // 音量范围0.0-1.0
muted: muteCheckBoxId.checked // 是否静音
}
videoOutput: videoOutputId // 视频输出
playbackRate: Number(speedComboBoxId.currentText) // 播放速度
}
ColumnLayout {
anchors.fill: parent
VideoOutput {
id: videoOutputId
Layout.fillWidth: true
Layout.fillHeight: true
fillMode: VideoOutput.PreserveAspectCrop // 视频输出模式
}
RowLayout {
spacing: 10
Text {
text: "进度"
Layout.leftMargin : 10
}
Slider {
id: progressSliderId
Layout.fillWidth: true
Layout.rightMargin : 10
from: 0
to: mediaPlayerId.duration
value: mediaPlayerId.position
enabled: false
onMoved: {
mediaPlayerId.position = value
}
}
}
Row {
padding: 10
spacing: 10
Button {
width: 90
text: "选择视频文件"
onClicked: {
fileDialogId.open() // 打开文件对话框
}
}
Button {
id: playButtonId
width: 90
enabled: false
text: "开始播放"
onClicked: {
if (mediaPlayerId.source == "") {
textId.text = "请先选择音频文件"
return
} else if (playButtonId.text == "开始播放" || playButtonId.text == "继续播放") {
mediaPlayerId.play() // 播放音频
playButtonId.text = "暂停播放"
stopButtonId.enabled = true
progressSliderId.enabled = true
} else if (playButtonId.text == "暂停播放") {
mediaPlayerId.pause() // 暂停播放
playButtonId.text = "继续播放"
}
}
}
Button {
id: stopButtonId
width: 90
enabled: false
text: "停止播放"
onClicked: {
mediaPlayerId.stop() // 停止播放
playButtonId.text = "开始播放"
stopButtonId.enabled = false
progressSliderId.enabled = false
}
}
Text {
text: "倍数"
}
ComboBox {
id: speedComboBoxId
model: ["0.25", "0.5", "0.75", "1.0", "1.25", "1.5", "1.75", "2.0"]
currentIndex: 3
}
CheckBox {
id: muteCheckBoxId
text: "是否静音"
}
Text {
text: "音量"
}
Slider {
id: volumeSliderId
width: 100
from: 0.0
to: 100
stepSize: 1
value: 80
}
}
}
// 文件对话框
FileDialog {
id: fileDialogId
title: "选择视频文件" // 文件对话框的标题
nameFilters: ["视频文件(*.mp4 *.ts)"] // 文件对话框的文件类型过滤器
onAccepted: {
// FileDialog的selectedFile保存了用户选择的文件路径,如果是多个文件,则显示第一个
mediaPlayerId.stop() // 停止播放
mediaPlayerId.source = selectedFile // 设置音频文件路径
playButtonId.text = "开始播放"
playButtonId.enabled = true
stopButtonId.enabled = false
progressSliderId.enabled = false
}
}
}

浙公网安备 33010602011771号