音视频之播放PCM(七)

音视频之播放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();