How to dynamically link to tee after pipeline started using gst_parse_launch

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

How to dynamically link to tee after pipeline started using gst_parse_launch

Nick_law
Good afternoon all,

Thanks to @gotsring in this post
http://gstreamer-devel.966125.n4.nabble.com/Is-it-possible-to-dynamically-update-tee-sink-interleave-src-td4696637.html

I have learnt how to unlink and relink pads in a pipeline.

What I would like to do now is start a pipeline with audiotestsrcs connected
to a Fakesink and once the pipeline is started then link the audiotestsrcs
using tees to the corresponding interleave pads.

I am unsure if this is even possible using gst_parse_launch but I am hoping
someone is able to lead me in a direction where I can get access to
interleave and tee pads without having them linked in the original parse
launch string.

I can see that the tee plugin has a num-src-pads property which would allow
to specify the number of pads needed but thereafter I don't know how to
access them. The main problem being that I would hope a tee src pad doesn't
need to be defined with a named pad in the string.

It would also be nice to be able to access individual interleave pads
without creating named pads as well.
i.e i.src_2 or t2_src3 etc just from the named tee and interleave values.

The main idea is to be able to create a multichannel (16 channels +)
pipeline that initially outputs 0 until audio sources are mapped to the
desired channels.

I have a current broken example that shows a little of what I'm trying to do
but obviously missing some core concepts:

```
        gst_init(NULL, NULL);

        GError* err = NULL;

        std::stringstream gst_parse;

        //audiotestsrc needed the is-live=true flag added to allow for remapping
and continued playout.
        //Currently unsure how that would react with a file player (wavparse and
rawaudioparse)
        gst_parse << "interleave name=i ! audioconvert ! wavenc ! filesink
location=test.wav ";
        gst_parse << "audiotestsrc is-live=true ! tee name=t1 ";
        gst_parse << "audiotestsrc is-live=true wave=2 ! tee name=t2 ";
        gst_parse << "audiotestsrc is-live=true wave=4 ! tee name=t3 "; //Silent
wave used as Fakesrc

        gst_parse << "t3. ! queue ! volume name=out0 volume=1.0 ! i. ";
        gst_parse << "t3. ! queue ! volume name=out1 volume=1.0 ! i. ";
        gst_parse << "t3. ! queue ! volume name=out2 volume=1.0 ! i. ";
        gst_parse << "t3. ! queue ! volume name=out3 volume=1.0 ! i. ";

        gst_parse << "t1. ! queue ! volume name=vol0 volume=1.0 ! fakesink ";
//Ideally this is not needed to start pipeline
        gst_parse << "t2. ! queue ! volume name=vol1 volume=1.0 ! fakesink ";

        GstElement* pipeline = gst_parse_launch(gst_parse.str().c_str(), &err);

        if (err != NULL) {
                g_print("Pipeline failed to be created! You did a bad parse!\n");
                g_printerr("Error: %s\n", err->message);
                return -1;
        }

        // Get elements by name
        GstElement* out0 = gst_bin_get_by_name(GST_BIN(pipeline), "out0");
        GstElement* out1 = gst_bin_get_by_name(GST_BIN(pipeline), "out1");
        GstElement* out2 = gst_bin_get_by_name(GST_BIN(pipeline), "out2");
        GstElement* out3 = gst_bin_get_by_name(GST_BIN(pipeline), "out3");

        GstElement* vol0 = gst_bin_get_by_name(GST_BIN(pipeline), "vol0");//Again
ideally not needed
        GstElement* vol1 = gst_bin_get_by_name(GST_BIN(pipeline), "vol1");

        if (
                !out0 ||
                !out1 ||
                !out2 ||
                !out3 ||
                !vol0 ||
                !vol1) {
                g_print("Error getting bins!\n");
                return -1;
        }

        // Get their the sink and src pads that need to be unlinked and re-linked
        GstPad* out0_sink_pad = gst_element_get_static_pad(out0, "sink"); //sink
side is where the fakesrc silent wave is conected
        GstPad* out1_sink_pad = gst_element_get_static_pad(out1, "sink");
        GstPad* out2_sink_pad = gst_element_get_static_pad(out2, "sink");
        GstPad* out3_sink_pad = gst_element_get_static_pad(out3, "sink");

        GstPad* vol0_src_pad = gst_element_get_static_pad(vol0, "src"); //src side
is where the Fakesink is connected
        GstPad* vol1_src_pad = gst_element_get_static_pad(vol1, "src");

        if (
                !out0_sink_pad ||
                !out1_sink_pad ||
                !out2_sink_pad ||
                !out3_sink_pad ||
                !vol0_src_pad ||
                !vol1_src_pad) {
                g_print("Error getting src pads!\n");
                return -1;
        }

        // I'm not sure that pad order is guaranteed when the pipeline is
constructed,
        // meaning interleave pad sink1 might be connected to vol0 instead of vol1
        // So, get pads based on peers! peers are the element that the pad is
connected to.
        GstPad* fakesrc0_pad = gst_pad_get_peer(out0_sink_pad);
        GstPad* fakesrc1_pad = gst_pad_get_peer(out1_sink_pad);
        GstPad* fakesrc2_pad = gst_pad_get_peer(out2_sink_pad);
        GstPad* fakesrc3_pad = gst_pad_get_peer(out3_sink_pad);

        GstPad* fakesink0_pad = gst_pad_get_peer(vol0_src_pad);
        GstPad* fakesink1_pad = gst_pad_get_peer(vol1_src_pad);

        if (!fakesrc0_pad ||
                !fakesrc1_pad ||
                !fakesrc2_pad ||
                !fakesrc3_pad ||
                !fakesink0_pad ||
                !fakesink1_pad) {
                g_print("Error getting peer pads!\n");
                return -1;
        }

        // Play for a bit
        g_print("Playing...\n");
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
        GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"playing_orig");
        g_usleep(2000000);

        // Swap around pads
        g_print("Rearranging pads...\n");
        gst_element_set_state(pipeline, GST_STATE_PAUSED);

        gboolean good = TRUE;
        good &= gst_pad_unlink(fakesrc0_pad, out0_sink_pad);
        good &= gst_pad_unlink(fakesrc1_pad, out1_sink_pad);
        good &= gst_pad_unlink(fakesrc2_pad, out2_sink_pad);
        good &= gst_pad_unlink(fakesrc3_pad, out3_sink_pad);

        good &= gst_pad_unlink(vol0_src_pad, fakesink0_pad);
        good &= gst_pad_unlink(vol1_src_pad, fakesink1_pad);

        if (!good) {
                g_print("Pad unlink was not good!");
                REQUIRE(0);
        }
        // This is where I am unsure how to relink to a tee that hasn't got
a named pad in the original parse launch string.
        // The relink won't work as the vol*_src_pads are being relinked to
more than one pad.

  good &= gst_pad_link(vol0_src_pad, out0_sink_pad) == GST_PAD_LINK_OK;
        good &= gst_pad_link(vol1_src_pad, out1_sink_pad) == GST_PAD_LINK_OK;

        good &= gst_pad_link(vol0_src_pad, out2_sink_pad) == GST_PAD_LINK_OK;
        good &= gst_pad_link(vol1_src_pad, out3_sink_pad) == GST_PAD_LINK_OK;

        //It seems that you MUST reconnect the fakesinks and Fakesrcs or the relink
doesn't play out
        good &= gst_pad_link(fakesrc0_pad, fakesink0_pad) == GST_PAD_LINK_OK;
        good &= gst_pad_link(fakesrc1_pad, fakesink1_pad) == GST_PAD_LINK_OK;
        good &= gst_pad_link(fakesrc2_pad, fakesink0_pad) == GST_PAD_LINK_OK;
        good &= gst_pad_link(fakesrc3_pad, fakesink1_pad) == GST_PAD_LINK_OK;

        if (!good) {
                g_print("Pad relink was not good!");
                REQUIRE(0);
        }

        // Play some more
        g_print("Playing more...\n");
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
        GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"playing_swapped");
        g_usleep(2000000);

        g_print("Stopping...\n");
        //TODO: test this with the hanging fifo issue and see if that stops
gstreamer from hanging
        gst_element_send_event(pipeline, gst_event_new_eos());
        gst_element_set_state(pipeline, GST_STATE_NULL);

        g_print("Done.\n");

```

I hope I have explained my problem clearly and that someone can help?

Thanks so much,
Nick



--
Sent from: http://gstreamer-devel.966125.n4.nabble.com/
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to dynamically link to tee after pipeline started using gst_parse_launch

gotsring
May I ask why you would like to use gst_parse_launch for this? Yes, it's
probably possible, but it seems either way you need to hold references to
certain elements or pads.

I'd wager that it's better to programmatically create elements and save
references to them in an array, especially for this amount of manipulation.



--
Sent from: http://gstreamer-devel.966125.n4.nabble.com/
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to dynamically link to tee after pipeline started using gst_parse_launch

Nick_law
Hi @gotsring,
Short answer legacy and time.

I am trying to get the go ahead to remove the dependency on gst_parse_launch
but will need a considerable amount of time to remove, refactor and learn
everything that is needed to construct the pipelines fully using the
gstreamer api.

So still would like to know how to use gst_parse_launch in the event I don't
have enough time to re-implement.

regards,
Nick


gotsring wrote
> May I ask why you would like to use gst_parse_launch for this? Yes, it's
> probably possible, but it seems either way you need to hold references to
> certain elements or pads.
>
> I'd wager that it's better to programmatically create elements and save
> references to them in an array, especially for this amount of
> manipulation.





--
Sent from: http://gstreamer-devel.966125.n4.nabble.com/
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to dynamically link to tee after pipeline started using gst_parse_launch

gotsring
Tee and interleave both have request pads, meaning you can just request a new
pad without necessarily pre-allocating them. So for example, if you find the
interleave element, you can request a new sink pad using
    gst_element_get_request_pad(interleave, "sink_%u")

Wasn't quite sure what your intended use case is, but I altered the example
code to create a pipeline that's not completely linked, and then grabs
references to interleave and the volume elements to finish linking
everything together before playing.

dynamic_interleave_link.cpp
<http://gstreamer-devel.966125.n4.nabble.com/file/t379531/dynamic_interleave_link.cpp>  

I also noticed just now that interleave has a channel-positions property to
(I think) rearrange which inputs map to which channel. Maybe this is useful
to you?
https://gstreamer.freedesktop.org/documentation/interleave/interleave.html?gi-language=c#interleave:channel-positions

Hope this helps!



--
Sent from: http://gstreamer-devel.966125.n4.nabble.com/
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel