Не показывать изображение сигнала, если отправитель видео закрыт - PullRequest
1 голос
/ 16 марта 2020

Если я закрою отправителя в этом примере, видео, отображаемое получателем, зависнет. Можно ли вместо этого отобразить статическое c изображение без сигнала, например, полностью синее изображение, и вернуть видео после перезапуска отправителя?

Отправитель

gst-launch-1.0 videotestsrc ! video/x-raw,format=GRAY8 ! videoconvert ! x264enc pass=qual quantizer=20 tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000

Получатель

gst-launch-1.0 udpsrc port=5000 ! application/x-rtp ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink

EDIT

Этот код кажется близким, но по какой-то причине, если я добавлю videotestsr c, раскомментировав закомментированные строки, udpsr c больше не будет вызывает обратный вызов тайм-аута:

// g++ gst_client.cpp `pkg-config --cflags gstreamer-1.0` `pkg-config --libs gstreamer-1.0`

#include <gst/gst.h>

#include <cstdlib>
#include <cstdio>


struct gstreamer_data {
    GstElement* pipeline;
    GstElement* no_signal_source;
    GstElement* udp_source;
    GstElement* rtp_decoder;
    GstElement* video_decoder;
    GstElement* input_selector;
    GstElement* video_converter;
    GstElement* video_sink;
    gulong signal_handler_id;
    GMainLoop* main_loop;
};


static void element_callback(GstBus* bus, GstMessage* message, gstreamer_data* data);
static GstPadProbeReturn have_data_callback(GstPad* pad, GstPadProbeInfo *info, gstreamer_data* user_data);


static GstPadProbeReturn have_data_callback(GstPad* pad, GstPadProbeInfo *info, gstreamer_data* user_data) {
    GstBus* bus;


    printf("have data\n");

    bus = gst_element_get_bus(user_data->pipeline);
    user_data->signal_handler_id = g_signal_connect(G_OBJECT(bus), "message::element", (GCallback) element_callback, user_data);
    gst_object_unref(bus);

    return GST_PAD_PROBE_REMOVE;
}


static void element_callback(GstBus* bus, GstMessage* message, gstreamer_data* data) {
    const GstStructure* st = gst_message_get_structure(message);
    GstPad* pad;


    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT) {
        if (gst_structure_has_name(st, "GstUDPSrcTimeout")) {
            printf("Timeout received from udpsrc\n");

            g_signal_handler_disconnect(G_OBJECT(bus), data->signal_handler_id);

            pad = gst_element_get_static_pad(data->udp_source, "src");
            gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) have_data_callback, data, NULL);
        }
    }
}


static void error_callback(GstBus* bus, GstMessage* message, gstreamer_data* data) {
    (void) bus;
    GError* err;
    gchar* debug_info;


    gst_message_parse_error(message, &err, &debug_info);
    g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(message->src), err->message);
    g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
    g_clear_error(&err);
    g_free(debug_info);

    g_main_loop_quit(data->main_loop);
}


int main() {
    gstreamer_data data;
    GstStateChangeReturn ret;
    GstBus* bus;
    GstPad* pad;


    gst_init(NULL, NULL);


    data.no_signal_source = gst_element_factory_make("videotestsrc", "no_signal_source");
    g_object_set(G_OBJECT(data.no_signal_source),
        "pattern", 6,
        NULL);

    data.udp_source = gst_element_factory_make("udpsrc", "udp_source");
    g_object_set(G_OBJECT(data.udp_source),
        "port", 5000,
        "caps", gst_caps_new_empty_simple("application/x-rtp"),
        "timeout", 1000000000,
        NULL);


    data.rtp_decoder = gst_element_factory_make("rtph264depay", "rtp_decoder");

    data.video_decoder = gst_element_factory_make("avdec_h264", "video_decoder");

    data.input_selector = gst_element_factory_make("input-selector", "input_selector");

    data.video_converter = gst_element_factory_make("videoconvert", "video_converter");

    data.video_sink = gst_element_factory_make("autovideosink", "video_sink");

    data.pipeline = gst_pipeline_new("pipeline");

    if (
        !data.pipeline ||
        !data.no_signal_source ||
        !data.udp_source ||
        !data.rtp_decoder ||
        !data.video_decoder ||
        !data.input_selector ||
        !data.video_converter ||
        !data.video_sink
        )
        {
            g_printerr("Not all elements could be created.\n");
            exit(-1);
        }

    gst_bin_add_many(
        GST_BIN(data.pipeline),
        //data.no_signal_source,
        data.udp_source,
        data.rtp_decoder,
        data.video_decoder,
        data.input_selector,
        data.video_converter,
        data.video_sink,
        NULL);


    if (gst_element_link_many(
        data.udp_source,
        data.rtp_decoder,
        data.video_decoder,
        NULL) != TRUE)
        {
            g_printerr("Elements could not be linked.\n");
            gst_object_unref(data.pipeline);
            exit(-1);
        }


    GstPad* src_1 = gst_element_get_static_pad(data.video_decoder, "src");
    GstPad* sink_1 = gst_element_get_request_pad(data.input_selector, "sink_%u");
    gst_pad_link(src_1, sink_1);

/*
    GstPad* src_2 = gst_element_get_static_pad(data.no_signal_source, "src");
    GstPad* sink_2 = gst_element_get_request_pad(data.input_selector, "sink_%u");
    gst_pad_link(src_2, sink_2);
*/


    g_object_set(G_OBJECT(data.input_selector),
        "active-pad", sink_1,
        NULL);


    if (gst_element_link_many(
        data.input_selector,
        data.video_converter,
        data.video_sink,
        NULL) != TRUE)
        {
            g_printerr("Elements could not be linked.\n");
            gst_object_unref(data.pipeline);
            exit(-1);
        }


    pad = gst_element_get_static_pad(data.udp_source, "src");
    gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) have_data_callback, &data, NULL);


    bus = gst_element_get_bus(data.pipeline);
    gst_bus_add_signal_watch(bus);
    data.signal_handler_id = g_signal_connect(G_OBJECT(bus), "message::error", (GCallback) error_callback, &data);
    gst_object_unref(bus);


    ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set the pipeline to the playing state.\n");
        gst_object_unref(data.pipeline);
        exit(-1);
    }

    data.main_loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(data.main_loop);

    return 0;
}

РЕДАКТИРОВАТЬ

Этот код кажется нормальным, пока я не раскомментирую выбор активной панели в обратных вызовах. Нужно ли что-то делать, прежде чем я сменить активную площадку, например, остановить конвейер?

// g++ gst_client.cpp `pkg-config --cflags gstreamer-1.0` `pkg-config --libs gstreamer-1.0`

#include <gst/gst.h>

#include <cstdlib>
#include <cstdio>


struct gstreamer_data {
    GstElement* pipeline;
    GstElement* video_source;
    GstElement* udp_source;
    GstElement* rtp_decoder;
    GstElement* video_decoder;
    GstElement* video_converter;
    GstElement* input_selector;
    GstPad* sink_1;
    GstPad* sink_2;
    GstElement* video_sink;
    gulong signal_handler_id;
    GMainLoop* main_loop;
};


static void element_callback(GstBus* bus, GstMessage* message, gstreamer_data* data);
static GstPadProbeReturn have_data_callback(GstPad* pad, GstPadProbeInfo *info, gstreamer_data* user_data);


static GstPadProbeReturn have_data_callback(GstPad* pad, GstPadProbeInfo *info, gstreamer_data* user_data) {
    GstBus* bus;


    printf("have data\n");
/*
    g_object_set(G_OBJECT(user_data->input_selector),
        "active-pad", user_data->sink_2,
        NULL);
*/
    bus = gst_element_get_bus(user_data->pipeline);
    user_data->signal_handler_id = g_signal_connect(G_OBJECT(bus), "message::element", (GCallback) element_callback, user_data);
    gst_object_unref(bus);

    return GST_PAD_PROBE_REMOVE;
}


static void element_callback(GstBus* bus, GstMessage* message, gstreamer_data* data) {
    const GstStructure* st = gst_message_get_structure(message);
    GstPad* pad;


    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT) {
        if (gst_structure_has_name(st, "GstUDPSrcTimeout")) {

            printf("no data\n");
/*
            g_object_set(G_OBJECT(data->input_selector),
                "active-pad", data->sink_1,
                NULL);
*/
            g_signal_handler_disconnect(G_OBJECT(bus), data->signal_handler_id);

            pad = gst_element_get_static_pad(data->udp_source, "src");
            gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) have_data_callback, data, NULL);
            gst_object_unref(pad);
        }
    }
}


static void error_callback(GstBus* bus, GstMessage* message, gstreamer_data* data) {
    (void) bus;
    GError* err;
    gchar* debug_info;


    gst_message_parse_error(message, &err, &debug_info);
    g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(message->src), err->message);
    g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
    g_clear_error(&err);
    g_free(debug_info);

    g_main_loop_quit(data->main_loop);
}


int main() {
    gstreamer_data data;
    GstStateChangeReturn ret;
    GstBus* bus;
    GstPad* pad;


    gst_init(NULL, NULL);


    data.video_source = gst_element_factory_make("videotestsrc", "video_source");
    g_object_set(G_OBJECT(data.video_source),
        "pattern", 6,
        "is-live", true,
        NULL);


    data.udp_source = gst_element_factory_make("udpsrc", "udp_source");
    g_object_set(G_OBJECT(data.udp_source),
        "port", 5000,
        "caps", gst_caps_new_empty_simple("application/x-rtp"),
        "timeout", 1000000000,
        NULL);

    data.rtp_decoder = gst_element_factory_make("rtph264depay", "rtp_decoder");

    data.video_decoder = gst_element_factory_make("avdec_h264", "video_decoder");

    data.video_converter = gst_element_factory_make("videoconvert", "video_converter");


    data.input_selector = gst_element_factory_make("input-selector", "input_selector");

    data.video_sink = gst_element_factory_make("autovideosink", "video_sink");


    data.pipeline = gst_pipeline_new("pipeline");


    if (
        !data.pipeline ||
        !data.video_source ||
        !data.udp_source ||
        !data.rtp_decoder ||
        !data.video_decoder ||
        !data.video_converter ||
        !data.input_selector ||
        !data.video_sink
        )
        {
            g_printerr("Not all elements could be created.\n");
            exit(-1);
        }


    gst_bin_add_many(
        GST_BIN(data.pipeline),
        data.video_source,
        data.udp_source,
        data.rtp_decoder,
        data.video_decoder,
        data.video_converter,
        data.input_selector,
        data.video_sink,
        NULL);


    if (gst_element_link_many(
        data.udp_source,
        data.rtp_decoder,
        data.video_decoder,
        data.video_converter,
        NULL) != TRUE)
        {
            g_printerr("Elements could not be linked.\n");
            gst_object_unref(data.pipeline);
            exit(-1);
        }


    GstPad* src_1 = gst_element_get_static_pad(data.video_source, "src");
    data.sink_1 = gst_element_get_request_pad(data.input_selector, "sink_%u");
    gst_pad_link(src_1, data.sink_1);
    gst_object_unref(src_1);    


    GstPad* src_2 = gst_element_get_static_pad(data.video_converter, "src");
    data.sink_2 = gst_element_get_request_pad(data.input_selector, "sink_%u");
    gst_pad_link(src_2, data.sink_2);
    gst_object_unref(src_2);


    if (gst_element_link_many(
        data.input_selector,
        data.video_sink,
        NULL) != TRUE)
        {
            g_printerr("Elements could not be linked.\n");
            gst_object_unref(data.pipeline);
            exit(-1);
        }


    pad = gst_element_get_static_pad(data.udp_source, "src");
    gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) have_data_callback, &data, NULL);
    gst_object_unref(pad);

    bus = gst_element_get_bus(data.pipeline);
    gst_bus_add_signal_watch(bus);
    data.signal_handler_id = g_signal_connect(G_OBJECT(bus), "message::error", (GCallback) error_callback, &data);
    gst_object_unref(bus);


    ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set the pipeline to the playing state.\n");
        gst_object_unref(data.pipeline);
        exit(-1);
    }

    data.main_loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(data.main_loop);

    return 0;
}

РЕДАКТИРОВАТЬ:

Я внезапно выгляжу нормально. Я не понимаю Это подходящий код? Можно ли его улучшить?

РЕДАКТИРОВАТЬ:

Установка ширины и высоты на видеотестере c в отправителе, кажется, заставляет его работать. Если я уберу их, это сломается. Почему?

...