Split webrtc_private.c 46/244946/3
authorSangchul Lee <sc11.lee@samsung.com>
Mon, 28 Sep 2020 07:44:42 +0000 (16:44 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 8 Oct 2020 07:08:30 +0000 (16:08 +0900)
webrtc_source.c is added and codes regarding media source are moved
into it.

webrtc_sink.c is added and codes regarding rendering audio and video
are moved into it.

[Version] 0.1.34
[Issue Type] Refactoring

Change-Id: I5835ddbc832386151cb537388da503714c44f64d
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc_private.c
src/webrtc_sink.c [new file with mode: 0644]
src/webrtc_source.c [new file with mode: 0644]

index 3205b45220f3d87c08d5ddd7ddbe7ffb48da5512..9ed043b25581f1199ef331ad54dde60bf7862b10 100644 (file)
@@ -107,6 +107,41 @@ do { \
 #define SAFE_FREE(src)    { if (src) { free(src); src = NULL; } }
 #define SAFE_STR(str)     (str) ? str : "null"
 
+#define CREATE_ELEMENT_FROM_REGISTRY(x_elem_info, x_klass_name, x_sink_caps, x_src_caps, x_element) \
+do { \
+       x_elem_info.klass_name = x_klass_name; \
+       x_elem_info.sink_caps = x_sink_caps; \
+       x_elem_info.src_caps = x_src_caps; \
+       x_element = _create_element_from_registry(&x_elem_info); \
+       if (x_elem_info.sink_caps) \
+               gst_caps_unref(x_elem_info.sink_caps); \
+       if (x_elem_info.src_caps) \
+               gst_caps_unref(x_elem_info.src_caps); \
+       if (!x_element) { \
+               LOG_ERROR("failed to create element of [%s]", x_klass_name); \
+               return WEBRTC_ERROR_INVALID_OPERATION; \
+       } \
+} while (0)
+
+#define MALLOC_AND_INIT_SLOT(x_slot, x_id, x_bin_name) \
+do { \
+       x_slot = g_new0(webrtc_gst_slot_s, 1); \
+       x_slot->id = x_id; \
+       x_slot->bin = gst_bin_new(x_bin_name); \
+       x_slot->mlines[MLINES_IDX_AUDIO] = -1; \
+       x_slot->mlines[MLINES_IDX_VIDEO] = -1; \
+} while (0)
+
+#define GENERATE_DOT(x_webrtc, x_fmt, x_arg...) \
+do { \
+       gchar *dot_name; \
+       if (!x_webrtc->ini.generate_dot) \
+               break; \
+       dot_name = g_strdup_printf(""x_fmt"", x_arg); \
+       _generate_dot(x_webrtc, dot_name); \
+       g_free(dot_name); \
+} while (0)
+
 typedef struct _webrtc_ini_s {
        gboolean generate_dot;
        gchar **gst_args;
@@ -186,6 +221,13 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id);
 int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction);
 int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction);
 
+GstElement *_create_element(const char *factory_name, const char *name);
+GstElement *_create_element_from_registry(element_info_s *elem_info);
+int _add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad);
+int _set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src);
+int _add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad);
+void _generate_dot(webrtc_s *webrtc, const gchar *name);
+
 int _webrtcbin_create_offer(webrtc_s *webrtc, char **offer);
 int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer);
 int _webrtcbin_set_session_description(webrtc_s *webrtc, const char *description, gboolean is_remote);
index c0be7d28a2fc546129ae5ccae686aa3bfd2bac3c..0ed53a9d582cae8e4fc341b753541b48ad0dd2e2 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.33
+Version:    0.1.34
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index eb0efbb021b5f4c25a1d5911cd8bb58d793c6278..e66b2e0b0a4a9d186b0e7282378891885f371881 100644 (file)
 #include "webrtc.h"
 #include "webrtc_private.h"
 
-#define GST_KLASS_NAME_ENCODER_AUDIO   "Codec/Encoder/Audio"
-#define GST_KLASS_NAME_ENCODER_VIDEO   "Codec/Encoder/Video"
-#define GST_KLASS_NAME_DECODER_AUDIO   "Codec/Decoder/Audio"
-#define GST_KLASS_NAME_DECODER_VIDEO   "Codec/Decoder/Video"
-#define GST_KLASS_NAME_PAYLOADER_RTP   "Codec/Payloader/Network/RTP"
-#define GST_KLASS_NAME_DEPAYLOADER_RTP "Codec/Depayloader/Network/RTP"
-#define GST_KLASS_NAME_CONVERTER_AUDIO "Filter/Converter/Audio"
-#define GST_KLASS_NAME_CONVERTER_VIDEO "Filter/Converter/Video"
-
-#define DEFAULT_ELEMENT_CAMERASRC     "camerasrc"
-#define DEFAULT_ELEMENT_AUDIOSRC      "pulsesrc"
-#define DEFAULT_ELEMENT_VIDEOTESTSRC  "videotestsrc"
-#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"
-#define DEFAULT_VIDEO_RAW_FORMAT          "I420"
-#define DEFAULT_VIDEO_WIDTH               352
-#define DEFAULT_VIDEO_HEIGHT              288
-
-#define DEFAULT_AUDIO_ENCODED_MEDIA_TYPE  "audio/x-opus"
-#define DEFAULT_AUDIO_RAW_MEDIA_TYPE      "audio/x-raw"
-#define DEFAULT_AUDIO_RAW_FORMAT          "S16LE"
-#define DEFAULT_AUDIO_CHANNELS            1
-#define DEFAULT_AUDIO_SAMPLERATE          8000
-
 #define DEFAULT_DOT_FILE_NAME_PREFIX      "webrtc"
 #define DEFAULT_DOT_DIRECTORY             "/tmp"
 
-typedef enum {
-       CODEC_TYPE_OPUS,
-       CODEC_TYPE_VORBIS,
-       CODEC_TYPE_VP8,
-       CODEC_TYPE_VP9,
-       CODEC_TYPE_THEORA,
-       CODEC_TYPE_H263,
-       CODEC_TYPE_H264,
-       CODEC_TYPE_H265,
-       CODEC_TYPE_NOT_SUPPORTED,
-} codec_type_e;
-
-typedef struct {
-       const char *encoding_name;
-       const int clock_rate;
-} payload_type_s;
-
-static payload_type_s payload_types[] = {
-       /* AUDIO */
-       [CODEC_TYPE_OPUS] = { "OPUS", 48000 },
-       [CODEC_TYPE_VORBIS] = { "VORBIS", -1 }, /* NOTE: -1 for various clock rate */
-       /* VIDEO */
-       [CODEC_TYPE_VP8] = { "VP8", 90000 },
-       [CODEC_TYPE_VP9] = { "VP9", 90000 },
-       [CODEC_TYPE_THEORA] = { "THEORA", 90000 },
-       [CODEC_TYPE_H263] = { "H263", 90000 },
-       [CODEC_TYPE_H264] = { "H264", 90000 },
-       [CODEC_TYPE_H265] = { "H265", 90000 },
-};
-
-#define CREATE_ELEMENT_FROM_REGISTRY(x_elem_info, x_klass_name, x_sink_caps, x_src_caps, x_element) \
-do { \
-       x_elem_info.klass_name = x_klass_name; \
-       x_elem_info.sink_caps = x_sink_caps; \
-       x_elem_info.src_caps = x_src_caps; \
-       x_element = __create_element_from_registry(&x_elem_info); \
-       if (x_elem_info.sink_caps) \
-               gst_caps_unref(x_elem_info.sink_caps); \
-       if (x_elem_info.src_caps) \
-               gst_caps_unref(x_elem_info.src_caps); \
-       if (!x_element) { \
-               LOG_ERROR("failed to create element of [%s]", x_klass_name); \
-               return WEBRTC_ERROR_INVALID_OPERATION; \
-       } \
-} while (0)
-
-#define MALLOC_AND_INIT_SLOT(x_slot, x_id, x_bin_name) \
-do { \
-       x_slot = g_new0(webrtc_gst_slot_s, 1); \
-       x_slot->id = x_id; \
-       x_slot->bin = gst_bin_new(x_bin_name); \
-       x_slot->mlines[MLINES_IDX_AUDIO] = -1; \
-       x_slot->mlines[MLINES_IDX_VIDEO] = -1; \
-} while (0)
-
-#define GENERATE_DOT(x_webrtc, x_fmt, x_arg...) \
-do { \
-       gchar *dot_name; \
-       if (!x_webrtc->ini.generate_dot) \
-               break; \
-       dot_name = g_strdup_printf(""x_fmt"", x_arg); \
-       __generate_dot(x_webrtc, dot_name); \
-       g_free(dot_name); \
-} while (0)
-
 static const char* __state_str[] = {
        "IDLE",
        "NEGOTIATING",
@@ -137,7 +39,7 @@ static GstWebRTCRTPTransceiverDirection __direction_gst[] = {
        GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
 };
 
-static void __generate_dot(webrtc_s *webrtc, const gchar *name)
+void _generate_dot(webrtc_s *webrtc, const gchar *name)
 {
        gchar *dot_name;
 
@@ -346,7 +248,7 @@ static gboolean __bus_watch_cb(GstBus *bus, GstMessage *message, gpointer user_d
        return TRUE;
 }
 
-static GstElement *__create_element(const char *factory_name, const char *name)
+GstElement *_create_element(const char *factory_name, const char *name)
 {
        GstElement *element = NULL;
 
@@ -412,7 +314,7 @@ static int __rank_compare(GstPluginFeature *first, GstPluginFeature *second)
        return second_rank - first_rank;
 }
 
-static GstElement *__create_element_from_registry(element_info_s *elem_info)
+GstElement *_create_element_from_registry(element_info_s *elem_info)
 {
        GstElement *element = NULL;
        GList *factories = NULL;
@@ -426,7 +328,7 @@ static GstElement *__create_element_from_registry(element_info_s *elem_info)
        if (factories) {
                factory = GST_ELEMENT_FACTORY(factories->data);
                LOG_DEBUG("sorted result element is [%s]", GST_OBJECT_NAME(factory));
-               element = __create_element(GST_OBJECT_NAME(factory), NULL);
+               element = _create_element(GST_OBJECT_NAME(factory), NULL);
        } else {
                LOG_DEBUG("could not find any compatible element for klass_name[%s]", elem_info->klass_name);
        }
@@ -759,185 +661,6 @@ 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");
-
-       sink->media_types |= MEDIA_TYPE_VIDEO;
-
-       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");
-
-       sink->media_types |= MEDIA_TYPE_AUDIO;
-
-       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");
-
-       GENERATE_DOT(webrtc, "%s", GST_ELEMENT_NAME(decodebin));
-}
-
-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;
@@ -966,7 +689,7 @@ static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name, b
        return ghost_pad;
 }
 
-static int __add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad)
+int _add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad)
 {
        gchar *pad_name;
 
@@ -985,7 +708,7 @@ static int __add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is
        return WEBRTC_ERROR_NONE;
 }
 
-static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
+int _set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src)
 {
        GstPad *target_pad;
 
@@ -1010,78 +733,6 @@ static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element,
        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);
-
-       MALLOC_AND_INIT_SLOT(sink, id, 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;
@@ -1094,8 +745,8 @@ static void __webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpo
 
        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()");
+       ret = _add_rendering_sink_bin(webrtc, new_pad);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "failed to _add_rendering_sink_bin()");
 
        GENERATE_DOT(webrtc, "webrtcbin_%s", GST_PAD_NAME(new_pad));
 }
@@ -1157,7 +808,7 @@ int _gst_build_pipeline(webrtc_s *webrtc)
                goto error;
        }
 
-       if (!(webrtc->gst.webrtcbin = __create_element("webrtcbin", NULL))) {
+       if (!(webrtc->gst.webrtcbin = _create_element("webrtcbin", NULL))) {
                LOG_ERROR("failed to create webrtcbin");
                goto error;
        }
@@ -1249,471 +900,6 @@ int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state)
        return WEBRTC_ERROR_NONE;
 }
 
-static GstCaps *__make_default_raw_caps(webrtc_media_source_type_e type)
-{
-       GstCaps *caps = NULL;
-
-       switch (type) {
-       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
-       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
-               /* FIXME: get default value from ini */
-               caps = gst_caps_new_simple(DEFAULT_VIDEO_RAW_MEDIA_TYPE,
-                                               "format", G_TYPE_STRING, DEFAULT_VIDEO_RAW_FORMAT,
-                                               "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
-                                               "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
-                                               NULL);
-               break;
-
-       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
-       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
-               /* FIXME: get default value from ini */
-               caps = gst_caps_new_simple(DEFAULT_AUDIO_RAW_MEDIA_TYPE,
-                                               "format", G_TYPE_STRING, DEFAULT_AUDIO_RAW_FORMAT,
-                                               "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
-                                               "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
-                                               NULL);
-               break;
-
-       default:
-               LOG_ERROR_IF_REACHED("type(%d)", type);
-               break;
-       }
-
-       return caps;
-}
-
-/* Use g_free() to free the media_type parameter. */
-static GstCaps *__make_default_encoded_caps(webrtc_media_source_type_e type, gchar **media_type)
-{
-       GstCaps *caps;
-       const char *_media_type;
-
-       switch (type) {
-       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
-       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
-               /* FIXME: get default value from ini */
-               _media_type = DEFAULT_VIDEO_ENCODED_MEDIA_TYPE;
-               caps = gst_caps_new_simple(_media_type,
-                                               "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
-                                               "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
-                                               NULL);
-               break;
-
-       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
-       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
-               /* FIXME: get default value from ini */
-               _media_type = DEFAULT_AUDIO_ENCODED_MEDIA_TYPE;
-               caps = gst_caps_new_simple(DEFAULT_AUDIO_ENCODED_MEDIA_TYPE,
-                                               "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
-                                               "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
-                                               NULL);
-               break;
-
-       default:
-               LOG_ERROR_IF_REACHED("type(%d)", type);
-               return NULL;
-       }
-
-       if (media_type)
-               *media_type = g_strdup(_media_type);
-
-       return caps;
-}
-
-static codec_type_e __get_codec_type(const gchar *media_type)
-{
-       if (!g_strcmp0(media_type, "audio/x-opus"))
-               return CODEC_TYPE_OPUS;
-       else if (!g_strcmp0(media_type, "audio/x-vorbis"))
-               return CODEC_TYPE_VORBIS;
-       else if (!g_strcmp0(media_type, "video/x-vp8"))
-               return CODEC_TYPE_VP8;
-       else if (!g_strcmp0(media_type, "video/x-vp9"))
-               return CODEC_TYPE_VP9;
-       else if (!g_strcmp0(media_type, "video/x-theora"))
-               return CODEC_TYPE_THEORA;
-       else if (!g_strcmp0(media_type, "video/x-h263"))
-               return CODEC_TYPE_H263;
-       else if (!g_strcmp0(media_type, "video/x-h264"))
-               return CODEC_TYPE_H264;
-       else if (!g_strcmp0(media_type, "video/x-h265"))
-               return CODEC_TYPE_H265;
-       else
-               return CODEC_TYPE_NOT_SUPPORTED;
-}
-
-static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id)
-{
-       gchar *caps_str;
-       GstCaps *caps;
-       codec_type_e codec_type = __get_codec_type(media_type);
-
-       RET_VAL_IF(codec_type == CODEC_TYPE_NOT_SUPPORTED, NULL, "media_type[%s] is not supported", media_type);
-
-       caps = gst_caps_new_simple("application/x-rtp",
-                               "media", G_TYPE_STRING, g_strrstr(media_type, "video") ? "video" : "audio",
-                               "clock-rate", G_TYPE_INT, payload_types[codec_type].clock_rate, /* FIXME: support various clock-rate */
-                               "encoding-name", G_TYPE_STRING, payload_types[codec_type].encoding_name,
-                               "payload", G_TYPE_INT, id + 95, NULL);
-
-       caps_str = gst_caps_to_string(caps);
-       LOG_DEBUG("RTP caps is created [%s]", caps_str);
-
-       g_free(caps_str);
-
-       return caps;
-}
-
-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;
-       element_info_s elem_info;
-       const gchar *encoder_klass_name;
-       gchar *media_type;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(capsfilter == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter is NULL");
-       RET_VAL_IF(encoder == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "encoder is NULL");
-       RET_VAL_IF(payloader == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payloader is NULL");
-       RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL");
-       RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL");
-
-       if (!(*capsfilter = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
-               LOG_ERROR("failed to create capsfilter");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       if ((sink_caps = __make_default_raw_caps(type))) {
-               g_object_set(G_OBJECT(*capsfilter), "caps", sink_caps, NULL);
-               gst_caps_unref(sink_caps);
-       }
-
-       if (type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA || type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST)
-               encoder_klass_name = GST_KLASS_NAME_ENCODER_VIDEO;
-       else
-               encoder_klass_name = GST_KLASS_NAME_ENCODER_AUDIO;
-
-       CREATE_ELEMENT_FROM_REGISTRY(elem_info, encoder_klass_name,
-                                               __make_default_raw_caps(type),
-                                               __make_default_encoded_caps(type, NULL),
-                                               *encoder);
-
-       CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP,
-                                               __make_default_encoded_caps(type, &media_type),
-                                               NULL,
-                                               *payloader);
-
-       if (!(*queue = __create_element(DEFAULT_ELEMENT_QUEUE, NULL))) {
-               LOG_ERROR("failed to create queue");
-               g_free(media_type);
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       if (!(*capsfilter2 = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
-               LOG_ERROR("failed to create capsfilter");
-               g_free(media_type);
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       if ((sink_caps = __make_rtp_caps(media_type, source->id))) {
-               g_object_set(G_OBJECT(*capsfilter2), "caps", sink_caps, NULL);
-               gst_caps_unref(sink_caps);
-       }
-
-       g_free(media_type);
-
-       return WEBRTC_ERROR_NONE;
-}
-
-static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       GstElement *camerasrc;
-       GstElement *capsfilter;
-       GstElement *videoenc;
-       GstElement *videopay;
-       GstElement *queue;
-       GstElement *capsfilter2;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
-       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
-       source->media_types |= MEDIA_TYPE_VIDEO;
-
-       /* FIXME: get factory name from ini */
-       if (!(camerasrc = __create_element(DEFAULT_ELEMENT_CAMERASRC, NULL))) {
-               LOG_ERROR("failed to create camerasrc");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       /* FIXME: set camera default setting from ini */
-
-       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);
-       if (!gst_element_link_many(camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
-               LOG_ERROR("failed to gst_element_link_many()");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       GstElement *audiosrc;
-       GstElement *capsfilter;
-       GstElement *audioenc;
-       GstElement *audiopay;
-       GstElement *queue;
-       GstElement *capsfilter2;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
-       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
-       source->media_types |= MEDIA_TYPE_AUDIO;
-
-       if (!(audiosrc = __create_element(DEFAULT_ELEMENT_AUDIOSRC, NULL))) {
-               LOG_ERROR("failed to create audiosrc");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       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);
-       if (!gst_element_link_many(audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
-               LOG_ERROR("failed to gst_element_link_many()");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       GstElement *videotestsrc;
-       GstElement *capsfilter;
-       GstElement *videoenc;
-       GstElement *videopay;
-       GstElement *queue;
-       GstElement *capsfilter2;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
-       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
-       source->media_types |= MEDIA_TYPE_VIDEO;
-
-       if (!(videotestsrc = __create_element(DEFAULT_ELEMENT_VIDEOTESTSRC, NULL))) {
-               LOG_ERROR("failed to create videotestsrc");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL);
-
-       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);
-       if (!gst_element_link_many(videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
-               LOG_ERROR("failed to gst_element_link_many()");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       GstElement *audiotestsrc;
-       GstElement *capsfilter;
-       GstElement *audioenc;
-       GstElement *audiopay;
-       GstElement *queue;
-       GstElement *capsfilter2;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
-       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
-
-       source->media_types |= MEDIA_TYPE_AUDIO;
-
-       if (!(audiotestsrc = __create_element(DEFAULT_ELEMENT_AUDIOTESTSRC, NULL))) {
-               LOG_ERROR("failed to create audiotestsrc");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-       g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL);
-
-       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);
-       if (!gst_element_link_many(audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
-               LOG_ERROR("failed to gst_element_link_many()");
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
-}
-
-static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type)
-{
-       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");
-
-       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 (type) {
-       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
-               return __build_camerasrc(source, src_pad);
-
-       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
-               return __build_audiosrc(source, src_pad);
-
-       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
-               return __build_videotestsrc(source, src_pad);
-
-       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
-               return __build_audiotestsrc(source, src_pad);
-
-       default:
-               LOG_ERROR_IF_REACHED("type(%d)", type);
-               return WEBRTC_ERROR_INVALID_PARAMETER;
-       }
-
-       return WEBRTC_ERROR_NONE;
-}
-
-static unsigned int __get_unoccupied_id(GHashTable *slots)
-{
-       int i;
-       gchar *key;
-
-       RET_VAL_IF(slots == NULL, 0, "slot is NULL");
-
-       /* Payload identifiers 96–127 are used for payloads defined dynamically during a session,
-        * hence the id range is limited here to 1-32. */
-       for (i = 1; i < 33; i++) {
-               key = g_strdup_printf("media_source_%u", i);
-               if (g_hash_table_contains(slots, key)) {
-                       g_free(key);
-                       continue;
-               }
-               g_free(key);
-               return i;
-       }
-
-       LOG_ERROR("all slots are occupied(1-32)");
-
-       return 0;
-}
-
-int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       unsigned int id;
-       webrtc_gst_slot_s *source = NULL;
-       gchar *bin_name = NULL;
-       GstPad *webrtc_sinkpad;
-       gchar *webrtc_sinkpad_name;
-       gchar *bin_srcpad_name = NULL;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
-       RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
-
-       /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
-       id = __get_unoccupied_id(webrtc->gst.source_slots);
-       RET_VAL_IF(id == 0, WEBRTC_ERROR_INVALID_OPERATION, "source_slots are full");
-
-       bin_name = g_strdup_printf("media_source_%u", id);
-
-       MALLOC_AND_INIT_SLOT(source, id, bin_name);
-
-       ret = __build_source_bin(source, type);
-       if (ret != WEBRTC_ERROR_NONE) {
-               LOG_ERROR("failed to __build_source_bin()");
-               goto error;
-       }
-
-       if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), source->bin)) {
-               LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
-               goto error;
-       }
-
-       /* The gst_element_get_request_pad() of webrtcbin will trigger the transciever callback. To update the mline value of
-        * new transceiver object to the source structure in the callback, hash table inserting should be preceded. */
-       if (!g_hash_table_insert(webrtc->gst.source_slots, bin_name, (gpointer)source)) {
-               LOG_ERROR("should not be reached here, bin_name[%s] already exist, source id[%u] will be removed", bin_name, source->id);
-               g_hash_table_remove(webrtc->gst.source_slots, bin_name);
-               return WEBRTC_ERROR_INVALID_OPERATION;
-       }
-
-       if (!(webrtc_sinkpad = gst_element_get_request_pad(webrtc->gst.webrtcbin, "sink_%u"))) {
-               LOG_ERROR("failed to gst_element_get_request_pad()");
-               goto error_after_insert;
-       }
-       if (!(webrtc_sinkpad_name = gst_pad_get_name(webrtc_sinkpad))) {
-               LOG_ERROR("failed to gst_pad_get_name()");
-               goto error_after_insert;
-       }
-       bin_srcpad_name = g_strdup_printf("src_%u", id);
-       if (!gst_element_link_pads(source->bin, bin_srcpad_name, webrtc->gst.webrtcbin, webrtc_sinkpad_name)) {
-               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
-                       GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
-               goto error_after_insert;
-       }
-       LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
-               GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
-
-       *source_id = id;
-
-       LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
-
-       g_free(bin_srcpad_name);
-
-       return WEBRTC_ERROR_NONE;
-
-error_after_insert:
-       g_hash_table_remove(webrtc->gst.source_slots, bin_name);
-       g_free(bin_srcpad_name);
-
-       return WEBRTC_ERROR_INVALID_OPERATION;
-
-error:
-       g_free(bin_name);
-       g_free(source);
-
-       return WEBRTC_ERROR_INVALID_OPERATION;
-}
-
-int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
-{
-       int ret = WEBRTC_ERROR_NONE;
-       gchar *bin_name = NULL;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
-
-       bin_name = g_strdup_printf("media_source_%u", source_id);
-
-       if (!g_hash_table_remove(webrtc->gst.source_slots, (gpointer)bin_name)) {
-               LOG_ERROR("failed to find media source by id[%u]", source_id);
-               ret = WEBRTC_ERROR_INVALID_PARAMETER;
-       }
-
-       g_free(bin_name);
-
-       return ret;
-}
-
 static gboolean __check_id_equal_cb(gpointer key, gpointer value, gpointer user_data)
 {
        webrtc_gst_slot_s *slot = value;
diff --git a/src/webrtc_sink.c b/src/webrtc_sink.c
new file mode 100644 (file)
index 0000000..5f07664
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc.h"
+#include "webrtc_private.h"
+
+#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"
+
+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");
+
+       sink->media_types |= MEDIA_TYPE_VIDEO;
+
+       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");
+
+       sink->media_types |= MEDIA_TYPE_AUDIO;
+
+       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");
+
+       GENERATE_DOT(webrtc, "%s", GST_ELEMENT_NAME(decodebin));
+}
+
+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;
+}
+
+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);
+
+       MALLOC_AND_INIT_SLOT(sink, id, 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;
+}
diff --git a/src/webrtc_source.c b/src/webrtc_source.c
new file mode 100644 (file)
index 0000000..b114684
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc.h"
+#include "webrtc_private.h"
+
+#define GST_KLASS_NAME_ENCODER_AUDIO   "Codec/Encoder/Audio"
+#define GST_KLASS_NAME_ENCODER_VIDEO   "Codec/Encoder/Video"
+#define GST_KLASS_NAME_DECODER_AUDIO   "Codec/Decoder/Audio"
+#define GST_KLASS_NAME_DECODER_VIDEO   "Codec/Decoder/Video"
+#define GST_KLASS_NAME_PAYLOADER_RTP   "Codec/Payloader/Network/RTP"
+#define GST_KLASS_NAME_DEPAYLOADER_RTP "Codec/Depayloader/Network/RTP"
+#define GST_KLASS_NAME_CONVERTER_AUDIO "Filter/Converter/Audio"
+#define GST_KLASS_NAME_CONVERTER_VIDEO "Filter/Converter/Video"
+
+#define DEFAULT_ELEMENT_CAMERASRC     "camerasrc"
+#define DEFAULT_ELEMENT_AUDIOSRC      "pulsesrc"
+#define DEFAULT_ELEMENT_VIDEOTESTSRC  "videotestsrc"
+#define DEFAULT_ELEMENT_AUDIOTESTSRC  "audiotestsrc"
+#define DEFAULT_ELEMENT_CAPSFILTER    "capsfilter"
+#define DEFAULT_ELEMENT_QUEUE         "queue"
+
+#define DEFAULT_VIDEO_ENCODED_MEDIA_TYPE  "video/x-vp8"
+#define DEFAULT_VIDEO_RAW_MEDIA_TYPE      "video/x-raw"
+#define DEFAULT_VIDEO_RAW_FORMAT          "I420"
+#define DEFAULT_VIDEO_WIDTH               352
+#define DEFAULT_VIDEO_HEIGHT              288
+
+#define DEFAULT_AUDIO_ENCODED_MEDIA_TYPE  "audio/x-opus"
+#define DEFAULT_AUDIO_RAW_MEDIA_TYPE      "audio/x-raw"
+#define DEFAULT_AUDIO_RAW_FORMAT          "S16LE"
+#define DEFAULT_AUDIO_CHANNELS            1
+#define DEFAULT_AUDIO_SAMPLERATE          8000
+
+typedef enum {
+       CODEC_TYPE_OPUS,
+       CODEC_TYPE_VORBIS,
+       CODEC_TYPE_VP8,
+       CODEC_TYPE_VP9,
+       CODEC_TYPE_THEORA,
+       CODEC_TYPE_H263,
+       CODEC_TYPE_H264,
+       CODEC_TYPE_H265,
+       CODEC_TYPE_NOT_SUPPORTED,
+} codec_type_e;
+
+typedef struct {
+       const char *encoding_name;
+       const int clock_rate;
+} payload_type_s;
+
+static payload_type_s payload_types[] = {
+       /* AUDIO */
+       [CODEC_TYPE_OPUS] = { "OPUS", 48000 },
+       [CODEC_TYPE_VORBIS] = { "VORBIS", -1 }, /* NOTE: -1 for various clock rate */
+       /* VIDEO */
+       [CODEC_TYPE_VP8] = { "VP8", 90000 },
+       [CODEC_TYPE_VP9] = { "VP9", 90000 },
+       [CODEC_TYPE_THEORA] = { "THEORA", 90000 },
+       [CODEC_TYPE_H263] = { "H263", 90000 },
+       [CODEC_TYPE_H264] = { "H264", 90000 },
+       [CODEC_TYPE_H265] = { "H265", 90000 },
+};
+
+static GstCaps *__make_default_raw_caps(webrtc_media_source_type_e type)
+{
+       GstCaps *caps = NULL;
+
+       switch (type) {
+       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+               /* FIXME: get default value from ini */
+               caps = gst_caps_new_simple(DEFAULT_VIDEO_RAW_MEDIA_TYPE,
+                                               "format", G_TYPE_STRING, DEFAULT_VIDEO_RAW_FORMAT,
+                                               "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
+                                               "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
+                                               NULL);
+               break;
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+               /* FIXME: get default value from ini */
+               caps = gst_caps_new_simple(DEFAULT_AUDIO_RAW_MEDIA_TYPE,
+                                               "format", G_TYPE_STRING, DEFAULT_AUDIO_RAW_FORMAT,
+                                               "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
+                                               "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
+                                               NULL);
+               break;
+
+       default:
+               LOG_ERROR_IF_REACHED("type(%d)", type);
+               break;
+       }
+
+       return caps;
+}
+
+/* Use g_free() to free the media_type parameter. */
+static GstCaps *__make_default_encoded_caps(webrtc_media_source_type_e type, gchar **media_type)
+{
+       GstCaps *caps;
+       const char *_media_type;
+
+       switch (type) {
+       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+               /* FIXME: get default value from ini */
+               _media_type = DEFAULT_VIDEO_ENCODED_MEDIA_TYPE;
+               caps = gst_caps_new_simple(_media_type,
+                                               "width", G_TYPE_INT, DEFAULT_VIDEO_WIDTH,
+                                               "height", G_TYPE_INT, DEFAULT_VIDEO_HEIGHT,
+                                               NULL);
+               break;
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+               /* FIXME: get default value from ini */
+               _media_type = DEFAULT_AUDIO_ENCODED_MEDIA_TYPE;
+               caps = gst_caps_new_simple(DEFAULT_AUDIO_ENCODED_MEDIA_TYPE,
+                                               "channels", G_TYPE_INT, DEFAULT_AUDIO_CHANNELS,
+                                               "rate", G_TYPE_INT, DEFAULT_AUDIO_SAMPLERATE,
+                                               NULL);
+               break;
+
+       default:
+               LOG_ERROR_IF_REACHED("type(%d)", type);
+               return NULL;
+       }
+
+       if (media_type)
+               *media_type = g_strdup(_media_type);
+
+       return caps;
+}
+
+static codec_type_e __get_codec_type(const gchar *media_type)
+{
+       if (!g_strcmp0(media_type, "audio/x-opus"))
+               return CODEC_TYPE_OPUS;
+       else if (!g_strcmp0(media_type, "audio/x-vorbis"))
+               return CODEC_TYPE_VORBIS;
+       else if (!g_strcmp0(media_type, "video/x-vp8"))
+               return CODEC_TYPE_VP8;
+       else if (!g_strcmp0(media_type, "video/x-vp9"))
+               return CODEC_TYPE_VP9;
+       else if (!g_strcmp0(media_type, "video/x-theora"))
+               return CODEC_TYPE_THEORA;
+       else if (!g_strcmp0(media_type, "video/x-h263"))
+               return CODEC_TYPE_H263;
+       else if (!g_strcmp0(media_type, "video/x-h264"))
+               return CODEC_TYPE_H264;
+       else if (!g_strcmp0(media_type, "video/x-h265"))
+               return CODEC_TYPE_H265;
+       else
+               return CODEC_TYPE_NOT_SUPPORTED;
+}
+
+static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id)
+{
+       gchar *caps_str;
+       GstCaps *caps;
+       codec_type_e codec_type = __get_codec_type(media_type);
+
+       RET_VAL_IF(codec_type == CODEC_TYPE_NOT_SUPPORTED, NULL, "media_type[%s] is not supported", media_type);
+
+       caps = gst_caps_new_simple("application/x-rtp",
+                               "media", G_TYPE_STRING, g_strrstr(media_type, "video") ? "video" : "audio",
+                               "clock-rate", G_TYPE_INT, payload_types[codec_type].clock_rate, /* FIXME: support various clock-rate */
+                               "encoding-name", G_TYPE_STRING, payload_types[codec_type].encoding_name,
+                               "payload", G_TYPE_INT, id + 95, NULL);
+
+       caps_str = gst_caps_to_string(caps);
+       LOG_DEBUG("RTP caps is created [%s]", caps_str);
+
+       g_free(caps_str);
+
+       return caps;
+}
+
+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;
+       element_info_s elem_info;
+       const gchar *encoder_klass_name;
+       gchar *media_type;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(capsfilter == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter is NULL");
+       RET_VAL_IF(encoder == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "encoder is NULL");
+       RET_VAL_IF(payloader == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payloader is NULL");
+       RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL");
+       RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL");
+
+       if (!(*capsfilter = _create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
+               LOG_ERROR("failed to create capsfilter");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       if ((sink_caps = __make_default_raw_caps(type))) {
+               g_object_set(G_OBJECT(*capsfilter), "caps", sink_caps, NULL);
+               gst_caps_unref(sink_caps);
+       }
+
+       if (type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA || type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST)
+               encoder_klass_name = GST_KLASS_NAME_ENCODER_VIDEO;
+       else
+               encoder_klass_name = GST_KLASS_NAME_ENCODER_AUDIO;
+
+       CREATE_ELEMENT_FROM_REGISTRY(elem_info, encoder_klass_name,
+                                               __make_default_raw_caps(type),
+                                               __make_default_encoded_caps(type, NULL),
+                                               *encoder);
+
+       CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP,
+                                               __make_default_encoded_caps(type, &media_type),
+                                               NULL,
+                                               *payloader);
+
+       if (!(*queue = _create_element(DEFAULT_ELEMENT_QUEUE, NULL))) {
+               LOG_ERROR("failed to create queue");
+               g_free(media_type);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!(*capsfilter2 = _create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) {
+               LOG_ERROR("failed to create capsfilter");
+               g_free(media_type);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       if ((sink_caps = __make_rtp_caps(media_type, source->id))) {
+               g_object_set(G_OBJECT(*capsfilter2), "caps", sink_caps, NULL);
+               gst_caps_unref(sink_caps);
+       }
+
+       g_free(media_type);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *camerasrc;
+       GstElement *capsfilter;
+       GstElement *videoenc;
+       GstElement *videopay;
+       GstElement *queue;
+       GstElement *capsfilter2;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       source->media_types |= MEDIA_TYPE_VIDEO;
+
+       /* FIXME: get factory name from ini */
+       if (!(camerasrc = _create_element(DEFAULT_ELEMENT_CAMERASRC, NULL))) {
+               LOG_ERROR("failed to create camerasrc");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       /* FIXME: set camera default setting from ini */
+
+       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);
+       if (!gst_element_link_many(camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *audiosrc;
+       GstElement *capsfilter;
+       GstElement *audioenc;
+       GstElement *audiopay;
+       GstElement *queue;
+       GstElement *capsfilter2;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       source->media_types |= MEDIA_TYPE_AUDIO;
+
+       if (!(audiosrc = _create_element(DEFAULT_ELEMENT_AUDIOSRC, NULL))) {
+               LOG_ERROR("failed to create audiosrc");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       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);
+       if (!gst_element_link_many(audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *videotestsrc;
+       GstElement *capsfilter;
+       GstElement *videoenc;
+       GstElement *videopay;
+       GstElement *queue;
+       GstElement *capsfilter2;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       source->media_types |= MEDIA_TYPE_VIDEO;
+
+       if (!(videotestsrc = _create_element(DEFAULT_ELEMENT_VIDEOTESTSRC, NULL))) {
+               LOG_ERROR("failed to create videotestsrc");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL);
+
+       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);
+       if (!gst_element_link_many(videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *audiotestsrc;
+       GstElement *capsfilter;
+       GstElement *audioenc;
+       GstElement *audiopay;
+       GstElement *queue;
+       GstElement *capsfilter2;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL");
+
+       source->media_types |= MEDIA_TYPE_AUDIO;
+
+       if (!(audiotestsrc = _create_element(DEFAULT_ELEMENT_AUDIOTESTSRC, NULL))) {
+               LOG_ERROR("failed to create audiotestsrc");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+       g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL);
+
+       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);
+       if (!gst_element_link_many(audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return _set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE);
+}
+
+static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type)
+{
+       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");
+
+       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 (type) {
+       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+               return __build_camerasrc(source, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+               return __build_audiosrc(source, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+               return __build_videotestsrc(source, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+               return __build_audiotestsrc(source, src_pad);
+
+       default:
+               LOG_ERROR_IF_REACHED("type(%d)", type);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static unsigned int __get_unoccupied_id(GHashTable *slots)
+{
+       int i;
+       gchar *key;
+
+       RET_VAL_IF(slots == NULL, 0, "slot is NULL");
+
+       /* Payload identifiers 96–127 are used for payloads defined dynamically during a session,
+        * hence the id range is limited here to 1-32. */
+       for (i = 1; i < 33; i++) {
+               key = g_strdup_printf("media_source_%u", i);
+               if (g_hash_table_contains(slots, key)) {
+                       g_free(key);
+                       continue;
+               }
+               g_free(key);
+               return i;
+       }
+
+       LOG_ERROR("all slots are occupied(1-32)");
+
+       return 0;
+}
+
+int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       unsigned int id;
+       webrtc_gst_slot_s *source = NULL;
+       gchar *bin_name = NULL;
+       GstPad *webrtc_sinkpad;
+       gchar *webrtc_sinkpad_name;
+       gchar *bin_srcpad_name = NULL;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+       RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
+
+       /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
+       id = __get_unoccupied_id(webrtc->gst.source_slots);
+       RET_VAL_IF(id == 0, WEBRTC_ERROR_INVALID_OPERATION, "source_slots are full");
+
+       bin_name = g_strdup_printf("media_source_%u", id);
+
+       MALLOC_AND_INIT_SLOT(source, id, bin_name);
+
+       ret = __build_source_bin(source, type);
+       if (ret != WEBRTC_ERROR_NONE) {
+               LOG_ERROR("failed to __build_source_bin()");
+               goto error;
+       }
+
+       if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), source->bin)) {
+               LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline));
+               goto error;
+       }
+
+       /* The gst_element_get_request_pad() of webrtcbin will trigger the transciever callback. To update the mline value of
+        * new transceiver object to the source structure in the callback, hash table inserting should be preceded. */
+       if (!g_hash_table_insert(webrtc->gst.source_slots, bin_name, (gpointer)source)) {
+               LOG_ERROR("should not be reached here, bin_name[%s] already exist, source id[%u] will be removed", bin_name, source->id);
+               g_hash_table_remove(webrtc->gst.source_slots, bin_name);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       if (!(webrtc_sinkpad = gst_element_get_request_pad(webrtc->gst.webrtcbin, "sink_%u"))) {
+               LOG_ERROR("failed to gst_element_get_request_pad()");
+               goto error_after_insert;
+       }
+       if (!(webrtc_sinkpad_name = gst_pad_get_name(webrtc_sinkpad))) {
+               LOG_ERROR("failed to gst_pad_get_name()");
+               goto error_after_insert;
+       }
+       bin_srcpad_name = g_strdup_printf("src_%u", id);
+       if (!gst_element_link_pads(source->bin, bin_srcpad_name, webrtc->gst.webrtcbin, webrtc_sinkpad_name)) {
+               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+                       GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
+               goto error_after_insert;
+       }
+       LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]",
+               GST_ELEMENT_NAME(source->bin), bin_srcpad_name, GST_ELEMENT_NAME(webrtc->gst.webrtcbin), webrtc_sinkpad_name);
+
+       *source_id = id;
+
+       LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
+
+       g_free(bin_srcpad_name);
+
+       return WEBRTC_ERROR_NONE;
+
+error_after_insert:
+       g_hash_table_remove(webrtc->gst.source_slots, bin_name);
+       g_free(bin_srcpad_name);
+
+       return WEBRTC_ERROR_INVALID_OPERATION;
+
+error:
+       g_free(bin_name);
+       g_free(source);
+
+       return WEBRTC_ERROR_INVALID_OPERATION;
+}
+
+int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       gchar *bin_name = NULL;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+
+       bin_name = g_strdup_printf("media_source_%u", source_id);
+
+       if (!g_hash_table_remove(webrtc->gst.source_slots, (gpointer)bin_name)) {
+               LOG_ERROR("failed to find media source by id[%u]", source_id);
+               ret = WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       g_free(bin_name);
+
+       return ret;
+}