Splitmuxsink within GstBin not linking

Hello,

I’m trying to create an element based on GstBin that would record video frames to file using splitmuxsink. So the elements I’m trying to add within the GstBin would be connected like so … ! queue ! tee ! splitmuxsink.

My test pipeline is something like this (where GstRecord is my elemented based on GstBin as parent):

gst-launch-1.0 mfvideosrc name=camera device-index=0 do-timestamp=true ! video/x-raw,width=1280,height=720,framerate=10/1,format=YUY2 ! queue name=test_queue ! GstRecord

I get the following error:

ERROR           GST_PIPELINE subprojects/gstreamer/gst/parse/grammar.y:1090:gst_parse_perform_link: could not link test_queue to recordbin0
WARNING: erroneous pipeline: could not link test_queue to recordbin0

I believe the issue is with splitmuxsink since if I replace it with a fakesink the pipeline connects and works fine. Please see gst_recordbin_sink_request_new_pad where I created and connect the elements to ghost pad. What could I be missing or doing wrong?

Thank you!!

Here is the source code to GstRecord:

record_bin.h


#ifndef __GST_RECORDBIN_H__
#define __GST_RECORDBIN_H__

#include <gst/base/gstbasetransform.h>
#include <gst/video/video.h>

#define PACKAGE "recordbin"
#define PLUGIN_NAME "GstRecord"
#define VERSION "1.0"
#define LICENSE "Proprietary"
#define DESCRIPTION "record video"
#define BINARY_PACKAGE "Recording Plugin"
#define URL "http://"


G_BEGIN_DECLS

typedef struct _GstRecordBin GstRecordBin;
typedef struct _GstRecordBinClass GstRecordBinClass;


#define GST_TYPE_RECORDBIN (gst_recordbin_get_type())
#define GST_IS_RECORDBIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RECORDBIN))
#define GST_IS_RECORDBIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RECORDBIN))
#define GST_RECORDBIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_RECORDBIN, GstRecordBinClass))
#define GST_RECORDBIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RECORDBIN, GstRecordBin))
#define GST_RECORDBIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RECORDBIN, GstRecordBinClass))
#define GST_RECORDBIN_CAST(obj)  ((GstRecordBin *)(obj))


struct _GstRecordBin
{
	GstBin parent_bin;
	GstElement* vqueue, * aqueue;
	GstElement* vtee, * atee;
	GstElement* splitmuxsink;
};

struct _GstRecordBinClass
{
	GstBinClass parent_class;
};

GType gst_recordbin_get_type(void);

G_END_DECLS
#endif 

record_bin.cpp


#include <string>
#include <sstream>
#include <iostream>
#include <ostream>
#include <fstream>
#include "recordbin.h"

GST_DEBUG_CATEGORY_STATIC(gst_recordbin_debug);
#define GST_CAT_DEFAULT gst_recordbin_debug

static GstStaticPadTemplate video_sink_template =
GST_STATIC_PAD_TEMPLATE("video",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);

static GstStaticPadTemplate audio_sink_template =
GST_STATIC_PAD_TEMPLATE("audio",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);

#define gst_recordbin_parent_class parent_class
G_DEFINE_TYPE(GstRecordBin, gst_recordbin, GST_TYPE_BIN);

static void gst_recordbin_sink_dispose(GObject* object);
static void gst_recordbin_sink_finalize(GObject* object);

static void gst_recordbin_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void gst_recordbin_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);

static GstPad* gst_recordbin_sink_request_new_pad(GstElement* element, GstPadTemplate* templ, const gchar* name, const GstCaps* caps);
static void gst_recordbin_sink_release_pad(GstElement* element, GstPad* pad);

static gboolean bus_watch(GstBus* bus, GstMessage* msg, gpointer user_data);

static void gst_recordbin_class_init(GstRecordBinClass* klass)
{
    GObjectClass* gobject_class;
    GstElementClass* gstelement_class;
    GstBin* gstbin_class;

    gobject_class = (GObjectClass*)klass;
    gstelement_class = (GstElementClass*)klass;
    gstbin_class = (GstBin*)klass;
    
    gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_recordbin_set_property);
    gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_recordbin_get_property);
    gobject_class->dispose = gst_recordbin_sink_dispose;
    gobject_class->finalize = gst_recordbin_sink_finalize;

    gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_recordbin_sink_request_new_pad);
    gstelement_class->release_pad = GST_DEBUG_FUNCPTR(gst_recordbin_sink_release_pad);
    
    gst_element_class_add_static_pad_template(gstelement_class, &video_sink_template);
    gst_element_class_add_static_pad_template(gstelement_class, &audio_sink_template);

    gst_element_class_set_details_simple(gstelement_class,
        PLUGIN_NAME,
        PLUGIN_NAME,
        DESCRIPTION,
        URL);
}

static void gst_recordbin_init(GstRecordBin* bin)
{
    bin->aqueue = NULL;
    bin->vqueue = NULL;
    bin->vtee = NULL;
    bin->atee = NULL;
}

void gst_recordbin_sink_dispose(GObject* object)
{
    G_OBJECT_CLASS(parent_class)->dispose(object);
}
void gst_recordbin_sink_finalize(GObject* object)
{
    GstRecordBin* bin = GST_RECORDBIN(object);
    G_OBJECT_CLASS(parent_class)->finalize(object);
}


GstPad* gst_recordbin_sink_request_new_pad(GstElement* element, GstPadTemplate* templ, const gchar* name, const GstCaps* caps) {
GstRecordBin* bin = (GstRecordBin*)element;

    /****** help needed here in this function? ******/

    if (!name)
        name = templ->name_template;

    GstElement* queue = gst_element_factory_make("queue", "vqueue");
    GstElement* sink = gst_element_factory_make("splitmuxsink", "splitmuxsink");
   
    g_object_set(G_OBJECT(sink), "location", "video%2d.mp4", NULL);
    g_object_set(G_OBJECT(sink), "message-forward", TRUE, NULL);
    
     if (!gst_bin_add(GST_BIN(bin), queue) || !gst_bin_add(GST_BIN(bin), sink))
    {
        GST_ERROR_OBJECT(bin, "failed to add");
        return NULL;
    }
    if (!gst_element_link_many(queue,sink, NULL))
    {
        GST_ERROR_OBJECT(bin, "failed to link");
        return NULL;
    }
   
    GstPad* q_sink = gst_element_get_static_pad(queue, "sink");
    GstPad* pad = gst_ghost_pad_new("sink", q_sink);
    gst_pad_set_active(pad, TRUE);
    gst_element_add_pad(GST_ELEMENT(element), pad);
   
    return pad;
}

void gst_recordbin_sink_release_pad(GstElement* element, GstPad* pad)
{
    GstRecordBin* bin = (GstRecordBin*)element;
    if (GST_PAD_PAD_TEMPLATE(pad) && g_str_equal(GST_PAD_TEMPLATE_NAME_TEMPLATE(GST_PAD_PAD_TEMPLATE(pad)), "video")) {
    }
    if (GST_PAD_PAD_TEMPLATE(pad) && g_str_equal(GST_PAD_TEMPLATE_NAME_TEMPLATE(GST_PAD_PAD_TEMPLATE(pad)), "audio")) {
    }
    gst_element_remove_pad(element, pad);
}

static void gst_recordbin_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
{
    GstRecordBin* recordbin = GST_RECORDBIN(object);
}

static void gst_recordbin_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
{
    GstRecordBin* recordbin = GST_RECORDBIN(object);
}

static gboolean recordbin_plugin_init(GstPlugin* plugin)
{
    GST_DEBUG_CATEGORY_INIT(gst_recordbin_debug, PLUGIN_NAME, 0, "recordbin plugin");

    return gst_element_register(plugin, PLUGIN_NAME, GST_RANK_PRIMARY, GST_TYPE_RECORDBIN);
}

GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    GstRecord,
    DESCRIPTION, recordbin_plugin_init, "1.0", LICENSE, BINARY_PACKAGE, URL)