Add to build source elements by media source type and link it with webrtcbin 23/242623/7
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 28 Aug 2020 09:01:53 +0000 (18:01 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 8 Sep 2020 05:28:23 +0000 (14:28 +0900)
Source type selection bug in webrtc_test is also fixed.

[Version] 0.1.5
[Issue Type] Implementation

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

index bff07c94ac953d9f44854b48b18bcc771b60e47c..6c94088f11618e8227ea3da79fc12a26cc68011e 100644 (file)
@@ -128,6 +128,12 @@ typedef struct _webrtc_s {
        webrtc_state_e pend_state;
 } webrtc_s;
 
+typedef struct _element_info_s {
+       const gchar *klass_name;
+       GstCaps *src_caps;
+       GstCaps *sink_caps;
+} element_info_s;
+
 int _ini_load(webrtc_s *webrtc);
 int _gst_init(webrtc_s *webrtc);
 int _gst_build_pipeline(webrtc_s *webrtc);
index 58dc368bced99349976e4b2206a339e4a6be8829..7b78611bf14d265154bd02dd57a7bba6e3148c4b 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.4
+Version:    0.1.5
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 76a79032d05570c0df168b7d63534dfd28bd8da7..0222a3a0bd163f8ebd1b60f5d232f02e6a4d09fe 100644 (file)
@@ -195,3 +195,4 @@ int webrtc_set_stun_server(webrtc_h webrtc, const char *stun_server)
 
        return WEBRTC_ERROR_NONE;
 }
+
index 8fe9bdf80a22f5be30ac2047330200f6663a20e9..e73f8c69f407be758f9757757add433de90ce3fb 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_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 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)
+
 static gboolean __meet_gst_state(webrtc_state_e state, GstState gst_state)
 {
        if (state == WEBRTC_STATE_IDLE && gst_state == GST_STATE_READY)
@@ -103,7 +147,7 @@ static gboolean __bus_watch_cb(GstBus *bus, GstMessage *message, gpointer user_d
        return TRUE;
 }
 
-static GstElement *__element_create(const char *factory_name, const char *name)
+static GstElement *__create_element(const char *factory_name, const char *name)
 {
        GstElement *element = NULL;
 
@@ -117,9 +161,86 @@ static GstElement *__element_create(const char *factory_name, const char *name)
        return element;
 }
 
+static gboolean __element_filter(GstPluginFeature *feature, gpointer data)
+{
+       element_info_s *elem_info = (element_info_s *)data;
+       gboolean src_can_accept = FALSE;
+       gboolean sink_can_accept = FALSE;
+       GstElementFactory *factory = NULL;
+       const gchar *factory_klass = NULL;
+
+       if (!GST_IS_ELEMENT_FACTORY(feature))
+               return FALSE;
+
+       factory = GST_ELEMENT_FACTORY(feature);
+       factory_klass = gst_element_factory_get_klass(factory);
+
+       if (!elem_info || !g_strrstr(factory_klass, elem_info->klass_name))
+               return FALSE;
+
+       if (GST_IS_CAPS(elem_info->src_caps))
+               src_can_accept = gst_element_factory_can_src_any_caps(factory, elem_info->src_caps);
+
+       if (GST_IS_CAPS(elem_info->sink_caps))
+               sink_can_accept = gst_element_factory_can_sink_any_caps(factory, elem_info->sink_caps);
+
+       if (GST_IS_CAPS(elem_info->src_caps) && GST_IS_CAPS(elem_info->sink_caps)) {
+               if (src_can_accept && sink_can_accept) {
+                       LOG_INFO("found compatible factory [%s] for klass [%s]", GST_OBJECT_NAME(factory), factory_klass);
+                       return TRUE;
+               }
+               return FALSE;
+       }
+
+       if (src_can_accept || sink_can_accept) {
+               LOG_INFO("found compatible factory [%s] for klass [%s]", GST_OBJECT_NAME(factory), factory_klass);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int __rank_compare(GstPluginFeature *first, GstPluginFeature *second)
+{
+       guint first_rank = 0, second_rank = 0;
+
+       first_rank = gst_plugin_feature_get_rank(first);
+       second_rank = gst_plugin_feature_get_rank(second);
+       LOG_DEBUG("second[%s]_rank(%d) - first[%s]_rank(%d) = (%d)",
+               GST_OBJECT_NAME(GST_ELEMENT_FACTORY(second)), second_rank,
+               GST_OBJECT_NAME(GST_ELEMENT_FACTORY(first)), first_rank, second_rank - first_rank);
+
+       return second_rank - first_rank;
+}
+
+static GstElement *__create_element_from_registry(element_info_s *elem_info)
+{
+       GstElement *element = NULL;
+       GList *factories = NULL;
+       GstElementFactory *factory = NULL;
+
+       RET_VAL_IF(elem_info == NULL, NULL, "elem_info is NULL");
+
+       factories = gst_registry_feature_filter(gst_registry_get(), __element_filter, FALSE, elem_info);
+       factories = g_list_sort(factories, (GCompareFunc) __rank_compare);
+
+       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);
+       } else {
+               LOG_DEBUG("could not find any compatible element for klass_name[%s]", elem_info->klass_name);
+       }
+
+       gst_plugin_list_free(factories);
+
+       return element;
+}
+
 static void __value_destroy_cb(gpointer data)
 {
        webrtc_gst_slot_s *source  = (webrtc_gst_slot_s *)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);
@@ -195,7 +316,7 @@ int _gst_build_pipeline(webrtc_s *webrtc)
 {
        RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
 
-       webrtc->gst.pipeline = gst_pipeline_new("native-webrtc-pipeline");
+       webrtc->gst.pipeline = gst_pipeline_new("webrtc-pipeline");
        RET_VAL_IF(webrtc->gst.pipeline == NULL, WEBRTC_ERROR_INVALID_OPERATION, "pipeline is NULL");
 
        if (!(webrtc->gst.bus = gst_pipeline_get_bus(GST_PIPELINE(webrtc->gst.pipeline)))) {
@@ -208,7 +329,7 @@ int _gst_build_pipeline(webrtc_s *webrtc)
                goto error;
        }
 
-       if (!(webrtc->gst.webrtcbin = __element_create("webrtcbin", NULL))) {
+       if (!(webrtc->gst.webrtcbin = __create_element("webrtcbin", NULL))) {
                LOG_ERROR("failed to create webrtcbin");
                goto error;
        }
@@ -232,6 +353,10 @@ void _gst_destroy_pipeline(webrtc_s *webrtc)
        if (!webrtc)
                return;
 
+       if (webrtc->gst.source_slots) {
+               g_hash_table_destroy(webrtc->gst.source_slots);
+               webrtc->gst.source_slots = NULL;
+       }
        if (webrtc->gst.bus_watcher > 0) {
                gst_bus_remove_watch(webrtc->gst.bus);
                webrtc->gst.bus_watcher = 0;
@@ -248,10 +373,6 @@ void _gst_destroy_pipeline(webrtc_s *webrtc)
                gst_object_unref(webrtc->gst.pipeline);
                webrtc->gst.pipeline = NULL;
        }
-       if (webrtc->gst.source_slots) {
-               g_hash_table_unref(webrtc->gst.source_slots);
-               webrtc->gst.source_slots = NULL;
-       }
 }
 
 int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state)
@@ -270,36 +391,377 @@ 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("should not be reached here");
+               break;
+       }
+
+       return caps;
+}
+
+static GstCaps *__make_default_encoded_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_ENCODED_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 */
+               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("should not be reached here");
+               break;
+       }
+
+       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_media_source_type_e type, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue)
+{
+       GstCaps *sink_caps;
+       element_info_s elem_info;
+       const gchar *encoder_klass_name;
+
+       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");
+
+       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),
+                                               *encoder);
+
+       CREATE_ELEMENT_FROM_REGISTRY(elem_info, GST_KLASS_NAME_PAYLOADER_RTP,
+                                               __make_default_encoded_caps(type),
+                                               NULL,
+                                               *payloader);
+
+       if (!(*queue = __create_element(DEFAULT_ELEMENT_QUEUE, NULL))) {
+               LOG_ERROR("failed to create queue");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __build_camerasrc(GstElement *bin, webrtc_media_source_type_e type, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *camerasrc;
+       GstElement *capsfilter;
+       GstElement *videoenc;
+       GstElement *videopay;
+       GstElement *queue;
+
+       RET_VAL_IF(bin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "bin is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+
+       /* 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(type, &capsfilter, &videoenc, &videopay, &queue)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       gst_bin_add_many(GST_BIN(bin), camerasrc, capsfilter, videoenc, videopay, queue, NULL);
+       if (!gst_element_link_many(camerasrc, capsfilter, videoenc, videopay, queue, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return __set_ghost_pad_target(ghost_src_pad, queue, TRUE);
+}
+
+static int __build_audiosrc(GstElement *bin, webrtc_media_source_type_e type, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *audiosrc;
+       GstElement *capsfilter;
+       GstElement *audioenc;
+       GstElement *audiopay;
+       GstElement *queue;
+
+       RET_VAL_IF(bin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "bin is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+
+       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(type, &capsfilter, &audioenc, &audiopay, &queue)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       gst_bin_add_many(GST_BIN(bin), audiosrc, capsfilter, audioenc, audiopay, queue, NULL);
+       if (!gst_element_link_many(audiosrc, capsfilter, audioenc, audiopay, queue, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return __set_ghost_pad_target(ghost_src_pad, queue, TRUE);
+}
+
+static int __build_videotestsrc(GstElement *bin, webrtc_media_source_type_e type, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *videotestsrc;
+       GstElement *capsfilter;
+       GstElement *videoenc;
+       GstElement *videopay;
+       GstElement *queue;
+
+       RET_VAL_IF(bin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "bin is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+
+       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(type, &capsfilter, &videoenc, &videopay, &queue)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       gst_bin_add_many(GST_BIN(bin), videotestsrc, capsfilter, videoenc, videopay, queue, NULL);
+       if (!gst_element_link_many(videotestsrc, capsfilter, videoenc, videopay, queue, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return __set_ghost_pad_target(ghost_src_pad, queue, TRUE);
+}
+
+static int __build_audiotestsrc(GstElement *bin, webrtc_media_source_type_e type, GstPad *ghost_src_pad)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       GstElement *audiotestsrc;
+       GstElement *capsfilter;
+       GstElement *audioenc;
+       GstElement *audiopay;
+       GstElement *queue;
+
+       RET_VAL_IF(bin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "bin is NULL");
+       RET_VAL_IF(ghost_src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_src_pad is NULL");
+
+       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(type, &capsfilter, &audioenc, &audiopay, &queue)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       gst_bin_add_many(GST_BIN(bin), audiotestsrc, capsfilter, audioenc, audiopay, queue, NULL);
+       if (!gst_element_link_many(audiotestsrc, capsfilter, audioenc, audiopay, queue, NULL)) {
+               LOG_ERROR("failed to gst_element_link_many()");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       return __set_ghost_pad_target(ghost_src_pad, queue, 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)
+{
+       GstPad *src_pad = NULL;
+       gchar *pad_name;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "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);
+
+       switch (source->type) {
+       case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
+               return __build_camerasrc(source->bin, source->type, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
+               return __build_audiosrc(source->bin, source->type, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST:
+               return __build_videotestsrc(source->bin, source->type, src_pad);
+
+       case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
+               return __build_audiotestsrc(source->bin, source->type, src_pad);
+
+       default:
+               LOG_ERROR("should not be reached here");
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
 int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigned int *source_id)
 {
+       int ret = WEBRTC_ERROR_NONE;
        static unsigned int id = 0;
        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");
 
-       /* key/value will be freed by function set via g_hash_table_new_full() */
-       bin_name = g_strdup_printf("media_source_%u", ++id); /* key */
-       source = g_new0(webrtc_gst_slot_s, 1); /* value */
+       /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
+       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);
 
-       /* FIXME: add proper elements to the bin according to the type */
+       ret = __build_source_bin(source);
+       if (ret != WEBRTC_ERROR_NONE) {
+               LOG_ERROR("failed to __build_source_bin()");
+               goto error_before_insert;
+       }
 
        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));
-               g_free(bin_name);
-               g_free(source);
-               return WEBRTC_ERROR_INVALID_OPERATION;
+               goto error_before_insert;
+       }
+
+       if (!(webrtc_sinkpad = gst_element_get_request_pad(webrtc->gst.webrtcbin, "sink_%u"))) {
+               LOG_ERROR("failed to gst_element_get_request_pad()");
+               goto error_before_insert;
+       }
+       if (!(webrtc_sinkpad_name = gst_pad_get_name(webrtc_sinkpad))) {
+               LOG_ERROR("failed to gst_pad_get_name()");
+               goto error_before_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_before_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);
 
        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);
+               g_free(bin_srcpad_name);
                return WEBRTC_ERROR_INVALID_OPERATION;
        }
 
@@ -307,11 +769,21 @@ int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigne
 
        LOG_INFO("type[%d] source_id[%u] source[%p]", type, *source_id, source);
 
+       g_free(bin_srcpad_name);
+
        return WEBRTC_ERROR_NONE;
+
+error_before_insert:
+       g_free(bin_name);
+       g_free(bin_srcpad_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");
@@ -321,12 +793,11 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int 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);
-               g_free(bin_name);
-               return WEBRTC_ERROR_INVALID_PARAMETER;
+               ret = WEBRTC_ERROR_INVALID_PARAMETER;
        }
 
        g_free(bin_name);
 
-       return WEBRTC_ERROR_NONE;
+       return ret;
 }
 
index 847a7d9a12bf82d52ea71ac2643afbf053eca5ac..1a56255c00a4e9fd324e43d827d4bc631483d3d9 100644 (file)
@@ -462,7 +462,7 @@ static void interpret(char *cmd)
                break;
        case CURRENT_STATUS_ADD_MEDIA_SOURCE: {
                value = atoi(cmd);
-               _webrtc_add_media_source(value);
+               _webrtc_add_media_source(value - 1);
                reset_menu_state();
                break;
        }