经过上一篇文章,咱们用ffmpeg分离出一个多媒体容器中的音视频数据,可是极可能这些数据是不能被正确解码的。为何呢?由于在解码这些数据以前,须要对解码器作一些配置,典型的就是目前流行的高清编码“黄金搭档”组合H264 + AAC的搭配。本文将讲述H264和AAC的关键解码配置参数的解析,若是没有这些配置信息,数据帧每每不完整,致使了解码器不能解码。程序员
H264的配置信息解析微信
前面咱们知道,ffmpeg的avformat_find_stream_info函数能够取得音视频媒体多种,好比播放持续时间、音视频压缩格式、音轨信息、字幕信息、帧率、采样率等。在信息结果中有一项扩展数据描述(avcodec.h文件中):微信公众平台
AVCodecContext定义以下:函数
若是视频流是H264,这个extradate里面就包含了H264的配置信息,这个扩展数据有以下定义:编码
详细解释能够参考“ISO-14496-15 AVC file format”文档。里面最重要的就是NAL长度和SPS,PPS数据和对应的长度信息。对该数据的解析在ffmpeg里面有现成的函数:ff_h264_decode_extradata,在个人项目里面是本身写的扩展数据解析。spa
AAC的配置信息解析及设置.net
若是音频数据是AAC流,在解码时须要ADTS(Audio Data Transport Stream)头部,无论是容器封装仍是流媒体,没有这个,通常都是不能播放的。不少朋友在作AAC流播放时遇到播不出声音,极可能就是这个缘由致使。code
ADTS所需的数据仍然是放在上面的扩展数据extradata中,咱们须要先解码这个扩展数据,而后再从解码后的数据信息里面从新封装成ADTS头信息,加到每一帧AAC数据以前再送解码器,这样就能够正常解码了。orm
extradate数据定义以下:视频
详细信息及说明请参考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。里面最重要的部分有采样频率、通道配置和音频对象类型,这几个通常都是AAC解码器须要的配置参数。
这个数据在ffmpeg中也有相应的解码函数:avpriv_aac_parse_header。在个人项目中,我没有使用这个函数,而是本身实现的:
typedef struct { int write_adts; int objecttype; int sample_rate_index; int channel_conf; }ADTSContext;
int aac_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; } } #ifdef AOT_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帧前面:
int aac_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),扫一扫下方二维码或者搜索微信号coder_online便可关注,里面有大量Android,Chromium,Linux等相关文章等着您,咱们还能够在线交流。
摘自:http://my.oschina.net/u/2336532/blog/400790