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

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

H264的配置信息解析

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

AVCodecContext定义如下:

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

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

AAC的配置信息解析及设置

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

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

extradate数据定义如下:

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

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

typedefstruct

{

intwrite_adts;

intobjecttype;

intsample_rate_index;

intchannel_conf;

}ADTSContext;

intaac_decode_extradata(ADTSContext*adts,unsignedchar*pbuf,intbufsize)

{

intaot,aotext,samfreindex;

inti,channelconfig;

unsignedchar*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;

return0;

}

上面的pbuf就是extradata。

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

intaac_set_adts_head(ADTSContext*acfg,unsignedchar*buf,intsize)

{

unsignedcharbyte;

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;

return0;

}

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

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

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

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

相关推荐