From: Sangchul Lee Date: Fri, 28 Aug 2020 09:01:53 +0000 (+0900) Subject: Add to build source elements by media source type and link it with webrtcbin X-Git-Tag: submit/tizen/20210729.023123~237 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d9676c7c0f0dc80078ca6056de4123f32331da97;p=platform%2Fcore%2Fapi%2Fwebrtc.git Add to build source elements by media source type and link it with webrtcbin 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 --- diff --git a/include/webrtc_private.h b/include/webrtc_private.h index bff07c94..6c94088f 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -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); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 58dc368b..7b78611b 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -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 diff --git a/src/webrtc.c b/src/webrtc.c index 76a79032..0222a3a0 100644 --- a/src/webrtc.c +++ b/src/webrtc.c @@ -195,3 +195,4 @@ int webrtc_set_stun_server(webrtc_h webrtc, const char *stun_server) return WEBRTC_ERROR_NONE; } + diff --git a/src/webrtc_private.c b/src/webrtc_private.c index 8fe9bdf8..e73f8c69 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -17,6 +17,50 @@ #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; } diff --git a/test/webrtc_test.c b/test/webrtc_test.c index 847a7d9a..1a56255c 100644 --- a/test/webrtc_test.c +++ b/test/webrtc_test.c @@ -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; }