你好啊,我是Gstreamer的新手,我想通过网络向接收方发送一个从摄像头捕获并使用OpenCV处理的视频。接收方随后读取并显示它。这应该是实时完成的。它基本上与下面的代码/gstreamer设置一起工作,但是一旦一个帧被丢弃(至少我认为这是原因)视频以灰色部分的形式损坏(附图)。
OpenCV发送部件:
cv::VideoWriter videoTransmitter("appsrc ! videoconvert ! videoscale ! x264enc ! rtph264pay config-interval=1 pt=96 ! udpsink host=192.168.168.99 port=5000", cv::VideoWriter::fourcc('H', '2', '6', '4'), 10, videoTransmitter_imageSize, true);
OpenCV接收部件:
cv::VideoCapture videoReceiver("udpsrc port=5000 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! videoconvert ! appsink", cv::CAP_GSTREAMER);
它基本上可以工作,但我经常会在视频中看到灰色部分,然后停留一段时间,直到视频正确显示。我猜这总是发生在由于传输而丢失帧的情况下。但是,我如何才能摆脱这些灰色/损坏的帧?任何提示?任何需要设置的Gstreamer参数来调整结果?有没有更好的方法通过网络使用opencv流传输视频?
任何帮助都是感激不尽的!
1条答案
按热度按时间c0vxltue1#
不,Gstreamer中没有任何机制来检测损坏的帧,因为这没有意义。
在大多数现代视频编解码器中,帧不再完整发送,而是分成片段(意味着只有帧的一小部分)。它可以使用多个内部包(每个包包含多个片段)来构建一个完整的帧,这是一件好事,因为它使您的流更容易出错,并允许对片段进行多线程解码(例如)。
为了实现您的目标,您有多种解决方案:
1.仅使用RTP/RTCP而不是UDP上的RTP。RTP至少包含一个序列号和“帧结束”标记,这样就可以检测到一些数据包丢失。默认情况下,Gstreamer不关心这些,除非您已经启动了RTP/RTCP会话。如果您使用RTCP设置会话,您可以在某些数据包被丢弃时得到报告。我不确定是否有一种 * 流水线 * 方式可以在数据包被丢弃时得到通知,所以你可能仍然需要在你的gstreamer管道中写一个appsink来添加一些代码来检测这个事件。但是,这会告诉你什么地方出错了,但是不会告诉你什么时候可以恢复,或者错误有多大。用Gstreamer的话来说,它叫做RTPSession,并且您对stats::XXX_nack_count属性感兴趣,
1.添加一些附加协议以计算编码器的输出帧/NAL/数据包的校验和,并在带外传输。确保解码器也计算传入帧/NAL/数据包的校验和,如果不匹配,您将知道解码将失败。注意数据包/帧重新排序(通常B帧将在其相关性之后重新排序),这可能会干扰您的算法。同样,您无法知道何时恢复错误。如果您只有部分数据包丢失,使用TCP而不是UDP可能足以修复它,但如果是带宽问题,它将无法恢复(如果视频带宽〉网络带宽,它将崩溃,因为TCP不能丢弃数据包来适应)
1.仅使用帧内视频编解码器(如APNG或JPEG)。JPEG也可以部分解码,但gstreamer的默认软件jpeg解码器不输出部分JPEG帧。
1.在编码器中设置一个封闭的较短GOP。许多编码器都有一个伪“gop = group of picture”参数,并在出现错误后对解码器中的帧进行计数。GOP确保无论编码状态如何,在GOP帧之后,编码器将发出非相关帧组,(可能有足够的帧内/片重建完整的帧)。这将允许在错误后通过丢弃
GOP - 1
帧来恢复(您必须对它们进行解码,但您不能 * 使用 * 它们,它们可能已损坏),您需要一种方法来检测错误,请参阅上面的第1点或第2点。对于x264enc
,该参数称为key-int-max
。您可能还想尝试intra-refresh=true
,这样出错时的断帧效果会更短。缺点是相同视频质量的带宽增加。1.使用可伸缩视频编码的视频编解码器(例如SVC而不是AVC)。在这种情况下,如果解码错误,你会得到一个较低的质量,而不是损坏的帧。没有任何免费的SVC编码器,我知道在Gstreamer。
1.处理它。用OpenCV计算图片的饱和度图,并计算其偏差和平均值。如果它与前一张图片有很大不同,停止计算,直到GOP过去,饱和度回到预期水平。