Multiple textoverlay with glsinkbin not working

Hello,

I am new to gstreamer, but I really like the flexibility what you can all do.
But now I have an issue that I don’t understand. Hopefully somebody can guide me into the right direction.

I am playing a video with ‘playbin’ and I am adding (at least) 2 text overlay over the video. Because I am switching between videos when something occurs I am putting the sink video in a GTK widget for smooth switching between the videos.

When I use ‘gtksink’ as my ‘sinkVideo’, the two overlay texts are displayed and it is all working fine. But I am wondering why it doesn’t work with the following code when I am using ‘glsinkbin’. I only see the first text, the second text is not shown.

How can I let both the texts shown? Do I need something like a compositor?

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

typedef struct _CustomData {
    GstElement *playbin;
    GstElement *binVideo;
    GstElement *sinkVideo;
    GstElement *text1;
    GstElement *text2;
    GtkWidget *sinkWidget;
} CustomData;

static gboolean handle_message(GstBus *bus, GstMessage *msg, CustomData *data) {
	GError *err;
	gchar *debug_info;
	gboolean ret;

	switch (GST_MESSAGE_TYPE (msg)) {
		case GST_MESSAGE_ERROR:
			gst_message_parse_error(msg, &err, &debug_info);
			g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
			g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
			g_clear_error(&err);
			g_free(debug_info);
			break;
		case GST_MESSAGE_EOS:
			g_print("End-Of-Stream reached.\n");

			ret = gst_element_seek(data->playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
			if (!ret) {
				g_print("Seek failed!\n");
			}
			break;
		case GST_MESSAGE_STATE_CHANGED:
			{
				if (GST_MESSAGE_SRC(msg) == GST_OBJECT(data->playbin)) {
					GstState old_state, new_state, pending_state;
					gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
					g_print("Pipeline state changed from %s to %s:\n", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
				}
			}
			break;
        default:
            break;
	}

	return TRUE;
}

static void delete_event_cb(GtkWidget *widget, GdkEvent *event, CustomData *data) {
    gtk_main_quit();
}

static void create_ui(CustomData *data) {
    GtkWidget *main_window;
    GtkWidget *main_box;
    GtkWidget *main_hbox;

    main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(main_window), "delete-event", G_CALLBACK(delete_event_cb), data);

    main_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_box_pack_start(GTK_BOX(main_hbox), data->sinkWidget, TRUE, TRUE, 0);

    main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_pack_start(GTK_BOX(main_box), main_hbox, TRUE, TRUE, 0);

    gtk_container_add(GTK_CONTAINER(main_window), main_box);
    gtk_window_fullscreen(GTK_WINDOW(main_window));

    gtk_widget_show_all(main_window);
}

int main(int argc, char *argv[]) {
    CustomData data;
    
    gst_init(&argc, &argv);
    gtk_init(&argc, &argv);

    data.playbin = gst_element_factory_make("playbin", "source");
    data.binVideo = gst_bin_new("binVideo");
    data.sinkVideo = gst_element_factory_make("glsinkbin", "sinkVideo");
    data.text1 = gst_element_factory_make("textoverlay", "text1");
	data.text2 = gst_element_factory_make("textoverlay", "text2");

    GstElement *gtkglsink = gst_element_factory_make("gtkglsink", "gtkglsink");
    if (gtkglsink != NULL && data.sinkVideo != NULL) {
        g_object_set(data.sinkVideo, "sink", gtkglsink, NULL);
        g_object_get(gtkglsink, "widget", &data.sinkWidget, NULL);
    } else {
        data.sinkVideo = gst_element_factory_make("gtksink", "gtksink");
        g_object_get(data.sinkVideo, "widget", &data.sinkWidget, NULL);
    }

    if (!data.playbin || !data.binVideo || !data.text1 || !data.text2 || !data.sinkVideo ) {
        g_printerr("Not all elements could be created\n");
        return -1;
    }

    gst_bin_add_many(GST_BIN(data.binVideo), data.text1, data.text2, data.sinkVideo, NULL);

    if (!gst_element_link_many(data.text1, data.text2, data.sinkVideo, NULL)) {
        g_printerr("Elements could not be linked\n");
        return -1;
    }
    
    g_object_set(data.text1, "text", "TEST-1", NULL);
    g_object_set(data.text1, "valignment", 2, NULL);
    g_object_set(data.text1, "halignment", 1, NULL);
    g_object_set(data.text1, "font-desc", "Sans, 20", NULL);

    g_object_set(data.text2, "text", "TEST-2", NULL);
    g_object_set(data.text2, "valignment", 1, NULL);
    g_object_set(data.text2, "halignment", 1, NULL);
    g_object_set(data.text2, "font-desc", "Sans, 20", NULL);
    
    GstPad *sink_pad = gst_element_get_static_pad(data.text1, "video_sink");
    GstPad *ghost_pad = gst_ghost_pad_new("sink", sink_pad);
    gst_element_add_pad(data.binVideo, ghost_pad);
    gst_object_unref(sink_pad);

    g_object_set(data.playbin, "video-sink", data.binVideo, NULL);

	// Listen to the bus
    GstBus *bus = gst_element_get_bus(data.playbin);
	gst_bus_add_watch(bus, (GstBusFunc)handle_message, &data);


    create_ui(&data);

    g_object_set(data.playbin, "uri", "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm", NULL);
    gst_element_set_state(data.playbin, GST_STATE_PLAYING);

	gtk_main();

    gst_object_unref(bus);
    gst_element_set_state(data.playbin, GST_STATE_NULL);
    gst_object_unref(data.playbin);

    return 0;
}

At first this seems like a bug with the offloading of the rendering to the GL elements, some further investigation is needed.