avatar


视频音频处理工具FFmpeg

ffmpeg下载

我们通过 http://ffmpeg.org/ 下载ffmpeg。解压之后会找到三个文件:

  1. ffmpeg,fast forword mpeg,音视频文件转换命令行工具
  2. ffplay,fast forword play,用ffmpeg实现的简单媒体播放器
  3. ffprobe,fast forword probe,用来分析查看多媒体文件的输入流

在这里,我们只需要利用"ffmpeg",音视频文件转换命令行工具。

视频文件

为了更好的讨论下文"ffmpeg"的应用,我们简单的介绍一下视频文件。

容器

视频文件被称作容器(container),一个视频文件就是一个容器(container),里面包括了视频和音频,也可能有字幕等其他内容。
常见的容器格式有:mp4、mkv、avi等,也就是我们常见的视频文件的格式。

通过命令ffmpeg -formats,可以查看ffmpeg支持的容器。示例代码:

1
ffmpeg -formats

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ffmpeg version 2021-11-07-git-45dc668aea-full_build-www.gyan.dev Copyright (c) 2000-2021 the ffmpeg developers
built with gcc 11.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 57. 7.100 / 57. 7.100
libavcodec 59. 12.100 / 59. 12.100
libavformat 59. 8.100 / 59. 8.100
libavdevice 59. 0.101 / 59. 0.101
libavfilter 8. 16.101 / 8. 16.101
libswscale 6. 1.100 / 6. 1.100
libswresample 4. 0.100 / 4. 0.100
libpostproc 56. 0.100 / 56. 0.100
File formats:
D. = Demuxing supported
.E = Muxing supported
--
D 3dostr 3DO STR
E 3g2 3GP2 (3GPP2 file format)
E 3gp 3GP (3GPP file format)
D 4xm 4X Technologies
E a64 a64 - video for Commodore 64

【部分运行结果略】

D xvag Sony PS3 XVAG
D xwd_pipe piped xwd sequence
D xwma Microsoft xWMA
D yop Psygnosis YOP
DE yuv4mpegpipe YUV4MPEG pipe

编码格式

视频和音频都需要经过编码,才能保存成文件。不同的编码格式(CODEC),有不同的压缩率,会导致文件大小和清晰度的差异。

常用的视频编码格式如下:

  • H.262
  • H.264
  • H.265
  • VP8
  • VP9
  • AV1

常用的音频编码格式如下:

  • MP3
  • AAC

通过命令ffmpeg -codecs,可以查看ffmpeg支持的编码格式。示例代码:

1
.\ffmpeg -codecs

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ffmpeg version 2021-11-07-git-45dc668aea-full_build-www.gyan.dev Copyright (c) 2000-2021 the ffmpeg developers
built with gcc 11.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 57. 7.100 / 57. 7.100
libavcodec 59. 12.100 / 59. 12.100
libavformat 59. 8.100 / 59. 8.100
libavdevice 59. 0.101 / 59. 0.101
libavfilter 8. 16.101 / 8. 16.101
libswscale 6. 1.100 / 6. 1.100
libswresample 4. 0.100 / 4. 0.100
libpostproc 56. 0.100 / 56. 0.100
Codecs:
D..... = Decoding supported
.E.... = Encoding supported
..V... = Video codec
..A... = Audio codec
..S... = Subtitle codec
...I.. = Intra frame-only codec
....L. = Lossy compression
.....S = Lossless compression
-------
D.VI.S 012v Uncompressed 4:2:2 10-bit
D.V.L. 4xm 4X Movie
D.VI.S 8bps QuickTime 8BPS video
.EVIL. a64_multi Multicolor charset for Commodore 64 (encoders: a64multi )
.EVIL. a64_multi5 Multicolor charset for Commodore 64, extended with 5th color (colram) (encoders: a64multi5 )
D.V..S aasc Autodesk RLE
D.V.L. agm Amuse Graphics Movie
D.VIL. aic Apple Intermediate Codec
DEVI.S alias_pix Alias/Wavefront PIX image
DEVIL. amv AMV Video
D.V.L. anm Deluxe Paint Animation
D.V.L. ansi ASCII/ANSI art
DEV..S apng APNG (Animated Portable Network Graphics) image

【部分运行结果略】

编码器

那么,如何按照编码格式进行文件的编码呢?
这就需要编码器(encoders)了。

ffmpeg内置的视频编码器有:

  • libx264:最流行的开源H.264编码器
  • NVENC:基于NVIDIA GPU的H.264编码器
  • libx265:开源的HEVC编码器
  • libvpx:谷歌的VP8VP9编码器
  • libaomAV1编码器

ffmpeg内置的音频编码器有:

  • libfdk-aac
  • aac

可以通过命令ffmpeg -encoders可以查看ffmpeg已安装的编码器。

示例代码:

1
ffmpeg -encoders

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ffmpeg version 2021-11-07-git-45dc668aea-full_build-www.gyan.dev Copyright (c) 2000-2021 the ffmpeg developers
built with gcc 11.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 57. 7.100 / 57. 7.100
libavcodec 59. 12.100 / 59. 12.100
libavformat 59. 8.100 / 59. 8.100
libavdevice 59. 0.101 / 59. 0.101
libavfilter 8. 16.101 / 8. 16.101
libswscale 6. 1.100 / 6. 1.100
libswresample 4. 0.100 / 4. 0.100
libpostproc 56. 0.100 / 56. 0.100
Encoders:
V..... = Video
A..... = Audio
S..... = Subtitle
.F.... = Frame-level multithreading
..S... = Slice-level multithreading
...X.. = Codec is experimental
....B. = Supports draw_horiz_band
.....D = Supports direct rendering method 1
------
V....D a64multi Multicolor charset for Commodore 64 (codec a64_multi)
V....D a64multi5 Multicolor charset for Commodore 64, extended with 5th color (colram) (codec a64_multi5)
V..... alias_pix Alias/Wavefront PIX image
V..... amv AMV Video
V....D apng APNG (Animated Portable Network Graphics) image
V..... asv1 ASUS V1
V..... asv2 ASUS V2
V....D libaom-av1 libaom AV1 (codec av1)
V....D librav1e librav1e AV1 (codec av1)
V..... libsvtav1 SVT-AV1(Scalable Video Technology for AV1) encoder (codec av1)
V....D avrp Avid 1:1 10-bit RGB Packer
V..X.D avui Avid Meridien Uncompressed

【部分运行结果略】

ffmpeg命令

上文我们简单的讨论了视频文件,知道了"容器"和"编码"两个概念。
现在,我们着重讨论一下ffmpeg命令。

ffmpeg命令格式

ffmpeg 命令的参数非常多,大致可以分成五个部分。

1
ffmpeg {1} {2} -i {3} {4} {5}
  • {1}:全局参数
  • {2}:输入文件参数
  • {3}:输入文件
  • {4}:输出文件参数
  • {5}:输出文件

为了便于阅读,我们还可以将ffmpeg命令还可以写成多行,用\标识换行

1
2
3
4
5
6
ffmpeg \
[全局参数] \
[输入文件参数] \
-i [输入文件] \
[输出文件参数] \
[输出文件]

示例代码:

1
2
3
4
5
6
ffmpeg \
-y \ # 全局参数
-c:a libfdk_aac -c:v libx264 \ # 输入文件参数
-i input.mp4 \ # 输入文件
-c:v libvpx-vp9 -c:a libvorbis \ # 输出文件参数
output.webm # 输出文件

解释说明:
上述的命令将"mp4容器"转成"webm容器",输入的"mp4容器"的音频编码格式是"aac",视频编码格式是"H.264";输出的"webm容器"的视频编码格式是"VP9",音频格式是"vorbis",如果存在同名的输出文件,直接覆盖。

常用命令参数

ffmpeg的常用命令行参数如下:

  • -c:指定编码器
    -c copy:直接复制,不经过重新编码。
    -c:v:指定视频编码器
    -c:a:指定音频编码器
  • -i:指定输入文件
  • -an:去除音频流
  • -vn: 去除视频流
  • -preset:指定输出的视频质量
    有以下几个可用的值:ultrafastsuperfastveryfastfasterfastmediumslowslowerveryslow
  • -y:对于命令执行期间的选项直接同意,在这里是输出时直接覆盖同名文件。

我们举几个例子。

1、查看视频文件的编码格式和比特率等

示例代码:

1
.\ffmpeg.exe -i .\01.avi

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ffmpeg version 4.4-full_build-www.gyan.dev Copyright (c) 2000-2021 the ffmpeg developers
built with gcc 10.2.0 (Rev6, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
Input #0, avi, from '.\01.avi':
Duration: 00:45:23.32, start: 0.000000, bitrate: 1619 kb/s
Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 720x486 [SAR 1:1 DAR 40:27], 1494 kb/s, 29.97 fps, 29.97 tbr, 29.97 tbn, 29.97 tbc
Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 44100 Hz, stereo, fltp, 111 kb/s
At least one output file must be specified

上面命令会输出很多冗余信息,加上-hide_banner参数,可以只显示元信息。
示例代码:

1
.\ffmpeg.exe -i .\01.avi -hide_banner

运行结果:

1
2
3
4
Input #0, avi, from '.\01.avi':
Duration: 00:45:23.32, start: 0.000000, bitrate: 1619 kb/s
Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 720x486 [SAR 1:1 DAR 40:27], 1494 kb/s, 29.97 fps, 29.97 tbr, 29.97 tbn, 29.97 tbc
Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 44100 Hz, stereo, fltp, 111 kb/s

2、转换编码格式
转换编码格式(transcoding)指的是,将视频文件从一种编码转成另一种编码。

示例代码:

1
.\ffmpeg -i 01.avi  -c:v libx264 output.mp4

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
ffmpeg version 4.4-full_build-www.gyan.dev Copyright (c) 2000-2021 the ffmpeg developers
built with gcc 10.2.0 (Rev6, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100

【部分运行结果略】

Press [q] to stop, [?] for help

【部分运行结果略】

frame= 5136 fps=364 q=29.0 size= 16128kB time=00:02:52.03 bitrate= 768.0kbits/s dup=3 drop=0 speed=12.2x

这个操作会比较慢,按q停止。

另外,我们也可以利用格式工厂等软件,这些软件会启用多线程进行转换。
如果当前电脑并不在处理复杂业务的话,用多线程会更快。如果正在处理复杂业务的话,会更慢。原因我们在《基于Java的后端开发入门:8.多线程 [2/2]》有过讨论,有两点:

  1. 线程创建和销毁都非常耗时并消耗资源。
  2. 线程之间的切换也会非常耗时并消耗资源。

3、转换容器
转换容器指的是,将视频文件从一种容器转到另一种容器。
示例代码:

1
ffmpeg -i input.mp4 -c copy output.webm

解释一下,该命令只是转了一下容器,内部的编码格式不变,使用-c copy指定直接拷贝,不转码。

4、调整码率
调整码率指的是,改变编码的比特率,这样视频文件的体积会更小。

1
2
3
4
ffmpeg \
-i input.mp4 \
-minrate 964K -maxrate 3856K -bufsize 2000K \
output.mp4

解释说明:该命令的含义是指定码率最小为964K,最大为3856K,缓冲区大小为2000K。

5、改变分辨率
示例代码:

1
2
3
4
ffmpeg \
-i input.mp4 \
-vf scale=480:-1 \
output.mp4

6、提取音频
示例代码:

1
2
3
4
ffmpeg \
-i input.mp4 \
-vn -c:a copy \
output.aac

解释说明:-vn表示去掉视频,-c:a copy表示不改变音频编码,直接拷贝。

7、添加音轨
添加音轨指的是,将外部音频加入视频,比如添加背景音乐或旁白。
示例代码:

1
2
3
ffmpeg \
-i input.aac -i input.mp4 \
output.mp4

解释说明:有音频和视频两个输入文件,ffmpeg会将它们合成为一个文件。

8、视频文件的合并
示例代码:

1
ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

切片命令

在讨论了ffmpeg命令之后,我们来解讨论一下和切片有关的命令。

  1. 将视频转换为ts格式
  2. 对ts文件进行切片

将视频转换为ts格式

示例代码:

1
.\ffmpeg -y -i 【文件名】.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 【文件名】.ts

解释说明:

  • -y:如果有同名文件,直接覆盖
  • -i:输入文件
  • -vcodec copy:指定视频编码
  • -acodec copy:指定音频编码
  • -vbsf:bitstream_filter

在对视频文件转换为ts格式,并进行切片之前,我们还需要做一个操作,“对视频文件进行标准化”,将视频流的编码转为AVC(H264),将音频流的编码转为AAC

为什么不直接转换为ts格式呢?
因为对于非标准的视频文件,切片会报错,具体的报错原因有待进一步讨论。
但直接进行格式转换,并在转换期间指定编码,可以解决问题。

对ts文件进行切片

通过上述操作,我们得到了一个大的ts文件,接下来要做的就是对ts文件进行切片。

1
.\ffmpeg -i 【文件名】.ts -c copy -map 0 -f segment -segment_list playlist.m3u8 -segment_time 30 【文件名】%03d.ts

解释说明:

  • -i:输入文件
  • -c copy:编码格式
  • -map-map用于手动控制每个输出文件中的流选择。
    -map 0:所有
  • -segment_time:表示每个片段的长度
  • -f:定义输出类型

切片建议

建议每个ts片段以上长度为30秒左右。

那么,为什么建议每个ts片段的长度为30秒呢?
因为每次建立HTTP连接,传输数据,都有一个复杂的过程。

  • 如果ts片段太短的话,比如5秒,意味着下一个ts片段需要在5秒内传输过来,而每一次请求都耗费了大量建立连接的时间。
    (我试验过ts片段为5秒的情况,卡顿的情况比30秒严重太多)
  • 如果ts片段太长的话,比如超过60秒,意味着文件太大,可能加载会非常缓慢。

那么为什么是30秒?不是28秒,29秒呢?30秒只是一个经验数字。

也有资料建议:第一个ts片段长度为3秒;第二个ts片段长度为5秒;第三个ts片段长度为10秒左右;第四个ts片段以上长度为30秒左右;最长ts片段不超过35秒。这种建议是为了提高视频初次载入速度,无论哪种建议,其原因都和网络请求的过程有关。

(在本章的附录部分,会讨论一下网络请求的过程。)

m3u8的概述

通过切片,我们除了得到了一个一个小的ts片段,还得到了"m3u8"文件。
文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:34
#EXTINF:33.433433,
01000.ts
#EXTINF:33.366700,
01001.ts
#EXTINF:25.025022,
01002.ts
#EXTINF:33.366700,
01003.ts
#EXTINF:25.025033,
01004.ts

【部分文件内容略】

#EXTINF:25.025022,
01086.ts
#EXTINF:33.366700,
01087.ts
#EXTINF:33.366700,
01088.ts
#EXTINF:25.025022,
01089.ts
#EXTINF:20.653989,
01090.ts
#EXT-X-ENDLIST

"m3u8"是HTTP Live Streaming(HLS)协议的一部分。
HLS是一种流媒体传输协议,其思路为将一个大的媒体文件进行切片,将该切片文件资源路径记录于"m3u8"文件内,并附带一些额外描述用于提供给客户端。客户端依据该"m3u8"文件即可获取对应的媒体资源,进行播放。

m3u8的标签

在上文,我们得到了一个m3u8文件。
现在,我们讨论一下常见标签及其含义。

EXTM3U

EXTM3U,表明该文件是一个"m3u8"文件,每个"m3u8"文件必须将该标签放置在第一行。

EXT-X-VERSION

EXT-X-VERSION,表示"HLS"的协议版本号,每个"m3u8"文件内最多只能出现一个该标签定义。

不同的版本号,对于后续的部分标签都或有影响。
(比如,对于EXTINF标签)

我们这里的版本号是3

EXT-X-MEDIA-SEQUENCE

EXT-X-MEDIA-SEQUENCE,表示播放列表第一个片段文件的序列号,每个媒体片段都拥有一个唯一的整型序列号,每个媒体片段序列号按出现顺序依次加11

EXT-X-ALLOW-CACHE

EXT-X-ALLOW-CACHE,表示是否允许缓存

EXT-X-TARGETDURATION

EXT-X-TARGETDURATION,表示每个视频分段最大的时长(单位秒)。

EXT-X-ENDLIST

EXT-X-ENDLIST,表示"m3u8"文件的结束。

EXT-X-PLAYLIST-TYPE

EXT-X-PLAYLIST-TYPE,表示流媒体类型,有点播和直播两种。

其格式为:

1
#EXT-X-PLAYLIST-TYPE:<type-enum>

"type-enum"的值表示是点播或直播:

  • VOD:表示该视屏流为点播源,通常带有EXT-X-ENDLIST标签。
  • EVENT:表示该视频流为直播源,一般不会有EXT-X-ENDLIST标签,如果有新的媒体片段会添加到播放列表末尾,因此也需要客户端定时获取该m3u8文件,直到访问到EXT-X-ENDLIST标签才停止。

EXTINF

EXTINF,表示其后URI指定的媒体片段时长(单位为秒),每个URI媒体片段之前必须指定该标签。

1
#EXTINF:<duration>,[<title>]

duration:可以为整型或者浮点型,其值必须小于或等于EXT-X-TARGETDURATION指定的值。
建议采用浮点型指定时长,这可以让客户端在定位流时,减少四舍五入错误。
但是如果EXT-X-VERSION小于33,则只支持整型。

m3u8的注意

关于"m3u8"还有几点注意事项。

文件的编码格式

"m3u8"文件必须以"utf-8"进行编码,这也是最后一个字符8的含义。

m3u8中的#号

"m3u8"文件中以#开头的字符串要么是注释,要么就是标签。标签以#EXT开头,大小写敏感。

m3u8中媒体片段的路径

"m3u8"文件中,媒体片段可以采用全路径表示,例如:https://example.com/movie1/fileSequenceA.ts
(所以,也可以将"m3u8"文件和"ts"片段分开存储)

请求m3u8的方法

请求"m3u8"有两种方法:
一是通过"m3u8"的"URL"进行请求,则该文件必须以.m3u8.m3u结尾。
二是通过"HTTP"进行请求,则Content-Type必须设置为application/vnd.apple.mpegurl或者audio/mpegurl

题外话:网络请求过程

以Chrome浏览器访问"www.kakawanyifan.com"为例。
整体的过程有两步:

  1. 域名解析
  2. TCP的3次握手

域名解析

域名解析是解析"www.kakawanyifan.com"这个域名对应的IP地址。

  1. 首先,Chrome浏览器会搜索浏览器自身的DNS缓存,检索自身的缓存中是否有"www.kakawanyifan.com"对应的条目且没有过期。
    该缓存时间比较短,大概只有1分钟。
    可以通过chrome://net-internals/#dns查看。
  2. 如果浏览器自身的缓存里面没有找到,那么Chrome会搜索操作系统自身的DNS缓存。
    可以通过命令ipconfig/displaydns查看。
  3. 如果在操作系统的DNS缓存也没有找到,那么尝试读取hosts文件(C:\Windows\System32\drivers\etc),检查hosts文件中是否有该域名对应的IP地址。
  4. 如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器(一般是运营商的DNS服务器)发起域名解析请求
    1. 运营商的DNS服务器首先查找自身的缓存,是否有对应的条目且没有过期。
    2. 如果没有,接下来就是递归调用了。运营商的DNS服务器首先会找根域的DNS的IP地址,并发起请求(请问"www.kakawanyifan.com"这个域名的IP地址是多少?)
    3. 根域会返回com域的DNS地址
    4. 接下来,运营商的DNS服务器向com域的DNS地址发起请求(请问"www.kakawanyifan.com"这个域名的IP地址是多少?)
    5. com域会kakawanyifan.com这个域的DNS地址
    6. 运营商的DNS服务器又向kakawanyifan.com这个域名的DNS地址(一般就是由域名注册商提供的)发起请求(请问"www.kakawanyifan.com"这个域的IP地址是多少?)
    7. 终于!"kakawanyifan.com"域的DNS服务器一查,把结果发送给运营商的DNS服务器,这个时候运营商的DNS服务器终于拿到了"www.kakawanyifan.com"这个域名对应的IP地址,并返回给操作系统,操作系统又把结果返回给浏览器,终于浏览器拿到了"www.kakawanyifan.com"对应的IP地址。

例如:"kakawanyifan.com"的DNS的解析:
kakawanyifan.com

正常情况下,通过上述步骤就找到了。如果没有的话,会有如下的操作

  1. 操作系统就会查找Net BIOS name Cache,但凡最近一段时间内成功通讯过的计算机的计算机名和IP地址,就都会在这个缓存里面。
  2. 如果没有找到,就会查询WINS服务器。
  3. 如果没有找到,那么就进行广播查找。
  4. 如果没有找到,那么就检索LMHOSTS文件中是否有。
  5. 如果还是没有,那么就失败了。

发起TCP的3次握手

通过域名解析到对应的IP地址之后,故事并没有结束。
这时候浏览器会以一个随机端口[1024,65535][1024,65535]向服务器的WEB程序的80端口发起TCP的连接请求。
这个连接请求,经过各种路由设备,到达服务器端,进入网卡,再进入系统内核的TCP/IP协议栈,最终到达WEB程序,建立了TCP/IP的连接。
然后

  1. 客户端首先发送一个连接试探,进入syn_sent状态,表示客户端等待服务端的回复。
  2. 服务端监听到连接请求报文后,如同意建立连接,向客户端发送确认。这时服务端进入syn_rcvd,表示服务端已经收到客户端的连接请求,等待客户端的确认。
  3. 客户端收到确认后还需再次发送确认给服务端,服务端收到客户端的确认之后,这个TCP连接才进入established状态,可以发起HTTP请求了。

然后,浏览器才发起了HTTP的请求,请求第一个ts片段,服务端把第一个ts片段返回给浏览器。

文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/19905
版权声明: 本博客所有文章版权为文章作者所有,未经书面许可,任何机构和个人不得以任何形式转载、摘编或复制。

评论区