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