通過上一篇文章,我們用ffmpeg分離出一個(gè)多媒體容器中的音視頻數(shù)據(jù),但是很可能這些數(shù)據(jù)是不能被正確解碼的。為什么呢?因?yàn)樵诮獯a這些數(shù)據(jù)之前,需要對解碼器做一些配置,典型的就是目前流行的高清編碼“黃金搭檔”組合H264 + AAC的搭配。本文將講述H264和AAC的關(guān)鍵解碼配置參數(shù)的解析,如果沒有這些配置信息,數(shù)據(jù)幀往往不完整,導(dǎo)致了解碼器不能解碼。
H264的配置信息解析
前面我們知道,ffmpeg的avformat_find_stream_info函數(shù)可以取得音視頻媒體多種,比如播放持續(xù)時(shí)間、音視頻壓縮格式、音軌信息、字幕信息、幀率、采樣率等。在信息結(jié)果中有一項(xiàng)擴(kuò)展數(shù)據(jù)描述(avcodec.h文件中):
AVCodecContext定義如下:
如果視頻流是H264,這個(gè)extradate里面就包含了H264的配置信息,這個(gè)擴(kuò)展數(shù)據(jù)有如下定義:
詳細(xì)解釋可以參考“ISO-14496-15 AVC file format”文檔。里面最重要的就是NAL長度和SPS,PPS數(shù)據(jù)和對應(yīng)的長度信息。對該數(shù)據(jù)的解析在ffmpeg里面有現(xiàn)成的函數(shù):ff_h264_decode_extradata,在我的項(xiàng)目里面是自己寫的擴(kuò)展數(shù)據(jù)解析。
AAC的配置信息解析及設(shè)置
如果音頻數(shù)據(jù)是AAC流,在解碼時(shí)需要ADTS(Audio Data Transport Stream)頭部,不管是容器封裝還是流媒體,沒有這個(gè),一般都是不能播放的。很多朋友在做AAC流播放時(shí)遇到播不出聲音,很可能就是這個(gè)原因?qū)е隆?/p>
ADTS所需的數(shù)據(jù)仍然是放在上面的擴(kuò)展數(shù)據(jù)extradata中,我們需要先解碼這個(gè)擴(kuò)展數(shù)據(jù),然后再從解碼后的數(shù)據(jù)信息里面重新封裝成ADTS頭信息,加到每一幀AAC數(shù)據(jù)之前再送解碼器,這樣就可以正常解碼了。
extradate數(shù)據(jù)定義如下:
詳細(xì)信息及說明請參考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。里面最重要的部分有采樣頻率、通道配置和音頻對象類型,這幾個(gè)一般都是AAC解碼器需要的配置參數(shù)。
這個(gè)數(shù)據(jù)在ffmpeg中也有相應(yīng)的解碼函數(shù):avPRiv_aac_parse_header。在我的項(xiàng)目中,我沒有使用這個(gè)函數(shù),而是自己實(shí)現(xiàn)的:
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數(shù)據(jù)編碼為ADTS頭信息插入每一個(gè)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;}
這個(gè)頭部是固定的7字節(jié)長度,所以可提前空出這7個(gè)字節(jié)供ADTS占用。
通過以上對H264和AAC的擴(kuò)展數(shù)據(jù)處理,播放各種“黃金搭檔”的多媒體文件、流媒體、視頻點(diǎn)播等都應(yīng)該沒有問題了。
想第一時(shí)間獲得更多原創(chuàng)文章,請關(guān)注個(gè)人微信公眾平臺:程序員互動聯(lián)盟(coder_online),掃一掃下方二維碼或者搜索微信號coder_online即可關(guān)注,里面有大量Android,Chromium,linux等相關(guān)文章等著您,我們還可以在線交流。
摘自:http://my.oschina.net/u/2336532/blog/400790
新聞熱點(diǎn)
疑難解答
圖片精選