Appsink does not produce new samples

Hi all,

I’m working on a rewrite of hlssink2 in Rust called flexhlssink. I’m having an issue that I cannot reason understand what is going on. I’m using an appsink to reproduce similar behavior of the giostreamsink in the Rust version, but the appsink seems to receive the new segment but does not pass it along.

I’ve tried to setting GST_DEBUG=6 and then dive into the GStreamer codebase (gst-plugins-base/gst/app/gstappsink.c) in an attempt to understand what is going on. But after several hours, I need some help.

This is how I’m setting up the appsink:

        let app_sink = gst::ElementFactory::make("appsink", Some("giostreamsink_replacement_sink"))
            .expect("Could not make element appsink");

        /// ...

        let appsink = app_sink.downcast_ref::<gst_app::AppSink>().unwrap();
        appsink.set_emit_signals(true);

        appsink.connect_eos(|appsink| {
            gst_info!(CAT, "Got EOS from giostreamsink_replacement_sink");
        });

        let this = self.clone();
        let element_weak = obj.downgrade();
        appsink.connect_new_sample(move |appsink| {
            gst_info!(CAT, "Got new sample from giostreamsink_replacement_sink");

           /// ...etc, not relevant code here...

            Ok(gst::FlowSuccess::Ok)
        });

The full code is available at: https://github.com/rafaelcaricio/gst-flexhlssink-rs/blob/8ffc3b65e6c9c22cc5f543e8863cdcc3616e5655/src/imp.rs#L481-L589

I’m trying to test the new plugin with this pipeline:

 GST_DEBUG=6 gst-launch-1.0 videotestsrc is-live=true ! x264enc ! h264parse ! flexhlssink target-duration=4 --gst-plugin-load=/Users/rafael.caricio/development/opensource/gst-flexhlssink/target/debug/libflexhlssink.dylib 

Here is the GST_DEBUG=6 output: https://gist.github.com/rafaelcaricio/7b8280615d9ab86f736c9aef52ae750a

Is anyone able to see why the appsink gets stuck after sometime and does not pass along the segment data?

Thank you!

The reason why I used gio::OutputStream in hlssink2 as the interface for external code to plug in file writing is because that works across languages and provides a generic interface that can be used to write to anywhere.

If you don’t provide any signals for customized writing then you can as well put a filesink there. Otherwise I would recommend going via gio::OutputStream. You can also easily implement that on the Rust side (or for file writing just go via gio::FileOutputStream).

I wrote an example for there here that uses hlssink2 and uses hyper for writing via HTTP.

What do you mean with segment here? The segment (as in gst::Segment) you would get from sample.segment(), there is no other API for that with appsink :slight_smile:

Note that you will likely get multiple samples (and buffers) for each HLS fragment and will have to write them out correctly, and also you might have to handle seeks (the data is not necessarily contiguous, but most likely it is in all cases here).

Your problem here is that there’s an appsink that is waiting in PAUSED state for being set to PLAYING state:

0:00:06.384456000 89559 0x7fb2b603bad0 DEBUG  basesink gstbasesink.c:2424:gst_base_sink_wait_preroll:<giostreamsink_replacement_sink> waiting in preroll for flush or PLAYING

This would happen wit async state changes (set async=false on your appsinks), or if you only set the state of the new appsinks to PAUSED instead of PLAYING.

That is a very good point. I want to keep the new plugin fully compatible with the hlssink2, so I will make the changes to use gio::OutputStream. I didn’t do it at first because I didn’t know it already had Rust bindings available. :sweat_smile:

I’ve tried to run my plugin code without setting the sink property in the splitmuxsink and the pipeline still gets stuck. It seems that the sink of splitmuxsink keeps changing state of the sink the element in the sink property, for some reason… and then blocks the pipeline. I’m leaving the property async-finalize as the default, which should be false.

My bad, I meant “sample”… :sweat_smile:

Maybe this is what is causing the element to move to PAUSED? :thinking: I don’t know what is calling this operation:

0:00:06.069195000 64438 0x7fd02403e190 DEBUG             GST_STATES gstelement.c:2921:gst_element_set_state_func:<giostream_sink> set_state to PAUSED

Since it is in the same thread ID, may I assume the splitmuxsink is setting it to PAUSED?

0:00:06.069189000 64438 0x7fd02403e190 INFO            splitmuxsink gstsplitmuxsink.c:3427:set_next_filename:<split_mux_sink> Setting file to segment00000.ts
0:00:06.069192000 64438 0x7fd02403e2d0 LOG               GST_BUFFER gstbuffer.c:457:_memory_add: buffer 0x7fd02305a480, idx -1, mem 0x7fd02404c800
0:00:06.069195000 64438 0x7fd02403e190 DEBUG             GST_STATES gstelement.c:2921:gst_element_set_state_func:<giostream_sink> set_state to PAUSED

Can I reproduce your problem somehow to take a short look?

1 Like

Yes, all the code is in on Github: https://github.com/rafaelcaricio/gst-flexhlssink-rs

This is the pipeline I ran to capture the outputs:

GST_DEBUG=6 gst-launch-1.0 videotestsrc is-live=true ! x264enc ! h264parse ! flexhlssink target-duration=4

That is up to date with my latest attempts. Thank you so much for the help! :rocket:

Looks like a bug with async state change handling, quite confusing because it works the same way reliably with hlssink2 and the code looks more or less the same.

I’ll investigate more in the next days :slight_smile:

1 Like

This seems a quite tricky one to debug. Spent hours looking at the code of splitmuxsink, hlssink2, giostreamsink, etc and finally figured out what is going on. The problem is solved now!

I was not passing along the parent element’s state changes. This caused the splitmuxsink to never be able to notify that it finished it’s async start. The pipeline never went fully to playing state… This is the code change that made it all work:

    fn change_state(
        &self,
        element: &Self::Type,
        transition: gst::StateChange,
    ) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {

      /// ...

-     self.parent_change_state(element, transition)?;
+     let ret = self.parent_change_state(element, transition)?;

      /// ...

-     Ok(gst::StateChangeSuccess::Success)
+     Ok(ret)
    }

:man_facepalming:

Thank you @sdroege! I guess I’m ready to my next issue in the journey of writing this flexhlssink plugin… that is fun! :grinning_face_with_smiling_eyes:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.