音视频之播放PCM(七)
使用命令行播放-ffplay
可以使用ffplay播放我们在上面博客中录制好的PCm文件,测试一下是否录制成功。播放PCM需要指定相关参数:
ar: 采样率
ac: 声道数
f: 采样格式
s16le: PCM signed 16-bit little-endian
更多PCM的采样格式可以使用命令查看
Windows: ffmpeg -formats | findstr OCM
Mac: ffmpeg -formats | grep PCM
播放命令如下:
ffplay -ar 44100 -ac 2 -f s16le /Users/muzi/Desktop/out.pcm
借助SDL使用代码播放
简介
SDL全称 Simple DirectMedia Layer,是一个跨平台的C语言多媒体开发库。
支持Windows、Mac OSX、Linux、iOS、Android
提供对音频、键盘、鼠标、游戏操纵杆、图形硬件的底层访问
很多的视频播放软件、模拟器、受欢迎的游戏都在使用它
目前最新的稳定版本是: 2.0.16
API文档: wiki
安装环境
从brew官网可以看的出来:之前执行 brew install ffmpeg时,已经顺带安装了SDL,安装目录是: /usr/local/Cellar/sdl2。如果没有这个目录,就执行brew install sdl2进行安装即可。
配置
.pro文件
win32 {
SDL_HOME = /muzi
}
mac {
SDL_HOME = /usr/local/Cellar/sdl2/2.0.16
}
INCLUDEPATH += $${SDL_HOME}/include
LIBS += -L $${SDL_HOME}/lib \
-lSDL2
播放PCM
初始化子系统
SDL分成好多个子系统:
Video: 显示和窗口管理
Audio: 音频设备管理
Joystick: 游戏摇杆控制
Timers: 定时器
...
目前只用到了音频功能,所以只需要通过SDL_init函数初始化Audio子系统即可。
打开音频设备
// 音频参数
SDL_AudioSpec spec;
// 采样率
spec.freq = SAMPLE_RATE;
// 采样格式 (s16le)
spec.format = AUDIO_S16LSB;
// 声道数
spec.channels = CHANNELS;
// 音频缓冲区的样本数量(这个值必须是2的幂)
spec.samples = 1024;
// 回调
spec.callback = pull_audio_data;
AudioBuffer buffer;
spec.userdata = &buffer;
// 打开设备
if (SDL_OpenAudio(&spec, nullptr)) {
qDebug() << "SDL_OpenAudio error" << SDL_GetError();
// 清除所有的子系统
SDL_Quit();
return;
}
打开文件
// 打开文件
QFile file(FILENAME);
if (!file.open(QFile::ReadOnly)) {
qDebug() << "file open error" << FILENAME;
// 关闭设备
SDL_CloseAudio();
// 清除所有的子系统
SDL_Quit();
return;
}
开始播放
// 开始播放 (0是取消暂停)
SDL_PauseAudio(0);
qDebug() << BUFFER_SIZE << "BUFFER_SIZE";
// 存放从文件中读取的数据
Uint8 data[BUFFER_SIZE];
while (!isInterruptionRequested()) {
// 只要从文件中读取的音频数据,还没有填充完毕,就跳过
if (buffer.len > 0) continue;
buffer.len = file.read((char *) data, BUFFER_SIZE);
// 文件数据已经读取完毕
if (buffer.len <= 0) {
// 剩余的样本数量
int samples = buffer.pullLen / BYTES_PER_SAMPLE;
int ms = samples * 1000 / SAMPLE_RATE;
SDL_Delay(ms);
break;
}
// 读取到了文件数据
buffer.data = data;
}
回调函数
// 等待音频设备回调(会回调多次)
void pull_audio_data(void *userdata,
Uint8 *stream, // 需要往stream中填充PCM数据
int len) { // 希望填充的大小(samples * format * channels / 8)
qDebug() << "pull_audio_data" << len;
// 清空stream (静音处理)
SDL_memset(stream, 0 , len);
// 取出AudioBuffer
AudioBuffer *buffer = (AudioBuffer *)userdata;
// 文件数据还没准备好
if (buffer->len <= 0) return;
// 取len、bufferLen的最小值(为了保证数据安全,防止指针越界)
buffer->pullLen = (len > buffer->len) ? buffer->len : len;
// 填充数据
SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME);
buffer->data += buffer->pullLen;
buffer->len -= buffer->pullLen;
}
释放资源
// 关闭文件
file.close();
// 关闭设备
SDL_CloseAudio();
// 清除所有的子系统
SDL_Quit();