先来看一张mp4的文件结构图:
mp4文件都是由各个box组成,box里也可以包含box,那么这种box就叫containerbox.每个box前四个字节为box的size,第二个四字节为box的type,box type有ftyp,moov,trak 等等好多种,moov是containerbox,包含mvhd、trak等box,如上图.
关于各种box的详细描述,请查看Apple官方文档:quicktime file format specification: 最权威的格式文档 点击下载
在MPEG4Extractor::parseChunk(off64_t *offset, int depth)里解析各个box,并把box里的重要媒体信息
通过track->meta->setXXX来保存。parseChunk遍历box的原理是:以当前box的偏移位置offset,加上该box的size, 就可以跳跃到下一个box。
调用一次parseChunk只解析一个box,遇到container box的话,一直递归下,直到把所有的子box 解析完。比如遇到ftype box,解析完就return了,但是遇到moov,
parseChunk就递归调用自己解析所有moov包含的子box
里面的Track主要是建立video track和audio track的一个链表:mFirstTrack--->track--->mLastTrack这样的链表,一般来说,只有音视频两个track。
Track是这样一个结构体:
struct Track {
Track *next;
sp<MetaData> meta;
uint32_t timescale;
sp<SampleTable> sampleTable;
bool includes_expensive_metadata;
bool skipTrack;
};
包含很重要的两个东西 meta和sampleTable,
meta主要是保存一些track的codec、language宽高、创建时间等媒体信息,通过track->meta->setXXX来保存,比如:
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
mLastTrack->meta->setCString(kKeyMediaLanguage, lang_code);
mLastTrack->meta->setInt32(kKeyWidth, width);
mLastTrack->meta->setInt32(kKeyHeight, height);
mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16);
mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16);
mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
那请问我们怎么知道当前的track是video track还是audio track?
开始我以我是通过trak下面的hdlr这个子box,因为他里面有个字段用vide来标识video trak,用soun来标识audio track
后面发现我错了,实际上,去解析一个track (trak box)的时候,只要发现该track下有以下子box,就认定该track为video track,
有实际代码为证:
case FOURCC('m', 'p', '4', 'v'):
case FOURCC('e', 'n', 'c', 'v'):
case FOURCC('s', '2', '6', '3'):
case FOURCC('H', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
case FOURCC('a', 'v', 'c', '1'):
{
mHasVideo = true;
............................
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
同理,audio track也一样,只要发现有如下box,就认为为audio track,
case FOURCC('m', 'p', '4', 'a'):
case FOURCC('e', 'n', 'c', 'a'):
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
{
.............................
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
然后在AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor)里
通过meta->findCString(kKeyMIMEType, &_mime)获取track的kKeyMIMEType来判断是video track还是audio track
我们再看上面代码片段屡次提到的FourCC2MIME,
static const char *FourCC2MIME(uint32_t fourcc) {
switch (fourcc) {
case FOURCC('m', 'p', '4', 'a'):
return MEDIA_MIMETYPE_AUDIO_AAC;//audio/mp4a-latm
case FOURCC('s', 'a', 'm', 'r'):
return MEDIA_MIMETYPE_AUDIO_AMR_NB;//audio/3gpp
case FOURCC('s', 'a', 'w', 'b'):
return MEDIA_MIMETYPE_AUDIO_AMR_WB;//audio/amr-wb
case FOURCC('m', 'p', '4', 'v'):
return MEDIA_MIMETYPE_VIDEO_MPEG4;//video/mp4v-es
case FOURCC('s', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
case FOURCC('H', '2', '6', '3'):
return MEDIA_MIMETYPE_VIDEO_H263;
case FOURCC('a', 'v', 'c', '1'):
return MEDIA_MIMETYPE_VIDEO_AVC;//video/avc
default:
CHECK(!"should not be here.");
return NULL;
}
}