GStreamer 1.16.0 将 tsdemux 链接到 h264parse 时出现问题

GStreamer 1.16.0 problem linking tsdemux to h264parse

所以,这有效:

gst-launch-1.0 filesrc location=Truck.H264 ! tsdemux ! h264parse ! avdec_h264 ! xvimagesink

这有效:

GstElement* pipeline = gst_parse_launch_full(
        "gst-launch-1.0 filesrc location=Truck.H264 ! tsdemux ! h264parse ! avdec_h264 ! xvimagesink",
        NULL, GST_PARSE_FLAG_NONE, NULL );

但这不起作用:

GstElement* pipeline = gst_pipeline_new(nullptr);
GstElement* filesrc = gst_element_factory_make("filesrc", nullptr);
g_object_set( G_OBJECT(filesrc), "location", "Truck.H264", NULL);
GstElement* tsdemux = gst_element_factory_make("tsdemux", nullptr);
GstElement* h264parse = gst_element_factory_make("h264parse", nullptr);
GstElement* avdec_h264 = gst_element_factory_make("avdec_h264", nullptr);
GstElement* xvimagesink = gst_element_factory_make("xvimagesink", nullptr);
gst_bin_add_many(GST_BIN(pipeline), filesrc, tsdemux, queue, h264parse, avdec_h264, xvimagesink, nullptr);
gst_element_link_many(filesrc, tsdemux, h264parse, avdec_h264, xvimagesink, nullptr);

在我没有经验的头脑中,这些都是一样的,应该以同样的方式工作。不过我猜不是。

所以我将 link 分解为单独的步骤,发现问题出在这里:

gst_element_link(tsdemux, h264parse);

我想也许 gst_element_link 在 gst_parse_launch_full 上没有那么聪明,并且不知道要 link 哪个垫,所以我尝试了:

gst_element_link_pads(tsdemux,"video_%01x_%05x", h264parse, "sink");

并且:

GstCaps* caps=gst_caps_new_simple("video/x-h264",
        "stream-format",G_TYPE_STRING,"byte-stream",
        "alignment",G_TYPE_STRING,"nal",
        nullptr);
gst_element_link_filtered(tsdemux, h264parse, caps);

但这没有用。

我尝试在 tsdemux 和 h264parse 之间插入一个队列,但随后错误转移到 linking tsdemux 和队列:

gst_element_link(tsdemux, queue);

我想我可以 link 任何东西到一个队列。我猜不是。

我尝试了一些我认为行不通的其他愚蠢的事情,但他们没有,现在我没有想法了。

我可能遗漏了每个 gstreamer 程序员都知道的东西。如果你是其中之一,能否请你分享给我?

OK,我今天聪明了一点。尴尬的是,我在文档中看到了这个,却没有意识到它的重要性。

答案是并非所有元素都是用它们的垫创建的。有时,垫是在需要时动态创建的。重要的是我不能 link 一个元素,直到它的垫被创建。所以要做到这一点,我必须创建一个函数来 link 制作垫子时的元素:

static void linkElements(GstElement* element,GstPad* sourcePad, gpointer sinkElement){
    GstPad* sinkPad=gst_element_get_static_pad((GstElement*)sinkElement,"sink");
    gst_pad_link(sourcePad,sinkPad);
    gst_object_unref(sinkPad);
}

然后我替换了link元素命令:

gst_element_link(tsdemux, h264parse);

使用在抛出 "pad-added" 事件时调用我的 linkElements 函数的命令:

g_signal_connect(tsdemux,"pad-added",G_CALLBACK(linkElements),h264parse);

我正在跟进

我修改了此处找到的 gstreamer 文档 hello world 示例:https://gstreamer.freedesktop.org/documentation/application-development/basics/helloworld.html?gi-language=c

我根据脚本顶部的 #define 为我的 ubuntu 笔记本电脑和 Jetson Xavier 创建了一个工作代码示例。神奇之处在于 g_signal_connect 函数,它使用 on_pad_added 来处理动态添加的 pad!这意味着当抛出“pad-added”事件时,两个 pad 就像我们想要的那样链接起来。

我将文件命名为“watch_video.c”,运行 是这样的:

gcc -Wall watch_video.c -o watch_video $(pkg-config --cflags --libs gstreamer-1.0)
./watch_video file.ts

这是代码!

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

/* If running on Jetson change to 1 */
#define JETSON 0

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
    GMainLoop *loop = (GMainLoop *) data;

    switch (GST_MESSAGE_TYPE(msg)) {

        case GST_MESSAGE_EOS:
            g_print("End of stream\n");
            g_main_loop_quit(loop);
            break;

        case GST_MESSAGE_ERROR: {
            gchar *debug;
            GError *error;

            gst_message_parse_error(msg, &error, &debug);
            g_free(debug);

            g_printerr("Error: %s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(loop);
            break;
        }
        default:
            break;
    }

    return TRUE;
}


static void on_pad_added(GstElement *element, GstPad *pad, gpointer data)
{
    GstPad *sinkpad;
    GstElement *decoder = (GstElement *) data;

    /* We can now link this pad with the its corresponding sink pad */
    g_print("Dynamic pad created, linking demuxer/decoder\n");
    sinkpad = gst_element_get_static_pad(decoder, "sink");
    gst_pad_link(pad, sinkpad);
    gst_object_unref(sinkpad);
}


int main(int argc, char *argv[])
{
    GMainLoop *loop;

    GstElement *pipeline, *source, *demuxer, *parser, *decoder, *sink;
    GstBus *bus;
    guint bus_watch_id;

    /* Initialisation */
    gst_init(&argc, &argv);
    loop = g_main_loop_new(NULL, FALSE);

    /* Check input arguments */
    if (argc != 2) {
        g_printerr("Usage: %s <mpegts filename>\n", argv[0]);
        return -1;
    }

    /* Create gstreamer elements */
    pipeline = gst_pipeline_new("video-player");
    if (!pipeline) {
        g_printerr ("Pipeline could not be created: [pipeline]. Exiting.\n");
        return EXIT_FAILURE;
    }

    source = gst_element_factory_make("filesrc", "file-source");
    demuxer = gst_element_factory_make ("tsdemux", "mpegts-demux");
    parser = gst_element_factory_make("h264parse", "h264parse-decoder");

    if(!JETSON){
        decoder = gst_element_factory_make("avdec_h264", "avdec_h264-decoder");
        sink = gst_element_factory_make("autovideosink", "video-output");
    }
    else{
        decoder = gst_element_factory_make("nvv4l2decoder", "nvv4l2-decoder");
        sink = gst_element_factory_make("nvoverlaysink", "video-output");
    }

    if(!source || !demuxer || !parser || !decoder || !sink){
        g_printerr ("An element could not be created. Exiting.\n");
        return EXIT_FAILURE;
    }

    /* Set up the pipeline */

    /* set the input filename to the source element */
    g_object_set(G_OBJECT(source), "location", argv[1], NULL);

    /* add a message handler */
    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
    bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
    gst_object_unref(bus);

    /* add all elements into the pipeline: file-source | ts-demuxer | h264parse | decoder | video-output */
    gst_bin_add_many(GST_BIN(pipeline), source, demuxer, parser, decoder, sink, NULL);


    /* note that the demuxer will be linked to the decoder dynamically.
     * The source pad(s) will be created at run time, by the demuxer when it detects the amount and nature of streams.
     * Therefore we connect a callback function which will be executed when the "pad-added" is emitted.
    */
    gst_element_link(source, demuxer);
    gst_element_link_many(parser, decoder, sink, NULL);
    g_signal_connect(demuxer, "pad-added", G_CALLBACK(on_pad_added), parser); // link dynamic pad

    /* Set the pipeline to "playing" state*/
    g_print("Now playing: %s\n", argv[1]);
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    /* Iterate */
    g_print("Running...\n");
    g_main_loop_run(loop);

    /* Out of the main loop, clean up nicely */
    g_print("Returned, stopping playback\n");
    gst_element_set_state(pipeline, GST_STATE_NULL);

    g_print("Deleting pipeline\n");
    gst_object_unref(GST_OBJECT(pipeline));
    g_source_remove(bus_watch_id);
    g_main_loop_unref(loop);

    return 0;
}