使用Qt6.2 multimedia QAudioFormat + QAudioSource录制PCM音频文件
作者 | 将狼才鲸 |
---|---|
创建日期 | 2022-03-26 |
本工程Gitee仓库源码地址:qt_gui_simple2complex/ source / 004_MultiMedia_VideoAudio / 004_pcm_input_save_file
本文章CSDN阅读地址:将狼才鲸
本文章配套B站视频讲解地址(待添加):才鲸嵌入式
- 因为网上的很多用例都是Qt5的版本的代码,和Qt6不兼容;有些使用了Qt6的用例又太复杂,使用了信号与槽,也看不到每帧pcm数据,不方便在音视频同步录制时加上时间戳信息,所以我决定自己阅读Qt官方的源码,自己写一个最简单的Qt6 pcm录制的用例,源码带工程放到Gitee仓库,可以下载直接运行,减少阅读者的工作量和试错成本。
参考网址:
Qt6 QAudioSource录 pcm 音频
Qt 6 中的Qt 多媒体:音频、视频、小部件更改概述
Qt录制本地PCM音频文件
QT 用QAudio语音库实现音频信号的采集 以及发送到另一台电脑 播放
【开源项目】基于QT录制PCM音频实例详细
QT学习_QAudioInput/output实现录音监听
Qt由入门到放弃-音频采集与录制
Qt6 录音
QApplication详解
一、相关依赖模块介绍
参考网址:
QAudioInput Class
List of All Members for QAudioInput
QAudioDevice Class
List of All Members for QAudioDevice
QAudioFormat Class
List of All Members for QAudioFormat
Audio Devices Example
Audio Source Example
Audio Recorder Example
-
原来的Qt5使用了QAudioInput、QAudioFormat
-
而新的Qt6中需要使用QAudioSource、 QAudioFormat、QIODevice
二、程序说明
/**
* \brief Qt6.2.3获取音频输入,并保存为PCM文件
* \details 编译运行后在源文件上级目录下的build*编译目录下会有录制5s时长的test.pcm文件
* 安装ffmpeg之后使用ffplay -ar 44100 -ac 2 -f s16le -i test.pcm
* 命令播放pcm音频。
* Qt软件的“应用程序输出”这一栏中有调试信息输出。
*
* \note 网上没有搜到用Qt6一帧一帧保存pcm数据的用例,所以自己写了一个
* QAudioFormat用于打开音频输入设备时的参数设置
* QAudioSource用于读取音频输入的数据(不像Qt5那样使用QAudioInput)
*
* \remarks QAudioFormat PCM格式的设置参考用例(网上的很多用例是Qt5的,编译不通过)
* [Audio Devices Example](https://doc.qt.io/qt-6/qtmultimedia-multimedia-audiodevices-example.html)
* 本地路径:Qt\Examples\Qt-6.2.3\multimedia\audiodevices
*
* QAudioSource 获取PCM数据帧的参考用例
* [Audio Source Example](https://doc.qt.io/qt-6/qtmultimedia-multimedia-audiosource-example.html)
* 本地路径:Qt\Examples\Qt-6.2.3\multimedia\audiosource
*
* 可借鉴的文档:[Qt6 QAudioSource录 pcm 音频](https://blog.csdn.net/TM1695648164/article/details/121364703)
*/
三、源文件内容
-
为方便阅读,特列出源码内容,Gitee仓库qt_gui_simple2complex/ source / 003_MultiMedia_VideoAudio / 005_pcm_input_save_file中可直接下载到源码。
-
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMediaDevices>
#include <QAudioDevice>
#include <QDebug>
#include <QDataStream>
using namespace std;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
QAudioFormat AudioFormat;
qDebug("Enter main()");
AudioFormat.setSampleRate(44100);
AudioFormat.setChannelCount(2);
AudioFormat.setSampleFormat(QAudioFormat::Int16);
AudioFile *audioFile = new AudioFile(AudioFormat);
audioSource = new QAudioSource(QMediaDevices::defaultAudioInput(), AudioFormat);
audioSource->start(audioFile);
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
audioSource->stop();
delete ui;
}
/******************************** Audio file **********************************/
AudioFile::AudioFile(const QAudioFormat &format)
: m_format(format)
{
open(QIODevice::WriteOnly);
start();
}
void AudioFile::start()
{
file.setFileName("test.pcm");
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
qDebug("open file: out.pcm");
}
void AudioFile::stop()
{
close();
file.close();
qDebug("close file: out.pcm");
}
qint64 AudioFile::readData(char * /* data */, qint64 /* maxlen */)
{
qDebug("readData ");
return 0;
}
qint64 AudioFile::writeData(const char *data, qint64 len)
{
file.write(data, len);
qDebug(". ");
total += len;
if (total > 1 * 1024 * 1024) {
file.close();
qDebug("close file: out.pcm");
//TODO: 通知MainWindow去delete audioSource
}
return len;
}
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QAudioSource>
#include <QMediaDevices>
#include <QAudioFormat>
#include <QIODevice>
#include <QFile>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QAudioSource *audioSource;
Ui::MainWindow *ui;
};
/******************************** Audio file **********************************/
/**
* \brief QAudioSource的输出必须是QIODevice
*/
class AudioFile : public QIODevice
{
Q_OBJECT
public:
AudioFile(const QAudioFormat &format);
void start();
void stop();
qint64 readData(char *data, qint64 maxlen) override;
qint64 writeData(const char *data, qint64 len) override;
private:
const QAudioFormat m_format;
QFile file;
int total = 0;
};
#endif // MAINWINDOW_H
- main.cpp
/**
* \brief Qt6.2.3获取音频输入,并保存为PCM文件
* \details 编译运行后在源文件上级目录下的build*编译目录下会有录制5s时长的test.pcm文件
* 安装ffmpeg之后使用ffplay -ar 44100 -ac 2 -f s16le -i test.pcm
* 命令播放pcm音频。
* Qt软件的“应用程序输出”这一栏中有调试信息输出。
*
* \note 网上没有搜到用Qt6一帧一帧保存pcm数据的用例,所以自己写了一个
* QAudioFormat用于打开音频输入设备时的参数设置
* QAudioSource用于读取音频输入的数据(不像Qt5那样使用QAudioInput)
*
* \remarks QAudioFormat PCM格式的设置参考用例(网上的很多用例是Qt5的,编译不通过)
* [Audio Devices Example](https://doc.qt.io/qt-6/qtmultimedia-multimedia-audiodevices-example.html)
* 本地路径:Qt\Examples\Qt-6.2.3\multimedia\audiodevices
*
* QAudioSource 获取PCM数据帧的参考用例
* [Audio Source Example](https://doc.qt.io/qt-6/qtmultimedia-multimedia-audiosource-example.html)
* 本地路径:Qt\Examples\Qt-6.2.3\multimedia\audiosource
*
* 可借鉴的文档:[Qt6 QAudioSource录 pcm 音频](https://blog.csdn.net/TM1695648164/article/details/121364703)
*/
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
- project.pro
QT += core gui
QT += multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
- mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar"/>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>