webrtc_source: Postpone the time of linking source with webrtcbin 60/275960/9
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 7 Jun 2022 10:36:22 +0000 (19:36 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 9 Jun 2022 13:53:48 +0000 (22:53 +0900)
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 <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc.c
src/webrtc_source.c

index 9082c57e4183112e26bbc6bed6dc0c100f0895f0..a68716f04c72a76aace72a03131736925b77bb63 100644 (file)
@@ -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);
index 5736a5d10bc9f6afd440bc61afb56b907eb20726..126d61f34b51dbacca5d7d79244aa25ae808f4b0 100644 (file)
@@ -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
index b125ac147e06a458ab3859b737e66cb7771fba94..39fe28609cb397ecaf0be372b37c32706dedd8c8 100644 (file)
@@ -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");
 
index 796242ee05da513b581f3e96d07ead1ea4a619de..15add739e955ce8d582c63f01793915cd651cb1b 100644 (file)
@@ -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) {