繁体   English   中英

在 EOS 上用 Python 重新启动 GStreamer 管道

[英]Restarting GStreamer Pipeline in Python on EOS

我正在处理在 RPi3 上运行的 Python 脚本,并使用 gstreamer 连接到我的 IP 摄像机的 RTSP 源,并将解码的 H264 帧提供给我的 Python 脚本。

这是用于从相机获取帧的 gstreamear 管道:

rtspsrc location=rtsp://ip:port/path ! rtph264depay ! h264parse ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink name=sink

问题:由于相机的互联网连接速度缓慢/不可靠,我时不时会掉帧,这会导致生成 EOS 信号。 互联网连接带宽可能是一个问题,有时会导致流出现问题。

目标:在 EOS 信号上,我想重新启动管道,以便 gstreamer 可以继续为我的程序提供帧。

我尝试过的:我有一个附加到总线的回调函数,它通过使用侦听消息

bus.connect("message", on_message)

在“on_message”函数中,我能够成功确定该消息是否为 EOS 信号。 如果我检测到 EOS 信号,我会尝试通过执行以下操作来重新启动管道:

pipline.set_state(Gst.State.NULL)
pipline.set_state(Gst.State.PAUSED)
pipline.set_state(Gst.State.PLAYING)

不幸的是,这不起作用。 一旦我的 scipt 尝试使用上面的代码段重新启动管道,我就会在总线上出现以下错误。 而且我知道相机在线,所以这不是问题。

('ERROR!!!:', 'source', '!:!', 'Could not read from resource.')
('Debug info:', 'gstrtspsrc.c(5583): gst_rtspsrc_send (): /GstPipeline:pipeline0/GstRTSPSrc:source:\nGot error response: 400 (Bad Request).')
('ERROR!!!:', 'source', '!:!', 'Could not write to resource.')
('Debug info:', 'gstrtspsrc.c(6933): gst_rtspsrc_close (): /GstPipeline:pipeline0/GstRTSPSrc:source:\nCould not send message. (Generic error)')
('ERROR!!!:', 'udpsrc2', '!:!', 'Internal data stream error.')
('Debug info:', 'gstbasesrc.c(2951): gst_base_src_loop (): /GstPipeline:pipeline0/GstRTSPSrc:source/GstUDPSrc:udpsrc2:\nstreaming stopped, reason not-linked (-1)')

如果问题出在 rtspsrc 上,我还尝试使用 filesrc 使用本地短视频,并使用 gstreamer 到达视频文件末尾时生成的 EOS 信号来测试我是否能够重新启动管道。 这是我用来播放本地视频的示例管道:

filesrc location=file.mp4 ! qtdemux ! decodebin ! videoconvert ! video/x-raw, format=BGR ! appsink name=sink

如果成功,它应该再次开始播放视频,但没有运气......相反,我收到以下错误,这让我认为 filesrc 需要以某种方式重置。 与 rtsp 示例相同,其中 rtspsrc 生成错误

('ERROR!!!:', 'qtdemux0', '!:!', 'Internal data stream error.')
('Debug info:', 'qtdemux.c(5847): gst_qtdemux_loop (): /GstPipeline:pipeline0/GstQTDemux:qtdemux0:\nstreaming stopped, reason not-linked (-1)')

任何人都可以对这个问题有所了解吗? 谢谢!

当你做

pipline.set_state(Gst.State.NULL)

这并不意味着管道已立即达到此状态。 确保您可以调用 pipeline.get_state()。 另外我建议处理 set_state() 的返回值。 最后要回到游戏中,没有必要先进入暂停(除非你想在 PUASED 和 PLAYING 之间做一些事情)。

我在块动态创建源填充的管道上遇到了这个 (-1) 错误,例如 rtpsrc、rtpbin、decodebin 块只是执行此操作的三个块。

我发现你总是需要一个“pad- added”处理程序。 即使在“脚本化”管道中。 您必须让程序代码在您的管道上安装填充添加的处理程序 - 在我的管道中,我将名称分配给块的实例( rtpbin name="rtpbin ),以便您可以通过已知名称找到它们。

这是因为例如使用 gst-launch-1.0 的一般 gstreamer 启动会正确构建管道一次,但不允许动态源填充。 如果为已经存在的连接创建了新的焊盘,它所做的就是退出。

尽管文档似乎暗示进入 NULL 状态并返回到 PLAYING 将重新连接管道,但这对于出现在文档中的这些动态创建的 pad 并非如此,例如“src_%u_%u_%u”,其中第二个和第三个参数为格式字符串是从相关块内部生成的,用户无法预测(第一个 %u 只是从零开始的实例编号)。

在正常的 gstreamer 示例中,如果接收器焊盘未连接,则它们倾向于显示的是添加焊盘的处理程序在源和接收器焊盘之间创建链接。

事实上,在这些动态创建的焊盘之一中添加焊盘的处理程序调用的情况下,源焊盘已更改身份并消失,但接收焊盘仍处于“连接”状态。

我发现有效的是明确断开水槽垫..像这样

// ... snip from pad_added_handler 

// If the pad believes it is linked, correctly or not, disconnect it 
if(gst_pad_is_linked(sinkPad))
{
   GstPad * peer = gst_pad_get_peer(sinkPad);
   if(peer) // unlink it if it has a peer
   { 
      gst_pad_unlink(peer,sinkPad);
      g_object_unref(peer); 
   }
}
// and link new or re-link existing pad
gst_pad_link( newPad, sinkPad); 
// .. 

在我的例子中,管道是通过调用 gst_parse_bin_from_description() 构建的,但是 pad_ added 处理程序是通过调用带有一些附加元数据的代码添加的,以允许按名称定位源实例,过滤新的焊盘名称并将其连接到接收器实例。

如果使用gst_parse_launch()你可以使用:而不是! 使“添加垫”处理自动发生。

fx: "videotestsrc ! x264enc ! mpegtsmux ! tsdemux : avdec_h264 ! videoconvert ! ximagesink"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM