您的位置:首页 > 其它

【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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息