MP4(MPEG-4 Part 14)是一種常見的多媒體容器格式,它是在“ISO/IEC 14496-14”標(biāo)準(zhǔn)文件中定義的,屬于MPEG-4的一部分,是“ISO/IEC 14496-12(MPEG-4 Part 12 ISO base media file format)”標(biāo)準(zhǔn)中所定義的媒體格式的一種實現(xiàn),后者定義了一種通用的媒體文件結(jié)構(gòu)標(biāo)準(zhǔn)。MP4是一種描述較為全面的容器格式,被認(rèn)為可以在其中嵌入任何形式的數(shù)據(jù),各種編碼的視頻、音頻等都不在話下,不過我們常見的大部分的MP4文件存放的AVC(H.264)或MPEG-4(Part 2)編碼的視頻和AAC編碼的音頻。MP4格式的官方文件后綴名是“.mp4”,還有其他的以mp4為基礎(chǔ)進(jìn)行的擴(kuò)展或者是縮水版本的格式,包括:M4V,3GP,F4V等。
首先看一下軟件對于mp4文件的解析如圖1所示:

從圖中可以看出這個視頻文件第一層有4部分,每一部分都是一個box,分別為:ftype,moov,free,mdat。其實mp4文件是有許多的box組成的。如圖2所示:

box的基本結(jié)構(gòu)如圖3所示,其中,size指明了整個box所占用的大小,包括header部分,type指明了box的類型。如果box很大(例如存放具體視頻數(shù)據(jù)的mdat box),超過了uint32的最大數(shù)值,size就被設(shè)置為1,并用接下來的8位uint64來存放大小。

一個mp4文件有可能包含非常多的box,在很大程度上增加了解析的復(fù)雜性,這個網(wǎng)頁上http://mp4ra.org/atoms.html記錄了一些當(dāng)前注冊過的box類型??吹竭@么多box,如果要全部支持,一個個解析,怕是頭都要爆了。還好,大部分mp4文件沒有那么多的box類型,下圖就是一個簡化了的,常見的mp4文件結(jié)構(gòu)如圖4所示

一般來說,解析媒體文件,最關(guān)心的部分是視頻文件的寬高、時長、碼率、編碼格式、幀列表、關(guān)鍵幀列表,以及所對應(yīng)的時戳和在文件中的位置,這些信息,在mp4中,是以特定的算法分開存放在stbl box下屬的幾個box中的,需要解析stbl下面所有的box,來還原媒體信息。下表是對于以上幾個重要的box存放信息的說明:

其實除了moov在前面的還有moov在后面的情況,如果要實現(xiàn)邊下邊播,就得把moov的box移到前面,因為只有獲得了moov的信息,播放器才可以獲取到播放器的信息來進(jìn)行播放。具體代碼如下:
- (NSData*)exchangestco:(NSMutableData*) moovdata{
inti, atom_size, offset_count, current_offset;
NSString*atom_type;
longlongmoov_atom_size = moovdata.length;
Byte*buffer = (Byte*)malloc(5);
buffer[4] =0;
Byte*buffer01 = (Byte*)malloc(moov_atom_size);
[moovdatagetBytes:buffer01 length:moov_atom_size];
for(i =4; i < moov_atom_size -4; i++) {
NSRangerange;
range.location= i;
range.length=4;
[moovdatagetBytes:buffer range:range];
atom_type = [selftosType:buffer];
if([atom_typeisEqualToString:@"stco"]) {
range.location= i-4;
range.length =4;
[moovdatagetBytes:bufferrange:range];
atom_size = [selftoSize:buffer];
if(i + atom_size -4> moov_atom_size) {
WBLog(LOG_ERROR,@"error i + atom_size - 4 > moov_atom_size");
returnnil;
}
range.location= i+8;
range.length=4;
[moovdatagetBytes:bufferrange:range];
offset_count = [selftoSize:buffer];
for(intj =0; j < offset_count; j++) {
range.location= i +12+ j *4;
range.length=4;
[moovdatagetBytes:bufferrange:range];
current_offset= [selftoSize:buffer];
current_offset += moov_atom_size;
buffer01[i +12+ j *4+0] = (Byte) ((current_offset >>24) &0xFF);
buffer01[i +12+ j *4+1] = (Byte) ((current_offset >>16) &0xFF);
buffer01[i +12+ j *4+2] = (Byte) ((current_offset >>8) &0xFF);
buffer01[i +12+ j *4+3] = (Byte) ((current_offset >>0) &0xFF);
}
i += atom_size -4;
}
elseif([atom_typeisEqualToString:@"co64"]) {
range.location= i-4;
range.length=4;
[moovdatagetBytes:bufferrange:range];
atom_size = [selftoSize:buffer];
if(i + atom_size -4> moov_atom_size) {
WBLog(LOG_ERROR,@"error i + atom_size - 4 > moov_atom_size");
returnnil;
}
range.location= i+8;
range.length=4;
[moovdatagetBytes:bufferrange:range];
offset_count = [selftoSize:buffer];
for(intj =0; j < offset_count; j++) {
range.location= i +12+ j *8;
range.length=4;
[moovdatagetBytes:bufferrange:range];
current_offset = [selftoSize:buffer];
current_offset += moov_atom_size;
buffer01[i +12+ j *8+0] = (Byte)((current_offset >>56) &0xFF);
buffer01[i +12+ j *8+1] = (Byte)((current_offset >>48) &0xFF);
buffer01[i +12+ j *8+2] = (Byte)((current_offset >>40) &0xFF);
buffer01[i +12+ j *8+3] = (Byte)((current_offset >>32) &0xFF);
buffer01[i +12+ j *8+4] = (Byte)((current_offset >>24) &0xFF);
buffer01[i +12+ j *8+5] = (Byte)((current_offset >>16) &0xFF);
buffer01[i +12+ j *8+6] = (Byte)((current_offset >>8) &0xFF);
buffer01[i +12+ j *8+7] = (Byte)((current_offset >>0) &0xFF);
}
i += atom_size -4;
}
}
NSData*moov = [NSDatadataWithBytes:buffer01length:moov_atom_size];
free(buffer);
free(buffer01);
returnmoov;
}