简体   繁体   English

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

[英]Restarting GStreamer Pipeline in Python on EOS

I am working on a Python script running on RPi3, and using gstreamer to connect to RTSP feed of my IP Camera, and serve decoded H264 frames to my Python script.我正在处理在 RPi3 上运行的 Python 脚本,并使用 gstreamer 连接到我的 IP 摄像机的 RTSP 源,并将解码的 H264 帧提供给我的 Python 脚本。

Here is the gstreamear pipeline used to get frames from camera:这是用于从相机获取帧的 gstreamear 管道:

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

Problem: Since the camera is on a slow/unreliable internet connection, I get dropped frames every now and then, which causes an EOS signal to be generated.问题:由于相机的互联网连接速度缓慢/不可靠,我时不时会掉帧,这会导致生成 EOS 信号。 The internet connection bandwidth can be a problem sometimes causing a hick up with the stream.互联网连接带宽可能是一个问题,有时会导致流出现问题。

Goal : On EOS signal, I would like to restart the pipeline so gstreamer can keep serving frames to my program.目标:在 EOS 信号上,我想重新启动管道,以便 gstreamer 可以继续为我的程序提供帧。

What I have tried: I have a callback function attached to the bus that listens for messages by using我尝试过的:我有一个附加到总线的回调函数,它通过使用侦听消息

bus.connect("message", on_message)

Inside the "on_message" function, I am able to successfully determine if the message is an EOS signal.在“on_message”函数中,我能够成功确定该消息是否为 EOS 信号。 If I detect an EOS signal, I try to restart the pipeline by doing:如果我检测到 EOS 信号,我会尝试通过执行以下操作来重新启动管道:

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

Unfortunately this does not work.不幸的是,这不起作用。 Once my scipt attemps to restart the pipeline using the snippet above, I get the following errors appearing on the bus.一旦我的 scipt 尝试使用上面的代码段重新启动管道,我就会在总线上出现以下错误。 And I know the camera is online so that's not the problem.而且我知道相机在线,所以这不是问题。

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

In case the problem was with rtspsrc, I also tried using a short local video using filesrc, and using the EOS signal generated when gstreamer reaches end of the video file to test whether I am able to restart the pipeline.如果问题出在 rtspsrc 上,我还尝试使用 filesrc 使用本地短视频,并使用 gstreamer 到达视频文件末尾时生成的 EOS 信号来测试我是否能够重新启动管道。 Here is an example pipeline I used to play local video:这是我用来播放本地视频的示例管道:

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

If it is successful, it should start going through the video again, but no luck... Instead I am getting the following error, which makes me think that filesrc somehow needs to be reset.如果成功,它应该再次开始播放视频,但没有运气......相反,我收到以下错误,这让我认为 filesrc 需要以某种方式重置。 Same with the rtsp example, where there is an error being generated by rtspsrc与 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)')

Can anybody shed any light on this problem?任何人都可以对这个问题有所了解吗? Thanks!谢谢!

When you do当你做

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

it does not mean that the pipeline has reached this state immediately.这并不意味着管道已立即达到此状态。 To be sure you can call pipeline.get_state().确保您可以调用 pipeline.get_state()。 Also I suggest to handle the return values for set_state().另外我建议处理 set_state() 的返回值。 Finally to get back to playing, there is no need to go to PAUSED first (unless you want to do something between PUASED and PLAYING).最后要回到游戏中,没有必要先进入暂停(除非你想在 PUASED 和 PLAYING 之间做一些事情)。

I have encountered this (-1) error on pipelines where blocks have dynamically created source pads eg the rtpsrc, rtpbin, decodebin blocks are just three of the blocks that do this.我在块动态创建源填充的管道上遇到了这个 (-1) 错误,例如 rtpsrc、rtpbin、decodebin 块只是执行此操作的三个块。

What I found was that you always need a "pad-added" handler.我发现你总是需要一个“pad- added”处理程序。 Even in a 'scripted' pipeline.即使在“脚本化”管道中。 You have to have program code installing pad-added handlers on your pipeline - in my pipelines I assigned names onto instances of blocks ( rtpbin name="rtpbin ) so you could locate them by known name.您必须让程序代码在您的管道上安装填充添加的处理程序 - 在我的管道中,我将名称分配给块的实例( rtpbin name="rtpbin ),以便您可以通过已知名称找到它们。

This is because the general gstreamer launch using gst-launch-1.0 for example constructs the pipeline once correctly, but does not allow for dynamic source pads.这是因为例如使用 gst-launch-1.0 的一般 gstreamer 启动会正确构建管道一次,但不允许动态源填充。 All it does is exit if a new pad is created for a connection which already existed.如果为已经存在的连接创建了新的焊盘,它所做的就是退出。

Despite the documentation which seems to imply that going to NULL state and back to PLAYING will reconnect the pipeline this is not true for these dynamically created pads which appear in documentation like "src_%u_%u_%u" where the second and third arguments to the format string are generated from inside the block concerned, and cannot be predicted by the user (the first %u is just the zero-based instance number ) .尽管文档似乎暗示进入 NULL 状态并返回到 PLAYING 将重新连接管道,但这对于出现在文档中的这些动态创建的 pad 并非如此,例如“src_%u_%u_%u”,其中第二个和第三个参数为格式字符串是从相关块内部生成的,用户无法预测(第一个 %u 只是从零开始的实例编号)。

In the normal gstreamer examples, all they tend to show is for a pad-added handler to create a link between a source and a sink pad if the sink pad is not connected.在正常的 gstreamer 示例中,如果接收器焊盘未连接,则它们倾向于显示的是添加焊盘的处理程序在源和接收器焊盘之间创建链接。

In fact in the case of the pad-added handler call in one of these dynamically created pads, the source pad has changed identity and vanished, but the sink pad is still in 'connected' state.事实上,在这些动态创建的焊盘之一中添加焊盘的处理程序调用的情况下,源焊盘已更改身份并消失,但接收焊盘仍处于“连接”状态。

What I found worked was to explicitly disconnect the sink pad .. something like this我发现有效的是明确断开水槽垫..像这样

// ... 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); 
// .. 

In my case, pipelines are constructed by calling gst_parse_bin_from_description() but the pad_added handlers are added by calling code with some additional metadata to allow locating the source instance by name, filter the new pad name and connect it to the sink instance.在我的例子中,管道是通过调用 gst_parse_bin_from_description() 构建的,但是 pad_ added 处理程序是通过调用带有一些附加元数据的代码添加的,以允许按名称定位源实例,过滤新的焊盘名称并将其连接到接收器实例。

If using gst_parse_launch() you can use : instead of !如果使用gst_parse_launch()你可以使用:而不是! to make the 'pad-added' handling happen automatically.使“添加垫”处理自动发生。

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

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

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