文档章节

解密H264、AAC硬件解码的关键扩展数据处理

q
 qiugc
发布于 2015/04/14 19:26
字数 1054
阅读 7
收藏 0

 通过上一篇文章,我们用ffmpeg分离出一个多媒体容器中的音视频数据,但是很可能这些数据是不能被正确解码的。为什么呢?因为在解码这些数据之前,需要对解码器做一些配置,典型的就是目前流行的高清编码“黄金搭档”组合H264 + AAC的搭配。本文将讲述H264AAC的关键解码配置参数的解析,如果没有这些配置信息,数据帧往往不完整,导致了解码器不能解码。

  • H264的配置信息解析

    前面我们知道,ffmpegavformat_find_stream_info函数可以取得音视频媒体多种,比如播放持续时间、音视频压缩格式、音轨信息、字幕信息、帧率、采样率等。在信息结果中有一项扩展数据描述(avcodec.h文件中):

          AVCodecContext定义如下:

    如果视频流是H264,这个extradate里面就包含了H264的配置信息,这个扩展数据有如下定义:

    详细解释可以参考ISO-14496-15 AVC file format文档。里面最重要的就是NAL长度和SPSPPS数据和对应的长度信息。对该数据的解析在ffmpeg里面有现成的函数:ff_h264_decode_extradata,在我的项目里面是自己写的扩展数据解析。

  • AAC的配置信息解析及设置

    如果音频数据是AAC流,在解码时需要ADTS(Audio Data Transport Stream)头部,不管是容器封装还是流媒体,没有这个,一般都是不能播放的。很多朋友在做AAC流播放时遇到播不出声音,很可能就是这个原因导致。

    ADTS所需的数据仍然是放在上面的扩展数据extradata中,我们需要先解码这个扩展数据,然后再从解码后的数据信息里面重新封装成ADTS头信息,加到每一帧AAC数据之前再送解码器,这样就可以正常解码了。

    extradate数据定义如下:

     

        详细信息及说明请参考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。里面最重要的部分有采样频率、通道配置和音频对象类型,这几个一般都是AAC解码器需要的配置参数。

        这个数据在ffmpeg中也有相应的解码函数:avpriv_aac_parse_header。在我的项目中,我没有使用这个函数,而是自己实现的:

  • [cpp] view plaincopy

    1. typedef struct  

    2. {  

    3.       int write_adts;  

    4.       int objecttype;  

    5.       int sample_rate_index;  

    6.       int channel_conf;  

    7. }ADTSContext;  

  • [cpp] view plaincopy

    1. int aac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize)  

    2. {  

    3.       int aot, aotext, samfreindex;  

    4.       int i, channelconfig;  

    5.       unsigned char *p = pbuf;  

    6.    

    7.       if (!adts || !pbuf || bufsize<2)  

    8.       {  

    9.             return -1;  

    10.       }  

    11.       aot = (p[0]>>3)&0x1f;  

    12.       if (aot == 31)  

    13.       {  

    14.             aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;  

    15.             aot = 32 + aotext;  

    16.             samfreindex = (p[1]>>1) & 0x0f;  

    17.              

    18.             if (samfreindex == 0x0f)  

    19.             {  

    20.                   channelconfig = ((p[4]<<3) | (p[5]>>5)) & 0x0f;  

    21.             }  

    22.             else  

    23.             {  

    24.                   channelconfig = ((p[1]<<3)|(p[2]>>5)) & 0x0f;  

    25.             }  

    26.       }  

    27.       else  

    28.       {  

    29.             samfreindex = ((p[0]<<1)|p[1]>>7) & 0x0f;  

    30.             if (samfreindex == 0x0f)  

    31.             {  

    32.                   channelconfig = (p[4]>>3) & 0x0f;  

    33.             }  

    34.             else  

    35.             {  

    36.                   channelconfig = (p[1]>>3) & 0x0f;  

    37.             }  

    38.       }  

    39.    

    40. #ifdef AOT_PROFILE_CTRL  

    41.       if (aot < 2) aot = 2;  

    42. #endif  

    43.       adts->objecttype = aot-1;  

    44.       adts->sample_rate_index = samfreindex;  

    45.       adts->channel_conf = channelconfig;  

    46.       adts->write_adts = 1;  

    47.    

    48.       return 0;  

    49. }  

           上面的pbuf就是extradata

    接下来,再用ADTSContext数据编码为ADTS头信息插入每一个AAC帧前面:

  • [cpp] view plaincopy

    1. int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)  

    2. {         

    3.       unsigned char byte;  

    4.    

    5.       if (size < ADTS_HEADER_SIZE)  

    6.       {  

    7.             return -1;  

    8.       }  

    9.        

    10.       buf[0] = 0xff;  

    11.       buf[1] = 0xf1;  

    12.       byte = 0;  

    13.       byte |= (acfg->objecttype & 0x03) << 6;  

    14.       byte |= (acfg->sample_rate_index & 0x0f) << 2;  

    15.       byte |= (acfg->channel_conf & 0x07) >> 2;  

    16.       buf[2] = byte;  

    17.       byte = 0;  

    18.       byte |= (acfg->channel_conf & 0x07) << 6;  

    19.       byte |= (ADTS_HEADER_SIZE + size) >> 11;  

    20.       buf[3] = byte;  

    21.       byte = 0;  

    22.       byte |= (ADTS_HEADER_SIZE + size) >> 3;  

    23.       buf[4] = byte;  

    24.       byte = 0;  

    25.       byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;  

    26.       byte |= (0x7ff >> 6) & 0x1f;  

    27.       buf[5] = byte;  

    28.       byte = 0;  

    29.       byte |= (0x7ff & 0x3f) << 2;  

    30.       buf[6] = byte;  

    31.    

    32.       return 0;  

    33. }  

  这个头部是固定的7字节长度,所以可提前空出这7个字节供ADTS占用。

  通过以上对H264AAC的扩展数据处理,播放各种“黄金搭档”的多媒体文件、流媒体、视频点播等都应该没有问题了。

 

  想第一时间获得更多原创文章,请关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或者搜索微信号coder_online即可关注,里面有大量AndroidChromiumLinux等相关文章等着您,我们还可以在线交流。

        如需转载本文,请注明出处:谢谢合作!


本文转载自:

q
粉丝 5
博文 20
码字总数 19649
作品 0
海淀
私信 提问
解密H264、AAC硬件解码的关键扩展数据处理

通过上一篇文章(http://my.oschina.net/u/2336532/blog/399058),我们用ffmpeg分离出一个多媒体容器中的音视频数据,但是很可能这些数据是不能被正确解码的。为什么呢?因为在解码这些数据之...

yang_danny
2015/04/14
0
2
使用librtmp进行H264与AAC直播

libx264 版本是 128 libfaac 版本是 1.28 1、帧的划分 1.1 H.264 帧 对于 H.264 而言每帧的界定符为 00 00 00 01 或者 00 00 01。 比如下面的 h264 文件片断这就包含三帧数据: 00 00 00 01 ...

Jerikc
2015/09/06
9.4K
3
解密FFmpeg播放track mode控制

上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有...

yang_danny
2015/04/19
0
0
解密FFmpeg播放状态控制内幕

上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有...

东辉在线
2015/04/21
0
0
iOS音频AAC视频H264编码 推流最佳方案

项目都是个人的调研与实验,可能很多不好或者不对的地方请多包涵。 1 功能概况 实现音视频的数据的采集 实现音视频数据的编码,视频编码成h264,音频编码成aac 实现音视频数据的发布,将编码...

人生好迈
2015/12/02
7.1K
3

没有更多内容

加载失败,请刷新页面

加载更多

策略模式

策略模式封装的是算法,而状态模式侧重的对象状态的转变。 /** * 策略,定义计算报价算法的接口 */public interface Strategy { /** * 计算应报的价格 * @param goo...

铁骨铮铮
24分钟前
0
0
如何用JavaScript写一个区块链?

Part1实现一个基本的区块链 1.区块链 区块链是由一个个任何人都可以访问的区块构成的公共数据库。这好像没什么特别的,不过它们有一个有趣的属性:它们是不可变的。一旦一个区块被添加到区块...

骚年锦时
27分钟前
0
0
HTTP协议

HTTP简介 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。 HTTP是一个基于TCP/IP通信协议...

惊尘大人
29分钟前
0
0
Feign输出Info级别日志

背景   spring cloud netfix组件中,feign相关的日志默认是不会输出的,需要自定义配置才能输出,并且Feign只对Debug基本的日志做出响应, 实际业务需要输出Info级别的日志,所以需要做自定...

xiaomin0322
34分钟前
3
0
面向解决问题的java编程,spring boot,mybatis generator和坑-1starter

1、start一个spring boot项目 第一课我们也不能免俗,要从starter开始,spring boot的起始项目脚手架可以从spring boot官方starter生成地址开始:https://start.spring.io/ 这张图列出了一个...

wphmoon
35分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部