本文主要讲解FFmpeg的音频解码具体流程,API使用。最后再以一个非常简单的demo演示将一个mp3格式的音频文件解码为原始数据pcm文件。 本文主要基于FFmpeg音频解码新接口。

一、FFmpeg音频解码API调用流程图      

API接口简单大体讲解如下:

av_register_all():注册FFmpeg所有编解码器。avformat_open_input():打开音频地址并获取里面的内容(解封装)avformat_find_stream_info():获取内容avcodec_find_decoder():寻找解码器avcodec_alloc_context3():申请解码器相关上下文avcodec_open2():打开解码器av_read_frame():从原始有格式文件中一帧一帧读取出来avcodec_send_packet():解码核心接口新接口,发送一帧音频给解码器。即是AVPacket(存储AAC等音频格式码流数据)。avcodec_receive_frame():解码核心接口新接口,接收解码器解码后的一帧视频,AVFrame(PCM原始数据)。

二、音频解码过程API调用流程

1、注册各大组件

这一步是ffmpeg的任何程序的第一步都是需要先注册ffmpeg相关的各大组件的:

    //注册各大组件av_register_all();LOGE("注册成功")

2、打开音频文件并获取相关上下文

在解码之前我们得获取里面的内容,这一步就是打开地址并且获取里面的内容。其中avFormatContext是内容的一个上下文。

并使用avformat_open_input打开播放源,inputPath为输入的地址,也就是音频文件,然后使用avformat_find_stream_info从获取的内容中寻找相关流。

//打开音频地址并获取里面的内容(解封装)
error = avformat_open_input(&avFormatContext, inputPath, NULL, NULL);
if (error < 0){LOGE("打开音频文件失败\n");return false;
}
if (avformat_find_stream_info(avFormatContext, NULL) < 0){LOGE("获取内容失败")return false;
}

3、寻找音频流

我们在上面已经获取了内容,我们再从中找出相对应的音频流。

//获取音频的编码信息
AVCodecParameters *origin_par = NULL;
int mAudioStreamIdx = -1;
mAudioStreamIdx = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (mAudioStreamIdx < 0) {av_log(NULL, AV_LOG_ERROR, "Can't find audio stream in input file\n");return false;
}
LOGE("成功找到音频流")

4、获取并打开解码器

如果要进行解码,那么得有解码器并打开解码器。在这一步中我加了个过滤器的相关配置,这个东西不是非必要的,所以我加了个宏ABSFILTER_ENABLE可以选择是否打开。

    // 寻找解码器 {startAVCodec *mAcodec = NULL;AVCodecContext *mAvContext = NULL;mAcodec = avcodec_find_decoder(origin_par->codec_id);mAvContext = avcodec_alloc_context3(mAcodec);if (!mAcodec || !mAvContext){return false;}#if ABSFILTER_ENABLE//过滤器相关配置,这个与音频码流格式相关const AVBitStreamFilter * absFilter = NULL;AVBSFContext *absCtx = NULL;AVCodecParameters *codecpar = NULL;absFilter = av_bsf_get_by_name("mp3decomp");//过滤器分配内存av_bsf_alloc(absFilter, &absCtx);//添加解码器属性codecpar = avFormatContext->streams[mAudioStreamIdx]->codecpar;avcodec_parameters_copy(absCtx->par_in, codecpar);absCtx->time_base_in = avFormatContext->streams[mAudioStreamIdx]->time_base;//初始化过滤器上下文av_bsf_init(absCtx);
#endif// 打开解码器if (avcodec_open2(mAvContext, mAcodec, NULL) != 0){LOGE("打开失败")return false;}LOGE("解码器打开成功")// 寻找解码器 end}

5、申请AVPacket和AVFrame以及相关设置

申请AVPacket和AVFrame,其中AVPacket的作用是:保存解码之前的数据和一些附加信息等;AVFrame的作用是:存放解码过后的数据。

    //申请AVPacketAVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));av_init_packet(packet);//申请AVFrameAVFrame *frame = av_frame_alloc();//分配一个AVFrame结构体,AVFrame结构体一般用于存储原始数据,指向解码后的原始帧

6、开始解码

接下来就可以开始解码,如下是解码的核心段代码,这里同样有上面所说的过滤器的,这个可以先不关心:

{// 发送待解码包int result = avcodec_send_packet(mAvContext, packet);av_packet_unref(packet);if (result < 0){av_log(NULL, AV_LOG_ERROR, "Error submitting a packet for decoding\n");continue;}// 接收解码数据while (result >= 0){result = avcodec_receive_frame(mAvContext, frame);if (result == AVERROR_EOF)break;else if (result == AVERROR(EAGAIN)){result = 0;break;}else if (result < 0){av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");av_frame_unref(frame);break;}av_frame_unref(frame);}
}

7、解码并保存为PCM文件

PCM是音频解码后的原始数据,我们将解码后的数据保存为PCM文件,用于分析。在上一步中解码后的地方加上保存为PCM文件的操作。

另外这里有用了上面所说的过滤器的东西,我们暂时先不关心。

完整过程如下:

while(1)
{int ret = av_read_frame(avFormatContext, packet);if (ret != 0){av_strerror(ret,buf,sizeof(buf));LOGE("--%s--\n",buf);av_packet_unref(packet);break;}if (ret >= 0 && packet->stream_index != mAudioStreamIdx){av_packet_unref(packet);continue;}#if ABSFILTER_ENABLEif (av_bsf_send_packet(absCtx, packet) < 0){LOGE("av_bsf_send_packet faile \n");av_packet_unref(packet);continue;}if (av_bsf_receive_packet(absCtx, packet) < 0) {LOGE("av_bsf_receive_packet faile \n");av_packet_unref(packet);continue;}
#endif{// 发送待解码包int result = avcodec_send_packet(mAvContext, packet);av_packet_unref(packet);if (result < 0){av_log(NULL, AV_LOG_ERROR, "Error submitting a packet for decoding\n");continue;}// 接收解码数据while (result >= 0){result = avcodec_receive_frame(mAvContext, frame);if (result == AVERROR_EOF)break;else if (result == AVERROR(EAGAIN)){result = 0;break;}else if (result < 0){av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");av_frame_unref(frame);break;}// 写文件保存音频数据data_size = av_get_bytes_per_sample(mAvContext->sample_fmt);if (data_size < 0) {/* This should not occur, checking just for paranoia */LOGE("Failed to calculate data size\n");return false;}for (i = 0; i < frame->nb_samples; i++)for (ch = 0; ch < mAvContext->channels; ch++)fwrite(frame->data[ch] + data_size*i, 1, data_size, fp_PCM);av_frame_unref(frame);}}}

8、收尾释放

最后释放相关资源

    fclose(fp_PCM);av_frame_free(&frame);avcodec_close(mAvContext);avformat_free_context(avFormatContext);
#if ABSFILTER_ENABLEav_bsf_free(&absCtx);absCtx = NULL;
#endif

至此,mp3格式的文件就被解码出原始数据并保存为PCM文件。

三、demo运行

demo中指定了视频源文件是/sdcard/input.mp3,编码后的pcm文件是/sdcard/audioDecodeOut.pcm,如下代码,若要改文件,可以在此处修改:

@Override
public void run() {String PATH = Environment.getExternalStorageDirectory().getPath();//视频解码String input = PATH + File.separator + "input.mp3";String output = PATH + File.separator + "audioDecodeOut.pcm";decode_audio(input,output);Toast.makeText(MainActivity.this, "音频解码完成,请自行从手机中拉取pcm文件", Toast.LENGTH_SHORT).show();
}

运行后截图如下:

点击"START AUDIO DECODE"按钮,开始对音频文件进行解码,界面不会有什么提示,等解码完成后会弹出提示词 "音频解码完成,请自行从手机中拉取pcm文件"

我们从运行log中也能看到一些信息,如下:

当有--- audio decode finished ---打印出来说明已经解码完成。

然后我们adb shell进去手机看看解码前后的文件:

可以看到解码后的pcm原始数据文件比解码前是大很多的。

四、分析PCM文件

接下来我们分析下解码后的PCM文件是否正确。

首先我们需要先知道一下解码前的mp3文件的一些信息,比如声道数,采样率等等。这些我们可以通过FFmpeg的ffprobe来获取,这里我在我的Ubuntu电脑上通过ffmpeg命令行获取一下:

红圈圈中的就是几个比较重要的信息,看到是采样率是44100,双声道,fltp格式。

接下来使用PCM分析工具,我使用的是Audacity:

打开工具,并点击导入原始数据:

然后选择pcm文件,弹出格式选择的弹窗,采样率就是刚才的44100,声道是双声道也就是2声道,字节序一般是小尾端,编码是fltp,也就是选32-bit float。

点击导入后,显示如下,可以看到音频数据的波形图,然后点击右上方的播放按钮播放pcm文件,若音乐听起来正常,与之前的mp3文件一致,则说明解码后的pcm数据正常。

完整例子已经放到github上,如下:

https://github.com/weekend-y/FFmpeg_Android_Demo/tree/master/demo6_audioDecode

FFmpeg音频解码流程详解及简单demo参考相关推荐

  1. MP3文件格式与编码原理解码流程详解

    1 文件格式 MP3文件格式四部分,按顺序排列如下: ID3V2  包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量 Frame  音频帧序列 APEV2  包含了作者,作曲,专辑等 ...

  2. ffmpeg播放器实现详解 - 音频同步控制

    ffplay是ffmpeg源码中一个自带的开源播放器实例,同时支持本地视频文件的播放以及在线流媒体播放,功能非常强大. FFplay: FFplay is a very simple and port ...

  3. 264编码基本概念 FFMpeg的解码流程

    下面转自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8楼 1.NAL.Slice与fr ...

  4. 各种音视频编解码学习详解之 编解码学习笔记(四):Mpeg系列——Mpeg 4

    最近在研究音视频编解码这一块儿,看到@bitbit大神写的[各种音视频编解码学习详解]这篇文章,非常感谢,佩服的五体投地.奈何大神这边文章太长,在这里我把它分解成很多小的篇幅,方便阅读.大神博客传送门 ...

  5. FFMPEG音频解码浅析

    结合各种资料和自己的理解,估计有些浅显. FFMPEG解码流程: 1. 注册所有容器格式和CODEC: av_register_all() 2. 打开文件: av_open_input_file() ...

  6. 音视频编解码学习详解

    音视频编解码学习详解 目录(?)[+] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列A ...

  7. 深入浅出:FFmpeg 音频解码与处理AVFrame全解析

    深入浅出:FFmpeg 音频解码与处理全解析 一.FFmpeg 简介 1.1 FFmpeg 的历史与发展 1.2 FFmpeg 的主要组成部分 二.音频编解码基础 (Basics of Audio E ...

  8. FFMpeg的解码流程

    FFMpeg的解码流程 1. 从基础谈起 先给出几个概念,以在后面的分析中方便理解 Container:在音视频中的容器,一般指的是一种特定的文件格式,里面指明了所包含的 音视频,字幕等相关信息 St ...

  9. 各种音视频编解码学习详解之 编解码学习笔记(三):Mpeg系列——Mpeg 1和Mpeg 2

    最近在研究音视频编解码这一块儿,看到@bitbit大神写的[各种音视频编解码学习详解]这篇文章,非常感谢,佩服的五体投地.奈何大神这边文章太长,在这里我把它分解很多小的篇幅,方便阅读.大神博客传送门: ...

最新文章

  1. shell sh: 1: matlab: not found 解决方案
  2. Dockerfile 最佳实践
  3. 动态矩阵控制 MATLAB代码
  4. 专访格灵深瞳CTO赵勇:为 计算机视觉 赋予智慧的光芒
  5. SSD相关文章及博客
  6. XCTF-高手进阶区:ics-06
  7. 怎么把文件导入python_如何导入其他Python文件?
  8. xp 设备管理器 android,XP设备管理器怎么打开?
  9. js几种常见排序的实现
  10. es6 filter函数的用法_Python 函数式编程指北,不只是面向对象哦!超级详细!
  11. 您企业的邮件系统够安全吗
  12. 为什么手机网速太慢_手机明明是满格信号,为什么网速却非常慢?原来是这些功能在捣鬼...
  13. HTML5--本地存储Web Storage
  14. sofia-sip帮助文档
  15. linux 查看指定目录的所有文件大小
  16. FME官方软件快速下载
  17. 企业微信api调用报50001错误
  18. 拔丝芋头的Java学习日记--Day6
  19. [异能程序员]第六章 遛狗(第二更)
  20. 什么软件打印二维码和条形码?

热门文章

  1. 易飞ERP 文件服务接口
  2. vim中使用cscope的方法
  3. xp apache php 配置文件,xp下面配置PHP+mysql+apache配置
  4. Windows系统下的Openface安装及使用--亲测有效
  5. 圣斗士星矢服务器维护时间,《圣斗士星矢》9月29日更新维护公告
  6. Java Date 24小时制和12小时制
  7. (理财八)普通必须掌握的理财方式----定投
  8. java - 解决idea导入项目,中文出现乱码的问题
  9. python编写死循环语句_Python 全栈开发:python循环语句while
  10. QFP PQFP LQFP TQFP封装形式及PCB详解!