From: Sangchul Lee Date: Tue, 15 Sep 2020 13:31:14 +0000 (+0900) Subject: Add support for audio/video rendering pipelines X-Git-Tag: submit/tizen/20210729.023123~216 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=413b4282ac6be9decf54c2360097aa808aa50eeb;p=platform%2Fcore%2Fapi%2Fwebrtc.git Add support for audio/video rendering pipelines Multiple rendering pipelines can be added. The decodebin is used to make each audio/video rendering pipeline. These will be triggered by webrtcbin based on the session description from remote peer during the negotiation. [Version] 0.1.26 [Issue Type] Improvement Change-Id: Iaade731f695181b8fe1a2a9aafa299c73feb4d32 Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 02294c99..68e66c53 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -113,7 +113,6 @@ typedef struct _webrtc_ini_s { } webrtc_ini_s; typedef struct _webrtc_gst_slot_s { - webrtc_media_source_type_e type; unsigned int id; GstElement *bin; } webrtc_gst_slot_s; @@ -124,6 +123,7 @@ typedef struct _webrtc_gst_s { GstBus *bus; guint bus_watcher; GHashTable *source_slots; + GHashTable *sink_slots; } webrtc_gst_s; typedef struct _webrtc_callbacks { diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 901854e8..b7384180 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.25 +Version: 0.1.26 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_private.c b/src/webrtc_private.c index 0ff7c962..98d0aa42 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -33,6 +33,11 @@ #define DEFAULT_ELEMENT_AUDIOTESTSRC "audiotestsrc" #define DEFAULT_ELEMENT_CAPSFILTER "capsfilter" #define DEFAULT_ELEMENT_QUEUE "queue" +#define DEFAULT_ELEMENT_VIDEOCONVERT "videoconvert" +#define DEFAULT_ELEMENT_AUDIOCONVERT "audioconvert" +#define DEFAULT_ELEMENT_AUDIORESAMPLE "audioresample" +#define DEFAULT_ELEMENT_VIDEOSINK "tizenwlsink" +#define DEFAULT_ELEMENT_AUDIOSINK "pulsesink" #define DEFAULT_VIDEO_ENCODED_MEDIA_TYPE "video/x-vp8" #define DEFAULT_VIDEO_RAW_MEDIA_TYPE "video/x-raw" @@ -384,7 +389,7 @@ static void __value_destroy_cb(gpointer data) RET_IF(data == NULL, "data is NULL"); - LOG_DEBUG("[%s, type:%u, id:%u] is removed", GST_ELEMENT_NAME(source->bin), source->type, source->id); + LOG_DEBUG("[%s, id:%u] is removed", GST_ELEMENT_NAME(source->bin), source->id); /* FIXME: do unlink */ @@ -692,6 +697,350 @@ static void __webrtcbin_ice_connection_state_cb(GstElement *webrtcbin, GParamSpe LOG_DEBUG("[IceConnectionState] is changed to [%s]", new_state); } +static webrtc_gst_slot_s* __find_sink_slot(webrtc_s *webrtc, const gchar *key) +{ + RET_VAL_IF(webrtc == NULL, NULL, "webrtc is NULL"); + RET_VAL_IF(key == NULL, NULL, "key is NULL"); + + return g_hash_table_lookup(webrtc->gst.sink_slots, key); +} + +static int __build_videosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad) +{ + webrtc_gst_slot_s *sink; + GstElement *videoconvert; + GstElement *videosink; + + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL"); + RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL"); + + sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin)); + RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin)); + RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); + + if (!(videoconvert = __create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) { + LOG_ERROR("failed to create videoconvert"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + /* FIXME: get factory name from ini */ + if (!(videosink = __create_element(DEFAULT_ELEMENT_VIDEOSINK, NULL))) { + LOG_ERROR("failed to create videosink"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + gst_bin_add_many(GST_BIN(sink->bin), videoconvert, videosink, NULL); + + if (!gst_element_sync_state_with_parent(videoconvert)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videoconvert)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + if (!gst_element_sync_state_with_parent(videosink)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(videosink)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!gst_element_link_many(decodebin, videoconvert, videosink, NULL)) { + LOG_ERROR("failed to gst_element_link_many()"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; +} + +static int __build_audiosink(webrtc_s *webrtc, GstElement *decodebin, GstPad *src_pad) +{ + webrtc_gst_slot_s *sink; + GstElement *audioconvert; + GstElement *audioresample; + GstElement *audiosink; + + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(decodebin == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "decodebin is NULL"); + RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL"); + + sink = __find_sink_slot(webrtc, GST_ELEMENT_NAME(decodebin)); + RET_VAL_IF(sink == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find an item by [%s] in sink slots", GST_ELEMENT_NAME(decodebin)); + RET_VAL_IF(sink->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); + + if (!(audioconvert = __create_element(DEFAULT_ELEMENT_AUDIOCONVERT, NULL))) { + LOG_ERROR("failed to create audioconvert"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!(audioresample = __create_element(DEFAULT_ELEMENT_AUDIORESAMPLE, NULL))) { + LOG_ERROR("failed to create audioresample"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + /* FIXME: get factory name from ini */ + if (!(audiosink = __create_element(DEFAULT_ELEMENT_AUDIOSINK, NULL))) { + LOG_ERROR("failed to create audiosink"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + gst_bin_add_many(GST_BIN(sink->bin), audioconvert, audioresample, audiosink, NULL); + + if (!gst_element_sync_state_with_parent(audioconvert)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioconvert)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + if (!gst_element_sync_state_with_parent(audioresample)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audioresample)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + if (!gst_element_sync_state_with_parent(audiosink)) { + LOG_ERROR("failed to gst_element_sync_state_with_parent() for [%s]", GST_ELEMENT_NAME(audiosink)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!gst_element_link_many(decodebin, audioconvert, audioresample, audiosink, NULL)) { + LOG_ERROR("failed to gst_element_link_many()"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; +} + +static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data) +{ + int ret = WEBRTC_ERROR_NONE; + webrtc_s *webrtc = (webrtc_s *)user_data; + const gchar *media_type; + + RET_IF(webrtc == NULL, "webrtc is NULL"); + + if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC) + return; + + media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0)); + LOG_INFO("[%s], new_pad[%s], media_type[%s]", GST_ELEMENT_NAME(decodebin), GST_PAD_NAME(new_pad), media_type); + + if (g_strrstr(media_type, "video")) { + ret = __build_videosink(webrtc, decodebin, new_pad); + + } else if (g_strrstr(media_type, "audio")) { + ret = __build_audiosink(webrtc, decodebin, new_pad); + + } else { + LOG_ERROR("not supported media type[%s]", media_type); + return; + } + + if (ret != WEBRTC_ERROR_NONE) + LOG_ERROR("failed to build a rendering pipeline"); +} + +static int __decodebin_autoplug_select_cb(GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, gpointer user_data) +{ + /* NOTE : GstAutoplugSelectResult is defined in gstplay-enum.h but not exposed */ + typedef enum { + GST_AUTOPLUG_SELECT_TRY, + GST_AUTOPLUG_SELECT_EXPOSE, + GST_AUTOPLUG_SELECT_SKIP + } GstAutoplugSelectResult; + gchar *factory_name; + const gchar *klass; + GstAutoplugSelectResult result = GST_AUTOPLUG_SELECT_TRY; + webrtc_s *webrtc = (webrtc_s *)user_data; + + RET_VAL_IF(webrtc == NULL, GST_AUTOPLUG_SELECT_SKIP, "webrtc is NULL, skip it"); + + factory_name = GST_OBJECT_NAME(factory); + klass = gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS); + + LOG_INFO("factory [name:%s, klass:%s]", factory_name, klass); + + return result; +} + +static unsigned int __get_id_from_pad_name(const gchar* name) +{ + gchar **tokens = NULL; + gint64 id; + + RET_VAL_IF(name == NULL, 0, "name is NULL"); + + tokens = g_strsplit(name, "_", 2); + id = g_ascii_strtoll(tokens[1], NULL, 10); + + g_strfreev(tokens); + + return (unsigned int)id; +} + +static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name, bool is_src) +{ + gchar *bin_name = NULL; + GstPad *ghost_pad = NULL; + + RET_VAL_IF(bin == NULL, NULL, "bin is NULL"); + RET_VAL_IF(pad_name == NULL, NULL, "pad_name is NULL"); + + if (!(ghost_pad = gst_ghost_pad_new_no_target(pad_name, is_src ? GST_PAD_SRC : GST_PAD_SINK))) { + LOG_ERROR("failed to gst_ghost_pad_new_no_target()"); + return NULL; + } + + bin_name = gst_element_get_name(bin); + if (gst_element_add_pad(GST_ELEMENT(bin), ghost_pad)) { + gst_pad_set_active(ghost_pad, TRUE); + LOG_DEBUG("added [%s] empty ghostpad into [%s]", pad_name, bin_name); + } else { + LOG_ERROR("failed to add empty [%s] ghostpad into [%s]", pad_name, bin_name); + g_object_unref(ghost_pad); + ghost_pad = NULL; + } + + g_free(bin_name); + + return ghost_pad; +} + +static int __add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad) +{ + gchar *pad_name; + + RET_VAL_IF(slot == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "slot is NULL"); + RET_VAL_IF(slot->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); + RET_VAL_IF(new_pad == NULL, WEBRTC_ERROR_INVALID_OPERATION, "new_pad is NULL"); + + pad_name = g_strdup_printf("%s_%u", is_src ? "src" : "sink", slot->id); + if (!(*new_pad =__add_no_target_ghostpad(slot->bin, pad_name, is_src))) { + LOG_ERROR("failed to add new ghost pad[%s] for bin[%s]", pad_name, GST_ELEMENT_NAME(slot->bin)); + g_free(pad_name); + return WEBRTC_ERROR_INVALID_OPERATION; + } + g_free(pad_name); + + return WEBRTC_ERROR_NONE; +} + +static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src) +{ + GstPad *target_pad; + + RET_VAL_IF(ghost_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_pad is NULL"); + RET_VAL_IF(target_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "target_element is NULL"); + + if (!(target_pad = gst_element_get_static_pad(target_element, is_src ? "src" : "sink"))) { + LOG_ERROR("failed to gst_element_get_static_pad() of [%s]", GST_ELEMENT_NAME(target_element)); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad), target_pad)) { + LOG_ERROR("failed to gst_ghost_pad_set_target(), ghostpad[%s] -> targetpad[%s]", GST_PAD_NAME(ghost_pad), GST_PAD_NAME(target_pad)); + gst_object_unref(target_pad); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + gst_object_unref(target_pad); + + LOG_DEBUG("ghostpad[%s] -> target[%s:%s]", GST_PAD_NAME(ghost_pad), GST_ELEMENT_NAME(target_element), GST_PAD_NAME(target_pad)); + + return WEBRTC_ERROR_NONE; +} + +static int __add_rendering_sink_bin(webrtc_s *webrtc, GstPad *src_pad) +{ + int ret = WEBRTC_ERROR_NONE; + unsigned int id; + gchar *bin_name; + gchar *decodebin_name; + webrtc_gst_slot_s *sink; + GstElement *decodebin; + GstPad *sink_pad; + + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "src_pad is NULL"); + + /* decodebin_name/sink will be freed by function which is set to g_hash_table_new_full() */ + id = __get_id_from_pad_name(GST_PAD_NAME(src_pad)); + bin_name = g_strdup_printf("rendering_sink_%u", id); + decodebin_name = g_strdup_printf("%s_decodebin", bin_name); + + sink = g_new0(webrtc_gst_slot_s, 1); + sink->id = id; + sink->bin = gst_bin_new(bin_name); + g_free(bin_name); + + decodebin = __create_element("decodebin", decodebin_name); + if (!decodebin) + goto error_before_insert; + + gst_bin_add(GST_BIN(sink->bin), decodebin); + + g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc); + g_signal_connect(decodebin, "autoplug-select", G_CALLBACK(__decodebin_autoplug_select_cb), webrtc); + + ret = __add_no_target_ghostpad_to_slot(sink, FALSE, &sink_pad); + if (ret != WEBRTC_ERROR_NONE) + goto error_before_insert; + + ret = __set_ghost_pad_target(sink_pad, decodebin, FALSE); + if (ret != WEBRTC_ERROR_NONE) + goto error_before_insert; + + if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), sink->bin)) { + LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(sink->bin), GST_ELEMENT_NAME(webrtc->gst.pipeline)); + goto error_before_insert; + } + + if (gst_pad_link(src_pad, sink_pad) != GST_PAD_LINK_OK) { + LOG_ERROR("failed to gst_pad_link(), %s:%s", GST_PAD_NAME(src_pad), GST_PAD_NAME(sink_pad)); + goto error_before_insert; + } + + LOG_DEBUG("link pads successfully, [%s:%s] - [%s:%s]", + GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_PAD_NAME(src_pad), GST_ELEMENT_NAME(sink->bin), GST_PAD_NAME(sink_pad)); + + if (!g_hash_table_insert(webrtc->gst.sink_slots, decodebin_name, (gpointer)sink)) { + LOG_ERROR("should not be reached here, bin_name[%s] already exist, sink id[%u] will be removed", decodebin_name, sink->id); + g_hash_table_remove(webrtc->gst.sink_slots, decodebin_name); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + gst_element_sync_state_with_parent(sink->bin); + + LOG_INFO("added a sink slot[%p, id:%u]", sink, sink->id); + + return WEBRTC_ERROR_NONE; + +error_before_insert: + gst_object_unref(sink_pad); + g_free(decodebin_name); + g_free(sink); + + return WEBRTC_ERROR_INVALID_OPERATION; +} + +static void __webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data) +{ + int ret = WEBRTC_ERROR_NONE; + webrtc_s *webrtc = (webrtc_s *)user_data; + + RET_IF(webrtc == NULL, "webrtc is NULL"); + + if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC) + return; + + LOG_INFO("new pad[%s] is added", GST_PAD_NAME(new_pad)); + + ret = __add_rendering_sink_bin(webrtc, new_pad); + RET_IF(ret != WEBRTC_ERROR_NONE, "failed to __add_rendering_sink_bin()"); +} + +static void __webrtcbin_no_more_pads_cb(GstElement *webrtcbin, gpointer user_data) +{ + webrtc_s *webrtc = (webrtc_s *)user_data; + + RET_IF(webrtcbin == NULL, "webrtcbin is NULL"); + RET_IF(webrtc == NULL, "webrtc is NULL"); + + LOG_DEBUG_LEAVE(); +} + int _gst_build_pipeline(webrtc_s *webrtc) { RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); @@ -719,6 +1068,8 @@ int _gst_build_pipeline(webrtc_s *webrtc) __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::signaling-state", G_CALLBACK(__webrtcbin_signaling_state_cb), webrtc); __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-gathering-state", G_CALLBACK(__webrtcbin_ice_gathering_state_cb), webrtc); __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-connection-state", G_CALLBACK(__webrtcbin_ice_connection_state_cb), webrtc); + __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "pad-added", G_CALLBACK(__webrtcbin_pad_added_cb), webrtc); + __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "no-more-pads", G_CALLBACK(__webrtcbin_no_more_pads_cb), webrtc); if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), webrtc->gst.webrtcbin)) { LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_ELEMENT_NAME(webrtc->gst.pipeline)); @@ -726,6 +1077,7 @@ int _gst_build_pipeline(webrtc_s *webrtc) } webrtc->gst.source_slots = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __value_destroy_cb); + webrtc->gst.sink_slots = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, __value_destroy_cb); return WEBRTC_ERROR_NONE; @@ -739,6 +1091,10 @@ void _gst_destroy_pipeline(webrtc_s *webrtc) if (!webrtc) return; + if (webrtc->gst.sink_slots) { + g_hash_table_destroy(webrtc->gst.sink_slots); + webrtc->gst.sink_slots = NULL; + } if (webrtc->gst.source_slots) { g_hash_table_destroy(webrtc->gst.source_slots); webrtc->gst.source_slots = NULL; @@ -908,35 +1264,9 @@ static GstCaps *__make_rtp_caps(const gchar *media_type, unsigned int id) return caps; } -static int __set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, gboolean is_src) -{ - GstPad *target_pad; - - RET_VAL_IF(ghost_pad == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "ghost_pad is NULL"); - RET_VAL_IF(target_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "target_element is NULL"); - - if (!(target_pad = gst_element_get_static_pad(target_element, is_src ? "src" : "sink"))) { - LOG_ERROR("failed to gst_element_get_static_pad()"); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad), target_pad)) { - LOG_ERROR("failed to gst_ghost_pad_set_target(), ghostpad[%s] -> targetpad[%s]", GST_PAD_NAME(ghost_pad), GST_PAD_NAME(target_pad)); - gst_object_unref(target_pad); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - gst_object_unref(target_pad); - - LOG_DEBUG("ghostpad[%s] -> target[%s:%s]", GST_PAD_NAME(ghost_pad), GST_ELEMENT_NAME(target_element), GST_PAD_NAME(target_pad)); - - return WEBRTC_ERROR_NONE; -} - -static int __create_rest_of_elements(webrtc_gst_slot_s *source, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2) +static int __create_rest_of_elements(webrtc_gst_slot_s *source, webrtc_media_source_type_e type, GstElement **capsfilter, GstElement **encoder, GstElement **payloader, GstElement **queue, GstElement **capsfilter2) { GstCaps *sink_caps; - webrtc_media_source_type_e type; element_info_s elem_info; const gchar *encoder_klass_name; gchar *media_type; @@ -948,8 +1278,6 @@ static int __create_rest_of_elements(webrtc_gst_slot_s *source, GstElement **cap RET_VAL_IF(queue == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "queue is NULL"); RET_VAL_IF(capsfilter2 == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "capsfilter2 is NULL"); - type = source->type; - if (!(*capsfilter = __create_element(DEFAULT_ELEMENT_CAPSFILTER, NULL))) { LOG_ERROR("failed to create capsfilter"); return WEBRTC_ERROR_INVALID_OPERATION; @@ -1016,7 +1344,7 @@ static int __build_camerasrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad) } /* FIXME: set camera default setting from ini */ - if ((ret = __create_rest_of_elements(source, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) + if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) return ret; gst_bin_add_many(GST_BIN(source->bin), camerasrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL); @@ -1047,7 +1375,7 @@ static int __build_audiosrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad) return WEBRTC_ERROR_INVALID_OPERATION; } - if ((ret = __create_rest_of_elements(source, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) + if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_MIC, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) return ret; gst_bin_add_many(GST_BIN(source->bin), audiosrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL); @@ -1079,7 +1407,7 @@ static int __build_videotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad } g_object_set(G_OBJECT(videotestsrc), "is-live", TRUE, NULL); - if ((ret = __create_rest_of_elements(source, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) + if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &capsfilter, &videoenc, &videopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) return ret; gst_bin_add_many(GST_BIN(source->bin), videotestsrc, capsfilter, videoenc, videopay, queue, capsfilter2, NULL); @@ -1111,7 +1439,7 @@ static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad } g_object_set(G_OBJECT(audiotestsrc), "is-live", TRUE, NULL); - if ((ret = __create_rest_of_elements(source, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) + if ((ret = __create_rest_of_elements(source, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, &capsfilter, &audioenc, &audiopay, &queue, &capsfilter2)) != WEBRTC_ERROR_NONE) return ret; gst_bin_add_many(GST_BIN(source->bin), audiotestsrc, capsfilter, audioenc, audiopay, queue, capsfilter2, NULL); @@ -1123,51 +1451,18 @@ static int __build_audiotestsrc(webrtc_gst_slot_s *source, GstPad *ghost_src_pad return __set_ghost_pad_target(ghost_src_pad, capsfilter2, TRUE); } -static GstPad* __add_no_target_ghostpad(GstElement *bin, const char *pad_name) -{ - gchar *bin_name = NULL; - GstPad *ghost_pad = NULL; - - RET_VAL_IF(bin == NULL, NULL, "bin is NULL"); - RET_VAL_IF(pad_name == NULL, NULL, "pad_name is NULL"); - - if (!(ghost_pad = gst_ghost_pad_new_no_target(pad_name, GST_PAD_SRC))) { - LOG_ERROR("failed to gst_ghost_pad_new_no_target()"); - return NULL; - } - - bin_name = gst_element_get_name(bin); - if (gst_element_add_pad(GST_ELEMENT(bin), ghost_pad)) { - gst_pad_set_active(ghost_pad, TRUE); - LOG_DEBUG("added [%s] empty ghostpad into [%s]", pad_name, bin_name); - } else { - LOG_ERROR("failed to add empty [%s] ghostpad into [%s]", pad_name, bin_name); - g_object_unref(ghost_pad); - ghost_pad = NULL; - } - - g_free(bin_name); - - return ghost_pad; -} - -static int __build_source_bin(webrtc_gst_slot_s *source) +static int __build_source_bin(webrtc_gst_slot_s *source, webrtc_media_source_type_e type) { - GstPad *src_pad = NULL; - gchar *pad_name; + int ret = WEBRTC_ERROR_NONE; + GstPad *src_pad; RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); RET_VAL_IF(source->bin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "bin is NULL"); - pad_name = g_strdup_printf("src_%u", source->id); - if (!(src_pad =__add_no_target_ghostpad(source->bin, pad_name))) { - LOG_ERROR("failed to add new ghost source pad for source bin"); - g_free(pad_name); - return WEBRTC_ERROR_INVALID_OPERATION; - } - g_free(pad_name); + ret = __add_no_target_ghostpad_to_slot(source, TRUE, &src_pad); + RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to __add_no_target_ghostpad_to_slot()"); - switch (source->type) { + switch (type) { case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA: return __build_camerasrc(source, src_pad); @@ -1181,7 +1476,7 @@ static int __build_source_bin(webrtc_gst_slot_s *source) return __build_audiotestsrc(source, src_pad); default: - LOG_ERROR_IF_REACHED("type(%d)", source->type); + LOG_ERROR_IF_REACHED("type(%d)", type); return WEBRTC_ERROR_INVALID_PARAMETER; } @@ -1232,12 +1527,10 @@ int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigne bin_name = g_strdup_printf("media_source_%u", id); source = g_new0(webrtc_gst_slot_s, 1); - - source->type = type; source->id = id; source->bin = gst_bin_new(bin_name); - ret = __build_source_bin(source); + ret = __build_source_bin(source, type); if (ret != WEBRTC_ERROR_NONE) { LOG_ERROR("failed to __build_source_bin()"); goto error_before_insert; @@ -1274,7 +1567,7 @@ int _add_media_source(webrtc_s *webrtc, webrtc_media_source_type_e type, unsigne *source_id = id; - LOG_INFO("type[%d] source_id[%u] source[%p]", type, *source_id, source); + LOG_INFO("added a source slot[%p, id:%u]", source, source->id); g_free(bin_srcpad_name);