Add webrtc_transceiver.c and related codes are moved into it 16/280616/3
authorSangchul Lee <sc11.lee@samsung.com>
Thu, 1 Sep 2022 07:37:42 +0000 (16:37 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 2 Sep 2022 05:28:51 +0000 (14:28 +0900)
[Version] 0.3.228
[Issue Type] Refactoring

Change-Id: Ic010a677849bc9253e3fcb0459af2a8dcf1eb9d5
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
include/webrtc_source_private.h
packaging/capi-media-webrtc.spec
src/webrtc_private.c
src/webrtc_source.c
src/webrtc_source_private.c
src/webrtc_transceiver.c [new file with mode: 0644]

index b7504c7ae0ff63b83c4e2feb62f69cd6f2ad3547..0f645e19df75476e6594edbc40e4e86c45b28116 100644 (file)
@@ -269,7 +269,9 @@ do { \
 #define MEDIA_TYPE_VIDEO_JPEG           "image/jpeg"
 
 #define WEBRTC_DISPLAY_TYPE_ECORE_WL    2
-#define PAYLOAD_TYPE_BITS               32 /* 96 ~ 127 */
+#define MIN_DYNAMIC_PAYLOAD_TYPE        96
+#define MAX_DYNAMIC_PAYLOAD_TYPE        127
+#define PAYLOAD_TYPE_BITS               MAX_DYNAMIC_PAYLOAD_TYPE - MIN_DYNAMIC_PAYLOAD_TYPE + 1
 #define TRACK_ID_THRESHOLD_OF_LOOPBACK  100
 #define MAX_MLINE_NUM                   32
 #define MAX_SOURCE_NUM                  32
@@ -633,6 +635,14 @@ typedef struct {
        GstWebRTCRTPTransceiverDirection gst;
 } direction_info_s;
 
+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;
+
 typedef struct _webrtc_signal_s {
        GObject *obj;
        gulong signal_id;
@@ -685,6 +695,21 @@ const char *_get_first_codec_from_ini(webrtc_ini_s *ini, int src_type, media_typ
 const char *_get_hw_encoder_from_ini(webrtc_ini_s *ini, int src_type, media_type_e media_type);
 GStrv _get_supported_codecs_from_ini(webrtc_ini_s *ini, int src_type, media_type_e media_type);
 
+/* transceiver */
+void _webrtcbin_on_new_transceiver_cb(GstElement *webrtcbin, GstWebRTCRTPTransceiver *transceiver, gpointer user_data);
+void _update_transceivers_fec(webrtc_s *webrtc, bool is_offer);
+void _update_transceivers_for_offer(webrtc_s *webrtc);
+int _set_payload_type(webrtc_s *webrtc, webrtc_gst_slot_s *source, int av_idx, const gchar *media_type);
+int _get_fixed_payload_type(const gchar *media_type);
+void _return_payload_type(webrtc_s *webrtc, unsigned int payload_type);
+void _check_and_add_recvonly_transceiver(webrtc_gst_slot_s *source);
+int _add_transceiver(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info);
+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, webrtc_transceiver_codec_e codec);
+int _get_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e *codec);
+int _foreach_supported_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_media_source_supported_transceiver_codec_cb callback, void *user_data);
+
 /* file source */
 int _gst_filesrc_pipeline_set_state(webrtc_s *webrtc, GstState state);
 int _set_filesrc_looping(webrtc_s *webrtc, unsigned int source_id, bool looping);
@@ -714,18 +739,10 @@ bool _is_screen_source_cropped(webrtc_gst_slot_s *source);
 int _complete_sources(webrtc_s *webrtc);
 const char *_get_audio_media_type(const char *codec_name);
 const char *_get_video_media_type(const char *codec_name);
-int _get_fixed_payload_type(const gchar *media_type);
-void _update_transceivers_for_offer(webrtc_s *webrtc);
 void _source_slot_destroy_cb(gpointer data);
 int _add_media_source(webrtc_s *webrtc, int type, unsigned int *source_id);
 int _add_media_source_internal(webrtc_s *webrtc, int type, unsigned int *source_id);
 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);
-direction_info_s *_convert_transceiver_direction(webrtc_transceiver_direction_e direction);
-int _set_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec);
-int _get_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e *codec);
-int _foreach_supported_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_media_source_supported_transceiver_codec_cb callback, void *user_data);
 bool _check_if_codec_is_set_to_null_sources(webrtc_s *webrtc);
 bool _check_if_path_is_set_to_file_sources(webrtc_s *webrtc);
 bool _check_if_format_is_set_to_packet_sources(webrtc_s *webrtc);
index 0a5c95516ca1eed06919d6f439edbf4abd064da8..4c052708bf857ad534293b9ea8021fe611b48fea 100644 (file)
@@ -74,14 +74,6 @@ typedef enum {
        ELEMENT_FAKESINK
 } gst_element_e;
 
-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;
-
 const char *_get_audio_format_name(media_format_mimetype_e mime_type);
 const char *_get_video_format_name(media_format_mimetype_e mime_type, bool zerocopy_enabled);
 const char *_get_source_element(webrtc_s *webrtc, int type);
@@ -93,17 +85,12 @@ GstCaps *_get_caps_from_encoded_audio_media_type(const char *media_type, int cha
 GstCaps *_get_caps_from_encoded_video_media_type(const char *media_type, int width, int height);
 GstCaps *_make_rtp_caps(const gchar *media_type, unsigned int payload_type, webrtc_gst_slot_s *source, GstElement *encoder);
 const char *_get_element_name(int av_idx, gst_element_e element);
-int _set_payload_type(webrtc_s *webrtc, webrtc_gst_slot_s *source, int av_idx, const gchar *media_type);
 GstPadProbeReturn _payloaded_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
 void _add_probe_to_pad_for_pause(webrtc_gst_slot_s *source, unsigned int idx, GstPad *pad, void *probe_cb);
 void _remove_probe_from_pad_for_pause(webrtc_gst_slot_s *source, unsigned int idx);
 GstPadProbeReturn _source_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
 void _add_probe_to_pad_for_render(webrtc_gst_slot_s *source, unsigned int idx, GstPad *pad, void *probe_cb);
 void _remove_probe_from_pad_for_render(webrtc_gst_slot_s *source, unsigned int idx);
-rtp_payload_info_s * _get_payload_info(webrtc_transceiver_codec_e codec);
-rtp_payload_info_s * _get_payload_info_by_encoding_name(const char *encoding_name);
-int _add_transceiver(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info);
-int _update_transceiver_with_pt(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info);
 int _link_source_with_webrtcbin(webrtc_gst_slot_s *source, GstElement *webrtcbin);
 int _create_rest_of_elements(webrtc_s *webrtc, webrtc_gst_slot_s *source, bool need_capsfilter, GList **element_list, bool is_audio);
 int _set_encoder_element_bitrate(GstElement *encoder, int target_bitrate);
index 282e60c81b270eae19630f780951387d0b6ba5f5..193410a212fb3ad615c912a11f551c2b06299726 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.3.227
+Version:    0.3.228
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index b852d44a91841152a4674f8535adf59d427ea306..a05b75fbec1a75a9d380f6832dafeda2ad071db2 100644 (file)
@@ -1333,37 +1333,6 @@ int _set_ghost_pad_target(GstPad *ghost_pad, GstElement *target_element, bool is
        return WEBRTC_ERROR_NONE;
 }
 
-static void __webrtcbin_transceiver_set_ulpfec_red(webrtc_s *webrtc, GstWebRTCRTPTransceiver *transceiver)
-{
-       GstElement *rtpbin;
-
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-       RET_IF(transceiver == NULL, "transceiver is NULL");
-
-       if (!(rtpbin = gst_bin_get_by_name(GST_BIN(webrtc->gst.webrtcbin), "rtpbin"))) {
-               LOG_ERROR("failed to get rtpbin");
-               return;
-       }
-
-       g_object_set(transceiver, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
-       LOG_INFO("set ULPFEC/RED to transceiver[%p]", transceiver);
-
-       g_object_set(G_OBJECT(rtpbin), "do-lost", TRUE, NULL);
-       LOG_DEBUG("set do-lost to rtpbin[%p], ULPFEC recovers a lost packet when do-lost event occurs", rtpbin);
-
-       gst_object_unref(rtpbin);
-}
-
-static void __webrtcbin_transceiver_set_fec_percentage(webrtc_s *webrtc, GstWebRTCRTPTransceiver *transceiver, unsigned int fec_percentage)
-{
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-       RET_IF(transceiver == NULL, "transceiver is NULL");
-
-       g_object_set(transceiver, "fec-percentage", fec_percentage, NULL);
-
-       LOG_INFO("set fec-percentage[%u] to transceiver[%p]", fec_percentage, transceiver);
-}
-
 //LCOV_EXCL_START
 gchar * _get_media_type_from_pad(GstPad *pad)
 {
@@ -1492,107 +1461,6 @@ static void __webrtcbin_no_more_pads_cb(GstElement *webrtcbin, gpointer user_dat
 }
 //LCOV_EXCL_STOP
 
-static void __webrtcbin_on_new_transceiver_cb(GstElement *webrtcbin, GstWebRTCRTPTransceiver *transceiver, gpointer user_data)
-{
-       webrtc_s *webrtc = (webrtc_s *)user_data;
-       webrtc_gst_slot_s *source;
-       int i, j;
-       guint mlineindex;
-       gchar *mid;
-       GstWebRTCRTPTransceiverDirection direction;
-       GstWebRTCKind kind;
-       GstCaps *caps;
-
-       RET_IF(webrtcbin == NULL, "webrtcbin is NULL");
-       RET_IF(transceiver == NULL, "transceiver is NULL");
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-
-       g_object_get(G_OBJECT(transceiver),
-               "mlineindex", &mlineindex,
-               "mid", &mid,
-               "direction", &direction,
-               "kind", &kind,
-               "codec-preferences", &caps,
-               NULL);
-
-       LOG_DEBUG("webrtc[%p] new transceiver[%p, mlineindex:%u, mid:%s, direction:%d, kind:%d] user_data[%p]",
-               webrtc, transceiver, mlineindex, mid, direction, kind, user_data);
-       PRINT_CAPS(caps, "codec preferences");
-
-       /* Code below is for the scenario of an answerer without any added media sources. */
-       if (g_hash_table_size(webrtc->gst.source_slots) == 0) {
-               if (mlineindex >= MAX_MLINE_NUM) {
-                       LOG_ERROR("mlineindex[%u] exceeds the max value", mlineindex);
-                       return;
-               }
-               if (webrtc->data_recovery_types[mlineindex].red &&
-                       webrtc->data_recovery_types[mlineindex].ulpfec)
-                       __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
-               return;
-       }
-
-       for (i = 0; i < MAX_SOURCE_NUM; i++) {
-               if (!(source = webrtc->gst.sources[i]))
-                       continue;
-               for (j = AV_IDX_AUDIO; j < AV_IDX_MAX; j++) {
-                       if (!(source->media_types & (j == AV_IDX_AUDIO ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO)))
-                               continue;
-                       if (source->av[j].transceiver)
-                               continue;
-
-                       source->av[j].transceiver = gst_object_ref(transceiver);
-                       g_object_set(G_OBJECT(transceiver), "direction", _convert_transceiver_direction(source->av[j].direction)->gst, NULL);
-
-                       LOG_INFO("source->id[%u] transceiver[%p for %s, direction:%s]",
-                               source->id, source->av[j].transceiver, j == AV_IDX_AUDIO ? "AUDIO" : "VIDEO",
-                               _convert_transceiver_direction(source->av[j].direction)->str);
-                       return;
-               }
-       }
-}
-
-void _update_transceivers_fec(webrtc_s *webrtc, bool is_offer)
-{
-       GstWebRTCRTPTransceiver *transceiver;
-       webrtc_gst_slot_s *source;
-       int i, j;
-       GstWebRTCRTPTransceiverDirection direction;
-
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-
-       for (i = 0; i < MAX_SOURCE_NUM; i++) {
-               if (!(source = webrtc->gst.sources[i]))
-                       continue;
-               for (j = AV_IDX_AUDIO; j < AV_IDX_MAX; j++) {
-                       if (!(source->media_types & (j == AV_IDX_AUDIO ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO)))
-                               continue;
-                       if (!(transceiver = source->av[j].transceiver))
-                               continue;
-
-                       g_object_get(G_OBJECT(transceiver), "direction", &direction, NULL);
-
-                       if (is_offer) {
-                               bool use_ulpfec_red;
-                               _get_use_ulpfec_red_from_ini(&webrtc->ini, source->type, &use_ulpfec_red);
-
-                               if (!use_ulpfec_red)
-                                       continue;
-
-                               __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
-                       } else {
-                               if (!webrtc->data_recovery_types[i].red ||
-                                       !webrtc->data_recovery_types[i].ulpfec)
-                                       continue;
-                               __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
-                       }
-
-                       if (direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
-                               direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)
-                               __webrtcbin_transceiver_set_fec_percentage(webrtc, transceiver, _get_fec_percentage_from_ini(&webrtc->ini, source->type));
-               }
-       }
-}
-
 int _gst_build_pipeline(webrtc_s *webrtc)
 {
        gchar *webrtcbin_name;
@@ -1647,7 +1515,7 @@ int _gst_build_pipeline(webrtc_s *webrtc)
        _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "notify::ice-connection-state", G_CALLBACK(__webrtcbin_ice_connection_state_cb), webrtc);
        _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "pad-added", G_CALLBACK(__webrtcbin_pad_added_cb), webrtc);
        _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "no-more-pads", G_CALLBACK(__webrtcbin_no_more_pads_cb), webrtc);
-       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-new-transceiver", G_CALLBACK(__webrtcbin_on_new_transceiver_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-new-transceiver", G_CALLBACK(_webrtcbin_on_new_transceiver_cb), webrtc);
        _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-data-channel", G_CALLBACK(_webrtcbin_on_data_channel_cb), webrtc);
 
        if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), webrtc->gst.webrtcbin)) {
index 711108a7d02b9bf066a459c91abd7751e9a97dd3..fa36b7b885881d3e957aab1c8f320b585c7bc550 100644 (file)
 #include <tbm_surface_internal.h>
 #include <sound_manager_internal.h>
 
-#define MIN_DYNAMIC_PAYLOAD_TYPE             96
-#define MAX_DYNAMIC_PAYLOAD_TYPE             127
-
-static direction_info_s __direction_info[] = {
-       [WEBRTC_TRANSCEIVER_DIRECTION_SENDONLY] = { "SENDONLY", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY },
-       [WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY] = { "RECVONLY", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY },
-       [WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV] = { "SENDRECV", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV }
-};
-
 static GstPadProbeReturn __camerasrc_probe_cb(GstPad *pad,  GstPadProbeInfo *info, gpointer u_data);
 
 static GstCaps *__make_video_raw_caps_with_framerate(webrtc_gst_slot_s *source, webrtc_ini_s *ini, int framerate)
@@ -62,22 +53,6 @@ static GstCaps *__make_video_raw_caps_with_framerate(webrtc_gst_slot_s *source,
        return caps;
 }
 
-static void __return_payload_type(webrtc_s *webrtc, unsigned int payload_type)
-{
-       int i;
-       int bitmask = 0x1;
-
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-       RET_IF(payload_type < MIN_DYNAMIC_PAYLOAD_TYPE || payload_type > MAX_DYNAMIC_PAYLOAD_TYPE, "invalid payload_type(%u)", payload_type);
-
-       i = payload_type - MIN_DYNAMIC_PAYLOAD_TYPE;
-
-       while (i-- > 0)
-               bitmask <<= 1;
-
-       webrtc->payload_types ^= bitmask;
-}
-
 static bool __link_switch_srcs(GstElement *switch_element, GList *switch_src_list)
 {
        GstElement *element;
@@ -492,73 +467,6 @@ exit:
        return WEBRTC_ERROR_INVALID_OPERATION;
 }
 
-static void __check_and_add_recvonly_transceiver(webrtc_gst_slot_s *source)
-{
-       rtp_payload_info_s *payload_info = NULL;
-
-       RET_IF(source == NULL, "source is NULL");
-
-       if (source->av[AV_IDX_AUDIO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_AUDIO].codec) {
-               if ((payload_info = _get_payload_info_by_encoding_name(source->av[AV_IDX_AUDIO].codec)))
-                       _add_transceiver(source, WEBRTC_MEDIA_TYPE_AUDIO, payload_info);
-       }
-
-       if (source->av[AV_IDX_VIDEO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_VIDEO].codec) {
-               if ((payload_info = _get_payload_info_by_encoding_name(source->av[AV_IDX_VIDEO].codec)))
-                       _add_transceiver(source, WEBRTC_MEDIA_TYPE_VIDEO, payload_info);
-       }
-}
-
-static void __check_and_update_transceiver(webrtc_gst_slot_s *source)
-{
-       rtp_payload_info_s *payload_info = NULL;
-
-       RET_IF(source == NULL, "source is NULL");
-
-       if (source->av[AV_IDX_AUDIO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_AUDIO].codec) {
-               if ((payload_info = _get_payload_info_by_encoding_name(source->av[AV_IDX_AUDIO].codec)))
-                       _update_transceiver_with_pt(source, WEBRTC_MEDIA_TYPE_AUDIO, payload_info);
-       }
-
-       if (source->av[AV_IDX_VIDEO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_VIDEO].codec) {
-               if ((payload_info = _get_payload_info_by_encoding_name(source->av[AV_IDX_VIDEO].codec)))
-                       _update_transceiver_with_pt(source, WEBRTC_MEDIA_TYPE_VIDEO, payload_info);
-       }
-}
-
-void _update_transceivers_for_offer(webrtc_s *webrtc)
-{
-       int i;
-       webrtc_gst_slot_s *source;
-       RET_IF(webrtc == NULL, "webrtc is NULL");
-
-       for (i = 0; i < MAX_SOURCE_NUM; i++) {
-               if (!(source = webrtc->gst.sources[i]))
-                       continue;
-
-               if (source->type != WEBRTC_MEDIA_SOURCE_TYPE_NULL)
-                       continue;
-
-               LOG_DEBUG("source[id:%u, type:%d, media_types:0x%x]", source->id, source->type, source->media_types);
-
-               __check_and_update_transceiver(source);
-       }
-}
-
-int _get_fixed_payload_type(const gchar *media_type)
-{
-       RET_VAL_IF(media_type == NULL, -1, "media_type is NULL");
-
-       if (!g_strcmp0(media_type, MEDIA_TYPE_AUDIO_MULAW))
-               return 0;
-       if (!g_strcmp0(media_type, MEDIA_TYPE_AUDIO_ALAW))
-               return 8;
-
-       LOG_DEBUG("%s might need to use dynamic id", media_type);
-
-       return -1;
-}
-
 int _complete_sources(webrtc_s *webrtc)
 {
        int i;
@@ -585,7 +493,7 @@ int _complete_sources(webrtc_s *webrtc)
                        __complete_rest_of_videosrc(webrtc, source);
 
 add_transceiver:
-               __check_and_add_recvonly_transceiver(source);
+               _check_and_add_recvonly_transceiver(source);
        }
 
        return WEBRTC_ERROR_NONE;
@@ -815,7 +723,7 @@ void _source_slot_destroy_cb(gpointer data)
                _remove_probe_from_pad_for_render(source, i);
 
                if (source->av[i].pt > 0)
-                       __return_payload_type(source->webrtc, source->av[i].pt);
+                       _return_payload_type(source->webrtc, source->av[i].pt);
 
                if (source->av[i].render.pipeline) {
                        gst_element_set_state(source->av[i].render.pipeline, GST_STATE_NULL);
@@ -1009,288 +917,6 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id)
        return ret;
 }
 
-direction_info_s* _convert_transceiver_direction(webrtc_transceiver_direction_e direction)
-{
-       return &__direction_info[direction];
-}
-
-int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
-{
-       webrtc_gst_slot_s *source;
-       GstWebRTCRTPTransceiver *trans;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(direction > WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV, WEBRTC_ERROR_INVALID_PARAMETER, "invalid direction");
-       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) {
-               trans = source->av[AV_IDX_AUDIO].transceiver;
-               source->av[AV_IDX_AUDIO].direction = direction;
-
-       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
-               trans = source->av[AV_IDX_VIDEO].transceiver;
-               source->av[AV_IDX_VIDEO].direction = direction;
-
-       } 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 (trans)
-               g_object_set(G_OBJECT(trans), "direction", __direction_info[direction].gst, NULL);
-
-       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] transceiver[%p, direction:%s]",
-               webrtc, source_id, media_type, trans, __direction_info[direction].str);
-
-       return WEBRTC_ERROR_NONE;
-}
-
-int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction)
-{
-       const webrtc_gst_slot_s *source;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(direction == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "direction 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) {
-               *direction = source->av[AV_IDX_AUDIO].direction;
-
-       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
-               *direction = source->av[AV_IDX_VIDEO].direction;
-
-       } 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;
-       }
-
-       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] direction[%s]",
-               webrtc, source_id, media_type, __direction_info[*direction].str);
-
-       return WEBRTC_ERROR_NONE;
-}
-
-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 _foreach_supported_transceiver_codec(webrtc_s *webrtc, webrtc_media_source_type_e source_type, webrtc_media_type_e media_type, webrtc_media_source_supported_transceiver_codec_cb callback, void *user_data)
-{
-       int ret;
-       webrtc_transceiver_codec_e codec;
-       GStrv codecs;
-       guint i;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback 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_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, "invalid source_type");
-
-       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO &&
-               (source_type == WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST ||
-               source_type == WEBRTC_MEDIA_SOURCE_TYPE_MIC ||
-               source_type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)) {
-               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source_type, MEDIA_TYPE_AUDIO);
-
-       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO &&
-               (source_type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST ||
-               source_type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA ||
-               source_type == WEBRTC_MEDIA_SOURCE_TYPE_SCREEN ||
-               source_type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)) {
-               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source_type, MEDIA_TYPE_VIDEO);
-
-       } else {
-               LOG_ERROR("invalid media_type[%d] for source_type[%u]", media_type, source_type);
-               return WEBRTC_ERROR_INVALID_PARAMETER;
-       }
-
-       LOG_INFO("webrtc[%p] source_type[%u] media_type[%d] callback[%p] user_data[%p]", webrtc, source_type, media_type, callback, user_data);
-
-       for (i = 0; i < g_strv_length(codecs); i++) {
-               if ((ret = convert_codec_funcs[media_type](codecs[i], &codec)) != WEBRTC_ERROR_NONE)
-                       return ret;
-               LOG_INFO(" - supported %s codec: %s", media_type == WEBRTC_MEDIA_TYPE_AUDIO ? "audio" : "video", codecs[i]);
-               if (!callback(codec, user_data)) {
-                       LOG_DEBUG("stop foreach callback");
-                       break;
-               }
-       }
-
-       return WEBRTC_ERROR_NONE;
-}
-
-static int __validate_codec(webrtc_s *webrtc, webrtc_gst_slot_s *source, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec)
-{
-       int ret;
-       GStrv codecs;
-       webrtc_transceiver_codec_e _codec;
-       guint i;
-
-       RET_VAL_IF(webrtc == NULL, FALSE, "webrtc is NULL");
-       RET_VAL_IF(source == NULL, FALSE, "source is NULL");
-
-       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO &&
-               source->media_types & MEDIA_TYPE_AUDIO &&
-               codec & CODEC_TYPE_AUDIO) {
-               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source->type, MEDIA_TYPE_AUDIO);
-
-       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO &&
-               source->media_types & MEDIA_TYPE_VIDEO &&
-               codec & CODEC_TYPE_VIDEO) {
-               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source->type, MEDIA_TYPE_VIDEO);
-
-       } else {
-               LOG_ERROR("invalid media_type[%d], codec[0x%x] for source[media_types:0x%x, id:%u]",
-                       media_type, codec, source->media_types, source->id);
-               return WEBRTC_ERROR_INVALID_PARAMETER;
-       }
-
-       for (i = 0; i < g_strv_length(codecs); i++) {
-               if ((ret = convert_codec_funcs[media_type](codecs[i], &_codec)) != WEBRTC_ERROR_NONE)
-                       return ret;
-               if (_codec == codec) {
-                       LOG_DEBUG("codec[0x%x] is supported", codec);
-                       return WEBRTC_ERROR_NONE;
-               }
-       }
-
-       LOG_ERROR("webrtc[%p] codec[0x%x] is not supported for this source type[%d]", webrtc, codec, source->type);
-
-       return WEBRTC_ERROR_INVALID_PARAMETER;
-}
-
-int _set_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec)
-{
-       int ret;
-       webrtc_gst_slot_s *source;
-       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
-       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_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");
-
-       payload_info = _get_payload_info(codec);
-       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find payload_info");
-
-       /* NOTE: for null source, set media_types here exceptionally. */
-       if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)
-               source->media_types |= (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO;
-
-       if ((ret = __validate_codec(webrtc, source, media_type, codec)) != WEBRTC_ERROR_NONE)
-               return ret;
-
-       source->av[av_idx].codec = payload_info->encoding_name;
-
-       if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL) {
-               if (source->av[av_idx].pt >= MIN_DYNAMIC_PAYLOAD_TYPE)
-                       __return_payload_type(webrtc, source->av[av_idx].pt);
-
-               if ((ret = _set_payload_type(webrtc, source, av_idx, payload_info->gst_media_type)) != WEBRTC_ERROR_NONE)
-                       return ret;
-       }
-
-       /* 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, webrtc_transceiver_codec_e *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;
-}
-
 int _set_pause(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, bool pause)
 {
        webrtc_gst_slot_s *source;
@@ -1622,7 +1248,7 @@ static void __release_filesrc_resources(webrtc_gst_slot_s *source)
                _remove_filesrc_pad_block_probe(source, av_idx);
 
                if (source->av[av_idx].pt > 0)
-                       __return_payload_type(source->webrtc, source->av[av_idx].pt);
+                       _return_payload_type(source->webrtc, source->av[av_idx].pt);
 
                if (source->av[av_idx].render.pipeline) {
                        gst_element_set_state(source->av[av_idx].render.pipeline, GST_STATE_NULL);
index 0b0418a18c3e13016484f22997a4be6603538f94..d998261b13b6bdbb3142fdb524c07edb3eacc5b3 100644 (file)
 #include "webrtc_private.h"
 #include "webrtc_source_private.h"
 
-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 /* TBD */, MEDIA_TYPE_AUDIO_VORBIS, "audio", "VORBIS", 8000 },
-       { 0 /* TBD */, MEDIA_TYPE_VIDEO_JPEG, "video", "JPEG", 90000 },
-       { 0, NULL, NULL, NULL, 0 }
-};
-
 const char *_get_audio_format_name(media_format_mimetype_e mime_type)
 {
        switch (mime_type) {
@@ -340,53 +328,6 @@ const char *_get_element_name(int av_idx, gst_element_e element)
        return _av_element_tbl[av_idx][element];
 }
 
-static int __get_available_payload_type(webrtc_s *webrtc)
-{
-       int bitmask = 0x1;
-       int count = 0;
-
-       RET_VAL_IF(webrtc == NULL, 0, "webrtc is NULL");
-
-       while (count++ < PAYLOAD_TYPE_BITS) {
-               if (webrtc->payload_types & bitmask) {
-                       bitmask <<= 1;
-                       continue;
-               }
-               webrtc->payload_types |= bitmask;
-               LOG_DEBUG("found available payload type[%d]", count + 95);
-               return count + 95; /* 96 ~ 127 */
-       }
-
-       LOG_ERROR("could not assign payload type");
-
-       return -1;
-}
-
-int _set_payload_type(webrtc_s *webrtc, webrtc_gst_slot_s *source, int av_idx, const gchar *media_type)
-{
-       int payload_type = -1;
-
-       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-
-       if (source->av[av_idx].pt_set_by_api) {
-               LOG_INFO("current pt[%u] might be set by api, skip it", source->av[av_idx].pt);
-               return WEBRTC_ERROR_NONE;
-       }
-
-       if (media_type)
-               if ((payload_type = _get_fixed_payload_type(media_type)) != -1)
-                       goto out;
-
-       if ((payload_type = __get_available_payload_type(webrtc)) == -1)
-               return WEBRTC_ERROR_INVALID_OPERATION;
-
-out:
-       source->av[av_idx].pt = payload_type;
-
-       return WEBRTC_ERROR_NONE;
-}
-
 GstPadProbeReturn _payloaded_data_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
 {
        probe_userdata_s *probe_data = (probe_userdata_s *)user_data;
@@ -532,112 +473,6 @@ void _remove_probe_from_pad_for_render(webrtc_gst_slot_s *source, unsigned int i
        _unset_caps_for_render(source, idx);
 }
 
-rtp_payload_info_s * _get_payload_info(webrtc_transceiver_codec_e codec)
-{
-       int i = 0;
-
-       for (i = 0; i != (int)__payload_info[i].codec; i++) {
-               if (__payload_info[i].codec == codec)
-                       return &__payload_info[i];
-       }
-
-       LOG_ERROR("could not find payload_info. codec[%d]", codec);
-
-       return NULL;
-}
-
-rtp_payload_info_s * _get_payload_info_by_encoding_name(const char *encoding_name)
-{
-       int i = 0;
-       size_t n = 0;
-
-       RET_VAL_IF(encoding_name == NULL, NULL, "encoding_name is NULL");
-
-       n = ARRAY_SIZE(__payload_info);
-
-       for (i = 0; i < (int)n; i++) {
-               if ((__payload_info[i].encoding_name) && !strcasecmp(__payload_info[i].encoding_name, encoding_name))
-                       return &__payload_info[i];
-       }
-
-       LOG_ERROR("could not find payload_info. encoding_name[%s]", encoding_name);
-
-       return NULL;
-}
-
-static GstCaps *__make_transceiver_caps(rtp_payload_info_s *payload_info)
-{
-       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,
-                               NULL);
-}
-
-static GstCaps *__make_transceiver_caps_with_pt(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);
-}
-
-int _add_transceiver(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info)
-{
-       GstWebRTCRTPTransceiver *trans;
-       GstCaps *caps;
-       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payload_info is NULL");
-
-       caps = __make_transceiver_caps(payload_info);
-       PRINT_CAPS(caps, "transceiver");
-
-       if (!source->av[av_idx].transceiver) {
-               g_signal_emit_by_name(source->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);
-       }
-
-       gst_caps_unref(caps);
-
-       LOG_DEBUG("webrtc[%p] source_id[%u] [%s] transceiver[%p] codec[%s]",
-               source->webrtc, source->id, payload_info->media_type, source->av[av_idx].transceiver, payload_info->encoding_name);
-
-       return WEBRTC_ERROR_NONE;
-}
-
-int _update_transceiver_with_pt(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info)
-{
-       GstCaps *caps;
-       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
-
-       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
-       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payload_info is NULL");
-       RET_VAL_IF(source->av[av_idx].transceiver == NULL, WEBRTC_ERROR_INVALID_OPERATION, "transceiver is NULL");
-
-       caps = __make_transceiver_caps_with_pt(payload_info, source->av[av_idx].pt);
-       PRINT_CAPS(caps, "transceiver");
-
-       g_object_set(G_OBJECT(source->av[av_idx].transceiver), "codec-preferences", caps, NULL);
-
-       gst_caps_unref(caps);
-
-       LOG_DEBUG("webrtc[%p] source_id[%u] [%s] transceiver[%p] codec[%s]",
-               source->webrtc, source->id, payload_info->media_type, source->av[av_idx].transceiver, payload_info->encoding_name);
-
-       return WEBRTC_ERROR_NONE;
-}
-
 static bool __is_linked_pad(webrtc_gst_slot_s *source, const char *pad_name)
 {
        GstIterator *iter = NULL;
diff --git a/src/webrtc_transceiver.c b/src/webrtc_transceiver.c
new file mode 100644 (file)
index 0000000..29fe1bc
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc_private.h"
+
+static direction_info_s __direction_info[] = {
+       [WEBRTC_TRANSCEIVER_DIRECTION_SENDONLY] = { "SENDONLY", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY },
+       [WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY] = { "RECVONLY", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY },
+       [WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV] = { "SENDRECV", GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV }
+};
+
+static direction_info_s* __convert_transceiver_direction(webrtc_transceiver_direction_e direction)
+{
+       return &__direction_info[direction];
+}
+
+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 /* TBD */, MEDIA_TYPE_AUDIO_VORBIS, "audio", "VORBIS", 8000 },
+       { 0 /* TBD */, MEDIA_TYPE_VIDEO_JPEG, "video", "JPEG", 90000 },
+       { 0, NULL, NULL, NULL, 0 }
+};
+
+static void __webrtcbin_transceiver_set_ulpfec_red(webrtc_s *webrtc, GstWebRTCRTPTransceiver *transceiver)
+{
+       GstElement *rtpbin;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(transceiver == NULL, "transceiver is NULL");
+
+       if (!(rtpbin = gst_bin_get_by_name(GST_BIN(webrtc->gst.webrtcbin), "rtpbin"))) {
+               LOG_ERROR("failed to get rtpbin");
+               return;
+       }
+
+       g_object_set(transceiver, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
+       LOG_INFO("set ULPFEC/RED to transceiver[%p]", transceiver);
+
+       g_object_set(G_OBJECT(rtpbin), "do-lost", TRUE, NULL);
+       LOG_DEBUG("set do-lost to rtpbin[%p], ULPFEC recovers a lost packet when do-lost event occurs", rtpbin);
+
+       gst_object_unref(rtpbin);
+}
+
+static void __webrtcbin_transceiver_set_fec_percentage(webrtc_s *webrtc, GstWebRTCRTPTransceiver *transceiver, unsigned int fec_percentage)
+{
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(transceiver == NULL, "transceiver is NULL");
+
+       g_object_set(transceiver, "fec-percentage", fec_percentage, NULL);
+
+       LOG_INFO("set fec-percentage[%u] to transceiver[%p]", fec_percentage, transceiver);
+}
+
+
+void _webrtcbin_on_new_transceiver_cb(GstElement *webrtcbin, GstWebRTCRTPTransceiver *transceiver, gpointer user_data)
+{
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+       webrtc_gst_slot_s *source;
+       int i, j;
+       guint mlineindex;
+       gchar *mid;
+       GstWebRTCRTPTransceiverDirection direction;
+       GstWebRTCKind kind;
+       GstCaps *caps;
+
+       RET_IF(webrtcbin == NULL, "webrtcbin is NULL");
+       RET_IF(transceiver == NULL, "transceiver is NULL");
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       g_object_get(G_OBJECT(transceiver),
+               "mlineindex", &mlineindex,
+               "mid", &mid,
+               "direction", &direction,
+               "kind", &kind,
+               "codec-preferences", &caps,
+               NULL);
+
+       LOG_DEBUG("webrtc[%p] new transceiver[%p, mlineindex:%u, mid:%s, direction:%d, kind:%d] user_data[%p]",
+               webrtc, transceiver, mlineindex, mid, direction, kind, user_data);
+       PRINT_CAPS(caps, "codec preferences");
+
+       /* Code below is for the scenario of an answerer without any added media sources. */
+       if (g_hash_table_size(webrtc->gst.source_slots) == 0) {
+               if (mlineindex >= MAX_MLINE_NUM) {
+                       LOG_ERROR("mlineindex[%u] exceeds the max value", mlineindex);
+                       return;
+               }
+               if (webrtc->data_recovery_types[mlineindex].red &&
+                       webrtc->data_recovery_types[mlineindex].ulpfec)
+                       __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
+               return;
+       }
+
+       for (i = 0; i < MAX_SOURCE_NUM; i++) {
+               if (!(source = webrtc->gst.sources[i]))
+                       continue;
+               for (j = AV_IDX_AUDIO; j < AV_IDX_MAX; j++) {
+                       if (!(source->media_types & (j == AV_IDX_AUDIO ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO)))
+                               continue;
+                       if (source->av[j].transceiver)
+                               continue;
+
+                       source->av[j].transceiver = gst_object_ref(transceiver);
+                       g_object_set(G_OBJECT(transceiver), "direction", __convert_transceiver_direction(source->av[j].direction)->gst, NULL);
+
+                       LOG_INFO("source->id[%u] transceiver[%p for %s, direction:%s]",
+                               source->id, source->av[j].transceiver, j == AV_IDX_AUDIO ? "AUDIO" : "VIDEO",
+                               __convert_transceiver_direction(source->av[j].direction)->str);
+                       return;
+               }
+       }
+}
+
+void _update_transceivers_fec(webrtc_s *webrtc, bool is_offer)
+{
+       GstWebRTCRTPTransceiver *transceiver;
+       webrtc_gst_slot_s *source;
+       int i, j;
+       GstWebRTCRTPTransceiverDirection direction;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       for (i = 0; i < MAX_SOURCE_NUM; i++) {
+               if (!(source = webrtc->gst.sources[i]))
+                       continue;
+               for (j = AV_IDX_AUDIO; j < AV_IDX_MAX; j++) {
+                       if (!(source->media_types & (j == AV_IDX_AUDIO ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO)))
+                               continue;
+                       if (!(transceiver = source->av[j].transceiver))
+                               continue;
+
+                       g_object_get(G_OBJECT(transceiver), "direction", &direction, NULL);
+
+                       if (is_offer) {
+                               bool use_ulpfec_red;
+                               _get_use_ulpfec_red_from_ini(&webrtc->ini, source->type, &use_ulpfec_red);
+
+                               if (!use_ulpfec_red)
+                                       continue;
+
+                               __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
+                       } else {
+                               if (!webrtc->data_recovery_types[i].red ||
+                                       !webrtc->data_recovery_types[i].ulpfec)
+                                       continue;
+                               __webrtcbin_transceiver_set_ulpfec_red(webrtc, transceiver);
+                       }
+
+                       if (direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
+                               direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)
+                               __webrtcbin_transceiver_set_fec_percentage(webrtc, transceiver, _get_fec_percentage_from_ini(&webrtc->ini, source->type));
+               }
+       }
+}
+
+static GstCaps *__make_transceiver_caps_with_pt(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);
+}
+
+static int __update_transceiver_with_pt(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info)
+{
+       GstCaps *caps;
+       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payload_info is NULL");
+       RET_VAL_IF(source->av[av_idx].transceiver == NULL, WEBRTC_ERROR_INVALID_OPERATION, "transceiver is NULL");
+
+       caps = __make_transceiver_caps_with_pt(payload_info, source->av[av_idx].pt);
+       PRINT_CAPS(caps, "transceiver");
+
+       g_object_set(G_OBJECT(source->av[av_idx].transceiver), "codec-preferences", caps, NULL);
+
+       gst_caps_unref(caps);
+
+       LOG_DEBUG("webrtc[%p] source_id[%u] [%s] transceiver[%p] codec[%s]",
+               source->webrtc, source->id, payload_info->media_type, source->av[av_idx].transceiver, payload_info->encoding_name);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static rtp_payload_info_s * __get_payload_info_by_encoding_name(const char *encoding_name)
+{
+       int i = 0;
+       size_t n = 0;
+
+       RET_VAL_IF(encoding_name == NULL, NULL, "encoding_name is NULL");
+
+       n = ARRAY_SIZE(__payload_info);
+
+       for (i = 0; i < (int)n; i++) {
+               if ((__payload_info[i].encoding_name) && !strcasecmp(__payload_info[i].encoding_name, encoding_name))
+                       return &__payload_info[i];
+       }
+
+       LOG_ERROR("could not find payload_info. encoding_name[%s]", encoding_name);
+
+       return NULL;
+}
+
+static void __check_and_update_transceiver(webrtc_gst_slot_s *source)
+{
+       rtp_payload_info_s *payload_info = NULL;
+
+       RET_IF(source == NULL, "source is NULL");
+
+       if (source->av[AV_IDX_AUDIO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_AUDIO].codec) {
+               if ((payload_info = __get_payload_info_by_encoding_name(source->av[AV_IDX_AUDIO].codec)))
+                       __update_transceiver_with_pt(source, WEBRTC_MEDIA_TYPE_AUDIO, payload_info);
+       }
+
+       if (source->av[AV_IDX_VIDEO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_VIDEO].codec) {
+               if ((payload_info = __get_payload_info_by_encoding_name(source->av[AV_IDX_VIDEO].codec)))
+                       __update_transceiver_with_pt(source, WEBRTC_MEDIA_TYPE_VIDEO, payload_info);
+       }
+}
+
+void _update_transceivers_for_offer(webrtc_s *webrtc)
+{
+       int i;
+       webrtc_gst_slot_s *source;
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       for (i = 0; i < MAX_SOURCE_NUM; i++) {
+               if (!(source = webrtc->gst.sources[i]))
+                       continue;
+
+               if (source->type != WEBRTC_MEDIA_SOURCE_TYPE_NULL)
+                       continue;
+
+               LOG_DEBUG("source[id:%u, type:%d, media_types:0x%x]", source->id, source->type, source->media_types);
+
+               __check_and_update_transceiver(source);
+       }
+}
+
+static rtp_payload_info_s * __get_payload_info(webrtc_transceiver_codec_e codec)
+{
+       int i = 0;
+
+       for (i = 0; i != (int)__payload_info[i].codec; i++) {
+               if (__payload_info[i].codec == codec)
+                       return &__payload_info[i];
+       }
+
+       LOG_ERROR("could not find payload_info. codec[%d]", codec);
+
+       return NULL;
+}
+
+static int __get_available_payload_type(webrtc_s *webrtc)
+{
+       int bitmask = 0x1;
+       int count = 0;
+
+       RET_VAL_IF(webrtc == NULL, 0, "webrtc is NULL");
+
+       while (count++ < PAYLOAD_TYPE_BITS) {
+               if (webrtc->payload_types & bitmask) {
+                       bitmask <<= 1;
+                       continue;
+               }
+               webrtc->payload_types |= bitmask;
+               LOG_DEBUG("found available payload type[%d]", count + 95);
+               return count + 95; /* 96 ~ 127 */
+       }
+
+       LOG_ERROR("could not assign payload type");
+
+       return -1;
+}
+
+int _set_payload_type(webrtc_s *webrtc, webrtc_gst_slot_s *source, int av_idx, const gchar *media_type)
+{
+       int payload_type = -1;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+
+       if (source->av[av_idx].pt_set_by_api) {
+               LOG_INFO("current pt[%u] might be set by api, skip it", source->av[av_idx].pt);
+               return WEBRTC_ERROR_NONE;
+       }
+
+       if (media_type)
+               if ((payload_type = _get_fixed_payload_type(media_type)) != -1)
+                       goto out;
+
+       if ((payload_type = __get_available_payload_type(webrtc)) == -1)
+               return WEBRTC_ERROR_INVALID_OPERATION;
+
+out:
+       source->av[av_idx].pt = payload_type;
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_fixed_payload_type(const gchar *media_type)
+{
+       RET_VAL_IF(media_type == NULL, -1, "media_type is NULL");
+
+       if (!g_strcmp0(media_type, MEDIA_TYPE_AUDIO_MULAW))
+               return 0;
+       if (!g_strcmp0(media_type, MEDIA_TYPE_AUDIO_ALAW))
+               return 8;
+
+       LOG_DEBUG("%s might need to use dynamic id", media_type);
+
+       return -1;
+}
+
+void _return_payload_type(webrtc_s *webrtc, unsigned int payload_type)
+{
+       int i;
+       int bitmask = 0x1;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(payload_type < MIN_DYNAMIC_PAYLOAD_TYPE || payload_type > MAX_DYNAMIC_PAYLOAD_TYPE, "invalid payload_type(%u)", payload_type);
+
+       i = payload_type - MIN_DYNAMIC_PAYLOAD_TYPE;
+
+       while (i-- > 0)
+               bitmask <<= 1;
+
+       webrtc->payload_types ^= bitmask;
+}
+
+void _check_and_add_recvonly_transceiver(webrtc_gst_slot_s *source)
+{
+       rtp_payload_info_s *payload_info = NULL;
+
+       RET_IF(source == NULL, "source is NULL");
+
+       if (source->av[AV_IDX_AUDIO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_AUDIO].codec) {
+               if ((payload_info = __get_payload_info_by_encoding_name(source->av[AV_IDX_AUDIO].codec)))
+                       _add_transceiver(source, WEBRTC_MEDIA_TYPE_AUDIO, payload_info);
+       }
+
+       if (source->av[AV_IDX_VIDEO].direction == WEBRTC_TRANSCEIVER_DIRECTION_RECVONLY && source->av[AV_IDX_VIDEO].codec) {
+               if ((payload_info = __get_payload_info_by_encoding_name(source->av[AV_IDX_VIDEO].codec)))
+                       _add_transceiver(source, WEBRTC_MEDIA_TYPE_VIDEO, payload_info);
+       }
+}
+
+static GstCaps *__make_transceiver_caps(rtp_payload_info_s *payload_info)
+{
+       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,
+                               NULL);
+}
+
+int _add_transceiver(webrtc_gst_slot_s *source, webrtc_media_type_e media_type, rtp_payload_info_s *payload_info)
+{
+       GstWebRTCRTPTransceiver *trans;
+       GstCaps *caps;
+       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
+
+       RET_VAL_IF(source == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "source is NULL");
+       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "payload_info is NULL");
+
+       caps = __make_transceiver_caps(payload_info);
+       PRINT_CAPS(caps, "transceiver");
+
+       if (!source->av[av_idx].transceiver) {
+               g_signal_emit_by_name(source->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);
+       }
+
+       gst_caps_unref(caps);
+
+       LOG_DEBUG("webrtc[%p] source_id[%u] [%s] transceiver[%p] codec[%s]",
+               source->webrtc, source->id, payload_info->media_type, source->av[av_idx].transceiver, payload_info->encoding_name);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
+{
+       webrtc_gst_slot_s *source;
+       GstWebRTCRTPTransceiver *trans;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(direction > WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV, WEBRTC_ERROR_INVALID_PARAMETER, "invalid direction");
+       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) {
+               trans = source->av[AV_IDX_AUDIO].transceiver;
+               source->av[AV_IDX_AUDIO].direction = direction;
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
+               trans = source->av[AV_IDX_VIDEO].transceiver;
+               source->av[AV_IDX_VIDEO].direction = direction;
+
+       } 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 (trans)
+               g_object_set(G_OBJECT(trans), "direction", __direction_info[direction].gst, NULL);
+
+       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] transceiver[%p, direction:%s]",
+               webrtc, source_id, media_type, trans, __direction_info[direction].str);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction)
+{
+       const webrtc_gst_slot_s *source;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(direction == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "direction 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) {
+               *direction = source->av[AV_IDX_AUDIO].direction;
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO && source->media_types & MEDIA_TYPE_VIDEO) {
+               *direction = source->av[AV_IDX_VIDEO].direction;
+
+       } 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;
+       }
+
+       LOG_INFO("webrtc[%p] source_id[%u] media_type[%d] direction[%s]",
+               webrtc, source_id, media_type, __direction_info[*direction].str);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+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 _foreach_supported_transceiver_codec(webrtc_s *webrtc, webrtc_media_source_type_e source_type, webrtc_media_type_e media_type, webrtc_media_source_supported_transceiver_codec_cb callback, void *user_data)
+{
+       int ret;
+       webrtc_transceiver_codec_e codec;
+       GStrv codecs;
+       guint i;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback 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_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, "invalid source_type");
+
+       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO &&
+               (source_type == WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST ||
+               source_type == WEBRTC_MEDIA_SOURCE_TYPE_MIC ||
+               source_type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)) {
+               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source_type, MEDIA_TYPE_AUDIO);
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO &&
+               (source_type == WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST ||
+               source_type == WEBRTC_MEDIA_SOURCE_TYPE_CAMERA ||
+               source_type == WEBRTC_MEDIA_SOURCE_TYPE_SCREEN ||
+               source_type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)) {
+               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source_type, MEDIA_TYPE_VIDEO);
+
+       } else {
+               LOG_ERROR("invalid media_type[%d] for source_type[%u]", media_type, source_type);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       LOG_INFO("webrtc[%p] source_type[%u] media_type[%d] callback[%p] user_data[%p]", webrtc, source_type, media_type, callback, user_data);
+
+       for (i = 0; i < g_strv_length(codecs); i++) {
+               if ((ret = convert_codec_funcs[media_type](codecs[i], &codec)) != WEBRTC_ERROR_NONE)
+                       return ret;
+               LOG_INFO(" - supported %s codec: %s", media_type == WEBRTC_MEDIA_TYPE_AUDIO ? "audio" : "video", codecs[i]);
+               if (!callback(codec, user_data)) {
+                       LOG_DEBUG("stop foreach callback");
+                       break;
+               }
+       }
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static int __validate_codec(webrtc_s *webrtc, webrtc_gst_slot_s *source, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec)
+{
+       int ret;
+       GStrv codecs;
+       webrtc_transceiver_codec_e _codec;
+       guint i;
+
+       RET_VAL_IF(webrtc == NULL, FALSE, "webrtc is NULL");
+       RET_VAL_IF(source == NULL, FALSE, "source is NULL");
+
+       if (media_type == WEBRTC_MEDIA_TYPE_AUDIO &&
+               source->media_types & MEDIA_TYPE_AUDIO &&
+               codec & CODEC_TYPE_AUDIO) {
+               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source->type, MEDIA_TYPE_AUDIO);
+
+       } else if (media_type == WEBRTC_MEDIA_TYPE_VIDEO &&
+               source->media_types & MEDIA_TYPE_VIDEO &&
+               codec & CODEC_TYPE_VIDEO) {
+               codecs = _get_supported_codecs_from_ini(&webrtc->ini, source->type, MEDIA_TYPE_VIDEO);
+
+       } else {
+               LOG_ERROR("invalid media_type[%d], codec[0x%x] for source[media_types:0x%x, id:%u]",
+                       media_type, codec, source->media_types, source->id);
+               return WEBRTC_ERROR_INVALID_PARAMETER;
+       }
+
+       for (i = 0; i < g_strv_length(codecs); i++) {
+               if ((ret = convert_codec_funcs[media_type](codecs[i], &_codec)) != WEBRTC_ERROR_NONE)
+                       return ret;
+               if (_codec == codec) {
+                       LOG_DEBUG("codec[0x%x] is supported", codec);
+                       return WEBRTC_ERROR_NONE;
+               }
+       }
+
+       LOG_ERROR("webrtc[%p] codec[0x%x] is not supported for this source type[%d]", webrtc, codec, source->type);
+
+       return WEBRTC_ERROR_INVALID_PARAMETER;
+}
+
+int _set_transceiver_codec(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_codec_e codec)
+{
+       int ret;
+       webrtc_gst_slot_s *source;
+       int av_idx = (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? AV_IDX_AUDIO : AV_IDX_VIDEO;
+       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_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");
+
+       payload_info = __get_payload_info(codec);
+       RET_VAL_IF(payload_info == NULL, WEBRTC_ERROR_INVALID_OPERATION, "could not find payload_info");
+
+       /* NOTE: for null source, set media_types here exceptionally. */
+       if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL)
+               source->media_types |= (media_type == WEBRTC_MEDIA_TYPE_AUDIO) ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO;
+
+       if ((ret = __validate_codec(webrtc, source, media_type, codec)) != WEBRTC_ERROR_NONE)
+               return ret;
+
+       source->av[av_idx].codec = payload_info->encoding_name;
+
+       if (source->type == WEBRTC_MEDIA_SOURCE_TYPE_NULL) {
+               if (source->av[av_idx].pt >= MIN_DYNAMIC_PAYLOAD_TYPE)
+                       _return_payload_type(webrtc, source->av[av_idx].pt);
+
+               if ((ret = _set_payload_type(webrtc, source, av_idx, payload_info->gst_media_type)) != WEBRTC_ERROR_NONE)
+                       return ret;
+       }
+
+       /* 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, webrtc_transceiver_codec_e *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;
+}
\ No newline at end of file