如何使用WebRTC和FFmpeg流媒体,以及为什么这是一个坏主意

2021-01-31 02:13:53

流媒体。特别是视频,是一个善变的野兽。这是一篇简短的文章,但它将涵盖您需要了解的所有使用FFmpeg流媒体到WebRTC客户端的知识。该技术具有许多应用程序,例如将同步视频流式传输到用户。这篇文章还介绍了FFmpeg的一些基本问题,这些问题限制了FFmpeg在媒体流中的实用性。

这些标志将日志级别设置为info,如果缺少日志则生成pt,并设置我们可以使用的协议。接下来,我们需要将输入提供给FFmpeg。

在此示例中,使用文件名in.mp4,但也可以使用http(s)URL。此命令从输入文件的开头开始播放。如果要在输入文件的中间开始播放,则可以添加-ss< time in secs>。在-i之前标记。

现在,需要将视频转换为适当的格式以进行流式传输。该格式特定于应用程序,但是常见的编解码器是H264,VP8和VP9。由于其无处不在的支持,此示例使用H264。

-vf realtime,scale = w = min(iw \,1280):h = -2 \ -map 0:v:0 \ -c:v libx264 \-线程3 \ -profile:v基线\ -level:v 3.1 \ -pix_fmt yuv420p \-调整零延迟\-最小500K \-最大1.3M \ -bufsize 500K \ -force_key_frames expr:gte(t,n_forced * 4)\ -bsv:v h264_metadata = level = 3.1

-vf指定要应用的视频过滤器。在这里,应用了两个过滤器。第一个是实时的,它导致回放实时发生,这对于流式传输是必需的。该过滤器与-re标志类似,但与开始时间标志(-ss)相比效果更好。第二个滤镜将视频宽度缩放到最大1280像素,同时保持宽高比。这对于保持适用于实时流的比特率很重要。

另一个重要的参数是-threads 3标志。 FFmpeg默认情况下将使用许多线程。通常,这很好,因为它会尽快产生最终结果。但是,对于实时编码,使用大量线程会产生一些开销,从而降低实时输出的速度。如果同时运行多个FFmpeg实例,则使用多个线程也是一个坏主意。

接下来的两个参数-profile:v和-level:v指定用于编码的配置文件和级别。这些特定于H264。 WebRTC客户端只能解码某些配置文件和级别,因此它们需要与应用程序的特定配置相匹配。这些大致对应于42e01f的配置文件级别ID。

使用-pix_fmt标志将像素格式设置为yuv420p。这是必需的,因为这是WebRTC支持的唯一像素格式。 -tune zerolatency调整编码器以实现低延迟流传输。

接下来是比特率参数,这些参数揭示了使用FFmpeg进行媒体流传输的缺点。流媒体时,比特率应尽可能低,同时保持所需的质量。这样可以确保所有客户端都可以实时播放视频。省略-minrate参数可能导致FFmpeg产生不必要的高比特率输出。设置-maxrate同样重要。 DSL连接只能拉低2 Mbps。为了使用户能够观看视频,他们必须能够实时下载视频,因此最大比特率必须低于用户之间最慢的连接速度。另一个要考虑的因素是,流视频可能不是用户网络上唯一消耗带宽的任务。

最后,我们遇到了一个大问题,没有一个好的解决方案。在编码视频中,关键帧是视频中的帧,其中包含呈现自身所需的所有视觉信息,而没有任何其他元数据。这些比普通帧大得多,并且对比特率有很大贡献。理想情况下,将有尽可能少的关键帧。但是,当新用户开始使用流时,他们需要至少一个关键帧才能观看​​视频。 WebRTC使用RTP控制协议(RTCP)解决了此问题。当新用户使用流时,他们向生产者发送完整的内部请求(FIR)。当生产者收到此请求时,他们将关键帧插入流中。这样可以保持较低的比特率,同时确保所有用户都可以观看流。 FFmpeg不支持RTCP。这意味着默认的FFmpeg设置将产生中途消费的输出,至少在收到关键帧之前无法看到。因此,需要参数-force_key_frames expr:gte(t,n_forced * 4),该参数每4秒生成一个关键帧。

这里要注意的主要事情是使用了arealtime过滤器,它类似于实时过滤器,但用于音频。

可以使用tee psuedomuxer将输出通过管道传输到RTP端点。不幸的是,FFmpeg不支持RTP上的多路复用,因此您将需要两个单独的RTP端点,一个用于视频流,一个用于音频流。

> ffmpeg \ -v info \ -fflags + genpts \ -protocol_whitelist pipe,tls,file,http,https,tcp,rtp \ -i in.mp4 -vf realtime,scale = w = min(iw \,1280):h = -2 \ -map 0:v:0 \ -c:v libx264 \-线程3 \ -profile:v基线\ -level:v 3.1 \ -pix_fmt yuv420p \-调谐零延迟\-最小500K \-最大1.3M \ -bufsize 500K \ -force_key_frames expr:gte(t,n_forced * 4)\ -bsv:v h264_metadata = level = 3.1 \ -af arealtime \ -map 0:a:0 \ -c:a libopus \ -ab 128k \- ac 2 \ -ar 48000 \ -f tee \ [select = a:f = rtp:ssrc = 1111:payload_type =< payload_type>] rtp://< ip>:< port>?rtcpport =< rtcpport&gt ; | [select = v:f = rtp:ssrc = 2222:payload_type =< payload_type>] rtp://< ip>:< port>?rtcpport =< rtcpport>

不幸的是,使用FFmpeg时,关键帧/比特率/ RTCP问题没有很好的解决方案。 Gstreamer(一种类似的媒体编码实用程序)确实支持RTCP,我将在以后的文章中介绍如何使用它。