【FFmpeg 3.x API应用三】音频解码
2018-02-13 15:46
513 查看
摘要
这篇文章介绍音频解码,示例程序是读取一个mp3格式或者aac格式的音频文件,解码输出为未压缩的pcm音频文件。初始化FFmepg和FormatContext
和视频解码一样,先使用av_register_all注册所有相关组件,然后使用
avformat_open_input打开指定的媒体文件,并使用
avformat_find_stream_info获取媒体流相关信息,把这些格式信息映射到
AVFormatContext *mFormatCtx这个结构中。
使用函数
av_dump_format可以从控制台输出媒体文件相关信息。
bool AudioDecoding::init(const char * audioFile) { av_register_all(); if ((avformat_open_input(&mFormatCtx, audioFile, 0, 0)) < 0) { printf("Failed to open input file\n"); return true; } if ((avformat_find_stream_info(mFormatCtx, 0)) < 0) { printf("Failed to retrieve input stream information\n"); return true; } av_dump_format(mFormatCtx, 0, audioFile, 0); return false; }
配置编解码器CodecContext
根据输入文件格式查找相应的解码器,因为使用的是aac/mp3格式的音频文件,只有一个音频流,所以就不用查找流Index了,直接使用streams[0]就可以了。根据解码器申请CodecContext。
填充CodecContext信息
完成CodecContext初始化。
bool AudioDecoding::initCodecContext() { // 根据id查找对应解码器 AVCodec *dec = avcodec_find_decoder(mFormatCtx->streams[0]->codecpar->codec_id); if (!dec) { printf("Failed to find codec!\n"); return true; } // 申请CodecContext if (!(mCodecCtx = avcodec_alloc_context3(dec))) { printf("Failed to allocate the codec context\n"); return true; } // 填充CodecContext信息 if (avcodec_parameters_to_context(mCodecCtx, mFormatCtx->streams[0]->codecpar) < 0) { printf("Failed to copy codec parameters to decoder context!\n"); return true; } // 初始化AVCodecContext结构 if (avcodec_open2(mCodecCtx, dec, NULL) < 0) { printf("Failed to open codec\n"); return true; } return false; }
音频解码,保存pcm文件
循环读取输入文件的数据,直接读取完毕。依次处理各个读取到的packet,进行解码。
解码出来的是未压缩的音频数据,把这些音频数据写入文件。
需要注意的是,输入音频格式不同时,解码后输出的pcm音频数据格式也会不同,我们这里没有进行重采样处理。本程序当使用echo.mp3(sample_fmt = AV_SAMPLE_FMT_S16P)作为输入文件时,,一个采样点是16bit,输出文件取名为out_s16le.pcm;使用echo.aac(sample_fmt = AV_SAMPLE_FMT_FLTP)作为输入文件,一个采样点是32bit,输出文件取名为out_f32le.pcm,以便于播放。
关于音频解码输出PCM数据的分析,请参考另一篇文章《音频解码输出PCM格式数据分析》。
bool AudioDecoding::readFrameProc() { // 使用echo.mp3作为输入文件,文件名为s16le.pcm // 使用echo.aac作为输入文件,文件名则为f32le.pcm FILE *fd = fopen("../assets/out_s16le.pcm", "wb"); if (!fd) { printf("Failed to open input file\n"); return true; } AVPacket packet; av_init_packet(&packet); AVFrame *frame = av_frame_alloc(); //循环读取音频数据packet while (int num = av_read_frame(mFormatCtx, &packet) >= 0) { //音频解码,同视频解码一样都使用这对函数 avcodec_send_packet(mCodecCtx, &packet); int ret = avcodec_receive_frame(mCodecCtx, frame); if (!ret) { // number of bytes per sample, 16bit is 2 Bytes //根据格式查询音频采样点深度,一个采样点使用多少字节表示。 //本文使用的mp3文件(sample_fmt = AV_SAMPLE_FMT_S16P),一个采样点是16bit,则返回字节数为2; //使用的aac文件(sample_fmt = AV_SAMPLE_FMT_FLTP),一个采样点是32bit,则返回字节数4。 int sampleBytes = av_get_bytes_per_sample(mCodecCtx->sample_fmt); // 使用Planar格式写音频文件,nb_samples为这个frame中一个声道的采样点的个数,channels为声道个数 for (int i = 0; i < frame->nb_samples; i++) for (int ch = 0; ch < mCodecCtx->channels; ch++) fwrite(frame->data[ch] + sampleBytes*i, 1, sampleBytes, fd); } av_packet_unref(&packet); } av_frame_free(&frame); fclose(fd); return false; }
释放系统资源
AudioDecoding::~AudioDecoding() { avcodec_free_context(&mCodecCtx); avformat_close_input(&mFormatCtx); }
PCM音频文件播放方法
上面说过使用MP3输入时,输出文件名为out_s16le.pcm,使用aac格式文件作为输入时,输出文件名为out_f32le.pcm。可以使用ffplay播放pcm文件,这两个文件对应的播放命令为:ffplay -f s16le -ac 2 -ar 44100 out_s16le.pcm ffplay -f f32le -ac 2 -ar 44100 out_f32le.pcm
如果播放不正常,很可能是播放参数不正确。
示例程序代码
上述示例的完整代码可以从Github下载: https://github.com/lmshao/FFmpeg-Basic 。相关文章推荐
- 【FFmpeg 3.x API应用四】音频编码
- 【FFmpeg 3.x API应用一】视频解码
- FFmpeg 音频解码:应用buffer缓冲机制
- 【FFmpeg 3.x API应用二】视频编码
- FFMPEG视音频编解码零基础学习方法
- FFmpeg开源代码中的视频/音频编解码
- [总结]FFMPEG视音频编解码零基础学习方法一
- 【转】[总结]FFMPEG视音频编解码零基础学习方法
- 用Qt线程及媒体类播放FFmpeg解码的音频数据
- 音频编解码应用系统设计(二)
- FFMPEG 之音频解码及AudioTrack播放音频
- [总结]FFMPEG视音频编解码零基础学习方法
- FFmpeg支持的音频编解码格式
- Android本地视频播放器开发--ffmpeg解码视频文件中的音频(1)
- FFmpeg解码音频
- [总结]FFMPEG视音频编解码零基础学习方法(转)
- 再谈使用ffmpeg进行单纯音频编解码
- 学习FFmpeg API – 解码视频
- [总结]FFMPEG视音频编解码零基础学习方法
- ffmpeg 解码音频的方法 .