Realtime/zero-latency 视频流:使用什么编解码器参数?

Realtime/zero-latency video stream: what codec parameters to use?

我正在编写一个 Android 应用程序,其中包括从台式 PC 发送和接收视频流。为了让应用程序正常运行,我们需要尽可能少的延迟,必要时牺牲视频质量。我们在两端都使用 gstreamer 1.45,但在当前管道中,我们在 Galaxy Note S2 上至少有 0.5 秒的延迟,如果两个设备都在同一网络上(稍后这应该通过 VPN 工作)。

发件人管道

appsrc name=vs_src format=time do-timestamp=true
    caps="video/x-raw, format=(string)RGB, width=(int)640, height=(int)480, framerate=(fraction)15/1000" 
    ! videoconvert 
    ! x264enc speed-preset=ultrafast tune=zerolatency byte-stream=true threads=1 key-int-max=15 intra-refresh=true ! h264parse ! rtph264pay pt=96
    ! queue ! udpsink name=vs_sink host=%s port=%d async=false

接收器管道

udpsrc name=vr_src 
    caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, payload=(int)96, encoding-name=(string)H264"
    ! rtpjitterbuffer 
    ! rtph264depay ! h264parse ! avdec_h264 
    ! videorate ! videoconvert
    ! glimagesink name=vr_sink async=false

设置 threads=2 或更高会发出 gstreamer 警告,它是在没有多线程支持的情况下编译的。我知道有些设备提供硬件解码器,但可靠地访问它们的唯一方法似乎是通过 encodebin/decodebin。我已经尝试使用 decodebin 但出于某种原因它抱怨找不到所需的插件(例如 No decoder to handle media type 'video/x-h264')。

在流媒体和视频方面,我无论如何都不是专家 encoding/decoding,让应用程序达到工作点已经是一场噩梦:/如果 H264 不合适,我们可以切换到任何其他gstreamer 支持的编解码器。有谁能够帮助我?

I am no expert by any means when it comes to streaming and video encoding/decoding, and getting the App to a working point was already a nightmare :/ If H264 should be inappropriate we can switch to any other codec supported by gstreamer

这很可能不是与编解码器相关的问题:编解码器引入的延迟在使用 B 帧时发生,IIRC x264 在零延迟模式下不使用 B 帧。

实时流应用程序中的其他延迟是您的

  • 编码延迟(例如,您的 phone 是否可以足够快地编码数据,如果不能,您可以尝试减少 resolution/quality/bitrate)
  • 网络(在 LAN 上应该不是问题)
  • jitter/playout缓冲区

我建议查看播放缓冲区,也许 gstreamer 有办法设置持续时间?此外,播放缓冲区的实现在实时性方面起着重要作用,例如在 VLC 的旧版本中,可以将网络缓存参数设置得非常低,例如在100ms的数量级。但是,在当前版本的 VLC 中,这会导致视频无法作为数据 "arrives" 延迟播放。另一方面,ffmpeg 更适合播放低延迟的实时数据。我不确定 gstreamer 比较如何。

您可以尝试使用 ~100ms 进行试验,然后根据其性能进行调整。当然,这是假设您可以在 gstreamer.

中设置此参数

我们从台式机到 Raspberry Pi 进行实时视频流传输,并且我们花费了大量时间来调整系统的编码和解码部分。不幸的是,大多数库和工具都有针对转码或一般视频播放(非实时)的开箱即用设置。我们最终编写了自己的 GStreamer 元素来进行编码(使用 vaapi)和我们自己的 Raspberry Pi 程序来进行解码(使用 OMX)。

我可以为您提供一些想法,但不幸的是,没有针对 Android 解码方案的具体想法。

  • 如果您在功能强大的桌面(如 i3-i7)上进行编码,请确保为任何重要操作添加队列:色彩空间转换、缩放、编码等。因此,在您的管道中,使确定 "videoconvert" 和 "x264enc" 之间有一个 "queue",所以它们 运行 在不同的线程上。

  • 正如 Ralf 提到的,您可能只想使用 P 帧,而不是 B 帧,并且您的 x264enc 设置可能已经这样做了。

  • 我们通常更喜欢丢帧和显示垃圾,而不是使用大的抖动缓冲区。我们还即时调整 QP(编码质量)以使其在我们的网络范围内。所以我建议将 sync=false 添加到您的接收程序中。您想在获得帧后立即渲染它。这可能会使您的视频不那么流畅,但是如果您有一个大的抖动缓冲区,您总是会被延迟。最好将流调整到网络并摆脱缓冲区。 x264enc 具有 "qp-min" 和 "qp-max" 属性,您可以尝试。

  • 尝试调整 rtpjitterbuffer 的 "latency" 和 "drop-on-latency" 属性,或者尝试完全摆脱它。

  • 我们发现的一个非常讨厌的事情是,在 Raspberry Pi 解码器中,它似乎总是有某种内置延迟,无论我们的流如何实时优化。事实证明,在 h264 流中有一种称为 VUI 数据包的东西,可用于告诉解码器预期的流类型,当我们提供此数据包时,解码器的反应非常不同。

bitstream_restriction_flag : 1
motion_vectors_over_pic_boundaries_flag : 1 
max_bytes_per_pic_denom : 0 
max_bits_per_mb_denom : 0 
log2_max_mv_length_horizontal : 10 
log2_max_mv_length_vertical : 10 
num_reorder_frames : 0 
max_dec_frame_buffering : 1   --- this makes a huge difference

供参考:https://www.raspberrypi.org/forums/viewtopic.php?t=41053

所以在上面的 VUI 设置中,我告诉解码器我们最多需要缓冲一个 P 帧。这有多大帮助真是太疯狂了。当然,我们还必须确保我们的编码器只发送一个 P 帧。我不确定这是否可能与 x264enc 相关。

这些东西会变得非常可怕。希望其他人有 Android 视频印章可以给你一个更简单的答案!

编辑:关于队列,我根本没有对它们进行参数化,并且在实时流式传输的情况下,如果您的队列已满,您无论如何都需要缩减(分辨率、质量等)。在 GStreamer 中,队列元素导致 GStreamer 启动一个新线程来处理管道的后续部分。您只想确保您的 encode/scaling/colorspace 转换元素独立工作。

gst-launch-1.0 [GET RAW VIDEO DATA] queue [SCALE] queue [COLORSPACE CONVERT] queue [ENCODE] queue [SEND WHEREVER]

以上将为您提供五个线程。

如果你在这里什么也得不到,我的建议是打开一个 Android 视频 API 子论坛或邮件列表,看看是否有其他人有直播视频,如果有,他们做了什么调整他们的流和解码器。

--- 附录 1-5-18

我们还注意到某些流会填满内核套接字缓冲区并导致数据包丢失——尤其是在大关键帧上。因此,如果您有更大的流,我建议使用 sysctl:

检查内核缓冲区大小
sysctl net.core.rmem_max;  sysctl net.core.rmem_default
net.core.rmem_max = 212992
net.core.rmem_default = 212992

在接收设备的 /etc/sysctl.conf 中添加 net.core.rmem_max = whatever,并在 udpsrc 上将 buffer-size 设置为这个新的最大值。您可以通过 运行 如下方式判断您是否仍然看到掉落:

watch -d 'cat /proc/net/snmp | grep Udp: '

...或在您的接收管道上类似这样的内容:

export GST_DEBUG=2,rtpjitterbuffer:5

gst-launch-1.0 udpsrc port=5100 buffer-size=825984 ! application/x-rtp,encoding-name=H264,payload=96 ! rtpjitterbuffer latency=200 ! rtph264depay ! h264parse disable-passthrough=true ! queue ! avdec_h264 output-corrupt=true ! queue ! videoconvert ! ximagesink 2>&1 | grep -i "buffer discon

--- 附录 1-11-19

如果您有办法了解专利情况,Cisco 的 openh264 库可以很好地工作。非常适合直播。

https://github.com/cisco/openh264

https://www.openh264.org/BINARY_LICENSE.txt

gst-plugins-bad下有一个 GStreamer 插件。