我正在编写一个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'
)。
在流媒体和视频编码/解码方面,我不是任何专家,将应用程序带到工作点已经是一场噩梦:/如果H264不合适,我们可以切换到gstreamer支持的任何其他编解码器。有谁能够帮我?
答案 0 :(得分:18)
我们从桌面电脑到Raspberry Pis实时播放视频,我们花了大量时间调整系统的编码和解码部分。不幸的是,大多数图书馆和工具都有其开箱即用的设置,适用于转码或一般视频播放(不是直播)。我们最终编写了自己的GStreamer元素来进行编码(使用vaapi)和我们自己的Raspberry Pi程序来进行解码(使用OMX)。
我可以为您提供一些想法,但遗憾的是没有针对Android解码方案的具体内容。
如果您在强大的桌面上进行编码,例如i3-i7,请确保为任何重要操作添加队列:颜色空间转换,缩放,编码等。因此,在您的管道中,请确保是一个"队列"介于" videoconvert"和" x264enc"所以他们分开运行。
正如拉尔夫所说,你可能只想使用P帧,而不是B帧,你的x264enc设置可能已经这样做了。
我们通常倾向于使用大抖动缓冲区丢帧并显示垃圾。我们还在运行中调整QP(编码质量),以便在我们的网络范围内。所以我建议在接收程序中添加sync = false。您希望在拥有框架后立即渲染框架。这可能会使您的视频不太流畅,但如果您有一个较大的抖动缓冲区,您将永远被延迟。最好将流调整到网络并摆脱缓冲区。 x264enc有" qp-min"和" qp-max"您可以尝试的属性。
尝试调整"延迟"和"延迟下降"你的rtpjitterbuffer的属性,或试着完全摆脱它。
我们发现的一个非常讨厌的事情是,在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启动一个新线程来处理管道的以下部分。您只是想确保编码/缩放/颜色空间转换元素单独工作。gst-launch-1.0 [GET RAW VIDEO DATA]队列[SCALE]队列 [COLORSPACE CONVERT]队列[ENCODE]队列[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: '
......或者接收管道上的类似内容:
导出GST_DEBUG = 2,rtpjitterbuffer:5
gst-launch-1.0 udpsrc port = 5100 buffer-size = 825984! application / x-rtp,encoding-name = H264,payload = 96! rtpjitterbuffer等待时间= 200! rtph264depay! h264parse disable-passthrough = true!排队! avdec_h264 output-corrupt = true! 排队!视频转换! ximagesink 2>& 1 | grep -i"缓冲区discon
---附录1-11-19
如果您有办法驾驭专利情况,思科的openh264库工作得非常好。它非常适合直播。
https://github.com/cisco/openh264
https://www.openh264.org/BINARY_LICENSE.txt
gst-plugins-bad
下有一个GStreamer插件。
答案 1 :(得分:1)
在流媒体和视频编码/解码方面,我不是任何专家,将应用程序带到工作点已经是一场噩梦:/如果H264不合适,我们可以切换到gstreamer支持的任何其他编解码器
这很可能不是与编解码器相关的问题:编解码器引入的延迟在使用B帧时发生,而IIRC x264在零延迟模式下不使用。
实时流应用程序中的其他延迟是您的
我建议查看播出缓冲区,或许gstreamer
有办法设置持续时间吗?此外,播出缓冲区的实现在你可以实现的实时性方面发挥着重要作用,例如在VLC
的旧版本中,可以将网络缓存参数设置得非常低,例如大约100ms。但是,在当前版本的VLC中,这会导致视频无法播放,因为数据"到达"晚了。另一方面,ffmpeg
更适合以低延迟播放实时数据。我不确定gstreamer
如何比较。
您可以尝试使用~100ms进行试验,然后根据其执行情况进行调整。当然,您可以假设您可以在gstreamer
中设置此参数。