mp4格式

作者: smm987 | 来源:发表于2017-07-24 17:01 被阅读588次

    MP4(MPEG-4 Part 14)是一种常见的多媒体容器格式,它是在“ISO/IEC 14496-14”标准文件中定义的,属于MPEG-4的一部分,是“ISO/IEC 14496-12(MPEG-4 Part 12 ISO base media file format)”标准中所定义的媒体格式的一种实现,后者定义了一种通用的媒体文件结构标准。MP4是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,各种编码的视频、音频等都不在话下,不过我们常见的大部分的MP4文件存放的AVC(H.264)MPEG-4(Part 2)编码的视频和AAC编码的音频。MP4格式的官方文件后缀名是“.mp4”,还有其他的以mp4为基础进行的扩展或者是缩水版本的格式,包括:M4V,3GP,F4V等。

    首先看一下软件对于mp4文件的解析如图1所示:

    图1

    从图中可以看出这个视频文件第一层有4部分,每一部分都是一个box,分别为:ftype,moov,free,mdat。其实mp4文件是有许多的box组成的。如图2所示:

    图2

    box的基本结构如图3所示,其中,size指明了整个box所占用的大小,包括header部分,type指明了box的类型。如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64来存放大小。

    图3

    一个mp4文件有可能包含非常多的box,在很大程度上增加了解析的复杂性,这个网页上http://mp4ra.org/atoms.html记录了一些当前注册过的box类型。看到这么多box,如果要全部支持,一个个解析,怕是头都要爆了。还好,大部分mp4文件没有那么多的box类型,下图就是一个简化了的,常见的mp4文件结构如图4所示

    图4

    一般来说,解析媒体文件,最关心的部分是视频文件的宽高、时长、码率、编码格式、帧列表、关键帧列表,以及所对应的时戳和在文件中的位置,这些信息,在mp4中,是以特定的算法分开存放在stbl box下属的几个box中的,需要解析stbl下面所有的box,来还原媒体信息。下表是对于以上几个重要的box存放信息的说明:

    图5

    其实除了moov在前面的还有moov在后面的情况,如果要实现边下边播,就得把moov的box移到前面,因为只有获得了moov的信息,播放器才可以获取到播放器的信息来进行播放。具体代码如下:

    - (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;

    }

    相关文章

      网友评论

      • fxxxchao:您好,我最近正在研究“视频边下边播”的功能,关于这篇文章有两个问题想请教:_______1、对于moov处于mp4文件尾部的情况,我写了一个demo去测试上面获取视频文件中moov对象的方法,下载部分使用的是NSURLConnection,在代理中将每次从服务器接收的通过上面的方法进行校验,显然无论是在实际应用中还是效率提升上都是不对的(不可能把所有数据都校验一边,校验需要的时间,文件都已经下载完了),所以我试图通过这篇文章(http://blog.qiusuo.im/blog/2015/02/05/play-mp4-using-http/)中的方法,先获取整个mp4文件的range,进而来缩小校验范围,不过moov的长度也不是固定的,请问这点您是如何解决的呢?_______2、上面方法中的代码并不完整,缺少tosType和toSize方法,如果方便,希望能够一并贴出作为参考。_______期待回复。
        smm987:@人性之光 不是的,如果采用https://www.jianshu.com/p/bb925a4a9180文章中的第二种方案的话,是不需要解析mp4格式的。
        fxxxchao:@smm987 感谢回复。_______为了进行“视频边下边播”,关于下载我尝试了几种方式,其中就包括NSURLConnection和NSURLSession(关于下载方式的建议我会采纳),经过测试后,发现我们的视频在下载一半的时候,是不能播放的,而网上找了部分测试视频是可以的,之前对于MP4信息不太了解,最后确认原因,是我们的视频moov在文件末尾导致的。_______您的另一篇文章我已经看过了,但是按照您本篇文章中说的“如果要实现边下边播,就得把moov的box移到前面,因为只有获得了moov的信息,播放器才可以获取到播放器的信息来进行播放”,所以是不是意味着参考另一篇文章之前,如何“先获得moov文件,并记录在本地”也是不可绕过的问题呢?
        smm987:解析mp4格式然后再进行播放是之前的文件缓存的方案,其实效果不是很好,建议研究一下我的另一篇文章https://www.jianshu.com/p/bb925a4a9180。另外NSURLConnection中是有bug的,不建议继续使用,建议使用NSURLSession这个新的api。

      本文标题:mp4格式

      本文链接:https://www.haomeiwen.com/subject/nsmbkxtx.html