FFmpeg开发笔记(十八)FFmpeg兼容各种音频格式的播放

FFmpeg结合SDL可以播放音频文件,也能播放视频文件中的音频流,《FFmpeg开发实战:从零基础到短视频上线》一书第10章的示例程序playaudio.c支持播放mp3和aac两种格式的音频,却不支持播放其他格式的音频。

因为mp3和aac两个格式拥有标准的规范定义,比如mp3规定每帧音频固定包含1152个样本,而aac规定每帧音频固定包含1024个样本。在它们的解码器实例AVCodecContext中,即可从frame_size字段获取每帧音频的样本数量。
然而其他音频格式(如ogg、amr、wma等)的每帧样本数并不固定,从frame_size字段取到的样本数量为0,这不仅导致SDL初始化失败,还导致重采样过程异常。为了能够播放其他格式的音频,需要对playaudio.c做下列三处修改。
1、从解码器实例获取音频样本数时,如果发现frame_size为0,就要把样本数变量设为512(注意该数值必须为2的n次幂,如256、512、1024等),修改后的赋值代码如下所示:

int out_nb_samples = audio_decode_ctx->frame_size; // 输出的采样数量
if (out_nb_samples <= 0) {
    out_nb_samples = 512;
}

2、在遍历音频帧的时候,要重新计算实际的采样位数,以便确定多少音频数据送给扬声器。具体的计算过程是这样的:先调用swr_convert函数对音频重采样,该函数的返回值为输出的数据大小;这个输入大小乘以声道数量乘以音频样本的位深(位深表示每个音频样本占据几个字节),最终的乘积便是要送给扬声器的音频数据大小。详细的计算代码如下所示:

// 重采样。也就是把输入的音频数据根据指定的采样规格转换为新的音频数据输出
int swr_size = swr_convert(swr_ctx, // 音频采样器的实例
    &out_buff, MAX_AUDIO_FRAME_SIZE, // 输出的数据内容和数据大小
    (const uint8_t **) frame->data, frame->nb_samples); // 输入的数据内容和数据大小
audio_pos = (unsigned char *) out_buff; // 把音频数据同步到缓冲区位置
// 这里要计算实际的采样位数
audio_len = swr_size * out_channels * av_get_bytes_per_sample(out_sample_fmt);

3、SDL的音频回调函数当中,注意每次要凑足len个字节。鉴于重采样后的音频数据可能较大(主要是amr格式有这种情况),因此要按照len指定的长度切割数据,确保每次回调函数都刚好把长度为len的音频数据送往扬声器。修改后的回调代码如下所示:

// 回调函数,在获取音频数据后调用
void fill_audio(void *para, uint8_t *stream, int len) {
    SDL_memset(stream, 0, len); // 将缓冲区清零
    if (audio_len == 0) {
        return;
    }
    while (len > 0) { // 每次都要凑足len个字节才能退出循环
        int fill_len = (len > audio_len ? audio_len : len);
        // 将音频数据混合到缓冲区
        SDL_MixAudio(stream, audio_pos, fill_len, SDL_MIX_MAXVOLUME);
        audio_pos += fill_len;
        audio_len -= fill_len;
        len -= fill_len;
        stream += fill_len;
        if (audio_len == 0) { // 这里要延迟一会儿,避免一直占据IO资源
            SDL_Delay(1);
        }
    }
}

上述修改后的代码已经附在了《FFmpeg开发实战:从零基础到短视频上线》一书第10章的源码chapter10/playaudio2.c,这个c代码是playaudio.c的改进版,除了支持原来mp3和aac格式的音频播放,还支持ogg、amr、wma等格式的音频播放,以及asf、webm等视频文件的音频播放。

接着执行下面的编译命令。

gcc playaudio2.c -o playaudio2 -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -I/usr/local/sdl2/include -L/usr/local/sdl2/lib -lsdl2 -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译完成后执行以下命令启动测试程序,期望播放音频文件ring.ogg。

./playaudio2 ../ring.ogg

程序运行完毕,发现控制台输出以下的日志信息。

Success open input_file ../ring.ogg.
out_sample_rate=11025, out_nb_samples=512
out_buffer_size=1024
256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256
Success play audio file.
Quit SDL.

同时电脑扬声器传来了两个"叮咚"的铃声,表示上述代码正确实现了播放ogg音频的功能。

相关推荐
EasyCVR1 小时前
私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
大数据·网络·音视频·h.265
天空中的野鸟1 小时前
Android音频采集
android·音视频
计算机毕设孵化场2 小时前
计算机毕设-基于springboot的高校网上缴费综合务系统视频的设计与实现(附源码+lw+ppt+开题报告)
java·spring boot·计算机外设·音视频·课程设计·高校网上缴费综合务系统视频·计算机毕设ppt
lxkj_20247 小时前
使用线程局部存储解决ffmpeg中多实例调用下自定义日志回调问题
ffmpeg
简鹿办公9 小时前
如何提取某站 MV 视频中的音乐为 MP3 音频
音视频·简鹿视频格式转换器·视频提取mp3音频
yufengxinpian9 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
runing_an_min11 小时前
ffmpeg视频滤镜:替换部分帧-freezeframes
ffmpeg·音视频·freezeframes
ruizhenggang11 小时前
ffmpeg本地编译不容易发现的问题 — Error:xxxxx not found!
ffmpeg
runing_an_min13 小时前
ffmpeg视频滤镜:提取缩略图-framestep
ffmpeg·音视频·framestep
小曲曲14 小时前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频