Strange problem with remuxing RTSP streams to Matroska

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

Strange problem with remuxing RTSP streams to Matroska

Carlos Rafael Giani
Hello, I am trying to remux a h.264/AAC stream that is transmitted over
RTSP to Matroska. So far, the result is a Matroska file with one
elementary stream, but not both. Sometimes, it contains h.264, sometimes
AAC, never both.

It seems as though somehow the stream starts after the first pad is
linked, and subsequent linking results in pads that aren't set up
properly. But I can't see what exactly is wrong.

This can be tested by using gst-rtsp-server's test-mp4 example. Pass an
mp4 file to it with one h.264 and one AAC stream. I tried the following
gst-launch line, and it works properly - the resulting Matroska data
contains both audio and video:


gst-launch-1.0 uridecodebin caps="video/x-h264, parsed=true; audio/mpeg"
uri="rtsp://127.0.0.1:8554/test" name=d   matroskamux name=e ! filesink
location=test.mkv  d. ! identity ! e.   d. ! aacparse ! e.


However, I wrote this example for doing it programmatically, and the
aforementioned error occurs - only one elementary stream is present in
the Matroska data. (I know the request pads are not released at the end,
I ignored this for now to keep the example small). So, what is the
difference? What is the GstParse functionality doing what I am not?
Anybody can spot anything I missed (especially in the pad_added_cb)?


Here is the test code:


#include <gst/gst.h>
#include <glib-unix.h>


static gboolean sigint(gpointer ptr)
{
     g_main_loop_quit((GMainLoop *)ptr);
     return TRUE;
}


GstElement *pipeline;


static void pad_added_cb(GstElement *decodebin, GstPad *pad, gpointer data)
{
     GstCaps *caps;
     GstStructure *str;
     gchar const *media_type;
     gchar const *req_pad_name;
     gchar const *parser_name = "";
     GstElement *mux;
     GstElement *parser;
     GstPad *req_pad;
     GstPad *parser_pad;

     if (GST_PAD_IS_LINKED(pad))
         return;

     mux = GST_ELEMENT_CAST(data);

     caps = gst_pad_query_caps(pad, NULL);
     str = gst_caps_get_structure(caps, 0);
     media_type = gst_structure_get_name(str);

     if (g_str_has_prefix(media_type, "video/x-h264"))
     {
         req_pad_name = "video_%u";
         parser_name = "identity";
     }
     else if (g_str_has_prefix(media_type, "audio/mpeg"))
     {
         req_pad_name = "audio_%u";
         parser_name = "aacparse";
     }
     else
         return;

     parser = gst_element_factory_make(parser_name, NULL);
     gst_bin_add(GST_BIN(pipeline), parser);
     gst_element_sync_state_with_parent(parser);

     parser_pad = gst_element_get_static_pad(parser, "sink");
     gst_pad_link(pad, parser_pad);
     gst_object_unref(GST_OBJECT(parser_pad));

     parser_pad = gst_element_get_static_pad(parser, "src");
     req_pad = gst_element_get_request_pad(mux, req_pad_name);
     gst_pad_link(parser_pad, req_pad);
     gst_object_unref(GST_OBJECT(parser_pad));
}


int main(int argc, char *argv[])
{
     GMainLoop *loop;
     GstCaps *dec_caps;
     GstElement *uridecodebin, *matroskamux, *filesink;

     gst_init(&argc, &argv);

     if (argc < 2)
         return -1;

     loop = g_main_loop_new(NULL, TRUE);
     g_unix_signal_add(SIGINT, sigint, loop);

     pipeline = gst_pipeline_new(NULL);
     uridecodebin = gst_element_factory_make("uridecodebin", NULL);
     matroskamux = gst_element_factory_make("matroskamux", NULL);
     filesink = gst_element_factory_make("filesink", NULL);

     gst_bin_add_many(GST_BIN(pipeline), uridecodebin, matroskamux,
filesink, NULL);

     gst_element_link(matroskamux, filesink);

     dec_caps = gst_caps_from_string("video/x-h264, parsed=true;
audio/mpeg");
     g_object_set(G_OBJECT(uridecodebin), "caps", dec_caps, "uri",
argv[1], NULL);

     g_object_set(G_OBJECT(filesink), "location", "test.mkv", NULL);

     g_signal_connect(G_OBJECT(uridecodebin), "pad-added",
G_CALLBACK(pad_added_cb), matroskamux);

     gst_element_set_state(pipeline, GST_STATE_PLAYING);

     g_main_loop_run(loop);

     gst_object_unref(GST_OBJECT(pipeline));

     return 0;
}

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

Re: Strange problem with remuxing RTSP streams to Matroska

Carlos Rafael Giani
Partially solved it thanks to thaytan and slomo. The important
difference between the gst-launch-1.0 line and my code is that GstParse
(which is what drives gst-launch-1.0) looks at the pipeline description
and *first* request *both* video_%u and audio_%u request-pads. My code
doesn't - it requests on-demand in the pad-added callback. The result is
indeed that after linking the first pad, the stream starts, and the
second one can't be linked anymore.

Unfortunately, remuxing is apparently tricky to get right in general. In
my case, what can work is to block the pads in the pad-added callback
and then unblock them all in the no-more-pads callback, to make sure the
stream doesn't start until *all* are linked. In the general case, this
would not work if for example a subtitle stream starts later.



On 2017-07-09 20:44, Carlos Rafael Giani wrote:

> Hello, I am trying to remux a h.264/AAC stream that is transmitted
> over RTSP to Matroska. So far, the result is a Matroska file with one
> elementary stream, but not both. Sometimes, it contains h.264,
> sometimes AAC, never both.
>
> It seems as though somehow the stream starts after the first pad is
> linked, and subsequent linking results in pads that aren't set up
> properly. But I can't see what exactly is wrong.
>
> This can be tested by using gst-rtsp-server's test-mp4 example. Pass
> an mp4 file to it with one h.264 and one AAC stream. I tried the
> following gst-launch line, and it works properly - the resulting
> Matroska data contains both audio and video:
>
>
> gst-launch-1.0 uridecodebin caps="video/x-h264, parsed=true;
> audio/mpeg" uri="rtsp://127.0.0.1:8554/test" name=d   matroskamux
> name=e ! filesink location=test.mkv  d. ! identity ! e.   d. !
> aacparse ! e.
>
>
> However, I wrote this example for doing it programmatically, and the
> aforementioned error occurs - only one elementary stream is present in
> the Matroska data. (I know the request pads are not released at the
> end, I ignored this for now to keep the example small). So, what is
> the difference? What is the GstParse functionality doing what I am
> not? Anybody can spot anything I missed (especially in the pad_added_cb)?
>
>
> Here is the test code:
>
>
> #include <gst/gst.h>
> #include <glib-unix.h>
>
>
> static gboolean sigint(gpointer ptr)
> {
>     g_main_loop_quit((GMainLoop *)ptr);
>     return TRUE;
> }
>
>
> GstElement *pipeline;
>
>
> static void pad_added_cb(GstElement *decodebin, GstPad *pad, gpointer
> data)
> {
>     GstCaps *caps;
>     GstStructure *str;
>     gchar const *media_type;
>     gchar const *req_pad_name;
>     gchar const *parser_name = "";
>     GstElement *mux;
>     GstElement *parser;
>     GstPad *req_pad;
>     GstPad *parser_pad;
>
>     if (GST_PAD_IS_LINKED(pad))
>         return;
>
>     mux = GST_ELEMENT_CAST(data);
>
>     caps = gst_pad_query_caps(pad, NULL);
>     str = gst_caps_get_structure(caps, 0);
>     media_type = gst_structure_get_name(str);
>
>     if (g_str_has_prefix(media_type, "video/x-h264"))
>     {
>         req_pad_name = "video_%u";
>         parser_name = "identity";
>     }
>     else if (g_str_has_prefix(media_type, "audio/mpeg"))
>     {
>         req_pad_name = "audio_%u";
>         parser_name = "aacparse";
>     }
>     else
>         return;
>
>     parser = gst_element_factory_make(parser_name, NULL);
>     gst_bin_add(GST_BIN(pipeline), parser);
>     gst_element_sync_state_with_parent(parser);
>
>     parser_pad = gst_element_get_static_pad(parser, "sink");
>     gst_pad_link(pad, parser_pad);
>     gst_object_unref(GST_OBJECT(parser_pad));
>
>     parser_pad = gst_element_get_static_pad(parser, "src");
>     req_pad = gst_element_get_request_pad(mux, req_pad_name);
>     gst_pad_link(parser_pad, req_pad);
>     gst_object_unref(GST_OBJECT(parser_pad));
> }
>
>
> int main(int argc, char *argv[])
> {
>     GMainLoop *loop;
>     GstCaps *dec_caps;
>     GstElement *uridecodebin, *matroskamux, *filesink;
>
>     gst_init(&argc, &argv);
>
>     if (argc < 2)
>         return -1;
>
>     loop = g_main_loop_new(NULL, TRUE);
>     g_unix_signal_add(SIGINT, sigint, loop);
>
>     pipeline = gst_pipeline_new(NULL);
>     uridecodebin = gst_element_factory_make("uridecodebin", NULL);
>     matroskamux = gst_element_factory_make("matroskamux", NULL);
>     filesink = gst_element_factory_make("filesink", NULL);
>
>     gst_bin_add_many(GST_BIN(pipeline), uridecodebin, matroskamux,
> filesink, NULL);
>
>     gst_element_link(matroskamux, filesink);
>
>     dec_caps = gst_caps_from_string("video/x-h264, parsed=true;
> audio/mpeg");
>     g_object_set(G_OBJECT(uridecodebin), "caps", dec_caps, "uri",
> argv[1], NULL);
>
>     g_object_set(G_OBJECT(filesink), "location", "test.mkv", NULL);
>
>     g_signal_connect(G_OBJECT(uridecodebin), "pad-added",
> G_CALLBACK(pad_added_cb), matroskamux);
>
>     gst_element_set_state(pipeline, GST_STATE_PLAYING);
>
>     g_main_loop_run(loop);
>
>     gst_object_unref(GST_OBJECT(pipeline));
>
>     return 0;
> }
>
> _______________________________________________
> gstreamer-devel mailing list
> [hidden email]
> https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel

_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel