音视频之播放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音视频之播放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();
上一篇:【新手入门必看】音频处理背景知识


下一篇:为什么“鸿蒙”不是第二个“安卓”,如何看待Harmony OS里的Android痕迹?