一、FFmpeg 命令行录音
1、查看当前可用设备:
$ ffmpeg -devices
输出如下:
Devices:
D. = Demuxing supported
.E = Muxing supported
--
D avfoundation AVFoundation input device
D lavfi Libavfilter virtual input device
E sdl,sdl2 SDL2 output device
在 Mac 平台输出结果中会有 avfoundation ,这是 Mac 平台的多媒体系统库,我们可以使用 avfoundation 操作多媒体输入设备。
2、查看 avfoundation 支持的设备:
$ ffmpeg -f avfoundation -list_devices true -i dummy
输出如下:
[AVFoundation indev @ 0x7fb3bcd10840] AVFoundation video devices:
[AVFoundation indev @ 0x7fb3bcd10840] [0] FaceTime HD Camera
[AVFoundation indev @ 0x7fb3bcd10840] [1] Capture screen 0
[AVFoundation indev @ 0x7fb3bcd10840] [2] Capture screen 1
[AVFoundation indev @ 0x7fb3bcd10840] AVFoundation audio devices:
[AVFoundation indev @ 0x7fb3bcd10840] [0] Built-in Microphone
: Input/output error
列举出了 avfoundation 支持的所有设备,-i dummy命令的作用是立即退出(或者 -i ‘’,或者 -i “”)。Built-in Microphone 是笔记本自带的内建麦克风,也就是我们接下来要使用的录音设备。
3、指定录音设备,开始录音:
$ ffmpeg -f avfoundation -i :0 out.wav
avfoundation 指定视频音频输入设备的语法:
-i "[[VIDEO]:[AUDIO]]"
在 Mac 平台上通过编号指定设备,冒号前是指定的音频设备,后面指定的是音频设备,例如 0:0 代表使用0号视频设备和0号音频设备,因为我们只采集音频,所以仅指定音频设备就可以。
输出如下:
Input #0, avfoundation, from ':0':
Duration: N/A, start: 28541.967596, bitrate: 2822 kb/s
Stream #0:0: Audio: pcm_f32le, 44100 Hz, stereo, flt, 2822 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (pcm_f32le (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'out.wav':
Metadata:
ISFT : Lavf58.45.100
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
Metadata:
encoder : Lavc58.91.100 pcm_s16le
size= 4608kB time=00:00:30.54 bitrate=1235.8kbits/s speed= 1x
结束录音直接使用 Ctrl + C 即可。可以看到输出到 out.wav 文件的音频参数:
采样率 44100 Hz
声道 2(stereo:立体声)
位深 16
比特率 1411 kb/s
二、FFmpeg 编程方式录音
使用 FFmpeg 录制 PCM 音频主要有五个步骤:
我们使用到的 FFmpeg 库有 avdevice(设备相关API)、avformat(格式相关API) 和 avutil (工具相关API,比如错误处理)三个:
extern "C" {
#include
#include
#include
}
1、首先初始化 libavdevice,注册所有输入和输出设备,仅需要注册一次:
avdevice_register_all();
2、根据格式名称获取输入格式对象:
#define FMT_NAME “avfoundation”
AVInputFormat *fmt = av_find_input_format(FMT_NAME);
if (!fmt) {
qDebug() << "获取输入格式对象失败:" << FMT_NAME;
return;
}
3、打开输入设备,获取到格式上下文:
AVFormatContext *ctx = nullptr;
int ret = avformat_open_input(&ctx, DEVICE_NAME, fmt, nullptr);
if (ret < 0) {
char errbuf[1024];
av_strerror(ret, errbuf, sizeof (errbuf));
qDebug() << "打开输入设备失败:" << errbuf;
return;
}
返回 0 成功,出错返回负数。我们可以使用 libavutil 库中的 av_strerror 函数获取到错误原因。当音频数据采集完成或者发生错误时我们需要使用 avformat_close_input
函数关闭设备,并释放格式上下文。
4、打开文件:
QFile file(fileName);
if (!file.open(QFile::WriteOnly)) {
qDebug() << "打开文件失败” << fileName;
// 文件打开失败,关闭设备
avformat_close_input(&ctx);
return;
}
5、采集音频数据:
AVPacket pkt;
while (!isInterruptionRequested()) {
ret = av_read_frame(ctx, &pkt);
if (ret == 0) {
file.write((const char *)pkt.data, pkt.size);
} else if (ret == AVERROR(EAGAIN)) {
continue;
} else {
char errbuf[1024];
av_strerror(ret, errbuf, sizeof (errbuf));
qDebug() << "av_read_frame error:" << errbuf;
}
av_packet_unref(&pkt);
}
返回 0 代表读取成功,此时把读取到的数据写入文件。当返回错误 AVERROR(EAGAIN) 时表示资源临时不可用,执行 continue 即可。pkt 不再需要时需要使用 av_packet_unref 函数释放。此处的 pkt 是在栈空间中,我们也可以在堆空间中创建,最后记得调用 av_packet_free 函数释放即可:
AVPacket *pkt = av_packet_alloc();
av_packet_free(&pkt);
6、关闭文件,关闭设备:
file.close();
avformat_close_input(&ctx);
7、查看录制的 PCM 参数:
AVStream *stream = ctx->streams[0];
AVCodecParameters *params = stream->codecpar;
qDebug() << "采样率:" << params->sample_rate;
qDebug() << "声道:" << params->channels;
qDebug() << "位深:" << av_get_bits_per_sample(params->codec_id);