简体   繁体   中英

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.

Here is the gstreamear pipeline used to get frames from camera:

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. 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.

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. If I detect an EOS signal, I try to restart the pipeline by doing:

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. 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. 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. Same with the rtsp example, where there is an error being generated by 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)

it does not mean that the pipeline has reached this state immediately. To be sure you can call pipeline.get_state(). Also I suggest to handle the return values for 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).

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.

What I found was that you always need a "pad-added" handler. 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.

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

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.

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.

If using gst_parse_launch() you can use : instead of ! to make the 'pad-added' handling happen automatically.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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