创建一个Qt项目
创建名称为audio
的Qt项目
下载FFmpeg
我们下载共享库版本的FFmpeg
解压缩
FFmepg下载解压缩后放在和前面创建的Qt项目在一个同级别路径下
修改Qt项目文件
修改Qt项目文件CmakeLists.txt
,引入头文件和库,添加内容如下
# 设置变量
set(ffmpeg444 "../ffmpeg444")
# 引入头文件目录
include_directories(${ffmpeg444}/include/)
# 引入库文件目录,注意link_directories要放在add_executable之前
link_directories(${ffmpeg444}/lib/)
# 链接库中添加avdevice avutil avformat
target_link_libraries(audio PRIVATE Qt${QT_VERSION_MAJOR}::Widgets avdevice avutil avformat)
添加一个开始录音的按钮
添加一个按钮点击的槽函数,后面我们的录音逻辑在此处编写
注册设备
设备只需要注册一次,因为我们在main.cpp
文件中添加
#include "mainwindow.h"
#include <QApplication>
extern "C" {
// 设备相关
#include <libavdevice/avdevice.h>
}
int main(int argc, char *argv[])
{
// 注册设备,程序整个运行过程只需要注册一次
avdevice_register_all();
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
开始录音
录音按钮点击相关代码, 修改mainwindow.cpp
文件,内容如下
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QDebug>
#include <QFile>
extern "C" {
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
}
#define BUF_SIZE 1024
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_audioButton_clicked()
{
const char* fmtName = "dshow"; //设置输入设备格式名称
// 获取输入格式对象
AVInputFormat *fmt = av_find_input_format(fmtName);
if (!fmt) {
qDebug() << "获取输入格式对象失败" << fmtName;
return;
}
AVFormatContext *ctx = nullptr; // 打开的设备上下文对象,可以利用这个上下文对象来操作设备
// 设备名称,可以使用 ffmpeg -f dshow -list_devices true -i dummy查看设备
const char* deviceName = "audio=麦克风 (K66)";
AVDictionary* options = nullptr; // 选项
// 利用输入格式对象打开设备
int ret = avformat_open_input(&ctx, deviceName, fmt, &options);
if (ret < 0) {
char errbuf[BUF_SIZE] = {0};
// av_strerror获取错误原因
av_strerror(ret, errbuf, BUF_SIZE);
qDebug() << "打开设备失败:" << errbuf;
return;
}
// 录音保存的文件
const char* fileName = "out.pcm"; // 采集的数据是原始pcm数据
QFile file(fileName);
// 打开文件
// WriteOnly: 只写模式,如果文件不存在,则创建文件。如果文件存在,则清空文件内容
if (!file.open(QIODevice::WriteOnly)) {
qDebug() << "文件打开失败" << fileName;
avformat_close_input(&ctx); // 关闭设备
return;
}
// 开始采集数据
int count = 50; // 采集数据的次数
AVPacket pkt; // 数据包
while(count-- > 0 && av_read_frame(ctx, &pkt) == 0) { // 一直采集数据,当为0时说明数据采集成功了
// 将数据写入文件
file.write((const char *)pkt.data, pkt.size);
}
// 释放资源
file.close(); // 关闭文件
avformat_close_input(&ctx); // 关闭设备
}
关于设备部分,我们可以使用条件编译
#ifdef Q_OS_LINUX
const char* fmtName = "pulse"; //设置输入设备格式名称
#elif Q_OS_WIN
const char* fmtName = "dshow"; //设置输入设备格式名称
#endif
#ifdef Q_OS_LINUX
// 设备名称,可以使用 arecord -l查看设备
const char* deviceName = "hw:0";
#elif Q_OS_WIN
const char* deviceName = "audio=麦克风 (K66)";
#endif
运行测试
运行之前我们需要先将FFmpeg的库文件就拷贝到可执行文件目录下
点击开始录音,稍等一会儿,发现二进制程序目录下多了一个out.pcm
我们录制的音频文件
播放
我们可以使用ffplay
播放我们刚才录制的pcm音频数据
查看PCM的格式
使用ffmpeg -hide_banner -formats
我们可以输出所有的格式
C:\Users\tang>ffmpeg -hide_banner -formats | findstr PCM
DE alaw PCM A-law
DE f32be PCM 32-bit floating-point big-endian
DE f32le PCM 32-bit floating-point little-endian
DE f64be PCM 64-bit floating-point big-endian
DE f64le PCM 64-bit floating-point little-endian
DE mulaw PCM mu-law
DE s16be PCM signed 16-bit big-endian
DE s16le PCM signed 16-bit little-endian
DE s24be PCM signed 24-bit big-endian
DE s24le PCM signed 24-bit little-endian
DE s32be PCM signed 32-bit big-endian
DE s32le PCM signed 32-bit little-endian
DE s8 PCM signed 8-bit
DE u16be PCM unsigned 16-bit big-endian
DE u16le PCM unsigned 16-bit little-endian
DE u24be PCM unsigned 24-bit big-endian
DE u24le PCM unsigned 24-bit little-endian
DE u32be PCM unsigned 32-bit big-endian
DE u32le PCM unsigned 32-bit little-endian
DE u8 PCM unsigned 8-bit
DE vidc PCM Archimedes VIDC
播放
- -ar 指定采样率
- -ac 指定声道数
- -f 指定音频格式, s16le表示有符号的16位小端模式
ffplay -ar 44100 -ac 2 -f s16le out.pcm