From: Sangchul Lee Date: Thu, 1 Sep 2022 07:37:42 +0000 (+0900) Subject: Add webrtc_transceiver.c and related codes are moved into it X-Git-Tag: submit/tizen/20220902.091410~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e7a55f09ea7bffb2e9f9855370fcc0ea674c3b0e;p=platform%2Fcore%2Fapi%2Fwebrtc.git Add webrtc_transceiver.c and related codes are moved into it [Version] 0.3.228 [Issue Type] Refactoring Change-Id: Ic010a677849bc9253e3fcb0459af2a8dcf1eb9d5 Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc_private.h b/include/webrtc_private.h index b7504c7a..0f645e19 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -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); diff --git a/include/webrtc_source_private.h b/include/webrtc_source_private.h index 0a5c9551..4c052708 100644 --- a/include/webrtc_source_private.h +++ b/include/webrtc_source_private.h @@ -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); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 282e60c8..193410a2 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -1,6 +1,6 @@ Name: capi-media-webrtc Summary: A WebRTC library in Tizen Native API -Version: 0.3.227 +Version: 0.3.228 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_private.c b/src/webrtc_private.c index b852d44a..a05b75fb 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -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)) { diff --git a/src/webrtc_source.c b/src/webrtc_source.c index 711108a7..fa36b7b8 100644 --- a/src/webrtc_source.c +++ b/src/webrtc_source.c @@ -24,15 +24,6 @@ #include #include -#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); diff --git a/src/webrtc_source_private.c b/src/webrtc_source_private.c index 0b0418a1..d998261b 100644 --- a/src/webrtc_source_private.c +++ b/src/webrtc_source_private.c @@ -18,18 +18,6 @@ #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 index 00000000..29fe1bcd --- /dev/null +++ b/src/webrtc_transceiver.c @@ -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