Add support for audio/video rendering pipelines 79/244179/8
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 15 Sep 2020 13:31:14 +0000 (22:31 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 24 Sep 2020 07:11:42 +0000 (07:11 +0000)
Multiple rendering pipelines can be added.
The decodebin is used to make each audio/video rendering pipeline.
These will be triggered by webrtcbin based on the session description
from remote peer during the negotiation.

[Version] 0.1.26
[Issue Type] Improvement

Change-Id: Iaade731f695181b8fe1a2a9aafa299c73feb4d32
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc_private.c

index 02294c99ff3f20b004f055c59f9c379f22f566fb..68e66c53ac2b9382e76171ea56300b1d67c03587 100644 (file)
@@ -113,7 +113,6 @@ typedef struct _webrtc_ini_s {
 } webrtc_ini_s;
 
 typedef struct _webrtc_gst_slot_s {
-       webrtc_media_source_type_e type;
        unsigned int id;
        GstElement *bin;
 } webrtc_gst_slot_s;
@@ -124,6 +123,7 @@ typedef struct _webrtc_gst_s {
        GstBus *bus;
        guint bus_watcher;
        GHashTable *source_slots;
+       GHashTable *sink_slots;
 } webrtc_gst_s;
 
 typedef struct _webrtc_callbacks {
index 901854e8fe8835f24e5a309e4524a9542cc71225..b7384180f4994e4d9d68845af8848cd0df5f723a 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.25
+Version:    0.1.26
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 0ff7c96201dbcaac36f6c4bbb7da9196eb76c747..98d0aa42df548b5e90124f3f18792aa4a55e3e1f 100644 (file)
 #define DEFAULT_ELEMENT_AUDIOTESTSRC  "audiotestsrc"
 #define DEFAULT_ELEMENT_CAPSFILTER    "capsfilter"
 #define DEFAULT_ELEMENT_QUEUE         "queue"
+#define DEFAULT_ELEMENT_VIDEOCONVERT  "videoconvert"
+#define DEFAULT_ELEMENT_AUDIOCONVERT  "audioconvert"
+#define DEFAULT_ELEMENT_AUDIORESAMPLE "audioresample"
+#define DEFAULT_ELEMENT_VIDEOSINK     "tizenwlsink"
+#define DEFAULT_ELEMENT_AUDIOSINK     "pulsesink"
 
 #define DEFAULT_VIDEO_ENCODED_MEDIA_TYPE  "video/x-vp8"
 #define DEFAULT_VIDEO_RAW_MEDIA_TYPE      "video/x-raw"
@@ -384,7 +389,7 @@ static void __value_destroy_cb(gpointer data)
 
        RET_IF(data == NULL, "data is NULL");
 
-       LOG_DEBUG("[%s, type:%u, id:%u] is removed", GST_ELEMENT_NAME(source->bin), source->type, source->id);
+       LOG_DEBUG("[%s, id:%u] is removed", GST_ELEMENT_NAME(source->bin), source->id);
 
        /* FIXME: do unlink */
 
@@ -692,6 +697,350 @@ static void __webrtcbin_ice_connection_state_cb(GstElement *webrtcbin, GParamSpe
        LOG_DEBUG("[IceConnectionState] is changed to [%s]", new_state);
 }
 
+static webrtc_gst_slot_s* __find_sink_slot(webrtc_s *webrtc, const gchar *key)
+{
+       RET_VAL_IF(webrtc == NULL, NULL, "webrtc is NULL");
+       RET_VAL_IF(key == NULL, NULL, "key is NULL");
+
+       return g_hash_table_lookup(webrtc->gst.sink_slots, key);
+}
+
+static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
+{
+       webrtc_gst_slot_s *sink;
+       GstElement *videoconvert;
+       GstElement *videosink;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
+       RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+       sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
+       RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
+       RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       if (!(videoconvert = __create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) {
+               LOG_ERROR("failed to create videoconvert");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       /* FIXME: get factory name from ini */
+       if (!(videosink = __create_element(DEFAULT_ELEMENT_VIDEOSINK, NULL))) {
+               LOG_ERROR("failed to create videosink");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       gst_bin_add_many(GST_BIN(sink->bin), videoconvert, videosink, NULL);
+
+       if (!gst_element_sync_state_with_parent(videoconvert)) {
+               LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videoconvert));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       if (!gst_element_sync_state_with_parent(videosink)) {
+               LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videosink));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!gst_element_link_many(decodebin, videoconvert, videosink, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __build_audiosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad)
+{
+       webrtc_gst_slot_s *sink;
+       GstElement *audioconvert;
+       GstElement *audioresample;
+       GstElement *audiosink;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL");
+       RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+       sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin));
+       RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin));
+       RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       if (!(audioconvert = __create_element(DEFAULT_ELEMENT_AUDIOCONVERT, NULL))) {
+               LOG_ERROR("failed to create audioconvert");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!(audioresample = __create_element(DEFAULT_ELEMENT_AUDIORESAMPLE, NULL))) {
+               LOG_ERROR("failed to create audioresample");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       /* FIXME: get factory name from ini */
+       if (!(audiosink = __create_element(DEFAULT_ELEMENT_AUDIOSINK, NULL))) {
+               LOG_ERROR("failed to create audiosink");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       gst_bin_add_many(GST_BIN(sink->bin), audioconvert, audioresample, audiosink, NULL);
+
+       if (!gst_element_sync_state_with_parent(audioconvert)) {
+               LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioconvert));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       if (!gst_element_sync_state_with_parent(audioresample)) {
+               LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioresample));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       if (!gst_element_sync_state_with_parent(audiosink)) {
+               LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audiosink));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!gst_element_link_many(decodebin, audioconvert, audioresample, audiosink, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+       const gchar *media_type;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
+               return;
+
+       media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0));
+       LOG_INFO("[%s], new_pad[%s], media_type[%s]", GST_ELEMENT_NAME(decodebin), GST_PAD_NAME(new_pad), media_type);
+
+       if (g_strrstr(media_type, "video")) {
+               ret = __build_videosink(webrtc, decodebin, new_pad);
+
+       } else if (g_strrstr(media_type, "audio")) {
+               ret = __build_audiosink(webrtc, decodebin, new_pad);
+
+       } else {
+               LOG_ERROR("not supported media type[%s]", media_type);
+               return;
+       }
+
+       if (ret != WEBRTC_ERROR_NONE)
+               LOG_ERROR("failed to build a rendering pipeline");
+}
+
+static int __decodebin_autoplug_select_cb(GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, gpointer user_data)
+{
+       /* NOTE : GstAutoplugSelectResult is defined in gstplay-enum.h but not exposed */
+       typedef enum {
+               GST_AUTOPLUG_SELECT_TRY,
+               GST_AUTOPLUG_SELECT_EXPOSE,
+               GST_AUTOPLUG_SELECT_SKIP
+       } GstAutoplugSelectResult;
+       gchar *factory_name;
+       const gchar *klass;
+       GstAutoplugSelectResult result = GST_AUTOPLUG_SELECT_TRY;
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+
+       RET_VAL_IF(webrtc == NULL, GST_AUTOPLUG_SELECT_SKIP, "webrtc is NULL, skip it");
+
+       factory_name = GST_OBJECT_NAME(factory);
+       klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS);
+
+       LOG_INFO("factory [name:%s, klass:%s]", factory_name, klass);
+
+       return result;
+}
+
+static unsigned int __get_id_from_pad_name(const gchar* name)
+{
+       gchar **tokens = NULL;
+       gint64 id;
+
+       RET_VAL_IF(name == NULL, 0, "name is NULL");
+
+       tokens = g_strsplit(name, "_", 2);
+       id = g_ascii_strtoll(tokens[1], NULL, 10);
+
+       g_strfreev(tokens);
+
+       return (unsigned int)id;
+}
+
+static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name, bool is_src)
+{
+       gchar *bin_name = NULL;
+       GstPad *ghost_pad = NULL;
+
+       RET_VAL_IF(bin == NULL, NULL, "bin is NULL");
+       RET_VAL_IF(pad_name == NULL, NULL, "pad_name is NULL");
+
+       if (!(ghost_pad = gst_ghost_pad_new_no_target(pad_name, is_src ? GST_PAD_SRC : GST_PAD_SINK))) {
+               LOG_ERROR("failed to gst_ghost_pad_new_no_target()");
+               return NULL;
+       }
+
+       bin_name = gst_element_get_name(bin);
+       if (gst_element_add_pad(GST_ELEMENT(bin), ghost_pad)) {
+               gst_pad_set_active(ghost_pad, TRUE);
+               LOG_DEBUG("added [%s] empty ghostpad into [%s]", pad_name, bin_name);
+       } else {
+               LOG_ERROR("failed to add empty [%s] ghostpad into [%s]", pad_name, bin_name);
+               g_object_unref(ghost_pad);
+               ghost_pad = NULL;
+       }
+
+       g_free(bin_name);
+
+       return ghost_pad;
+}
+
+static int __add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad)
+{
+       gchar *pad_name;
+
+       RET_VAL_IF(slot == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "slot is NULL");
+       RET_VAL_IF(slot->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+       RET_VAL_IF(new_pad == NULL, WEBRTC_ERROR_INVALID_OPERATION, "new_pad is NULL");
+
+       pad_name = g_strdup_printf("%s_%u", is_src ? "src" : "sink", slot->id);
+       if (!(*new_pad =__add_no_target_ghostpad(slot->bin, pad_name, is_src))) {
+               LOG_ERROR("failed to add new ghost pad[%s] for bin[%s]", pad_name, GST_ELEMENT_NAME(slot->bin));
+               g_free(pad_name);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       g_free(pad_name);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
+{
+       GstPad *target_pad;
+
+       RET_VAL_IF(ghost_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_pad is NULL");
+       RET_VAL_IF(target_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "target_element is NULL");
+
+       if (!(target_pad = gst_element_get_static_pad(target_element, is_src ? "src" : "sink"))) {
+               LOG_ERROR("failed to gst_element_get_static_pad() of [%s]", GST_ELEMENT_NAME(target_element));
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad), target_pad)) {
+               LOG_ERROR("failed to gst_ghost_pad_set_target(), ghostpad[%s] -> targetpad[%s]", GST_PAD_NAME(ghost_pad), GST_PAD_NAME(target_pad));
+               gst_object_unref(target_pad);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       gst_object_unref(target_pad);
+
+       LOG_DEBUG("ghostpad[%s] -> target[%s:%s]", GST_PAD_NAME(ghost_pad), GST_ELEMENT_NAME(target_element), GST_PAD_NAME(target_pad));
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       unsigned int id;
+       gchar *bin_name;
+       gchar *decodebin_name;
+       webrtc_gst_slot_s *sink;
+       GstElement *decodebin;
+       GstPad *sink_pad;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL");
+
+       /* decodebin_name/sink will be freed by function which is set to g_hash_table_new_full() */
+       id = __get_id_from_pad_name(GST_PAD_NAME(src_pad));
+       bin_name = g_strdup_printf("rendering_sink_%u", id);
+       decodebin_name = g_strdup_printf("%s_decodebin", bin_name);
+
+       sink = g_new0(webrtc_gst_slot_s, 1);
+       sink->id = id;
+       sink->bin = gst_bin_new(bin_name);
+       g_free(bin_name);
+
+       decodebin = __create_element("decodebin", decodebin_name);
+       if (!decodebin)
+               goto error_before_insert;
+
+       gst_bin_add(GST_BIN(sink->bin), decodebin);
+
+       g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc);
+       g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__decodebin_autoplug_select_cb), webrtc);
+
+       ret = __add_no_target_ghostpad_to_slot(sink, FALSE, &sink_pad);
+       if (ret != WEBRTC_ERROR_NONE)
+               goto error_before_insert;
+
+       ret = __set_ghost_pad_target(sink_pad, decodebin, FALSE);
+       if (ret != WEBRTC_ERROR_NONE)
+               goto error_before_insert;
+
+       if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), sink->bin)) {
+               LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(sink->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
+               goto error_before_insert;
+       }
+
+       if (gst_pad_link(src_pad, sink_pad) != GST_PAD_LINK_OK) {
+               LOG_ERROR("failed to gst_pad_link(), %s:%s", GST_PAD_NAME(src_pad), GST_PAD_NAME(sink_pad));
+               goto error_before_insert;
+       }
+
+       LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
+               GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_PAD_NAME(src_pad), GST_ELEMENT_NAME(sink->bin), GST_PAD_NAME(sink_pad));
+
+       if (!g_hash_table_insert(webrtc->gst.sink_slots, decodebin_name, (gpointer)sink)) {
+               LOG_ERROR("should not be reached here, bin_name[%s] already exist, sink id[%u] will be removed", decodebin_name, sink->id);
+               g_hash_table_remove(webrtc->gst.sink_slots, decodebin_name);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       gst_element_sync_state_with_parent(sink->bin);
+
+       LOG_INFO("added a sink slot[%p, id:%u]", sink, sink->id);
+
+       return WEBRTC_ERROR_NONE;
+
+error_before_insert:
+       gst_object_unref(sink_pad);
+       g_free(decodebin_name);
+       g_free(sink);
+
+       return WEBRTC_ERROR_INVALID_OPERATION;
+}
+
+static void __webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
+               return;
+
+       LOG_INFO("new pad[%s] is added", GST_PAD_NAME(new_pad));
+
+       ret = __add_rendering_sink_bin(webrtc, new_pad);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "failed to __add_rendering_sink_bin()");
+}
+
+static void __webrtcbin_no_more_pads_cb(GstElement *webrtcbin, gpointer user_data)
+{
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+
+       RET_IF(webrtcbin == NULL, "webrtcbin is NULL");
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       LOG_DEBUG_LEAVE();
+}
+
 int _gst_build_pipeline(webrtc_s *webrtc)
 {
        RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
@@ -719,6 +1068,8 @@ int _gst_build_pipeline(webrtc_s *webrtc)
        __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::signaling-state", G_CALLBACK(__webrtcbin_signaling_state_cb), webrtc);
        __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-gathering-state", G_CALLBACK(__webrtcbin_ice_gathering_state_cb), webrtc);
        __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-connection-state", G_CALLBACK(__webrtcbin_ice_connection_state_cb), webrtc);
+       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "pad-added", G_CALLBACK(__webrtcbin_pad_added_cb), webrtc);
+       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "no-more-pads", G_CALLBACK(__webrtcbin_no_more_pads_cb), webrtc);
 
        if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), webrtc->gst.webrtcbin)) {
                LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
@@ -726,6 +1077,7 @@ int _gst_build_pipeline(webrtc_s *webrtc)
        }
 
        webrtc->gst.source_slots = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __value_destroy_cb);
+       webrtc->gst.sink_slots = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __value_destroy_cb);
 
        return WEBRTC_ERROR_NONE;
 
@@ -739,6 +1091,10 @@ void _gst_destroy_pipeline(webrtc_s *webrtc)
        if (!webrtc)
                return;
 
+       if (webrtc->gst.sink_slots) {
+               g_hash_table_destroy(webrtc->gst.sink_slots);
+               webrtc->gst.sink_slots = NULL;
+       }
        if (webrtc->gst.source_slots) {
                g_hash_table_destroy(webrtc->gst.source_slots);
                webrtc->gst.source_slots = NULL;
@@ -908,35 +1264,9 @@ static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id)
        return caps;
 }
 
-static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
-{
-       GstPad *target_pad;
-
-       RET_VAL_IF(ghost_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_pad is NULL");
-       RET_VAL_IF(target_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "target_element is NULL");
-
-       if (!(target_pad = gst_element_get_static_pad(target_element, is_src ? "src" : "sink"))) {
-               LOG_ERROR("failed to gst_element_get_static_pad()");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad), target_pad)) {
-               LOG_ERROR("failed to gst_ghost_pad_set_target(), ghostpad[%s] -> targetpad[%s]", GST_PAD_NAME(ghost_pad), GST_PAD_NAME(target_pad));
-               gst_object_unref(target_pad);
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       gst_object_unref(target_pad);
-
-       LOG_DEBUG("ghostpad[%s] -> target[%s:%s]", GST_PAD_NAME(ghost_pad), GST_ELEMENT_NAME(target_element), GST_PAD_NAME(target_pad));
-
-       return WEBRTC_ERROR_NONE;
-}
-
-static int __create_rest_of_elements(webrtc_gst_slot_s *source, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2)
+static int __create_rest_of_elements(webrtc_gst_slot_s *source, webrtc_media_source_type_e type, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2)
 {
        GstCaps *sink_caps;
-       webrtc_media_source_type_e type;
        element_info_s elem_info;
        const gchar *encoder_klass_name;
        gchar *media_type;
@@ -948,8 +1278,6 @@ static int __create_rest_of_elements(webrtc_gst_slot_s *source, GstElement **cap
        RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL");
        RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL");
 
-       type = source->type;
-
        if (!(*capsfilter = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
                LOG_ERROR("failed to create capsfilter");
                return WEBRTC_ERROR_INVALID_OPERATION;
@@ -1016,7 +1344,7 @@ static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
        }
        /* FIXME: set camera default setting from ini */
 
-       if ((ret = __create_rest_of_elements(source, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+       if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
                return ret;
 
        gst_bin_add_many(GST_BIN(source->bin), camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
@@ -1047,7 +1375,7 @@ static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
                return WEBRTC_ERROR_INVALID_OPERATION;
        }
 
-       if ((ret = __create_rest_of_elements(source, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+       if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_MIC, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
                return ret;
 
        gst_bin_add_many(GST_BIN(source->bin), audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
@@ -1079,7 +1407,7 @@ static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad
        }
        g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL);
 
-       if ((ret = __create_rest_of_elements(source, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+       if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
                return ret;
 
        gst_bin_add_many(GST_BIN(source->bin), videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL);
@@ -1111,7 +1439,7 @@ static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad
        }
        g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL);
 
-       if ((ret = __create_rest_of_elements(source, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
+       if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE)
                return ret;
 
        gst_bin_add_many(GST_BIN(source->bin), audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL);
@@ -1123,51 +1451,18 @@ static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad
        return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
 }
 
-static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name)
-{
-       gchar *bin_name = NULL;
-       GstPad *ghost_pad = NULL;
-
-       RET_VAL_IF(bin == NULL, NULL, "bin is NULL");
-       RET_VAL_IF(pad_name == NULL, NULL, "pad_name is NULL");
-
-       if (!(ghost_pad = gst_ghost_pad_new_no_target(pad_name, GST_PAD_SRC))) {
-               LOG_ERROR("failed to gst_ghost_pad_new_no_target()");
-               return NULL;
-       }
-
-       bin_name = gst_element_get_name(bin);
-       if (gst_element_add_pad(GST_ELEMENT(bin), ghost_pad)) {
-               gst_pad_set_active(ghost_pad, TRUE);
-               LOG_DEBUG("added [%s] empty ghostpad into [%s]", pad_name, bin_name);
-       } else {
-               LOG_ERROR("failed to add empty [%s] ghostpad into [%s]", pad_name, bin_name);
-               g_object_unref(ghost_pad);
-               ghost_pad = NULL;
-       }
-
-       g_free(bin_name);
-
-       return ghost_pad;
-}
-
-static int __build_source_bin(webrtc_gst_slot_s *source)
+static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type)
 {
-       GstPad *src_pad = NULL;
-       gchar *pad_name;
+       int ret = WEBRTC_ERROR_NONE;
+       GstPad *src_pad;
 
        RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
        RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
 
-       pad_name = g_strdup_printf("src_%u", source->id);
-       if (!(src_pad =__add_no_target_ghostpad(source->bin, pad_name))) {
-               LOG_ERROR("failed to add new ghost source pad for source bin");
-               g_free(pad_name);
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       g_free(pad_name);
+       ret = __add_no_target_ghostpad_to_slot(source, TRUE, &src_pad);
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to __add_no_target_ghostpad_to_slot()");
 
-       switch (source->type) {
+       switch (type) {
        case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
                return __build_camerasrc(source, src_pad);
 
@@ -1181,7 +1476,7 @@ static int __build_source_bin(webrtc_gst_slot_s *source)
                return __build_audiotestsrc(source, src_pad);
 
        default:
-               LOG_ERROR_IF_REACHED("type(%d)", source->type);
+               LOG_ERROR_IF_REACHED("type(%d)", type);
                return WEBRTC_ERROR_INVALID_PARAMETER;
        }
 
@@ -1232,12 +1527,10 @@ int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigne
 
        bin_name = g_strdup_printf("media_source_%u", id);
        source = g_new0(webrtc_gst_slot_s, 1);
-
-       source->type = type;
        source->id = id;
        source->bin = gst_bin_new(bin_name);
 
-       ret = __build_source_bin(source);
+       ret = __build_source_bin(source, type);
        if (ret != WEBRTC_ERROR_NONE) {
                LOG_ERROR("failed to __build_source_bin()");
                goto error_before_insert;
@@ -1274,7 +1567,7 @@ int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigne
 
        *source_id = id;
 
-       LOG_INFO("type[%d] source_id[%u] source[%p]", type, *source_id, source);
+       LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
 
        g_free(bin_srcpad_name);