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

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

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

AVCodecContext定义如下:

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

详细解释可以参考“ISO-14496-15AVC file format”文档。里面最重要的就是NAL长度和SPS,PPS数据和对应的长度信息。对该数据的解析在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。在我的项目中,我没有使用这个函数,而是自己实现的: typedefstruct { int write_adts; int objecttype; int sample_rate_index; int channel_conf; }ADTSContext; intaac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize) { int aot, aotext, samfreindex; int i, channelconfig; unsigned char *p = pbuf; if (!adts || !pbuf || bufsize<2) { return -1; } aot = (p[0]>>3)&0x1f; if (aot == 31) { aotext = (p[0]<<3 |(p[1]>>5))&0x3f; aot = 32 + aotext; samfreindex =(p[1]>>1)&0x0f; if (samfreindex == 0x0f) { channelconfig =((p[4]<<3)|(p[5]>>5))&0x0f; } else { channelconfig =((p[1]<<3)|(p[2]>>5))&0x0f; } } else { samfreindex =((p[0]<<1)|p[1]>>7)&0x0f; if (samfreindex == 0x0f) { channelconfig = (p[4]>>3)&0x0f; } else { channelconfig =(p[1]>>3)&0x0f; } } #ifdefAOT_PROFILE_CTRL if (aot < 2) aot = 2; #endif adts->objecttype = aot-1; adts->sample_rate_index = samfreindex; adts->channel_conf = channelconfig; adts->write_adts = 1; return 0; }

上面的pbuf就是extradata。

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

intaac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)

{

unsigned char byte;

if (size < ADTS_HEADER_SIZE)

{

return -1;

}

buf[0] = 0xff;

buf[1] = 0xf1;

byte = 0;

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

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

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

buf[2] = byte;

byte = 0;

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

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

buf[3] = byte;

byte = 0;

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

buf[4] = byte;

byte = 0;

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

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

buf[5] = byte;

byte = 0;

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

buf[6] = byte;

return 0;

}

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

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

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-04-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏不会写文章的程序员不是好厨师

伪共享(False Sharing)和缓存行(Cache Line) 大杂烩

在上篇介绍LongAdder的文章中,我们最后留下了一个问题,为什么Cell中要插入很多个实际上并没有使用的Long变量?这个问题就得从False Sharin...

861
来自专栏琦小虾的Binary

交叉编译学习笔记(二)——交叉编译器的命名规则

交叉编译学习笔记(二)——交叉编译器的命名规则 在进行嵌入式开发过程中,用到的交叉编译器经常是这样的名称: arm-linux-gcc arm-none-lin...

2235
来自专栏owent

关于BUS通信系统的一些思考(二)

虽然我很不愿意再设计一套BUS系统,但是现有的一些确实都没有特别符合我的口味的。所以还是尝试设计一个出来。

813
来自专栏熊二哥

快速入门系列--WCF--04元数据和异常处理

本章节将进行元数据和异常处理的介绍,这部分内容概念型比较强,可以快速浏览一下就好。 ? 客户端和服务器借助于终结点进行通信,服务的提供者通过一个或者多个终结点...

1758
来自专栏Seebug漏洞平台

CVE-2017-5123 漏洞利用全攻略

原文:https://salls.github.io/Linux-Kernel-CVE-2017-5123/ 译者:hello1900@知道创宇404实验室...

3217
来自专栏大内老A

WCF服务端运行时架构体系详解[上篇]

WCF的服务端架构体系又可以成为服务寄宿端架构体系。我们知道,对于一个基于某种类型的服务进行寄宿只需要使用到一个唯一的对象,那就是ServiceHost。甚至在...

1809
来自专栏用户2442861的专栏

操作系统各大公司笔试题汇总

A、一维                               B、二维                                C、三维    ...

681
来自专栏涤生的博客

天池中间件大赛——单机百万消息队列存储设计与实现

这次天池中间件性能大赛初赛和复赛的成绩都正好是第五名,本次整理了复赛《单机百万消息队列的存储设计》的思路方案分享给大家,实现方案上也是决赛队伍中相对比较特别的。

1031
来自专栏吉浦迅科技

DAY64:阅读 Memory Model

我们正带领大家开始阅读英文的《CUDA C Programming Guide》,今天是第64天,我们正在讲解CUDA C语法,希望在接下来的36天里,您可以学...

573
来自专栏大数据

python 斗鱼爬虫

看了许久的斗鱼直播,突然心血来潮,想用爬虫对斗鱼所有直播间的信息抓取 一开始,我简单对斗鱼代码进行了分析,直观地认为所有直播间都在html文件里。 就直接 选择...

2685

扫码关注云+社区