From: Sangchul Lee Date: Tue, 7 Jun 2022 10:36:22 +0000 (+0900) Subject: webrtc_source: Postpone the time of linking source with webrtcbin X-Git-Tag: submit/tizen/20220613.225802~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5ce79c8d2e58c7ecc2b1fcc2ffeaf218b09e5aad;p=platform%2Fcore%2Fapi%2Fwebrtc.git webrtc_source: Postpone the time of linking source with webrtcbin This makes it possible for a source that elements would be fixed by looking something before starting the webrtc handle. [Version] 0.3.122 [Issue Type] Improvement Change-Id: I8578a642d25dc246b2d81ebae5936545316c8852 Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 9082c57e..a68716f0 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -648,6 +648,7 @@ void _invoke_state_changed_cb(webrtc_s *webrtc, webrtc_state_e old, webrtc_state void _post_state_cb_in_idle(webrtc_s *webrtc, webrtc_state_e new_state); void _post_error_cb_in_idle(webrtc_s *webrtc, webrtc_error_e error); void _remove_remained_event_sources(webrtc_s *webrtc); +int _complete_sources(webrtc_s *webrtc); void _connect_and_append_signal(GList **signals, GObject *obj, const char *sig_name, GCallback cb, gpointer user_data); void _disconnect_signal(gpointer data); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 5736a5d1..126d61f3 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.3.121 +Version: 0.3.122 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc.c b/src/webrtc.c index b125ac14..39fe2860 100644 --- a/src/webrtc.c +++ b/src/webrtc.c @@ -196,6 +196,9 @@ int webrtc_start(webrtc_h webrtc) return ret; } #endif + ret = _complete_sources(_webrtc); + RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to complete sources"); + ret = _gst_pipeline_set_state(_webrtc, GST_STATE_PLAYING); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to change GST state to PLAYING"); diff --git a/src/webrtc_source.c b/src/webrtc_source.c index 796242ee..15add739 100644 --- a/src/webrtc_source.c +++ b/src/webrtc_source.c @@ -46,6 +46,7 @@ #define ELEMENT_NAME_VIDEO_SWITCH "videoSwitch" #define ELEMENT_NAME_VIDEO_MUTE_SRC "videoMuteSrc" #define ELEMENT_NAME_VOLUME "volume" +#define ELEMENT_NAME_AUDIO_SRC "audioSrc" #define ELEMENT_NAME_MIC_SRC "micSrc" #define ELEMENT_NAME_FILE_SRC "fileSrc" #define ELEMENT_NAME_AUDIO_QUEUE "audioQueue" @@ -121,6 +122,7 @@ static av_mapping_table_s _av_tbl[AV_IDX_MAX] = { }; static int __link_source_with_webrtcbin(webrtc_gst_slot_s *source, GstElement *webrtcbin); +static GstPadProbeReturn __camerasrc_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer u_data); static const char * __get_audio_media_type(const char *codec_name) { @@ -937,6 +939,8 @@ static int __create_rest_of_elements(webrtc_s *webrtc, webrtc_gst_slot_s *source PRINT_CAPS(sink_caps, "capsfilter"); g_object_set(G_OBJECT(capsfilter), "caps", sink_caps, NULL); source->av[idx].render.appsrc_caps = sink_caps; + if (source->av[idx].render.appsrc) + g_object_set(G_OBJECT(source->av[idx].render.appsrc), "caps", sink_caps, NULL); } source->av[idx].render.need_decoding = true; @@ -949,6 +953,8 @@ static int __create_rest_of_elements(webrtc_s *webrtc, webrtc_gst_slot_s *source PRINT_CAPS(sink_caps, "capsfilter"); g_object_set(G_OBJECT(capsfilter), "caps", sink_caps, NULL); source->av[idx].render.appsrc_caps = sink_caps; + if (source->av[idx].render.appsrc) + g_object_set(G_OBJECT(source->av[idx].render.appsrc), "caps", sink_caps, NULL); } __add_probe_to_pad_for_render(source, idx, gst_element_get_static_pad(capsfilter, "src"), __source_data_probe_cb); @@ -1315,21 +1321,22 @@ static GstElement *__find_element_in_bin(GstBin *bin, const gchar *name) static int __build_screensrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { int ret; - GstElement *screensrc; - GstElement *videoconvert; - GstElement *videotestsrc; - GstElement *videoswitch; - GstElement *capsfilter; GList *switch_src_list = NULL; - GList *element_list = NULL; - GstPad *src_pad; + GstElement *screensrc = NULL; + GstElement *videotestsrc = NULL; + GstElement *videoswitch = NULL; const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + goto exit; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_VIDEO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_VIDEO; @@ -1339,10 +1346,6 @@ static int __build_screensrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) return WEBRTC_ERROR_INVALID_OPERATION; APPEND_ELEMENT(switch_src_list, screensrc); - if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { - LOG_ERROR("ini_source is NULL"); - goto exit; - } _gst_set_element_properties(screensrc, ini_source->source_element_properties); if (!(videotestsrc = _create_element(DEFAULT_ELEMENT_VIDEOTESTSRC, ELEMENT_NAME_VIDEO_MUTE_SRC))) @@ -1354,98 +1357,130 @@ static int __build_screensrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) "pattern", 2, /* black */ NULL); - if (!(videoswitch = _create_element(DEFAULT_ELEMENT_INPUT_SELECTOR, ELEMENT_NAME_VIDEO_SWITCH))) - goto exit; - APPEND_ELEMENT(element_list, videoswitch); - - if (!source->zerocopy_enabled) { - if (!(videoconvert = _create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) - goto exit; - APPEND_ELEMENT(element_list, videoconvert); - } - - if (__create_rest_of_elements(webrtc, source, true, &element_list, false) != WEBRTC_ERROR_NONE) - goto exit; - if (!__add_elements_to_bin(source->bin, switch_src_list)) { SAFE_G_LIST_FREE(switch_src_list); return WEBRTC_ERROR_INVALID_OPERATION; } - if (!__add_elements_to_bin(source->bin, element_list)) { - __remove_elements_from_bin(source->bin, switch_src_list); - SAFE_G_LIST_FREE(switch_src_list); - SAFE_G_LIST_FREE(element_list); - return WEBRTC_ERROR_INVALID_OPERATION; - } - - if (!__link_switch_srcs(videoswitch, switch_src_list)) + if (!(videoswitch = _create_element(DEFAULT_ELEMENT_INPUT_SELECTOR, ELEMENT_NAME_VIDEO_SWITCH))) goto exit_with_remove_from_bin; - if (source->av[AV_IDX_VIDEO].mute) { - GstPad *pad = gst_element_get_static_pad(videoswitch, "sink_1"); - if (!pad) - goto exit_with_remove_from_bin; - g_object_set(G_OBJECT(videoswitch), "active-pad", pad, NULL); - gst_object_unref(pad); - } - - if (!__link_elements(element_list)) + if (!gst_bin_add(source->bin, videoswitch)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], videoswitch[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(videoswitch)); + SAFE_GST_OBJECT_UNREF(videoswitch); goto exit_with_remove_from_bin; + } - if (!(capsfilter = gst_bin_get_by_name(source->bin, ELEMENT_NAME_RTP_CAPSFILTER))) + if (!__link_switch_srcs(videoswitch, switch_src_list)) { + SAFE_GST_OBJECT_UNREF(videoswitch); goto exit_with_remove_from_bin; - - if (_set_ghost_pad_target(src_pad, capsfilter, true) != WEBRTC_ERROR_NONE) - goto exit_with_remove_from_bin; - - __add_probe_to_pad_for_pause(source, AV_IDX_VIDEO, src_pad, __payloaded_data_probe_cb); - - SAFE_G_LIST_FREE(switch_src_list); - SAFE_G_LIST_FREE(element_list); + } return WEBRTC_ERROR_NONE; exit_with_remove_from_bin: __remove_elements_from_bin(source->bin, switch_src_list); - __remove_elements_from_bin(source->bin, element_list); SAFE_G_LIST_FREE(switch_src_list); - SAFE_G_LIST_FREE(element_list); return WEBRTC_ERROR_INVALID_OPERATION; exit: SAFE_G_LIST_FREE_FULL(switch_src_list, gst_object_unref); - SAFE_G_LIST_FREE_FULL(element_list, gst_object_unref); return WEBRTC_ERROR_INVALID_OPERATION; } -static int __build_rest_of_videosrc(webrtc_s *webrtc, GstPad *src_pad, GstElement *videosrc_element, webrtc_gst_slot_s *source) +//LCOV_EXCL_START +static int __mute_by_changing_property(webrtc_gst_slot_s *source, GstElement *videotestsrc, bool mute) +{ + RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(videotestsrc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "videotestsrc is NULL"); + + if (!g_object_class_find_property(G_OBJECT_GET_CLASS(videotestsrc), "pattern")) { + LOG_ERROR("there is no pattern property"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + /* FIXME: get original value from ini file */ + g_object_set(G_OBJECT(videotestsrc), "pattern", mute ? 2 : 18, NULL); /* 2: black 18: ball */ + + return WEBRTC_ERROR_NONE; +} + +static int __mute_by_manipulating_buffer(webrtc_gst_slot_s *source, GstElement *camerasrc, bool mute) +{ + g_autoptr(GstPad) src_pad = NULL; + + RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(camerasrc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "camerasrc is NULL"); + + src_pad = gst_element_get_static_pad(camerasrc, "src"); + RET_VAL_IF(src_pad == NULL, WEBRTC_ERROR_INVALID_OPERATION, "src_pad is NULL"); + + if (mute && source->camerasrc_probe_id == 0) { + source->camerasrc_probe_id = gst_pad_add_probe(src_pad, GST_PAD_PROBE_TYPE_BUFFER, __camerasrc_probe_cb, NULL, NULL); + if (source->camerasrc_probe_id == 0) { + LOG_ERROR("failed to gst_pad_add_probe()"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + } else if (!mute && source->camerasrc_probe_id != 0) { + gst_pad_remove_probe(src_pad, source->camerasrc_probe_id); + source->camerasrc_probe_id = 0; + + } else { + LOG_ERROR("failed to change mute to (%d)", mute); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; +} + +static int __mute_by_switching_video(webrtc_gst_slot_s *source, GstElement *videoswitch, bool mute) +{ + GstPad *sink_pad = NULL; + + RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(videoswitch == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "videoswitch is NULL"); + + sink_pad = gst_element_get_static_pad(videoswitch, mute ? "sink_1" : "sink_0"); + RET_VAL_IF(sink_pad == NULL, WEBRTC_ERROR_INVALID_OPERATION, "sink_pad is NULL"); + + g_object_set(G_OBJECT(videoswitch), "active-pad", sink_pad, NULL); + gst_object_unref(sink_pad); + + return WEBRTC_ERROR_NONE; +} +//LCOV_EXCL_STOP + +static int __complete_rest_of_videosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { GList *element_list = NULL; + GstElement *videosrc; GstElement *capsfilter; - const ini_item_media_source_s *ini_source; + GstElement *videoswitch = NULL; /* only for screen source */ 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"); - RET_VAL_IF(videosrc_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "videosrc_element is NULL"); 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"); - APPEND_ELEMENT(element_list, videosrc_element); + /* skip when it is already completed. e.g) start() -> stop() -> start() again */ + if (source->av[AV_IDX_VIDEO].render.src_pad_probe_id > 0) + return WEBRTC_ERROR_NONE; - if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { - LOG_ERROR("ini_source is NULL"); - goto exit; - } - _gst_set_element_properties(videosrc_element, ini_source->source_element_properties); + if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_SCREEN) { + GstElement *videoconvert; - /* NOTE: in case of videotestsrc with mute operation */ - if (g_object_class_find_property(G_OBJECT_GET_CLASS(videosrc_element), "pattern") && source->av[AV_IDX_VIDEO].mute) - g_object_set(G_OBJECT(videosrc_element), "pattern", 2, NULL); /* 2: black */ + videoswitch = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SWITCH); + RET_VAL_IF(videoswitch == NULL, WEBRTC_ERROR_INVALID_OPERATION, "videoswitch is NULL"); - /* NOTE: in case of an element that supports tizen zerocopy format, not to emit an error in GST_STATE_PLAYING - * without buffer consumption before finishing negotiation, set this property to 0 here. */ - if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(videosrc_element)), "empty-buffer-timeout")) - g_object_set(G_OBJECT(videosrc_element), "empty-buffer-timeout", 0, NULL); + if (source->av[AV_IDX_VIDEO].mute && + (__mute_by_switching_video(source, videoswitch, source->av[AV_IDX_VIDEO].mute) != WEBRTC_ERROR_NONE)) + goto exit; + + if (!source->zerocopy_enabled) { + if (!(videoconvert = _create_element(DEFAULT_ELEMENT_VIDEOCONVERT, NULL))) + goto exit; + APPEND_ELEMENT(element_list, videoconvert); + } + } if (__create_rest_of_elements(webrtc, source, true, &element_list, false) != WEBRTC_ERROR_NONE) goto exit; @@ -1455,16 +1490,50 @@ static int __build_rest_of_videosrc(webrtc_s *webrtc, GstPad *src_pad, GstElemen return WEBRTC_ERROR_INVALID_OPERATION; } + if ((videosrc = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SRC))) { + + if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST) { + if (source->av[AV_IDX_VIDEO].mute && + (__mute_by_changing_property(source, videosrc, source->av[AV_IDX_VIDEO].mute) != WEBRTC_ERROR_NONE)) + goto exit_with_remove_from_bin; + + } else if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA) { + if (source->av[AV_IDX_VIDEO].mute) { + if (!g_strcmp0(GST_ELEMENT_NAME(videosrc), DEFAULT_ELEMENT_VIDEOTESTSRC)) { + /* in case of emulator */ + if (__mute_by_changing_property(source, videosrc, source->av[AV_IDX_VIDEO].mute) != WEBRTC_ERROR_NONE) + goto exit_with_remove_from_bin; + } else { + if (__mute_by_manipulating_buffer(source, videosrc, source->av[AV_IDX_VIDEO].mute) != WEBRTC_ERROR_NONE) + goto exit_with_remove_from_bin; + } + } + /* NOTE: in case of an element that supports tizen zerocopy format, not to emit an error in GST_STATE_PLAYING + * without buffer consumption before finishing negotiation, set this property to 0 here. */ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(videosrc)), "empty-buffer-timeout")) + g_object_set(G_OBJECT(videosrc), "empty-buffer-timeout", 0, NULL); + } + } + + if (!videoswitch && !videosrc) { + LOG_ERROR("both videoswitch and videosrc are NULL"); + goto exit_with_remove_from_bin; + } + + PREPEND_ELEMENT(element_list, videoswitch ? videoswitch : videosrc); if (!__link_elements(element_list)) goto exit_with_remove_from_bin; if (!(capsfilter = gst_bin_get_by_name(source->bin, ELEMENT_NAME_RTP_CAPSFILTER))) goto exit_with_remove_from_bin; - if (_set_ghost_pad_target(src_pad, capsfilter, true) != WEBRTC_ERROR_NONE) + if (_set_ghost_pad_target(source->av[AV_IDX_VIDEO].src_pad, capsfilter, true) != WEBRTC_ERROR_NONE) + goto exit_with_remove_from_bin; + + if (__link_source_with_webrtcbin(source, webrtc->gst.webrtcbin) != WEBRTC_ERROR_NONE) goto exit_with_remove_from_bin; - __add_probe_to_pad_for_pause(source, AV_IDX_VIDEO, src_pad, __payloaded_data_probe_cb); + __add_probe_to_pad_for_pause(source, AV_IDX_VIDEO, source->av[AV_IDX_VIDEO].src_pad, __payloaded_data_probe_cb); SAFE_G_LIST_FREE(element_list); @@ -1483,13 +1552,18 @@ static int __build_camerasrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { int ret; GstElement *camerasrc; - GstPad *src_pad; + const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_VIDEO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_VIDEO; @@ -1501,29 +1575,42 @@ static int __build_camerasrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) if (!(camerasrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA), ELEMENT_NAME_VIDEO_SRC))) return WEBRTC_ERROR_INVALID_OPERATION; - return __build_rest_of_videosrc(webrtc, src_pad, camerasrc, source); + _gst_set_element_properties(camerasrc, ini_source->source_element_properties); + + /* NOTE: in case of an element that supports tizen zerocopy format, not to emit an error in GST_STATE_PLAYING + * without buffer consumption before finishing negotiation, set this property to 0 here. */ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(camerasrc)), "empty-buffer-timeout")) + g_object_set(G_OBJECT(camerasrc), "empty-buffer-timeout", 0, NULL); + + if (!gst_bin_add(source->bin, camerasrc)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], camerasrc[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(camerasrc)); + SAFE_GST_OBJECT_UNREF(camerasrc); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; } -static int __build_rest_of_audiosrc(webrtc_s *webrtc, GstPad *src_pad, GstElement *audiosrc_element, webrtc_gst_slot_s *source) +static int __complete_rest_of_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { GList *element_list = NULL; + GstElement *audiosrc; GstElement *volume; GstElement *capsfilter; const ini_item_media_source_s *ini_source; 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"); - RET_VAL_IF(audiosrc_element == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "audiosrc_element is NULL"); 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"); - APPEND_ELEMENT(element_list, audiosrc_element); - if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { LOG_ERROR("ini_source is NULL"); - goto exit; + return WEBRTC_ERROR_INVALID_OPERATION; } - _gst_set_element_properties(audiosrc_element, ini_source->source_element_properties); + + /* skip when it is already completed. e.g) start() -> stop() -> start() again */ + if (source->av[AV_IDX_AUDIO].render.src_pad_probe_id > 0) + return WEBRTC_ERROR_NONE; if (!(volume = _create_element(DEFAULT_ELEMENT_VOLUME, ELEMENT_NAME_VOLUME))) goto exit; @@ -1546,16 +1633,26 @@ static int __build_rest_of_audiosrc(webrtc_s *webrtc, GstPad *src_pad, GstElemen return WEBRTC_ERROR_INVALID_OPERATION; } + if (!(audiosrc = gst_bin_get_by_name(source->bin, source->type == WEBRTC_MEDIA_SOURCE_TYPE_MIC ? + ELEMENT_NAME_MIC_SRC : ELEMENT_NAME_AUDIO_SRC))) { + LOG_ERROR("failed to gst_bin_get_by_name()"); + goto exit_with_remove_from_bin; + } + + PREPEND_ELEMENT(element_list, audiosrc); if (!__link_elements(element_list)) goto exit_with_remove_from_bin; if (!(capsfilter = gst_bin_get_by_name(source->bin, ELEMENT_NAME_RTP_CAPSFILTER))) goto exit_with_remove_from_bin; - if (_set_ghost_pad_target(src_pad, capsfilter, true) != WEBRTC_ERROR_NONE) + if (_set_ghost_pad_target(source->av[AV_IDX_AUDIO].src_pad, capsfilter, true) != WEBRTC_ERROR_NONE) goto exit_with_remove_from_bin; - __add_probe_to_pad_for_pause(source, AV_IDX_AUDIO, src_pad, __payloaded_data_probe_cb); + if (__link_source_with_webrtcbin(source, webrtc->gst.webrtcbin) != WEBRTC_ERROR_NONE) + goto exit_with_remove_from_bin; + + __add_probe_to_pad_for_pause(source, AV_IDX_AUDIO, source->av[AV_IDX_AUDIO].src_pad, __payloaded_data_probe_cb); SAFE_G_LIST_FREE(element_list); @@ -1570,41 +1667,85 @@ exit: return WEBRTC_ERROR_INVALID_OPERATION; } +static void __complete_source_foreach_cb(gpointer key, gpointer value, gpointer user_data) +{ + webrtc_gst_slot_s *source = (webrtc_gst_slot_s *)value; + webrtc_s *webrtc = (webrtc_s *)user_data; + + if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_FILE || + source->type == WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET) + return; + + LOG_DEBUG("source[%s, id:%u, type:%d]", (gchar *)key, source->id, source->type); + + if (source->media_types == MEDIA_TYPE_AUDIO) + __complete_rest_of_audiosrc(webrtc, source); + else + __complete_rest_of_videosrc(webrtc, source); +} + +int _complete_sources(webrtc_s *webrtc) +{ + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + + g_hash_table_foreach(webrtc->gst.source_slots, __complete_source_foreach_cb, webrtc); + + return WEBRTC_ERROR_NONE; +} + static int __build_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source, bool use_mic) { int ret; const char *source_factory_name; - GstPad *src_pad; GstElement *audiosrc; + const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_AUDIO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_AUDIO; source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types); source_factory_name = __get_source_element(webrtc, use_mic ? WEBRTC_MEDIA_SOURCE_TYPE_MIC : WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST); - if (!(audiosrc = _create_element(source_factory_name, use_mic ? ELEMENT_NAME_MIC_SRC : NULL))) + if (!(audiosrc = _create_element(source_factory_name, use_mic ? ELEMENT_NAME_MIC_SRC : ELEMENT_NAME_AUDIO_SRC))) return WEBRTC_ERROR_INVALID_OPERATION; - return __build_rest_of_audiosrc(webrtc, src_pad, audiosrc, source); + _gst_set_element_properties(audiosrc, ini_source->source_element_properties); + + if (!gst_bin_add(source->bin, audiosrc)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], audiosrc[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(audiosrc)); + SAFE_GST_OBJECT_UNREF(audiosrc); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; } static int __build_videotestsrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { int ret; GstElement *videotestsrc; - GstPad *src_pad; + const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_VIDEO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_VIDEO; @@ -1613,7 +1754,15 @@ static int __build_videotestsrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) if (!(videotestsrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST), ELEMENT_NAME_VIDEO_SRC))) return WEBRTC_ERROR_INVALID_OPERATION; - return __build_rest_of_videosrc(webrtc, src_pad, videotestsrc, source); + _gst_set_element_properties(videotestsrc, ini_source->source_element_properties); + + if (!gst_bin_add(source->bin, videotestsrc)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], videotestsrc[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(videotestsrc)); + SAFE_GST_OBJECT_UNREF(videotestsrc); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; } //LCOV_EXCL_START @@ -1621,13 +1770,18 @@ static int __build_custom_videosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) { int ret; GstElement *custom_videosrc; - GstPad *src_pad; + const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_VIDEO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_VIDEO; @@ -1636,7 +1790,15 @@ static int __build_custom_videosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) if (!(custom_videosrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO), ELEMENT_NAME_VIDEO_SRC))) return WEBRTC_ERROR_INVALID_OPERATION; - return __build_rest_of_videosrc(webrtc, src_pad, custom_videosrc, source); + _gst_set_element_properties(custom_videosrc, ini_source->source_element_properties); + + if (!gst_bin_add(source->bin, custom_videosrc)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], custom_videosrc[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(custom_videosrc)); + SAFE_GST_OBJECT_UNREF(custom_videosrc); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; } static int __build_custom_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) @@ -1644,13 +1806,18 @@ static int __build_custom_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) int ret; const char *source_factory_name; GstElement *custom_audiosrc; - GstPad *src_pad; + const ini_item_media_source_s *ini_source; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); 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); + if (!(ini_source = _ini_get_source_by_type(&webrtc->ini, source->type))) { + LOG_ERROR("ini_source is NULL"); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + ret = _add_no_target_ghostpad_to_slot(source, true, &source->av[AV_IDX_AUDIO].src_pad); RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()"); source->media_types = MEDIA_TYPE_AUDIO; @@ -1660,7 +1827,15 @@ static int __build_custom_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source) if (!(custom_audiosrc = _create_element(source_factory_name, NULL))) return WEBRTC_ERROR_INVALID_OPERATION; - return __build_rest_of_audiosrc(webrtc, src_pad, custom_audiosrc, source); + _gst_set_element_properties(custom_audiosrc, ini_source->source_element_properties); + + if (!gst_bin_add(source->bin, custom_audiosrc)) { + LOG_ERROR("failed to gst_bin_add(), bin[%s], custom_audiosrc[%s]", GST_ELEMENT_NAME(source->bin), GST_ELEMENT_NAME(custom_audiosrc)); + SAFE_GST_OBJECT_UNREF(custom_audiosrc); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + return WEBRTC_ERROR_NONE; } static int __build_filesrc_bin(webrtc_gst_slot_s *source, media_type_e media_type) @@ -2680,23 +2855,12 @@ static int __add_media_source(webrtc_s *webrtc, int type, unsigned int *source_i return WEBRTC_ERROR_INVALID_OPERATION; } - if (type != WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET && type != WEBRTC_MEDIA_SOURCE_TYPE_FILE && - __link_source_with_webrtcbin(source, webrtc->gst.webrtcbin) != WEBRTC_ERROR_NONE) { - LOG_ERROR("failed to __link_source_with_webrtcbin()"); - goto error_after_insert; - } - *source_id = source->id; LOG_INFO("webrtc[%p] source[%p, name:%s, id:%u]", webrtc, source, bin_name, *source_id); return WEBRTC_ERROR_NONE; -error_after_insert: - g_hash_table_remove(webrtc->gst.source_slots, bin_name); - - return WEBRTC_ERROR_INVALID_OPERATION; - error: g_free(bin_name); g_free(source); @@ -3579,30 +3743,23 @@ static GstPadProbeReturn __camerasrc_probe_cb(GstPad *pad, GstPadProbeInfo *inf static int __mute_camerasrc(webrtc_gst_slot_s *source, bool mute) { - GstElement *camerasrc = NULL; - g_autoptr(GstPad) src_pad = NULL; + int ret; + GstElement *camerasrc; RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(source->type != WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, WEBRTC_ERROR_INVALID_PARAMETER, "invalid source type"); RET_VAL_IF(source->av[AV_IDX_VIDEO].mute == mute, WEBRTC_ERROR_NONE, "Already %s", mute ? "muted" : "unmuted"); - if ((camerasrc = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SRC))) { - src_pad = gst_element_get_static_pad(camerasrc, "src"); - - if (mute && source->camerasrc_probe_id == 0) { - source->camerasrc_probe_id = gst_pad_add_probe(src_pad, GST_PAD_PROBE_TYPE_BUFFER, __camerasrc_probe_cb, NULL, NULL); - if (source->camerasrc_probe_id == 0) { - LOG_ERROR("failed to gst_pad_add_probe()"); - return WEBRTC_ERROR_INVALID_OPERATION; - } + camerasrc = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SRC); + RET_VAL_IF(camerasrc == NULL, WEBRTC_ERROR_INVALID_OPERATION, "camerasrc is NULL"); - } else if (!mute && source->camerasrc_probe_id != 0) { - gst_pad_remove_probe(src_pad, source->camerasrc_probe_id); - source->camerasrc_probe_id = 0; - - } else { - LOG_ERROR("failed to change mute to (%d)", mute); - return WEBRTC_ERROR_INVALID_OPERATION; - } + if (!g_strcmp0(GST_ELEMENT_NAME(camerasrc), DEFAULT_ELEMENT_VIDEOTESTSRC)) { + /* in case of emulator */ + if ((ret = __mute_by_changing_property(source, camerasrc, mute)) != WEBRTC_ERROR_NONE) + return ret; + } else { + if ((ret = __mute_by_manipulating_buffer(source, camerasrc, mute)) != WEBRTC_ERROR_NONE) + return ret; } source->av[AV_IDX_VIDEO].mute = mute; @@ -3610,20 +3767,18 @@ static int __mute_camerasrc(webrtc_gst_slot_s *source, bool mute) return WEBRTC_ERROR_NONE; } -static int __mute_videosrc(webrtc_gst_slot_s *source, bool mute) +static int __mute_screensrc(webrtc_gst_slot_s *source, bool mute) { - GstElement *video_switch = NULL; - GstPad *new_pad = NULL; + int ret; + GstElement *videoswitch; RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(source->type != WEBRTC_MEDIA_SOURCE_TYPE_SCREEN, WEBRTC_ERROR_INVALID_PARAMETER, "invalid source type"); RET_VAL_IF(source->av[AV_IDX_VIDEO].mute == mute, WEBRTC_ERROR_NONE, "Already %s", mute ? "muted" : "unmuted"); - if ((video_switch = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SWITCH))) { - new_pad = gst_element_get_static_pad(video_switch, mute ? "sink_1" : "sink_0"); - RET_VAL_IF(new_pad == NULL, WEBRTC_ERROR_INVALID_OPERATION, "new_pad is NULL"); - - g_object_set(G_OBJECT(video_switch), "active-pad", new_pad, NULL); - gst_object_unref(new_pad); + if ((videoswitch = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SWITCH))) { + if ((ret = __mute_by_switching_video(source, videoswitch, mute)) != WEBRTC_ERROR_NONE) + return ret; } source->av[AV_IDX_VIDEO].mute = mute; @@ -3634,18 +3789,16 @@ static int __mute_videosrc(webrtc_gst_slot_s *source, bool mute) static int __mute_videotestsrc(webrtc_gst_slot_s *source, bool mute) { - GstElement *src_element = NULL; + int ret; + GstElement *videotestsrc; RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL"); + RET_VAL_IF(source->type != WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, WEBRTC_ERROR_INVALID_PARAMETER, "invalid source type"); RET_VAL_IF(source->av[AV_IDX_VIDEO].mute == mute, WEBRTC_ERROR_NONE, "Already %s", mute ? "muted" : "unmuted"); - if ((src_element = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SRC))) { - if (!g_object_class_find_property(G_OBJECT_GET_CLASS(src_element), "pattern")) { - LOG_ERROR("there is no pattern property"); - return WEBRTC_ERROR_INVALID_OPERATION; - } - /* FIXME: get original value from ini file */ - g_object_set(G_OBJECT(src_element), "pattern", mute ? 2 : 18, NULL); /* 2: black 18: ball */ + if ((videotestsrc = gst_bin_get_by_name(source->bin, ELEMENT_NAME_VIDEO_SRC))) { + if ((ret = __mute_by_changing_property(source, videotestsrc, mute)) != WEBRTC_ERROR_NONE) + return ret; } source->av[AV_IDX_VIDEO].mute = mute; @@ -3684,7 +3837,7 @@ typedef int (*videosrc_mute_func)(webrtc_gst_slot_s *source, bool mute); static videosrc_mute_func videosrc_mute_funcs[] = { [WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST] = __mute_videotestsrc, [WEBRTC_MEDIA_SOURCE_TYPE_CAMERA] = __mute_camerasrc, - [WEBRTC_MEDIA_SOURCE_TYPE_SCREEN] = __mute_videosrc + [WEBRTC_MEDIA_SOURCE_TYPE_SCREEN] = __mute_screensrc }; int _set_video_mute(webrtc_s *webrtc, unsigned int source_id, bool mute) @@ -4026,9 +4179,11 @@ static int __build_loopback_render_pipeline(webrtc_s *webrtc, webrtc_gst_slot_s g_object_set(G_OBJECT(appsrc), "is-live", TRUE, "format", GST_FORMAT_TIME, - "caps", source->av[idx].render.appsrc_caps, NULL); + if (source->av[idx].render.appsrc_caps) + g_object_set(G_OBJECT(appsrc), "caps", source->av[idx].render.appsrc_caps, NULL); + if (source->av[idx].render.need_decoding) { GstElement *decodebin = _create_element("decodebin", NULL); if (!decodebin) {