在 EOS 上用 Python 重启 GStreamer 管道

Posted

技术标签:

【中文标题】在 EOS 上用 Python 重启 GStreamer 管道【英文标题】:Restarting GStreamer Pipeline in Python on EOS 【发布时间】:2016-12-04 23:49:00 【问题描述】:

我正在编写一个在 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 尝试使用上面的 sn-p 重新启动管道,我就会在总线上出现以下错误。而且我知道相机在线,所以这不是问题。

('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)')

有人能解释一下这个问题吗?谢谢!

【问题讨论】:

您是否使用 gst_parse_launch 或类似工具创建管道? 【参考方案1】:

当你这样做时

pipline.set_state(Gst.State.NULL)

这并不意味着管道已立即达到此状态。确保您可以调用 pipeline.get_state()。我还建议处理 set_state() 的返回值。最后要重新开始播放,无需先进入 PAUSED(除非您想在 PUASED 和 PLAYING 之间做点什么)。

【讨论】:

所以我添加了一些调试代码以在某些时间点在命令行上打印管道状态。发行EOS时,管道仍处于PLAYING状态。然后我可以将它放入READY 状态,然后放入NULL 状态。为了恢复管道,我将管道放回READY(使用get_state() 确认它已准备好)。但是当我尝试将其放回 PLAYINGPAUSED 时,我得到与以前相同的错误:qtdemux.c(5847): gst_qtdemux_loop (): /GstPipeline:pipeline0/GstQTDemux:qtdemux0:\nstreaming stopped, reason not-linked (-1) 当我使用我的 RTSP 源而不是本地视频播放时,我得到一个非常相似的错误,除了这次来自 rtspsrc 而不是 qtdemux'gstbasesrc.c(2951): gst_base_src_loop (): /GstPipeline:pipeline0/GstRTSPSrc:source/GstUDPSrc:udpsrc2:\nstreaming stopped, reason not-linked (-1)' 嗯,来自 qtdemux 的未链接告诉它在重用时不会经历与新时相同的初始化。那可能是一个错误。如果您在 EOS 上重新创建管道并且它可以工作,这将确认它。您可以为 gstreamer 提交错误并添加您的 python 示例吗? 是的,先生,通过覆盖管道变量重新创建管道确实让 gstreamer 再次运行。我尝试一个一个地取消链接元素,然后重新链接它们,但是 qtdemux 不想链接到解码器。我什至从管道中删除了 qtdemux,重新创建了一个新的 qtdemux 元素,并将其链接到那里,但没有骰子。您是否有创建错误的说明,以前没有这样做过。谢谢! 你让它工作了吗?我还尝试将管道状态设置为 null,然后在 gstreamer 总线上收到 EOS 消息时播放【参考方案2】:

我在块动态创建源垫的管道上遇到了这个 (-1) 错误,例如rtpsrc、rtpbin、decodebin 块只是其中的三个块。

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

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

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

在正常的 gstreamer 示例中,他们倾向于显示的只是添加了 pad 的处理程序,如果 sink pad 未连接,则在 source 和 sink pad 之间创建链接。

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

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

// ... 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 处理程序是通过调用带有一些额外元数据的代码来添加的,以允许按名称定位源实例,过滤新的焊盘名称并将其连接到接收器实例。

【讨论】:

【参考方案3】:

如果使用gst_parse_launch(),您可以使用: 而不是! 来自动进行“添加垫”处理。

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

【讨论】:

以上是关于在 EOS 上用 Python 重启 GStreamer 管道的主要内容,如果未能解决你的问题,请参考以下文章

区块链Python开发EOS交易机器人WAX链游脚本常用工具

区块链Python开发EOS交易机器人与WAX链游脚本基础教程

已安装完系统的硬盘可换到其它机上用吗?

在Hadoop上用Python实现WordCount

Linux下设置Swap后, 重启后设置失效 怎么处理?

python 在Windows上用Python设置当前工作目录