Add internal API regarding NULL source type 97/278697/4
authorSangchul Lee <sc11.lee@samsung.com>
Mon, 25 Jul 2022 05:27:28 +0000 (14:27 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 29 Jul 2022 01:51:43 +0000 (10:51 +0900)
New internal enum is added as below.
 : WEBRTC_MEDIA_SOURCE_TYPE_NULL

New internal functions are added as below.
 : webrtc_media_source_set_transceiver_codec()
 : webrtc_media_source_get_transceiver_codec()

Not like, tizen 7.0 branch, webrtc_media_source_set_transceiver_codec()
only supports WEBRTC_MEDIA_SOURCE_TYPE_NULL.

[Version] 0.2.174
[Issue Type] New feature

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

index 860988776a927c0e0ec1c918ec2b63eb33b72a25..b46e048f0f4301c8f47731bf44f639315227764f 100644 (file)
@@ -55,8 +55,23 @@ typedef void *webrtc_signaling_client_h;
 typedef enum {
        WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_AUDIO = 7,  /**<  Custom audio */
        WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO,      /**<  Custom video */
+       WEBRTC_MEDIA_SOURCE_TYPE_NULL,              /**<  Null */
 } webrtc_media_source_type_internal_e;
 
+/**
+ * @internal
+ * @brief Enumeration for WebRTC transceiver codec.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       WEBRTC_TRANSCEIVER_CODEC_PCMU = 0x00000100 | 0x01,   /**< PCMU audio codec */
+       WEBRTC_TRANSCEIVER_CODEC_PCMA = 0x00000100 | 0x02,   /**< PCMA audio codec */
+       WEBRTC_TRANSCEIVER_CODEC_OPUS = 0x00000100 | 0x03,   /**< OPUS audio codec */
+       WEBRTC_TRANSCEIVER_CODEC_VP8  = 0x00000200 | 0x01,   /**< VP8 video codec */
+       WEBRTC_TRANSCEIVER_CODEC_VP9  = 0x00000200 | 0x02,   /**< VP9 video codec */
+       WEBRTC_TRANSCEIVER_CODEC_H264 = 0x00000200 | 0x03,   /**< H264 video codec */
+} webrtc_transceiver_codec_e;
+
 /**
  * @internal
  * @brief Enumeration for WebRTC signaling message type.
@@ -152,6 +167,50 @@ int webrtc_media_source_set_video_loopback_to_ecore_wl(webrtc_h webrtc, unsigned
  */
 int webrtc_add_media_source_internal(webrtc_h webrtc, webrtc_media_source_type_internal_e type, unsigned int *source_id);
 
+/**
+ * @internal
+ * @brief Sets the transceiver codec to the media source.
+ * @since_tizen 6.5
+ * @remarks If @a source_id is not a media source of #WEBRTC_MEDIA_SOURCE_TYPE_NULL,
+ *          this function will return #WEBRTC_ERROR_INVALID_PARAMETER.
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] source_id   The media source id
+ * @param[in] media_type  The media type
+ * @param[in] codec       The transceiver codec to set
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #WEBRTC_ERROR_NONE    Successful
+ * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state
+ * @pre Add media source to @a webrtc to get @a source_id by calling webrtc_add_media_source().
+ * @pre @a webrtc state must be set to #WEBRTC_STATE_IDLE.
+ * @see webrtc_media_source_get_transceiver_codec()
+ * @see webrtc_media_source_foreach_supported_transceiver_codec()
+ */
+int webrtc_media_source_set_transceiver_codec(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec);
+
+/**
+ * @internal
+ * @brief Gets the transceiver codec of the media source.
+ * @since_tizen 6.5
+ * @remarks If @a source_id is a media source of #WEBRTC_MEDIA_SOURCE_TYPE_FILE or #WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET,
+ *          this function will return #WEBRTC_ERROR_INVALID_PARAMETER.
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] source_id   The media source id
+ * @param[in] media_type  The media type
+ * @param[out] codec      Current transceiver codec
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #WEBRTC_ERROR_NONE    Successful
+ * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation
+ * @pre Add media source to @a webrtc to get @a source_id by calling webrtc_add_media_source().
+ * @see webrtc_media_source_set_transceiver_codec()
+ * @see webrtc_media_source_foreach_supported_transceiver_codec()
+ */
+int webrtc_media_source_get_transceiver_codec(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e *codec);
+
 /**
  * @internal
  * @brief Sets media path to the file source.
index a4a0cae4c8e2be75852a440ca36d1345d6324850..492e9a11600ee86d8eb5d8e1676cb90ff4a73b2f 100644 (file)
@@ -479,7 +479,9 @@ typedef struct _webrtc_gst_slot_s {
        int type;
        int media_types;    /* values of media_type_e combined with bitwise 'or' */
        struct {
+               GstWebRTCRTPTransceiver *transceiver;
                int mline;
+               const char *codec;
                GstPad *src_pad;
                gulong src_pad_probe_id;
                gchar *raw_format;
@@ -594,6 +596,9 @@ int _add_media_source_internal(webrtc_s *webrtc, int type, unsigned int *source_
 int _remove_media_source(webrtc_s *webrtc, unsigned int source_id);
 int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction);
 int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction);
+int _set_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, int codec);
+int _get_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, int *codec);
+bool _check_if_transceiver_is_set_to_null_sources(webrtc_s *webrtc);
 int _set_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, bool pause);
 int _get_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, bool *paused);
 int _set_audio_mute(webrtc_s *webrtc, unsigned int source_id, bool mute);
index f53e1e3688caeadaa738680e66739e09a42582d2..19f216b42c9e402a1a87eceffc3209d5b6dc64c1 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.2.173
+Version:    0.2.174
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 2a67eea19e44008cae48916b39f0a9faa1e4c02d..4f829ab97389d70e9deb4de795619bcaf51770de 100644 (file)
@@ -178,6 +178,7 @@ int webrtc_start(webrtc_h webrtc)
        RET_VAL_IF(_webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
        RET_VAL_IF(!_check_if_format_is_set_to_packet_sources(_webrtc), WEBRTC_ERROR_INVALID_OPERATION, "the media format should be set");
        RET_VAL_IF(!_check_if_path_is_set_to_file_sources(_webrtc), WEBRTC_ERROR_INVALID_OPERATION, "the media path should be set");
+       RET_VAL_IF(!_check_if_transceiver_is_set_to_null_sources(_webrtc), WEBRTC_ERROR_INVALID_OPERATION, "transceiver codec is should be set");
 
 #ifndef TIZEN_TV
        ret = _acquire_resource_if_needed(_webrtc);
index ada526817ae00f2fe505b02187739430d0ff57d3..fc6b8e1575058f6c8ae90088b427bca5d7e0b7ed 100644 (file)
@@ -38,6 +38,7 @@
 #define INI_CATEGORY_SOURCE_FILE          "source file"
 #define INI_CATEGORY_SOURCE_CUSTOM_AUDIO  "source custom audio"
 #define INI_CATEGORY_SOURCE_CUSTOM_VIDEO  "source custom video"
+#define INI_CATEGORY_SOURCE_NULL          "source null"
 #define INI_CATEGORY_RENDERING_SINK       "rendering sink"
 #define INI_CATEGORY_RESOURCE_ACQUISITION "resource acquisition"
 #define INI_CATEGORY_VPXENC_PARAMS        "vpxenc params"
@@ -143,7 +144,8 @@ static const char* category_source_names[] = {
        [WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET] = INI_CATEGORY_SOURCE_MEDIA_PACKET,
        [WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_AUDIO] = INI_CATEGORY_SOURCE_CUSTOM_AUDIO,
        [WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO] = INI_CATEGORY_SOURCE_CUSTOM_VIDEO,
-       [WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO + 1] = NULL,
+       [WEBRTC_MEDIA_SOURCE_TYPE_NULL] = INI_CATEGORY_SOURCE_NULL,
+       [WEBRTC_MEDIA_SOURCE_TYPE_NULL + 1] = NULL,
 };
 
 static void __dump_item(const char *prefix_str, ini_item_type_e type, void *item)
index 69424587b76d7defc4a1e5dd8921f433527f7878..6d05ead4e7accc12f3a677c25457b869b57fe341 100644 (file)
@@ -79,6 +79,33 @@ int webrtc_add_media_source_internal(webrtc_h webrtc, webrtc_media_source_type_i
        return ret;
 }
 
+int webrtc_media_source_set_transceiver_codec(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec)
+{
+       g_autoptr(GMutexLocker) locker = NULL;
+       webrtc_s *_webrtc = (webrtc_s *)webrtc;
+
+       RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+
+       locker = g_mutex_locker_new(&_webrtc->mutex);
+
+       RET_VAL_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, "the state should be IDLE");
+
+       return _set_transceiver_codec(webrtc, source_id, media_type, (int) codec);
+}
+
+int webrtc_media_source_get_transceiver_codec(webrtc_h webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e *codec)
+{
+       g_autoptr(GMutexLocker) locker = NULL;
+       webrtc_s *_webrtc = (webrtc_s *)webrtc;
+
+       RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(codec == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec is NULL");
+
+       locker = g_mutex_locker_new(&_webrtc->mutex);
+
+       return _get_transceiver_codec(webrtc, source_id, media_type, (int *)codec);
+}
+
 int webrtc_file_source_set_path(webrtc_h webrtc, unsigned int source_id, const char *path)
 {
        g_autoptr(GMutexLocker) locker = NULL;
index 45d99d81f3cf3884643e6f725b7bd35c4e461833..7a7961342496e714525a1739f0dabcfbfe2eafc4 100644 (file)
@@ -1254,10 +1254,12 @@ static void __webrtcbin_on_new_transceiver_cb(GstElement *webrtcbin, GstWebRTCRT
                for (i = AV_IDX_AUDIO; i < AV_IDX_MAX; i++) {
                        if (!(source->media_types & (i == AV_IDX_AUDIO ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO)))
                                continue;
-                       if (source->av[i].mline == -1) {
+                       if (source->av[i].mline == -1 && !source->av[i].transceiver) {
                                source->av[i].mline = transceiver->mline;
-                               LOG_DEBUG("source[%s, id:%u, mline:%d for %s]",
-                                       (gchar*)key, source->id, source->av[i].mline, i == AV_IDX_AUDIO ? "AUDIO" : "VIDEO");
+                               source->av[i].transceiver = gst_object_ref(transceiver);
+
+                               LOG_DEBUG("source[%s, id:%u, mline:%d, transceiver:%p for %s]",
+                                       (gchar*)key, source->id, source->av[i].mline, source->av[i].transceiver, i == AV_IDX_AUDIO ? "AUDIO" : "VIDEO");
 
                                ini_source = _ini_get_source_by_type(&webrtc->ini, source->type);
                                if (ini_source == NULL)
@@ -1430,7 +1432,7 @@ static gboolean __check_id_equal_cb(gpointer key, gpointer value, gpointer user_
        webrtc_gst_slot_s *slot = value;
 
        if (slot->id == GPOINTER_TO_UINT(user_data)) {
-               LOG_DEBUG("found slot[%s] of id[%u]", GST_ELEMENT_NAME(slot->bin), slot->id);
+               LOG_DEBUG("found slot[%s] of id[%u]", slot->bin ? GST_ELEMENT_NAME(slot->bin) : "null", slot->id);
                return TRUE;
        }
        return FALSE;
index 00740a4fd5be3cd2da2031249c23e93a94ca2258..cefde50ff987176fd4ad9236c68f48dc325dac62 100644 (file)
@@ -102,6 +102,24 @@ static direction_info_s __direction_info[] = {
        [WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV] = { "SENDRECV", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV }
 };
 
+typedef struct {
+       webrtc_transceiver_codec_e codec;
+       const char *gst_media_type;
+       const char *media_type;
+       const char *encoding_name;
+       int clock_rate;
+} rtp_payload_info_s;
+
+static rtp_payload_info_s __payload_info[] = {
+       { WEBRTC_TRANSCEIVER_CODEC_PCMU, MEDIA_TYPE_AUDIO_MULAW, "audio", "PCMU", 8000 },
+       { WEBRTC_TRANSCEIVER_CODEC_PCMA, MEDIA_TYPE_AUDIO_ALAW, "audio", "PCMA", 8000 },
+       { WEBRTC_TRANSCEIVER_CODEC_OPUS, MEDIA_TYPE_AUDIO_OPUS, "audio", "OPUS", 48000 },
+       { WEBRTC_TRANSCEIVER_CODEC_VP8, MEDIA_TYPE_VIDEO_VP8, "video", "VP8", 90000 },
+       { WEBRTC_TRANSCEIVER_CODEC_VP9, MEDIA_TYPE_VIDEO_VP9, "video", "VP9", 90000 },
+       { WEBRTC_TRANSCEIVER_CODEC_H264, MEDIA_TYPE_VIDEO_H264, "video", "H264", 90000 },
+       { 0, NULL, NULL, NULL, 0 }
+};
+
 typedef struct {
        const char *appsrc_name;
        const char *queue_name;
@@ -516,7 +534,7 @@ static GstCaps *__make_default_encoded_caps(webrtc_gst_slot_s *source, webrtc_in
        case WEBRTC_MEDIA_SOURCE_TYPE_CAMERA:
        case WEBRTC_MEDIA_SOURCE_TYPE_SCREEN:
        case WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO:
-               _media_type = __get_video_media_type(ini_source->v_codec);
+               _media_type = __get_video_media_type(source->av[AV_IDX_VIDEO].codec);
                RET_VAL_IF(_media_type == NULL, NULL, "_media_type is NULL");
                caps = __get_caps_from_encoded_video_media_type(_media_type, ini_source->v_width, ini_source->v_height);
 
@@ -527,7 +545,7 @@ static GstCaps *__make_default_encoded_caps(webrtc_gst_slot_s *source, webrtc_in
        case WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST:
        case WEBRTC_MEDIA_SOURCE_TYPE_MIC:
        case WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_AUDIO:
-               _media_type = __get_audio_media_type(ini_source->a_codec);
+               _media_type = __get_audio_media_type(source->av[AV_IDX_AUDIO].codec);
                RET_VAL_IF(_media_type == NULL, NULL, "_media_type is NULL");
                caps = __get_caps_from_encoded_audio_media_type(_media_type, ini_source->a_channels, ini_source->a_samplerate);
                break;
@@ -559,6 +577,18 @@ static GstCaps *__make_default_encoded_caps(webrtc_gst_slot_s *source, webrtc_in
 }
 
 //LCOV_EXCL_START
+static GstCaps *__make_transceiver_caps(rtp_payload_info_s *payload_info, int payload_type)
+{
+       RET_VAL_IF(payload_info == NULL, NULL, "payload_info is NULL");
+
+       return gst_caps_new_simple("application/x-rtp",
+                               "media", G_TYPE_STRING, payload_info->media_type,
+                               "encoding-name", G_TYPE_STRING, payload_info->encoding_name,
+                               "clock-rate", G_TYPE_INT, payload_info->clock_rate,
+                               "payload", G_TYPE_INT, payload_type,
+                               NULL);
+}
+
 /* Use g_free() to free the media_type parameter. */
 static GstCaps *__make_encoded_caps_from_media_format(webrtc_gst_slot_s *source, gchar **media_type)
 {
@@ -1307,6 +1337,7 @@ static int __build_screensrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
        source->media_types = MEDIA_TYPE_VIDEO;
        source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types);
        source->av[AV_IDX_VIDEO].raw_format = g_strdup(ini_source->v_raw_format);
+       source->av[AV_IDX_VIDEO].codec = ini_source->v_codec;
 
        if (!(screensrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_SCREEN), ELEMENT_NAME_SCREENSRC)))
                return WEBRTC_ERROR_INVALID_OPERATION;
@@ -1407,6 +1438,7 @@ static int __build_camerasrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
        source->media_types = MEDIA_TYPE_VIDEO;
        source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types);
        source->av[AV_IDX_VIDEO].raw_format = g_strdup(ini_source->v_raw_format);
+       source->av[AV_IDX_VIDEO].codec = ini_source->v_codec;
 
 #ifndef TIZEN_TV
        if (webrtc->ini.resource_acquisition.camera)
@@ -1485,6 +1517,7 @@ static int __build_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source, bool us
        source->media_types = MEDIA_TYPE_AUDIO;
        source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types);
        source->av[AV_IDX_AUDIO].raw_format = g_strdup(ini_source->a_raw_format);
+       source->av[AV_IDX_AUDIO].codec = ini_source->a_codec;
 
        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)))
@@ -1558,6 +1591,7 @@ static int __build_videotestsrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
        source->media_types = MEDIA_TYPE_VIDEO;
        source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types);
        source->av[AV_IDX_VIDEO].raw_format = g_strdup(ini_source->v_raw_format);
+       source->av[AV_IDX_VIDEO].codec = ini_source->v_codec;
 
        if (!(videotestsrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST), ELEMENT_NAME_VIDEO_SRC)))
                return WEBRTC_ERROR_INVALID_OPERATION;
@@ -1608,6 +1642,7 @@ exit:
 static int __build_custom_videosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
 {
        int ret = WEBRTC_ERROR_NONE;
+       const ini_item_media_source_s *ini_source;
        GstElement *custom_videosrc;
        GstElement *capsfilter;
        GList *element_list = NULL;
@@ -1617,11 +1652,17 @@ static int __build_custom_videosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
        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");
 
+       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, &src_pad);
        RET_VAL_IF(ret != WEBRTC_ERROR_NONE, ret, "failed to _add_no_target_ghostpad_to_slot()");
 
        source->media_types = MEDIA_TYPE_VIDEO;
        source->zerocopy_enabled = __is_hw_encoder_used(webrtc, source->type, source->media_types);
+       source->av[AV_IDX_VIDEO].codec = ini_source->v_codec;
 
        if (!(custom_videosrc = _create_element(__get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO), ELEMENT_NAME_VIDEO_SRC)))
                return WEBRTC_ERROR_INVALID_OPERATION;
@@ -1666,6 +1707,7 @@ exit:
 static int __build_custom_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
 {
        int ret = WEBRTC_ERROR_NONE;
+       const ini_item_media_source_s *ini_source;
        const char *source_factory_name;
        GstElement *custom_audiosrc;
        GstElement *volume;
@@ -1677,11 +1719,17 @@ static int __build_custom_audiosrc(webrtc_s *webrtc, webrtc_gst_slot_s *source)
        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");
 
+       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, &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->av[AV_IDX_AUDIO].codec = ini_source->a_codec;
 
        source_factory_name = __get_source_element(webrtc, WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_AUDIO);
        if (!(custom_audiosrc = _create_element(source_factory_name, NULL)))
@@ -2553,9 +2601,11 @@ void _source_slot_destroy_cb(gpointer data)
        RET_IF(source == NULL, "source is NULL");
 
        LOG_DEBUG("[%s, id:%u, media_types:0x%x, [AUDIO]mline:%d, [VIDEO]mline:%d] is removed",
-               GST_ELEMENT_NAME(source->bin), source->id, source->media_types, source->av[AV_IDX_AUDIO].mline, source->av[AV_IDX_VIDEO].mline);
+               source->bin ? GST_ELEMENT_NAME(source->bin) : "null", source->id, source->media_types,
+               source->av[AV_IDX_AUDIO].mline, source->av[AV_IDX_VIDEO].mline);
 
-       gst_element_foreach_src_pad(GST_ELEMENT(source->bin), __foreach_src_pad_cb, source);
+       if (source->bin)
+               gst_element_foreach_src_pad(GST_ELEMENT(source->bin), __foreach_src_pad_cb, source);
 
        for (i = 0; i < AV_IDX_MAX; i++) {
                __remove_probe_from_pad_for_pause(source, i);
@@ -2569,10 +2619,14 @@ void _source_slot_destroy_cb(gpointer data)
                        SAFE_GST_OBJECT_UNREF(source->av[i].render.pipeline);
                }
 
+               if (source->av[i].transceiver)
+                       gst_object_unref(source->av[i].transceiver);
+
                g_free(source->av[i].raw_format);
        }
 
-       gst_bin_remove(GST_BIN(gst_element_get_parent(source->bin)), GST_ELEMENT(source->bin));
+       if (source->bin)
+               gst_bin_remove(GST_BIN(gst_element_get_parent(source->bin)), GST_ELEMENT(source->bin));
 
        if (source->media_format)
                media_format_unref(source->media_format);
@@ -2672,6 +2726,44 @@ exit:
        return ret;
 }
 
+//LCOV_EXCL_START
+static int __add_null_source(webrtc_s *webrtc, unsigned int *source_id)
+{
+       unsigned int id;
+       webrtc_gst_slot_s *source = NULL;
+       gchar *bin_name = NULL;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+
+       /* bin_name/source will be freed by function which is set to g_hash_table_new_full() */
+       id = __get_unoccupied_id(webrtc->gst.source_slots);
+       RET_VAL_IF(id == 0, WEBRTC_ERROR_INVALID_OPERATION, "source_slots are full");
+
+       bin_name = g_strdup_printf("media_source_%u", id);
+
+       MALLOC_AND_INIT_SLOT(source, id, bin_name, webrtc);
+       source->type = WEBRTC_MEDIA_SOURCE_TYPE_NULL;
+       /* This functionality comes from 7.0 branch, but it is not prepared for all the codes of 7.0, we unref the bin here temporarily. */
+       SAFE_GST_OBJECT_UNREF(source->bin);
+
+       /* The gst_element_get_request_pad() of webrtcbin will trigger the transceiver callback. To update the mline value of
+        * new transceiver object to the source structure in the callback, hash table inserting should be preceded. */
+       if (!g_hash_table_insert(webrtc->gst.source_slots, bin_name, (gpointer)source)) {
+               LOG_ERROR("should not be reached here, name[%s] already exist, source id[%u] will be removed", bin_name, source->id);
+               g_hash_table_remove(webrtc->gst.source_slots, bin_name);
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       *source_id = source->id;
+
+       LOG_INFO("added a source slot[%p, id:%u]", source, source->id);
+
+       return WEBRTC_ERROR_NONE;
+}
+//LCOV_EXCL_STOP
+
 static int __add_media_source(webrtc_s *webrtc, int type, unsigned int *source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
@@ -2751,9 +2843,12 @@ int _add_media_source_internal(webrtc_s *webrtc, int type, unsigned int *source_
        RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
        RET_VAL_IF(source_id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source_id is NULL");
        RET_VAL_IF(type <= WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET, WEBRTC_ERROR_INVALID_PARAMETER, "invalid internal source type(%d)", type);
-       RET_VAL_IF(type > WEBRTC_MEDIA_SOURCE_TYPE_CUSTOM_VIDEO, WEBRTC_ERROR_INVALID_PARAMETER, "invalid internal source type(%d)", type);
+       RET_VAL_IF(type > WEBRTC_MEDIA_SOURCE_TYPE_NULL, WEBRTC_ERROR_INVALID_PARAMETER, "invalid internal source type(%d)", type);
        RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
 
+       if (type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)
+               return __add_null_source(webrtc, source_id);
+
        return __add_media_source(webrtc, type, source_id);
 }
 //LCOV_EXCL_STOP
@@ -2812,6 +2907,8 @@ int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_
        RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
        RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
        RET_VAL_IF((source = _get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL && direction != WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY,
+               WEBRTC_ERROR_INVALID_PARAMETER, "null source only allow RECVONLY direction");
 
        if (media_type == WEBRTC_MEDIA_TYPE_AUDIO && source->media_types & MEDIA_TYPE_AUDIO) {
                mline = source->av[AV_IDX_AUDIO].mline;
@@ -2890,6 +2987,153 @@ end:
        return ret;
 }
 
+//LCOV_EXCL_START
+static int __convert_audio_codec(const char *codec_name, webrtc_transceiver_codec_e *codec)
+{
+       RET_VAL_IF(codec_name == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec_name is NULL");
+       RET_VAL_IF(codec == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec is NULL");
+
+       if (!strcmp(codec_name, "pcmu") || !strcmp(codec_name, "PCMU")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_PCMU;
+               return WEBRTC_ERROR_NONE;
+       }
+       if (!strcmp(codec_name, "pcma") || !strcmp(codec_name, "PCMA")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_PCMA;
+               return WEBRTC_ERROR_NONE;
+       }
+       if (!strcmp(codec_name, "opus") || !strcmp(codec_name, "OPUS")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_OPUS;
+               return WEBRTC_ERROR_NONE;
+       }
+
+       LOG_ERROR("not supported audio codec_name[%s]", codec_name);
+
+       return WEBRTC_ERROR_INVALID_PARAMETER;
+}
+
+static int __convert_video_codec(const char *codec_name, webrtc_transceiver_codec_e *codec)
+{
+       RET_VAL_IF(codec_name == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec_name is NULL");
+       RET_VAL_IF(codec == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec is NULL");
+
+       if (!strcmp(codec_name, "vp8") || !strcmp(codec_name, "VP8")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_VP8;
+               return WEBRTC_ERROR_NONE;
+       }
+       if (!strcmp(codec_name, "vp9") || !strcmp(codec_name, "VP9")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_VP9;
+               return WEBRTC_ERROR_NONE;
+       }
+       if (!strcmp(codec_name, "h264") || !strcmp(codec_name, "H264")) {
+               *codec = WEBRTC_TRANSCEIVER_CODEC_H264;
+               return WEBRTC_ERROR_NONE;
+       }
+
+       LOG_ERROR("not supported video codec_name[%s]", codec_name);
+
+       return WEBRTC_ERROR_INVALID_PARAMETER;
+}
+
+typedef int (*convert_codec_func)(const char *codec_name, webrtc_transceiver_codec_e *codec);
+
+static convert_codec_func convert_codec_funcs[] = {
+       [WEBRTC_MEDIA_TYPE_AUDIO] = __convert_audio_codec,
+       [WEBRTC_MEDIA_TYPE_VIDEO] = __convert_video_codec,
+};
+
+int _set_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, int codec)
+{
+       webrtc_gst_slot_s *source;
+       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
+       int i;
+       rtp_payload_info_s *payload_info = NULL;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+       RET_VAL_IF((source = _get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF((source->type != WEBRTC_MEDIA_SOURCE_TYPE_NULL), WEBRTC_ERROR_INVALID_PARAMETER, "this API only supports the null source");
+
+       for (i = 0; i != (int)__payload_info[i].codec; i++) {
+               if (__payload_info[i].codec != (webrtc_transceiver_codec_e)codec)
+                       continue;
+               payload_info = &__payload_info[i];
+               break;
+       }
+       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find payload_info");
+
+       /* FIXME: check media_type with valid codec type */
+       source->media_types |= (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO;
+
+       source->av[av_idx].codec = payload_info->encoding_name;
+
+       if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL) {
+               GstWebRTCRTPTransceiver *trans = NULL;
+               GstCaps *caps;
+               int payload_id;
+
+               if ((payload_id = __get_fixed_payload_id(payload_info->gst_media_type)) == -1)
+                       if ((payload_id = __get_available_payload_id(webrtc)) == 0)
+                               return WEBRTC_ERROR_NONE;
+               source->av[av_idx].payload_id = payload_id;
+
+               caps = __make_transceiver_caps(payload_info, payload_id);
+               PRINT_CAPS(caps, "transceiver");
+
+               if (!source->av[av_idx].transceiver) {
+                       g_signal_emit_by_name(webrtc->gst.webrtcbin, "add-transceiver",
+                               GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, caps, &trans, NULL);
+                       gst_object_unref(trans);
+               } else {
+                       g_object_set(G_OBJECT(source->av[av_idx].transceiver), "codec-preferences", caps, NULL);
+               }
+               LOG_DEBUG("webrtc[%p] source_id[%u] [%s] transceiver[%p] codec[%s]",
+                       webrtc, source_id, payload_info->media_type, source->av[av_idx].transceiver, payload_info->encoding_name);
+
+               gst_caps_unref(caps);
+       }
+
+       /* FIXME: to utilize 'codec-preferences' of trans object, we need to re-create and re-link elements again */
+       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] codec[%s]", webrtc, source_id, media_type, payload_info->encoding_name);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, int *codec)
+{
+       int ret;
+       webrtc_gst_slot_s *source;
+       const char *codec_str;
+       webrtc_transceiver_codec_e _codec;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(codec == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "codec is NULL");
+       RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+       RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
+       RET_VAL_IF((source = _get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+
+       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO && source->media_types & MEDIA_TYPE_AUDIO) {
+               codec_str = source->av[AV_IDX_AUDIO].codec;
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
+               codec_str = source->av[AV_IDX_VIDEO].codec;
+
+       } else {
+               LOG_ERROR("invalid media_type[%d] for source[media_types:0x%x, id:%u]", media_type, source->media_types, source_id);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       if ((ret = convert_codec_funcs[media_type](codec_str, &_codec)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       *codec = _codec;
+
+       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] codec[%s]", webrtc, source_id, media_type, codec_str);
+
+       return WEBRTC_ERROR_NONE;
+}
+//LCOV_EXCL_STOP
+
 int _set_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, bool pause)
 {
        webrtc_gst_slot_s *source;
@@ -2898,6 +3142,7 @@ int _set_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e med
        RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
        RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
        RET_VAL_IF((source = _get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "could not find source");
+       RET_VAL_IF(source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL, WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the null source");
 
        switch (media_type) {
        case WEBRTC_MEDIA_TYPE_AUDIO:
@@ -2931,6 +3176,7 @@ int _get_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e med
        RET_VAL_IF(paused == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "paused is NULL");
        RET_VAL_IF(webrtc->gst.source_slots == NULL, WEBRTC_ERROR_INVALID_OPERATION, "source_slots is NULL");
        RET_VAL_IF((source = _get_slot_by_id(webrtc->gst.source_slots, source_id)) == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "could not find source");
+       RET_VAL_IF(source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL, WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the null source");
 
        switch (media_type) {
        case WEBRTC_MEDIA_TYPE_AUDIO:
@@ -2964,6 +3210,7 @@ int _set_video_resolution(webrtc_s *webrtc, unsigned int source_id, int width, i
        RET_VAL_IF((source->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_PARAMETER, "it's not a video source");
        RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_FILE), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the file source");
        RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the media packet source");
+       RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the null source");
 
        if (webrtc->state != WEBRTC_STATE_IDLE) {
                ini_item_media_source_s *ini_source;
@@ -2999,6 +3246,7 @@ int _get_video_resolution(webrtc_s *webrtc, unsigned int source_id, int *width,
        RET_VAL_IF((source->media_types & MEDIA_TYPE_VIDEO) == 0x0, WEBRTC_ERROR_INVALID_PARAMETER, "it's not a video source");
        RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_FILE), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the file source");
        RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_MEDIA_PACKET), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the media packet source");
+       RET_VAL_IF((source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL), WEBRTC_ERROR_INVALID_PARAMETER, "this API does not support the null source");
 
        *width = source->video_info.width;
        *height = source->video_info.height;
@@ -3130,6 +3378,38 @@ bool _check_if_format_is_set_to_packet_sources(webrtc_s *webrtc)
 }
 
 //LCOV_EXCL_START
+static gboolean __check_transceiver_is_not_set_cb(gpointer key, gpointer value, gpointer user_data)
+{
+       const webrtc_gst_slot_s *source = (webrtc_gst_slot_s *)value;
+       int i;
+
+       if (source->type == GPOINTER_TO_INT(user_data)) {
+               LOG_INFO("found media null source[%p, id:%u]", source, source->id);
+               for (i = 0; i < AV_IDX_MAX; i++) {
+                       if (source->av[i].transceiver)
+                               return FALSE;
+               }
+               return TRUE;
+       }
+       return FALSE;
+}
+
+bool _check_if_transceiver_is_set_to_null_sources(webrtc_s *webrtc)
+{
+       const webrtc_gst_slot_s *source;
+
+       RET_VAL_IF(webrtc == NULL, false, "webrtc is NULL");
+
+       source = g_hash_table_find(webrtc->gst.source_slots, __check_transceiver_is_not_set_cb, GINT_TO_POINTER(WEBRTC_MEDIA_SOURCE_TYPE_NULL));
+       if (source) {
+               LOG_ERROR("transceiver is not set to the media null source[%u]", source->id);
+               LOG_ERROR("please use webrtc_media_source_set_transceiver_codec()");
+               return false;
+       }
+
+       return true;
+}
+
 static void __release_filesrc_resources(webrtc_gst_slot_s *source)
 {
        GstElement *appsrc = NULL;