博客
关于我
【秒懂音视频开发】09_播放PCM
阅读量:458 次
发布时间:2019-03-06

本文共 3846 字,大约阅读时间需要 12 分钟。

ffplay

可以使用ffplay播放《音频录制02_编程》中录制好的PCM文件,测试一下是否录制成功。

播放PCM需要指定相关参数:

  • ar:采样率
  • ac:声道数
  • f:采样格式
    • s16le:PCM signed 16-bit little-endian
    • 更多PCM的采样格式可以使用命令查看
      • Windows:ffmpeg -formats | findstr PCM
      • Mac:ffmpeg -formats | grep PCM
ffplay -ar 44100 -ac 2 -f s16le out.pcm

接下来演示一下,如何通过编程的方式播放PCM数据。

SDL

ffplay是基于FFmpeg、两个库实现的。通过编程的方式播放音视频,也是需要用到这2个库。FFmpeg大家都已经清楚了,比较陌生的是SDL。

简介

SDL(Simple DirectMedia Layer),是一个跨平台的C语言多媒体开发库。

  • 支持Windows、Mac OS X、Linux、iOS、Android
  • 提供对音频、键盘、鼠标、游戏操纵杆、图形硬件的底层访问
  • 很多的视频播放软件、模拟器、受欢迎的游戏都在使用它
  • 目前最新的稳定版是:2.0.14
  • API文档:

下载

SDL官网下载地址:。

Windows

由于我们使用的是MinGW编译器,所以选择下载。

解压后的目录结构如下图所示,跟FFmpeg的目录结构类似,因此就不再赘述每个文件夹的作用。

Mac

从可以看得出来:之前执行brew install ffmpeg时,已经顺带安装了SDL,安装目录是:/usr/local/Cellar/sdl2

如果没有这个目录,就执行brew install sdl2进行安装即可。

HelloWorld

来个简单的SDL HelloWorld吧,打印一下SDL的版本号。

.pro文件

win32 {    FFMPEG_HOME = F:/Dev/ffmpeg-4.3.2    SDL_HOME = F:/Dev/SDL2-2.0.14/x86_64-w64-mingw32}macx {    FFMPEG_HOME = /usr/local/Cellar/ffmpeg/4.3.2    SDL_HOME = /usr/local/Cellar/sdl2/2.0.14_1}INCLUDEPATH += $${FFMPEG_HOME}/includeLIBS += -L$${FFMPEG_HOME}/lib \        -lavdevice \        -lavcodec \        -lavformat \        -lavutilINCLUDEPATH += $${SDL_HOME}/includeLIBS += -L$${SDL_HOME}/lib \        -lSDL2

在Windows环境中,还需要处理一下dll文件,参考:。

cpp代码

#include 
SDL_version v;SDL_VERSION(&v);// 2 0 14qDebug() << v.major << v.minor << v.patch;

播放PCM

初始化子系统

SDL分成好多个子系统(subsystem):

  • Video:显示和窗口管理
  • Audio:音频设备管理
  • Joystick:游戏摇杆控制
  • Timers:定时器
  • ...

目前只用到了音频功能,所以只需要通过函数初始化Audio子系统即可。

// 初始化Audio子系统if (SDL_Init(SDL_INIT_AUDIO)) {    // 返回值不是0,就代表失败    qDebug() << "SDL_Init Error" << SDL_GetError();    return;}

打开音频设备

/* 一些宏定义 */// 采样率#define SAMPLE_RATE 44100// 采样格式#define SAMPLE_FORMAT AUDIO_S16LSB// 采样大小#define SAMPLE_SIZE SDL_AUDIO_BITSIZE(SAMPLE_FORMAT)// 声道数#define CHANNELS 2// 音频缓冲区的样本数量#define SAMPLES 1024// 用于存储读取的音频数据和长度typedef struct {    int len = 0;    int pullLen = 0;    Uint8 *data = nullptr;} AudioBuffer;// 音频参数SDL_AudioSpec spec;// 采样率spec.freq = SAMPLE_RATE;// 采样格式(s16le)spec.format = SAMPLE_FORMAT;// 声道数spec.channels = CHANNELS;// 音频缓冲区的样本数量(这个值必须是2的幂)spec.samples = SAMPLES;// 回调spec.callback = pull_audio_data;// 传递给回调的参数AudioBuffer buffer;spec.userdata = &buffer;// 打开音频设备if (SDL_OpenAudio(&spec, nullptr)) {    qDebug() << "SDL_OpenAudio Error" << SDL_GetError();    // 清除所有初始化的子系统    SDL_Quit();    return;}

打开文件

#define FILENAME "F:/in.pcm"// 打开文件QFile file(FILENAME);if (!file.open(QFile::ReadOnly)) {    qDebug() << "文件打开失败" << FILENAME;    // 关闭音频设备    SDL_CloseAudio();    // 清除所有初始化的子系统    SDL_Quit();    return;}

开始播放

// 每个样本占用多少个字节#define BYTES_PER_SAMPLE ((SAMPLE_SIZE * CHANNELS) / 8)// 文件缓冲区的大小#define BUFFER_SIZE (SAMPLES * BYTES_PER_SAMPLE)// 开始播放SDL_PauseAudio(0);// 存放文件数据Uint8 data[BUFFER_LEN];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;}

回调函数

// userdata:SDL_AudioSpec.userdata// stream:音频缓冲区(需要将音频数据填充到这个缓冲区)// len:音频缓冲区的大小(SDL_AudioSpec.samples * 每个样本的大小)void pull_audio_data(void *userdata, Uint8 *stream, int len) {    // 清空stream    SDL_memset(stream, 0, len);    // 取出缓冲信息    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();

转载地址:http://vxhyz.baihongyu.com/

你可能感兴趣的文章
NIFI大数据进阶_Kafka使用相关说明_实际操作Kafka消费者处理器_来消费kafka数据---大数据之Nifi工作笔记0037
查看>>
NIFI大数据进阶_Kafka使用相关说明_实际操作Kafka生产者---大数据之Nifi工作笔记0036
查看>>
NIFI大数据进阶_NIFI的模板和组的使用-介绍和实际操作_创建组_嵌套组_模板创建下载_导入---大数据之Nifi工作笔记0022
查看>>
NIFI大数据进阶_NIFI监控功能实际操作_Summary查看系统和处理器运行情况_viewDataProvenance查看_---大数据之Nifi工作笔记0026
查看>>
NIFI大数据进阶_NIFI监控的强大功能介绍_处理器面板_进程组面板_summary监控_data_provenance事件源---大数据之Nifi工作笔记0025
查看>>
NIFI大数据进阶_NIFI集群知识点_认识NIFI集群以及集群的组成部分---大数据之Nifi工作笔记0014
查看>>
NIFI大数据进阶_NIFI集群知识点_集群的断开_重连_退役_卸载_总结---大数据之Nifi工作笔记0018
查看>>
NIFI大数据进阶_使用NIFI表达式语言_来获取自定义属性中的数据_NIFI表达式使用体验---大数据之Nifi工作笔记0024
查看>>
NIFI大数据进阶_内嵌ZK模式集群1_搭建过程说明---大数据之Nifi工作笔记0015
查看>>
NIFI大数据进阶_内嵌ZK模式集群2_实际操作搭建NIFI内嵌模式集群---大数据之Nifi工作笔记0016
查看>>
NIFI大数据进阶_外部ZK模式集群1_实际操作搭建NIFI外部ZK模式集群---大数据之Nifi工作笔记0017
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_操作方法说明_01---大数据之Nifi工作笔记0033
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_操作方法说明_02---大数据之Nifi工作笔记0034
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_01_实际操作---大数据之Nifi工作笔记0029
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_02_实际操作_splitjson处理器_puthdfs处理器_querydatabasetable处理器---大数据之Nifi工作笔记0030
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_说明操作步骤---大数据之Nifi工作笔记0028
查看>>
NIFI大数据进阶_连接与关系_设置数据流负载均衡_设置背压_设置展现弯曲_介绍以及实际操作---大数据之Nifi工作笔记0027
查看>>
NIFI数据库同步_多表_特定表同时同步_实际操作_MySqlToMysql_可推广到其他数据库_Postgresql_Hbase_SqlServer等----大数据之Nifi工作笔记0053
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南001---大数据之Nifi工作笔记0068
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南002---大数据之Nifi工作笔记0069
查看>>