一、MP4格式
1.1 简介
MP4是跨平台最好且最常见的格式。MP4格式标准为ISO-14496 Part 12和Part 14。
MP4文件由许多个Box与FullBox组成。每个Box由Header(包含整个box的长度和类型)和Data两部分组成。当一个Box中Data是一系列的子Box时,这个Box又可以称为Container(容器)。
容器会嵌套容器,主要的一级容器包括ftyp、moov和mdat容器等,在互联网点播中moov存放在mdat前mp4文件可以快速打开。
一级容器 | 二级容器 | 必选 | 描述 |
---|---|---|---|
ftyp | √ | 文件类型 | |
free | 空闲区域 | ||
moov | √ | 音视频数据meta信息 | |
mvhd | √ | Movie Header Atom,存放未压缩过的影片信息 | |
trak | √ | 流的trak(可能有多个) |
1.2 分析工具
- FFmpeg
- ElecardStreamEye
- mp4box,是GPAC项目中的一个组件,可对媒体文件进行合成、拆解等操作。
- mp4info,MP4可视化分析工具
1.3 moov容器
使用mp4info打开mp4文件后,moov数据如上图所示。
数据解析过程如下:
- moov容器有4个字节的尺寸和4个字节的类型,图中可以看到,该容器包含0x00010B6D(68461)字节,类型为0x6D6F6F76(moov)。
- 继续向下解析,子容器有8个字节的header,包含0x0000006C(108)字节,类型为0x6D766864(mvhd)。后续跟着(108-8)个字节的内容。
- 继续向下解析,子容器有8个字节的header,包含0x00007356(29526)字节,类型为0x7472616B(trak)。
- 继续向下解析,子容器有8个字节的header,包含0x00009741(38721)字节,类型为0x7472616B(trak)。
- 继续向下解析,子容器有8个字节的header,包含0x00000062(98)字节,类型为0x75647461(udta)。
- 通过分析,udta+视频trak+音频trak+mvhd+moov头信息,得出来的总大小刚好为68461字节,与前面得出来的moov的大小相等。moov读取完毕。
mvhd子容器
mvhd的108字节,包含的参数如下表所示,主要有影片时长、播放速度和音量等参数。
实例数据如下:
其中 0x000003E8为时间单位1000,0x00014FEB为影片长度85.995s。
1 | 00 00 00 6C 6D 76 68 64 00 00 00 00 00 00 00 00 |
1.4 trak容器
三级容器 | 四级容器 | 五级容器 | 六级容器 | 必选 | 描述 |
---|---|---|---|---|---|
tkhd | √ | 流信息的trak头 | |||
edts | edit list容器 | ||||
elst | edit list元素信息 | ||||
mdia | √ | trak里的media信息 | |||
mdhd | √ | media信息头 | |||
hdlr | √ | media信息的句柄 | |||
minf | √ | media信息容器 | |||
vmhd | 视频media头(存在与视频的trak) | ||||
smhd | 音频media头(存在与音频的trak) | ||||
dinf | √ | 数据信息容器 | |||
dref | √ | 数据参考容器 | |||
stbl | √ | 采样表容器 | |||
stsd | √ | 采样描述 | |||
stts | √ | decoding采样时间 | |||
stsc | √ | chunk采样,数据片段信息 | |||
stco | √ | chunk偏移信息,数据偏移信息 | |||
stss | 同步采样表 | ||||
ctts | composition采样时间 | ||||
stsz | 采样大小 |
数据解析过程如下:
- trak容器有4个字节的尺寸和4个字节的类型,图中可以看到,有0x00007356(29526)字节,类型为0x7472616B(trak)。
- 继续向下解析,子容器有8个字节的header,包含0x0000005C(92)字节,类型为0x746B6864(tkhd)。
- 继续向下解析,子容器有8个字节的header,包含0x00000024(36)字节,类型为0x65647473(edts)。
- 继续向下解析,子容器有8个字节的header,包含0x000072CE(29390)字节,类型为0x6D646961(mdia)。
- 通过分析,trak头+tkhd+edts+mdia头信息,得出大小加起来刚好为29526字节,这个视频流的trak读取完毕。
- 同理读取音频流的trak。
tkhd子容器
实例数据如下:
其中0x07800000为1920.00,0x04380000为1080.00,为16.16浮点表示。视频的Track ID为0x00000001,音频的Track ID为0x00000002。
1 | 00 00 00 5C 74 6B 68 64 00 00 00 03 00 00 00 00 |
mdia子容器
数据解析过程如下:
- mdia容器的大小为0x000072CE(29390)字节,类型为mdia。
- mdia容器下面包含了三大子容器,分别为mdhd、hdlr和minf。
- 继续往下解析,mdhd的大小为0x00000020(32)字节
- 继续往下解析,hdlr大小为0x0000002D(45)字节。
- minf大小为0x00007279(29305)字节。
- mdia头信息+mdhd+hdlr+minf容器大小刚好为29390字节。至此mdia容器解析完毕。
mdhd子容器
实例数据如下:
其中视频时间计算单位0x00003200为12800。时长0x0010CA00为1100288即85.96s。语言编码为为0x55C4(参考标准ISO 639-2/T)。
音频时间计算单位0x0000BB80为48000。时长0x003EFBF0为4127728即85.99s。
1 | #视频的mdhd |
hdlr子容器
实例数据如下:
1 | 00 00 00 2D 68 64 6C 72 00 00 00 00 00 00 00 00 |
minf子容器
minf容器中包含了很多重要的子容器,例如音视频采样等信息相关的容器,minf容器中的信息将作为音视频数据的映射存在,其内容信息具体如下:
- 视频信息头(vmhd):Video Media Information Header
- 音频信息头(smhd):Sound Media Information Header
- 数据信息(dinf):Data Information
- 采样表(stbl):Sample Table
数据解析过程如下:
- minf容器的大小为0x00007279(29305)字节,类型为minf。
- minf容器下面包含了三大子容器,分别为vmhd(或smhd)、dinf和stbl。
- 继续往下解析,vmhd的大小为0x00000014(20)字节
- 继续往下解析,dinf大小为0x00000024(36)字节。
- stbl大小为0x00007279(29241)字节。
- minf头信息+vmhd+dinf+stbl容器大小刚好为29305字节。至此minf容器解析完毕。
vmhd子容器
实例数据如下:
1 | 00 00 00 14 76 6D 68 64 00 00 00 01 00 00 00 00 |
smhd子容器
实例数据如下:
1 | 00 00 00 10 73 6D 68 64 00 00 00 00 00 00 00 00 |
dinf子容器
实例数据如下,包含dref类型的子容器。
1 | 00 00 00 24 64 69 6E 66 00 00 00 1C 64 72 65 66 |
stbl子容器
又称为采样参数列表的容器(Sample Table Atom),该容器包含转化媒体时间到实际的sample的信息,例如,视频数据是否需要解压缩、解压缩算法是什么等信息。其所包含的子容器具体如下。
- 采样描述容器:Sample Description Atom(stsd)
- 采样时间容器:Time To Sample Atom(stts)
- 采样同步容器:Sync Sample Atom(stss)
- Chunk采样容器:Sample To Chunk Atom(stsc)
- 采样大小容器:Sample Size Atom(stsz)
- Chunk偏移容器:Chunk Offset Atom(stco)
- Shadow同步容器:Shadow Sync Atom(stsh)
edts子容器
数据解析过程如下:
- edts容器的大小为0x00000024(36)字节,类型为edts。
- 继续往下解析,edts容器下面包含了elst子容器。elst的大小为0x0000001C(28)字节。
- edts头信息+elst容器大小刚好为36字节。至此edts容器解析完毕。
实例数据如下,包含elst类型的子容器。
1 | 00 00 00 24 65 64 74 73 00 00 00 1C 65 6C 73 74 |
1.5 MP4封装和解封装
MP4在FFmpeg中的解封装
1 | $ ffmpeg -h demuxer=mp4 xxx.mp4 |
MP4的Demuxer与mov,m4a,3gp,3g2,mj2等相同。
MP4在FFmpeg中的封装
封装的主要参数为 movflags。用于控制封装形式。
1 | # moov容器默认在mdat容器后 |
如果需要合并多个flv,可使用如下方式:
1 | $ ffmpeg -f concat -i **all.txt** -c copy output.mp4 |
二、FLV格式
2.1 简介
在网络的直播与点播场景中,FLV也是一种常见的格式,是Adobe发布的一种可以作为直播也可以作为点播的封装格式,其封装格式非常简单,均以FLVTAG的形式存在,并且每一个TAG都是独立存在的。
FLV文件格式分为两部分:一部分为FLV文件头,另一部分为FLV文件内容。
flv可视化分析工具有
- FlvParse
- FlvAnalyzer
flv_analyzer打开如下所示:
也可使用ffprobe进行分析,命令为
1 | $ ffprobe -v trace -i output.flv |
2.2 flv文件头
文件头实例数据如下:
1 | 46 4c 56 01 05 00 00 00 09 |
数据解析过程如下:
- 3字节的标签:F、L和V
- 1字节的FLV文件版本:0x01
- 5位的保留标记类型:00000
- 1位的音频显示标记类型:1
- 1位的保留标记类型:0
- 1位的视频显示标记类型:1
- 4字节的文件头数据偏移:0x00000009
- 至此,FLV的文件头解析完毕。
2.3 flv文件内容
FLV文件内容的格式主要为FLVTAG, FLVTAG分为TAGHeader和TAGBody两部分。FLVTAG前的PreTagSize为上一个tag的大小。
TAGHeader
文件头实例数据如下:
解析情况如下:
- 保留位占用2位,为00
- 滤镜位占用1位,为0,在处理时,一般默认将保留位与滤镜位设置为0
- TAG类型占用5位,为10010,与保留位、滤镜位共用一个字节,常见的为0x08、0x09、0x12,分别代表音频TAG、视频TAG和脚本数据
- 数据大小占用24位(3字节),最大为0xFFFFFF(16777215)字节。
- 时间戳大小占用24位(3字节),最大为0xFFFFFF(16777215)毫秒,转换为秒等于16777秒,转换为分钟为279分钟,转换为小时为4.66小时,所以如果使用FLV的格式,采用这个时间戳最大可以存储至4.66小时
- 扩展时间戳大小占用8位(1字节),最大为0xFF(255),扩展时间戳使得FLV原有的时间戳得到了扩展,不仅仅局限于4.66个小时,还可以存储得更久,1193个小时,以天为单位转换过来大约为49.7天
- 流ID占用24位(3字节),FLV中一直将其存储为0
TAGBody
TAGBody是TAG的Data数据,即音视频媒体数据。有3类,AudioTag、VideoTag和ScriptData,对应TagType中的0x08、0x09、0x12。
Data的数据大小,等于下一个Tag中的PreTagSize-TAGHeader(11字节)。
1、AudioTag,有多个
实际数据如下:
解析过程如下:
- 解析声音格式,0x2代表mp3
- 解析音频采样率,11代表44kHz
- 解析采样大小,1代表16位采样
- 解析音频类型,1代表立体声
- 剩下的是SoundData数据
2、VideoTag,有多个
3、ScriptData,只有1个
实际数据如下,解析出来的type=2,代表String类型。
2.4 FFmpeg转FLV参数
使用Flv_Analyzer查看VideoTag中的codecID字段,可以看到FLV封装支持的视频编码包括:
1 | CodecID: |
查看VideoTag中的SoundFormat字段,可以看到FLV封装支持的音频编码包括:
1 | SoundFormat: 2 (4 bits) |
因此在封装FLV中,如音视频不符合标准,是无法进行封装的。
1 | # 会报Audio codec ac3 not compatible with flv |
在网络视频点播文件为FLV格式文件时,人们常用yamdi工具先对FLV文件进行一次转换,主要是将FLV文件中的关键帧建立一个索引,并将索引写入Metadata头中,FFmpeg支持生成的flv文件的metadata中带有关键帧索引信息,命令如下:
1 | $ ffmpeg -i input.mp4 -c copy -f flv -flvflags add_keyframe_index output.flv |
三、M3U8格式
3.1 简介
M3U8是一种常见的流媒体格式,主要以文件列表的形式存在,既支持直播又支持点播,尤其在Android、iOS等平台最为常用。
1 | #EXTM3U |
- EXTM3U,m3u8必须包含,必须在文件的第1行。
- EXT-X-VERSION,m3u8版本,常见的是3,目前到7。
- EXT-X-TARGETDURATION,分片的duration,四舍五入。
- EXT-X-MEDIA-SEQUENCE,直播时直播切片序列。
- EXT-X-ENDLIST,结束符号,如有该符号,说明不会产生新的切片,是点播。如没有该符号,则从倒数第3片开始播放,认为是直播。
- EXTINF,每一分片的duration,标注切片信息。
3.2 FFmpeg转HLS参数
常规的从文件转换HLS直播时,使用的命令如下:
1 | $ ffmpeg -re -i input.mp4 -c copy -f hls -bsf:v h264_mp4toannexb output.m3u8 |
输出的output.m3u8内容如下,且包含40个ts视频文件。
1 | #EXTM3U |
额外的参数有:
- start_number,代表M3U8列表中第一片的序列数。
- hls_time,代表每个切片的duration。
- hls_list_size,代表直播列表中切片的最大数量,新的切片会覆盖原有的切片。
- -bsf:v h264_mp4toannexb,将H.264数据转换为H.264 AnnexB标准的编码,用于实时传输。如从FLV、TS等支持直播流的文件转则不需要。
- hls_wrap,M3U8列表中TS的回滚参数,对CDN不友好。
- hls_base_url,用于TS的路径设置,绝对/相对/网络路径。
- hls_segment_filename,设置切片文件名规则模板。
- hls_flags,包含了一些子参数,包含了正常文件索引忽略关键帧时才切片(split_by_tim)、删除过期切片(delete_segments)、整数显示duration(round_durations)、列表开始插入discontinuity标签(discont_start)、M3U8结束不追加endlist标签(omit_endlist)等。
- use_localtime,以本地系统时间为切片文件名
- method,用于设置HLS将M3U8和TS文件上传至HTTP服务器,如配合nginx的webdav模块。
四、视频文件切片
视频文件切片与HLS基本类似,但是HLS切片在标准中只支持TS格式的切片,并且是直播与点播切片,除了可以使用segment方式进行切片,也可以使用ss加上t参数进行切片。
segment切片
segment参数包含如下:
1、segment_format指定切片文件的格式
1 | $ ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 test_output-%d.mp4 |
2、segment_list与segment_list_type指定切片索引列表
1 | # 常用于虚拟轮播 |
3、reset_timestamps使切片时间戳归0
1 | $ ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -reset_timestamps 1 test_output-%d.mp4 |
4、 segment_times按照时间点剪切
1 | #切片的时间点分别为第3秒、第9秒和第12秒,在这三个时间点进行切片。 |
ss和t参数切片
1、使用ss指定剪切开头部分
1 | # 从文件的第10s开始截取内容,注意可能差距不是10s,需要比对两者的duration差异。 |
2、使用t指定视频总长度
1 | # 截取前10s |
3、使用output_ts_offset指定输出start_time
1 | #FFmpeg支持ss与t两个参数一同使用以达到切割视频的某一段的效果,但其并不能指定输出文件的start_time,而且也不希望时间戳归0,可以使用output_ts_offset来达到指定输出文件的start_time |
五、音视频文件音视频流抽取
1、FFmpeg抽取音视频文件中的AAC音频流
1 | $ ffmpeg -i input.mp4 -vn -acodec copy ouput.aac |
2、FFmpeg抽取音视频文件中的H.264视频流
1 | $ ffmpeg -i input.mp4 -an -vcodec copy ouput.h264 |
3、FFmpeg抽取音视频文件中的H.265数据
1 | $ ffmpeg -i input.mp4 -an -vcodec copy -bsf hevc_mp4toannexb -f hevc ouput.hevc |
六、系统资源使用情况
使用FFmpeg进行封装转换时并不会占用大量的CPU资源,因为使用FFmpeg进行封装转换时主要是以读取音视频数据、写入音视频数据为主,并不会涉及复杂的计算。
如果使用FFmpeg进行编码转换,则需要进行大量的计算,从而将会占用大量的CPU资源。