1 音视频文件转MP4格式
在互联网常见的格式中,跨平台最好的应该是MP4文件,因为MP4文件既可以在PC平台的 Flashplayer 中播放,又可以在移动平台的 Android、ios 等平台中进行播放,而且使用系统默认的播放器即可播放,因此我们说MP4格式是最常见的多媒体文件格式。本章首先重点介绍MP4封装的基本格式。
1.1 MP4格式标准介绍
如果要了解 MP4的格式信息,首先要清楚几个概念,具体如下。
- MP4文件由许多个Box与FullBox组成
- 每个Box由Header和 Data两部分组成
- FullBox是Box的扩展,其在Box结构的基础上,在Header中增加8位version标志和 24 位的 flags 标志
- Header 包含了整个Box的长度的大小(size)和类型(type),当size等于0时,代表这个Box是文件的最后一个Box。当size等于1时,说明Box长度需要更多的位来描述,在后面会定义一个64位的largesize用来描述Box的长度。当Type为uuid 时,说明这个 Box中的数据是用户自定义扩展类型
- Data为Box的实际数据,可以是纯数据,也可以是更多的子Box
- 当一个 Box中 Data是一系列的子Box时,这个Box又可以称为Container(容器)Box
MP4文件中Box的组成可以用表所示的列表进行排列,表中标记“V”的Box为必要Box,否则为可选Box。
解析 MP4 多媒体文件时需要一些关键的信息
1 moov容器
2 解析mvhd容器
3 解析trak子容器
4 解析tkhd
5 解析mdia容器
6 解析mdhd容器
7 解析hdlr容器
8 解析minf容器
9 解析vmhd容器
10 解析smhd容器
11 解析dinf容器
12 解析stbl容器
13 解析edts容器
1.2 MP4分析工具
1 Elecard StreamEye
2 mp4box
3 mp4info
1.3 MP4在FFmpeg中的Demuxer
根据前面介绍过的查看FFmpeg的MP4文件的Demuxer的方法,使用命令行ffmpeg -h demuxer=mp4 查看 MP4 文件的 Demuxer 信息:
ffmpeg -h demuxer=mp4
Demuxer mov,mp4,m4a,3gp,3g2,mj2 [QuickTime / MOV]:
Common extensions: mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif,heic,heif.
如输出内容所示,通过查看FFmpeg的help信息,可以看到MP4的Demuxer与mov、3gp、m4a、3g2、mj2的 Demuxer 相同,解析 MP4 文件的参数如表所示。
参数 | 类型 | 说明 |
---|---|---|
use_absolute_path | 布尔 | 可以通过绝对路径加载外部的 track,可能会有安全因素的影响,默认不开启 |
seek_streams_individually | 布尔 | 根据单独流进行 seek,默认开启 |
ignore_editlist | 布尔 | 忽略 EditList Atom 信息,默认不开启 |
ignore_chapters | 布尔 | 忽略 Chapters 信息,默认不开启 |
enable_drefs | 布尔 | 外部 track 支持,默认不开启 |
1.4 MP4在FFmpeg中的Muxer
MP4的封装相对解封装来说稍微复杂一些
从参数的列表中可以看到,MP4的muxer支持的参数比较复杂,例如支持在视频关键帧处切片、支持设置moov容器大小的最大值、支持设置encrypt加密等。下面就对常见的参数进行举例说明。
1 faststart参数使用案例
正常情况下 ffmpeg生成moov是在mdat写完成之后再写入,可以通过参数faststart将moov容器移动至mdat的前面,下面参考一个例子:
ffmpeg -i input.mp4 -c copy -f mp4 output.mp4
2 dash参数使用案例
3 isml参数使用案例
2 视频文件转 FLV格式
在网络的直播与点播场景中,FLV也是一种常见的格式,FLV是Adobe发布的一种可以作为直播也可以作为点播的封装格式,其封装格式非常简单,均以FLVTAG的形式存在,并且每一个TAG都是独立存在的,接下来就来详细介绍一下FLV 标准。
2.1 FLV格式标准介绍
FLV 文件格式分为两部分:一部分为FLV文件头,另一部分为FLV文件内容。
1 FLV 文件头格式解析
根据表可以看出FLV文件头格式中签名字段占用了三字节,最终组成的三个字符分别为“FLV”;然后是文件的版本,常见的为1;接下来的一个字节前边5位为0,接着音频展示设置为1,然后下一位为0,再下一位为视频展示设置为1。如果是一个音视频都展示的FLV文件,那么这个字节会设置为0x05(00000101)。然后是4字节的数据,为FLV文件头数据的偏移位置。
2 FLV文件内容格式解析
3 FLVTAG格式解析
4 VideoTag 数据解析
5 AudioTag数据格式解析
6 ScriptData格式解析
2.2 FFmpeg 转 FLV 参数
使用FFmpeg生成FLV格式相对来说比较简单,下面就来查看FFmpeg生成FLV文件时可以使用的参数,具体见表
根据表中的参数可以看出,在生成FLV文件时,写人视频、音频数据时均需要写人 Sequence Header数据,如果FLV的视频流中没有Sequence Header,那么视频很有可能不会显示出来;如果FLV的音频流中没有Sequence Header,那么音频很有可能不会被播放出来。所以需要将fmpeg中的参数fvflags的值设置为aac_seqheader_detect,其将会写人音频 AAC的Sequence Header。
2.3 FFmpeg 文件转 FLV 举例
从前文的 FLV 标准中可以看到,FLV封装中可以支持的视频编码主要包含如下内容。
- Sorenson H.263
- Screen Video
- On2 VP6
- 带 Alpha 通道的 On2 VP6
- Screen Video 2
- H.264(AVC)
而 FLV 封装中支持的音频主要包含如下内容。
- 限行 PCM,大小端取决于平台
- ADPCM音频格式
- MP3
- 线性PCM,小端
- Nellymoser 16kHz Mono
- Nellymoser 8kHz Mono
- Nellymoser
- G.711 A-law
- G.711 mu-law
- 保留
- AAC
- Speex
- MP3 8kHz
如果封装FLV时,内部的音频或者视频不符合标准时,那么它们是肯定封装不进FLV的,而且还会报错。
解决方式:将不支持的音频转成flv支持的格式。
2.4 FFmpeg 生成带关键索引的FLV
在网络视频点播文件为FLV格式文件时,人们常用yamdi 工具先对FLV文件进行次转换,主要是将FLV文件中的关键帧建立一个索引,并将索引写人Metadata头中,这个步骤用FFmpeg 同样也可以实现,使用参数add_keyframe_index即可:
ffmpeg -i input.mp4 -c copy -f flv -flvflags add_keyframe_index output.flv
没成功
2.5 FLV 文件格式分析工具
flvparse和FlvAnalyzer,还有ffprobe
ffprobe -v trace -i output.flv
没成功
3 视频文件转 M3U8格式
3.1 M3U8 格式标准介绍
M3U8是一种常见的流媒体格式,主要以文件列表的形式存在,既支持直播又支持点播,尤其在 Android、iOS等平台最为常用,下面就来看一下M3U8的最简单的例子:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:3.760000
out0.ts
#EXTINF:1.880000
outl.ts
#EXTINF:1.760000
out2.ts
#EXTINF:1.040000
out3.ts
#EXTINF:1.560000
out4.ts
EXTM3U
M3U8文件必须包含的标签,并且必须在文件的第一行,所有的M3U8文件中必须包含这个标签
EXT-X-VERSION
M3U8文件的版本,常见的是3,其实版本已经发展了很多了,直至截稿时,已经发布到了版本7,经历了这么多版本,期间也对不少标记进行了增删
EXT-X-TARGETDURATION
每一个分片都会有一个分片自己的 duration,这个标签是最大的那个分片的浮点数四舍五入后的整数值,例如1.02四舍五入后的整数为1,2.568四舍五人后的整数为3,如果在M3U8 分片列表中的最大的duration 的数值为5.001,那么这个 EXT-X-TARGETDURATION值为 5。
EXT-X-MEDIA-SEOUENCE
M3U8直播时的直播切片序列,当播放打开M3U8时,以这个标签的值为参考,播放对应的序列号的切片。
EXTINF
EXTINF为M3U8列表中每一个分片的duration,如上面例子输出信息中的第一个分片的 duration为 4.120000秒;在EXTINF标签中除了duration值,还可以包含可选的描述信息,主要为标注切片信息,使用逗号分隔开。
3.2 FFmpeg转HLS参数
FFmpeg中自带HLS的封装参数,使用HLS格式即可进行HLS的封装,但是生成HLS的时候有各种参数可以进行参考,例如设置HLS列表中切片的前置路径、生成HLS的TS切片时设置TS的分片参数、生成HLS时设置M3U8列表中保存的TS个数等,详细参数请参考表
参数 | 类型 | 说明 |
---|---|---|
start_number | 整数 | 设置 M3U8 列表中的第一片的序列数 |
hls_time | 浮点数 | 设置每一片时长 |
hls_list_size | 整数 | 设置 M3U8 中分片的个数 |
hls_ts_options | 字符串 | 设置 TS 切片的参数 |
hls_wrap | 整数 | 设置切片索引回滚的边界值 |
hls_allow_cache | 整数 | 设置 M3U8 中 EXT-X-ALLOW-CACHE 的标签 |
hls_base_url | 字符串 | 设置 M3U8 中每一片的前置路径 |
hls_segment_filename | 字符串 | 设置切片名模板 |
hls_key_info_file | 字符串 | 设置 M3U8 加密的 key 文件路径 |
hls_subtitle_path | 字符串 | 设置 M3U8 字幕路径 |
hls_flags | 标签(整数) | 设置 M3U8 文件列表的操作,具体如下:<br>single_file: 生成一个媒体文件索引与字节范围<br>delete_segments: 删除 M3U8 文件中不包含的过期的 TS 切片文件<br>round_durations: 生成的 M3U8 切片信息的 duration 为整数<br>discont_start: 生成 M3U8 的时候在列表前边加上 discontinuity 标签<br>omit_endlist: 在 M3U8 末尾不追加 endlist 标签 |
use_localtime | 布尔 | 设置 M3U8 文件序号为本地时间戳 |
use_localtime_mkdir | 布尔 | 根据本地时间戳生成目录 |
hls_playlist_type | 整数 | 设置 M3U8 列表为事件或者点播列表 |
method | 字符串 | 设置 HTTP 属性 |
3.3 FFmpeg转HLS举例
常规的从文件转换 HLS 直播时,使用的参数如下
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -f hls output.m3u8
因为默认是HLS直播,所以生成的M3U8文件内容会随着切片的产生而更新,如果仔细观察,会发现命令行中多了一个参数“-bsf:vh264 mp4toannexb”,这个参数的作用是将MP4中的H.264数据转换为H.264 AnnexB 标准的编码,AnnexB 标准的编码常见于实时传输流中。如果源文件为FLV、TS等可作为直播传输流的视频,则不需要这个参数。生成HLS时还有一些参数可以设置,下面就来逐一介绍。有很多参数一已经不适用了,需要的时候再查询。
1 start_number 参数
2 hls_time 参数
3 hls_list_size 参数
4 hls_wrap 参数
5 hls_base_url参数
6 hls_segment_filename 参数
7 hls_flags 参数
8 use_localtime 参数
9 method 参数
4 视频文件切片
视频文件切片与HLS基本类似,但是HLS切片在标准中只支持TS格式的切片,并且是直播与点播切片,既可以使用segment方式进行切片,也可以使用ss加上t参数进行切片,下面重点介绍一下segment与ss加上t参数对视频文件进行剪切的方法。
4.1 FFmpeg切片 segment 参数
FFmpeg切片segment参数具体见表
4.2 FFmpeg切片segment 举例
1 segment_format指定切片文件的格式
通过使用segment format来指定切片文件的格式,前面讲述过HLS切片的格式主要为MPEGTS文件格式,那么在segment中,可以根据segmentformat来指定切片文件的格式,其既可以为MPEGTS切片,也可以为MP4切片,还也可以为FLV切片等,下面举例说明:
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 test_output-%d.mp4
然后查看第一片分片MP4的最后的时间戳:
ffprobe -v quiet -show_packets -select_streams c test_output-0.mp4 2 > x | grep pts_time | tail -n 3
2 segment_list与segment list type指定切片索引列表
使用 segment 切割文件时,不仅仅可以切割 MP4,同样也可以切割 TS或者 FLV 等文件,生成的文件索引列表名称也可以指定名称,当然,列表不仅仅是M3U8,也可以是其他的格式:
生成ffconcat格式索引文件
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -segment_list_type ffconcat -segment_list output.lst test_output-%d.mp4
生成FLAT格式索引文件
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -segment_list_type flat -segment_list filelist.txt test_output-%d.mp4
生成CSV格式索引文件
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -segment_list_type flat -segment_list filelist.csv test_output-%d.mp4
生成M3U8格式索引文件
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -segment_list_type m3u8 -segment_list output.m3u8 test_output-%d.mp4
3 reset_timestamps使切片时间截归0
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -reset_timestamps 1 test_output-%d.mp4
然后查看一下第一片末尾的时间戳:
ffprobe -v quiet -show_packets -select_streams c test_output-0.mp4 2 > x | grep pts_time | tail -n 3
4 segment_times按照时间点剪切
对文件进行切片时,有时候需要均匀的切片,有时候需要按照指定的时间长度进行切片,segment可以根据指定的时间点进行切片,下面举例说明:
ffmpeg -re -i input.mp4 -c copy -f segment -segment_format mp4 -segment_times 3,9,12 test_output-%d.mp4
4.3 FFmpeg 使用ss与t参数进行切片
在FFmpeg 中,使用ss可以进行视频文件的seek定位,ss所传递的参数为时间值,t所传递的参数也为时间值,下面就来举例说明ss与t的作用。
1 使用ss指定剪切开头部分
在前面章节中介绍FFmpeg基本参数时,粗略地介绍过FFmpeg的基本转码原理FFmpeg自身的ss参数可以用作切片定位起始时间点,例如从一个视频文件的第10秒钟开始截取内容:
ffmpeg -ss 10 -i input.mp4 -c copy output.ts
命令行执行之后,生成的output.ts将会比input.mp4的视频少8秒,因为output.ts是从input.mp4的第8秒开始截取的,使用前面介绍过的fprobe分别获得input.mp4与output.ts 的文件 duration 并进行对比,信息如下:
ffprobe -v quiet -show_format input.mp4 | grep duration
ffprobe -v quiet -show_format output.ts | grep duration
2 使用t指定视频总长度
使用FFmpeg截取视频除了可以指定开始截取位置,还可以指定截取数据的长度FFmpeg的t参数可以指定截取的视频长度,例如截取input.mp4文件的前10秒的数据:
ffmpeg -i input.mp4 -c copy -t 10 -copyts output.mp4
命令行执行完之后,会生成一个时间从0开始的output.mp4,查看一下input.mp4与output.mp4的起始时间与长度相关信息:
ffprobe -v quiet -show_format input.mp4 | grep start_time
ffprobe -v quiet -show_format output.mp4 | grep start_time
ffprobe -v quiet -show_format input.mp4 | grep duration
ffprobe -v quiet -show_format output.mp4 | grep duration
从两个文件的 duration信息可以看到,input的start time是0,duration是322.947948,而output.mp4的start time也是0,duration 则是10.010010,参数生效。
3 使用 output_ts_offset指定输出 start_time
FFmpeg 支持ss与t两个参数一同使用以达到切割视频的某一段的效果,但其并不能指定输出文件的 start time,而且也不希望时间戳归0,可以使用 output ts offset来达到指定输出文件的 start time的目的:
ffmpeg -i input.mp4 -c copy -t 10 -output_ts_offset 120 output.mp4
5 音视频文件音视频流抽取
当音视频文件出现异常时,除了分析封装数据之外,还需要分析音视频流部分,本节将重点介绍如何抽取音视频流,FFmpeg支持从音视频封装中直接抽取音视频数据,下面就来列举几个例子。
5.1 FFmpeg 抽取音视频文件中的 AAC 音频流
FFmpeg除了转封装、转码之外,还可以提取音频流,例如需要将音频流提取出来然后合成之后插人到另一个封装中的情况,下面就来看一下FFmpeg 提取 MP4文件中的AAC音频流的方法:
ffmpeg -i input.mp4 -vn -acodec copy output.aac
5.2 FFmpeg 抽取音视频文件中的 H.264 视频流
有时在视频编辑场景中需要将视频流提取出来进行编辑,或者与另一路视频流进行合并等操作,这时可以使用FFmpeg来完成:
ffmpeg -i input.mp4 -c:v libx264 -an output.h264
5.3 FFmpeg 抽取音视频文件中的 H.265 数据
ffmpeg -i input.mp4 -c:v libx265 -an output.hevc
6 系统资源使用情况
在使用FFmpeg进行格式转换、编码转换操作时,所占用的系统资源各有不同,如果使用FFmpeg 仅仅转换封装格式而并非转换编码,那么其使用的CPU资源并不多,下面来看一下转换封装时的CPU使用率:
ffmpeg -re -i input.mp4 -c copy -f mpegts output.ts
top
通过图可以看出,使用FFmpeg进行封装转换时并不会占用大量的CPU资源,因为使用FFmpeg进行封装转换时主要是以读取音视频数据、写人音视频数据为主,并不会涉及复杂的计算。
如果使用 FFmpeg 进行编码转换,则需要进行大量的计算,从而将会占用大量的CPU资源
ffmpeg -re -i input.mp4 -vcodec libx264 -acodec copy -f mpegts output.ts
top