美文网首页通信与流媒体编程实践
Asterisk播放mp4(5)——MP4文件

Asterisk播放mp4(5)——MP4文件

作者: 全栈顾问 | 来源:发表于2020-08-28 09:22 被阅读0次

    Asterisk 现有版本不支持播放视频文件(支持视频通话),无法满足发送视频通知、视频 IVR 等场景。本系列文章,通过学习音视频的相关知识和工具,尝试实现一个通过 Asterisk 播放 mp4 视频文件的应用。

    本文关注在MP4文件中,h264aac的媒体数据是如何存放的,又如何进行访问?为后续解析文件后,通过RTP进行发送进行准备。

    MP4文件格式

    MP4文件封装格式,对应的标准为ISO/IEC 14496-12,即:信息技术视听对象编码的第12部分:ISO 基本媒体文件格式(Information technology Coding of audio-visual objects Part 12: ISO base media file format)。MP4文件可以在其中嵌入任何形式的数据,不过通常存放的是AVC(H.264)编码视频和AAC/MPEG-4(Part 2)编码的音频。

    MP4文件由许多box组成,box可以嵌套。box分为headerdata两部分。header包括size(4字节),type(4字节)和largesize(8字节,可选)。其中,size指明了整个box的大小,包括header部分。如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64来存放大小。box中的字节序为网络字节序,也就是大端字节序(Big-Endian),就是一个32位的4字节整数存储方式为高位字节在内存的低端。

    下面看看几个主要的box

    box类型 层级 名称 说明
    ftyp 1 File Type Box 有且只有1个,只能被包含在文件层,而不能被其他 box 包含。该 box 应该被放在文件的最开始,指示该 MP4 文件应用的相关信息。
    moov 1 Movie Box 有且只有1个,只能被包含在文件层。moov是一个 container box,包含若干子box,例如:mvhd,trak等,子box中存放媒体的元数据(metadata)信息。
    mvhd 2 Movie Header Box 描述了媒体一些基本信息,例如:timescale,duration等。
    trak 2 Track Box trak也是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外)。一个MP4文件中的媒体可以包含1个或多个track,它们之间彼此独立,有自己的时间和空间信息。trak至少包含tkhd和mdia这两个box,此外还有很多可选的box。其中tkhd为track header box,mdia为media box,该box是一个包含一些track媒体数据信息box的container box。
    mdat 1 Media Data 实际媒体数据,最终解码播放的音视频数据都在这里面。对于h264aac编码的媒体来说,其视频mdat中内容是nal,对于音频来说,其内容为aac的一帧。mdat中的帧依次存放,每个帧的位置、时间、长度都由moov中的信息指定。
    mp4 box

    trak是mp4中最复杂的部分,包含了获取媒体数据的各种信息。要理解trak下的box首先要掌握几个基本概念,包括:track,sample,trunk。

    • track:表示sample的集合,对于媒体数据来说,track表示一个视频或音频序列。

    • sample:video sample即为一帧视频,或一组连续视频帧,audio sample即为一段连续的压缩音频。

    • chunk:一个track中的几个sample组成的单元。

    trak下有很多box,最重要也是最复杂的是trak/mdia/minf/stbl,它下面又包含了多个boxsample table指明sampe时序和存储地址的表。利用这个表,可以解析sample的时序、类型、大小以及在文件中的位置。下面对这些box做个简单的说明:

    box类型 名称 说明
    stsd Sample Description Box 包含h264编码的spspps。可以有一个到多个sample description
    stsc Sample To Chunk Box 指定了chunksample的对应关系。从first_chunk这个chunk序号开始,每个chunk都有samples_per_chunksample,每个sample都可以通过sample_description_index这个索引,在stsd中找到描述信息。
    stsz Sample Size Box 指定了每个sample的大小。
    stco Sample Size Box 指定了每个chunk的在整个文件中的起始地址。
    stss Sync Sample Box 关键帧。
    stts DecodingTime to Sample 用于计算sampledts,其中sample_counts定义连续多少个sample的dts具有相同的差值,sample_deltadts的差值。
    ctts Composition Time to Sample 每个sample的构成时间(Composition Time)和解码时间(DT)之间的差值。如果不存在ctts,则代表该流不存在B帧,那么CT就直接等于DT。
    帧顺序及时间

    上图是ISO/IEC 14496-12规范中给出的示例。第2行代表了视频帧的存储序列,帧后面的编号代表了显示顺序。视频流编码时,如果支持B帧,P帧会先于B帧编码,因此帧编码顺序(存储顺序)和帧播放顺序不一致。通过这个图我们就更容理解上面两个和时间相关的box的含义。stts定义的是表格中的第3行,DT,用于计算出每个sampledtsctts对应的是表格中的第6行,Composition offset,用于计算出每个samplepts(Compostion Time)

    DTS(Decode Time Stamp):标识读入内存中的视频帧什么时候开始送入解码器中进行解码。PTS(Presentation Time Stamp):用于度量解码后的视频帧什么时候被显示出来 。这两个值对应的并不是秒,毫秒这些时间单位,而是时间刻度,它们的值除以mdhd中的timescale(每秒钟有多少个时间刻度)转换为时间。

    制作样本

    为了控制篇幅我们忽略音频流,只分析视频流。

    ffmpeg -t 10 -lavfi sine -t 10 -lavfi color=red sine-red-10s.mp4

    FFmpeg命令行输出 sine-red-10s.mp4 vi %!xxd sine-red-10s.mp4

    mdat中存放的就是媒体数据,视频就是h264NALU,因此我们可以将mp4中的数据和h264裸流中的数据进行对比。

    ffmpeg -t 10 -lavfi color=red red-default.h264

    我们可以直接生成h264的裸流,其中I/P/B帧的数据和生成mp4文件中的h264流的数量是一致的。

    FFmpeg命令行输出 red.h264

    通过ffmpeg生成mp4文件时有很多参数可以指定,faststart是比较常用的一个。

    ffmpeg -t 10 -lavfi sine -t 10 -lavfi color=red -movflags faststart sine-red-10s-faststart.mp4

    FFmpeg输出 sine-red-10s-faststart.mp4 sine-red-10s-faststart.mp4

    mp4文件的结构太复杂了,直接看二进制数据很费劲,下面我们通过一个在线工具解析mp4数据。

    ftyp video - mdhd

    后面计算时序时要用到timescaleduration这两个参数。

    video - stsd

    stsd - avc1 -avcC中可以获得h264spspps。另外,还需要特别注意lengthSizeMinusOne这个参数,其含义是用几个字节表示nalu的长度,实际的长度是值加1,这里就是3+1=4h264裸流中,分割nalu通常采用的是Annex B这种用起始码的方式,但是在MP4中使用的是AVCC方式。

    video - stsz

    共有250个采样,每个采样的大小。这里需要注意两点:1、采样的大小是NALU的实际大小加4,因为前面有4字节记录用来记录尺寸;2、采样不一定是一个NALU,实际上第一个采样就包括了SEIIDR两个NALU

    video - stsc

    定义了chunksample的对应关系,以及sample的表述信息(stsd)。这里第一个chunk包含了两个sample,后面每个chunk包含一个sample

    video -stco

    定义了chunk在文件中的地址。通过stszstscstco就可以在文件中获得任意一个sample9792是第一个chunk的偏移量,第一个chunk包含2个采样,前2个采样的大小分别为75917,那么可以计算出下一个chunk的偏移量是10568,并不是下一个视频chunk的偏移量10991,通过查看音频的stco,可知音频流的第一个chunk的偏移量是10568,这说明视频和音频的chunk是交错保存的。

    video - stts

    记录了每个sample间相差的时间刻度512。前面的timescale的值为12800,512/12800=0.04秒,说明每帧之间的解码时间间隔为0.04秒。

    video - ctts

    ctts提供的是composition timedecoding time的差值,它们的和就是composition time

    通过解析red.h264文件的slice可以知道每帧的大小和类型,这样方便我们理解decode time

    解析red.h264文件的nalu
    1 视频帧 I P B B B P B B B
    2 DT 0 512 1024 1536 2048 2560 3072 3584 4096
    3 Composition offset 1024 2560 1024 0 512 2560 1024 0 512
    4 CT 1024 3072 2048 1536 2560 5120 4096 3584 4608
    5 seq 1 5 3 2 4 9 7 6 8
    6 size 65 13 10 10 10 19 12 10 10

    上表中的第1行(DT)代表的是文件中视频帧的存储顺序,第4行(CT)代表的是视频帧的播放时间(单位是时间刻度),第5行(seq)代表的是视频帧的播放顺序。可以看到存储顺序和播放顺序是不一致的,这是因为视频帧中包含B帧,它是双向依赖帧,解析是可能要依赖P(或B帧),所以虽然B帧的播放顺序靠前,但是解码的时候必须要先解码P帧,才能解码B帧。另外,在avcc中一个sample的前4个字节代表这个包的大小,因此stsz中记录的采样大小比NALU的大小多4。

    参考

    QuickTime container 有box类型的定义

    MP4文件格式解析

    在线MP4解析工具

    ISO/IEC 14496-12:2015 下载地址

    FFmpeg formats - mov, mp4, ismv

    相关文章

      网友评论

        本文标题:Asterisk播放mp4(5)——MP4文件

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