Control one video input when using multiple

Hi,

I have been struggling with the following issue and I am wondering if I am on the correct path to make it work.

I want to create multiple video inputs from a file to be shown on the screen.
The video inputs contain out of the following elements:

| Bin Video 1 | filesrc → decodebin → videoconvert → ghost pad to bin
| Bin Video 2 | filesrc → decodebin → videoconvert → ghost pad to bin
| Bin Video 3 | filesrc → decodebin → videoconvert → ghost pad to bin
| Bin Video 4 | filesrc → decodebin → videoconvert → ghost pad to bin

These video inputs are connected to a compositor with sink_u% of the compositor → videoConvert → gtksink.
This is working and the videos are playing.

Now I have the following issue:
Not every video has the same length, so I want to loop some videos individually. I do not get that managed.
I can only start over the entire pipeline, this will also restart the other videos.

How can I control one video input?

Looking forward to any reaction.

Ok, I have been struggling for a few days now.
I believe it must/can work but I am missing something.
So I would like to approach it step by step and hope someone can help me with these little steps.

So,

I have my application code simplified for this question.
I have a pipeline with two bins that contain filesrc, decodebin, videoconvert.
I add the bins to a compositor to overlay the video outputs.

I want to loop video 1 when the EOS signal has been received.
So I do not want to see video 2 again, this happens when I seek the entire pipeline.

How can I individually loop video 1 when they are both connected to the compositor in the pipeline?

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

typedef struct {
    GstElement *pipeline = nullptr;
    GstElement *compositor = nullptr;
    GstElement *videoConvert = nullptr;
    GstElement *sinkVideo = nullptr;
} s_base;

typedef struct {
    GstElement *bin = nullptr;
    GstElement *source = nullptr;
    GstElement *decode = nullptr;
    GstElement *videoConvert = nullptr;
    gint xpos = 0;
    gint ypos = 0;
    guint zorder = 0;
} s_video;

static void pad_added_handler(GstElement *src, GstPad *pad, gpointer data) {
    GstElement *videoconvert = static_cast<GstElement *>(data);
    GstPad *sinkPad = gst_element_get_static_pad(videoconvert, "sink");
    if (!gst_pad_is_linked(sinkPad)) {
        gst_pad_link(pad, sinkPad);
    }
    gst_object_unref(sinkPad);
}

s_video create_video_element(const std::string &filePath, gint xpos, gint ypos, guint zorder) {
    s_video ve;
    ve.bin = gst_bin_new(nullptr);

    ve.source = gst_element_factory_make("filesrc", nullptr);
    g_object_set(ve.source, "location", filePath.c_str(), nullptr);

    ve.decode = gst_element_factory_make("decodebin", nullptr);
    ve.videoConvert = gst_element_factory_make("videoconvert", nullptr);

    ve.xpos = xpos;
    ve.ypos = ypos;
    ve.zorder = zorder;

    gst_bin_add_many(GST_BIN(ve.bin), ve.source, ve.decode, ve.videoConvert, nullptr);
    gst_element_link(ve.source, ve.decode);
    gst_element_link(ve.videoConvert, nullptr);

    g_signal_connect(ve.decode, "pad-added", G_CALLBACK(pad_added_handler), ve.videoConvert);

    GstPad *ghostPad = gst_ghost_pad_new("src", gst_element_get_static_pad(ve.videoConvert, "src"));
    gst_element_add_pad(ve.bin, ghostPad);

    return ve;
}

static void bus_message_handler(GstBus *bus, GstMessage *msg, gpointer user_data) {
    GstElement *element = static_cast<GstElement *>(user_data);

    if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS) {
        gboolean res = gst_element_seek(
            element,
            1.0,
            GST_FORMAT_TIME,
            (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
            GST_SEEK_TYPE_SET,
            0,
            GST_SEEK_TYPE_NONE,
            GST_CLOCK_TIME_NONE
        );
        if (!res) {
            std::cerr << "Loop seek video1 failed!" << std::endl;
        }
    }
}

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

    s_base base;
    base.pipeline = gst_pipeline_new("main-pipeline");
    base.compositor = gst_element_factory_make("compositor", "compositor");
    base.videoConvert = gst_element_factory_make("videoconvert", "videoConvert");
    base.sinkVideo = gst_element_factory_make("autovideosink", "sinkVideo");

    gst_bin_add_many(GST_BIN(base.pipeline), base.compositor, base.videoConvert, base.sinkVideo, nullptr);
    gst_element_link_many(base.compositor, base.videoConvert, base.sinkVideo, nullptr);

    s_video video1 = create_video_element("video1", 0, 0, 0);
    s_video video2 = create_video_element("video2", 320, 0, 1);

    gst_bin_add(GST_BIN(base.pipeline), video1.bin);
    gst_bin_add(GST_BIN(base.pipeline), video2.bin);

    auto link_to_compositor = [](s_video &ve, GstElement *compositor) {
        GstPadTemplate *templ = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(compositor), "sink_%u");
        GstPad *compSinkPad = gst_element_request_pad(compositor, templ, nullptr, nullptr);

        g_object_set(compSinkPad, "xpos", ve.xpos, "ypos", ve.ypos, "zorder", ve.zorder, nullptr);

        GstPad *srcPad = gst_element_get_static_pad(ve.bin, "src");
        gst_pad_link(srcPad, compSinkPad);

        gst_object_unref(srcPad);
        gst_object_unref(compSinkPad);
    };

    link_to_compositor(video1, base.compositor);
    link_to_compositor(video2, base.compositor);

    GstBus *bus = gst_element_get_bus(base.pipeline);
    gst_bus_add_signal_watch(bus);
    g_signal_connect(bus, "message", G_CALLBACK(bus_message_handler), video1.bin);
    gst_object_unref(bus);

    gst_element_set_state(base.pipeline, GST_STATE_PLAYING);

    GMainLoop *loop = g_main_loop_new(nullptr, FALSE);
    g_main_loop_run(loop);

    gst_element_set_state(base.pipeline, GST_STATE_NULL);
    gst_object_unref(base.pipeline);
	
	return 0;
}

Looking forward to some reaction!