Strange problem with remuxing RTSP streams to Matroska
Carlos Rafael Giani
dv at pseudoterminal.org
Sun Jul 9 18:44:58 UTC 2017
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;
}
More information about the gstreamer-devel
mailing list