目录

网络视频流传输与ffmpeg编解码推拉流初探

前置知识

常见音视频编码格式

  • 视频

    • ITU-T: H.261、H.263、H.263+、H.264
    • MPEG: MPEG-1、MPEG-2、MPEG-4
  • 音频

    • AAC,MP3,WMA

视频封装格式

  • AVI : 可用 MPEG-2, DIVX, XVID, WMV3, WMV4, AC-1, H.264
  • WMV : 可用 WMV3, WMV4, AC-1
  • RM/RMVB : 可用 RV40, RV50, RV60, RM8, RM9, RM10
  • MOV : 可用 MPEG-2, MPEG4-ASP(XVID), H.264
  • MKV : 所有
  • FLV: 封装有较为严格的音视频编码限制,即 FLV 封装的音频格式以 AAC/MP3/SPEEX 为主,视频以 H264 为主

流媒体传输方案

  • RTSP (国标 GB28181) 使用 RTP 封装视频流, 基于 udp, 视频编码常为 MPEG-4/H.264/SVAC, 音频 G.711/G.723.1/G.729

  • RTMP (adobe) 视频必须是 H264 编码,音频必须是 AAC 或 MP3 编码,且多以 flv 格式封包 (FLV 封装有较为严格的音视频编码限制,即 FLV 封装的音频格式以 AAC/MP3/SPEEX 为主,视频则以 H264 为主).

  • http-flv (adobe) 相当于 http 版本的 rtmp, 使用 http 传输 flv 封装的视频流

  • HLS(apple) 服务器端会将最新的直播数据生成新的小文件,客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播.基本上,HLS 是以点播的技术实现了直播的体验。因为每个小文件的时长很短,客户端可以很快地切换码率,以适应不同带宽条件下的播放。 传输内容包括两部分:一是 M3U8 描述文件,二是 TS 媒体文件。TS 媒体文件中的视频必须是 H264 编码,音频必须是 AAC 或 MP3 编码。

  • WebRTC 基于 udp 视频封装使用 RTP/RTCP, 侧重 p2p 视频会议 https://static-1251996892.file.myqcloud.com/img/markdown/2022/webrtc_protocol.jpg

网络直播延迟

  • RTMP 协议基于 TCP 来实现,每个时刻的数据,收到后立刻转发,一般延迟在 1-3s 左右
  • HTTP-FLV 基于 HTTP 长连接,通 RTMP 一样,每个时刻的数据,收到后立刻转发,只不过使用的是 HTTP 协议,一般延迟在 1-3s
  • HLS 协议基于 HTTP 短连接来实现,集合一段时间数据,生成 ts 切片文件,然后更新 m3u8(HTTP Live Streaming 直播的索引文件),一般延迟会大于 10s

ffmpeg

典型工作流程

  • 编解码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 _______              ______________
|       |            |              |
| input |  demuxer   | encoded data |   decoder
| file  | ---------> | packets      | -----+
|_______|            |______________|      |
                                           v
                                      _________
                                     |         |
                                     | decoded |
                                     | frames  |
                                     |_________|
 ________             ______________       |
|        |           |              |      |
| output | <-------- | encoded data | <----+
| file   |   muxer   | packets      |   encoder
|________|           |______________|
  • 流拷贝
1
2
3
4
5
 _______              ______________            ________
|       |            |              |          |        |
| input |  demuxer   | encoded data |  muxer   | output |
| file  | ---------> | packets      | -------> | file   |
|_______|            |______________|          |________|

滤波

在编码之前,FFmpeg 通过调用 libavfilter 库的滤波器(filters)来处理原生(raw)音频和视频帧。我们将由多个滤波器(chained filters)组成的链式结构称为滤波图(filter graph)。ffmpeg 中区分两类滤波图:简单的(simple)和复杂的(complex)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 _________                        ______________
|         |                      |              |
| decoded |                      | encoded data |
| frames  |\                   _ | packets      |
|_________| \                  /||______________|
             \   __________   /
  simple     _\||          | /  encoder
  filtergraph   | filtered |/
                | frames   |
                |__________|
  • simple filtergraph
1
2
3
4
 _______        _____________        _______        ________
|       |      |             |      |       |      |        |
| input | ---> | deinterlace | ---> | scale | ---> | output |
|_______|      |_____________|      |_______|      |________|

ffmpeg 命令行示例

1
2
# ffmpeg 模板
ffmpeg -i INPUT -c:v libx264 -c:a copy OUTPUT
1
2
# ffmpeg 从文件推到 rtmp
ffmpeg -re -stream_loop -1 -i input.mp4 -c copy -f flv -y rtmp://192.168.20.6:1937/live/stream114514
1
2
# ffmpeg 从文件推到 rtsp
ffmpeg -re -stream_loop -1 -i v2.mp4 -rtsp_transport tcp -c copy -f rtsp rtsp://localhost:8554/rtsp233
1
2
# ffmpeg 转码推到 rtmp
ffmpeg -re -stream_loop -1 -i input.mp4 -c:v libx264 -c:a aac -f flv -y rtmp://192.168.20.6:1937/live/stream114514
1
2
# ffmpeg 转码并限制码率帧数分辨率推到 rtmp
ffmpeg -re -stream_loop -1 -i input.mp4 -c:v libx264 -c:a aac -b:v 2000k -b:a 32k -r 24 -s 640x480 -f flv -y rtmp://192.168.20.6:1937/live/stream114514
1
2
3
# 保存流到文件
ffmpeg -i rtsp://192.168.20.6:8554/rtsp233 -c copy dump.flv
ffmpeg -i rtmp://10.244.2.150:1935/live/test12345 -c copy dump.flv
1
2
# rtsp to rtmp
ffmpeg -i rtsp://192.168.20.6:8554/rtsp233 -f flv -an rtmp://192.168.20.6:1937/live/stream114514

ffmpeg 参数

  • -f fmt (input/output) Force input or output file format. The format is normally auto detected for input files and guessed from the file extension for output files, so this option is not needed in most cases.

  • -re (input) Read input at native frame rate. This is equivalent to setting -readrate 1.

  • -y (global) Overwrite output files without asking.

  • -stream_loop number (input) Set number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.

  • -c[:stream_specifier] -codec[:stream_specifier] codec (input/output,per-stream) Select an encoder (when used before an output file) or a decoder (when used before an input file) for one or more streams. codec is the name of a decoder/encoder or a special value copy (output only) to indicate that the stream is not to be re-encoded.

  • -vframes number (output) Set the number of video frames to output. This is an obsolete alias for -frames:v, which you should use instead.

  • -map 选项(多音轨, 字幕流相关, 只在封装文件中有大余一路视频流和一路音频流的时候需要考虑, 与视频推拉流相关不大)

    引用: fmpeg 命令行 map 参数的使用

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    # fmpeg -i input.mkv
    
    ffmpeg version ... Copyright (c) 2000-2012 the FFmpeg developers
    ...
    Input #0, matroska,webm, from 'input.mkv':
      Duration: 01:39:44.02, start: 0.000000, bitrate: 5793 kb/s
        Stream #0:0(eng): Video: h264 (High), yuv420p, 1920x800, 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
        Stream #0:1(ger): Audio: dts (DTS), 48000 Hz, 5.1(side), s16, 1536 kb/s (default)
        Stream #0:2(eng): Audio: dts (DTS), 48000 Hz, 5.1(side), s16, 1536 kb/s
        Stream #0:3(ger): Subtitle: text (default)
    At least one output file must be specified
    

    那么现在,我们说我们想要:

    • 将视频流复制
    • 将德语音频流编码为 MP3(128kbps)和 AAC(96kbps)(在输出中创建两个音频流)
    • 将英语音频流删除
    • 将字幕流复制
    • 这可以用以下的 ffmpeg 命令来完成:
    1
    2
    3
    4
    5
    6
    7
    
    ffmpeg -i input.mkv \
        -map 0:0 -map 0:1 -map 0:1 -map 0:3 \
        -c:v copy \
        -c🅰️0 libmp3lame -b🅰️0 128k \
        -c🅰️1 libfaac -b🅰️1 96k \
        -c:s copy \
        output.mkv
    

    注意一下参数里没有“-map 0:2”,并且“-map 0:1”被写了两次。 使用“-map 0:0 -map 0:1 -map 0:1 -map 0:3”,我们告诉 ffmpeg 选择/映射指定的输入流按相应顺序输出。 因此,我们的输出将具有以下流:

    1
    2
    3
    4
    5
    
    Output #0, matroska, to 'output.mkv':
        Stream #0:0(eng): Video ...
        Stream #0:1(ger): Audio ...
        Stream #0:2(ger): Audio ...
        Stream #0:3(ger): Subtitle ...