From: Sangchul Lee Date: Fri, 5 Jun 2020 06:00:47 +0000 (+0900) Subject: Add new APIs to set/unset decoded ready callback of the node X-Git-Tag: submit/tizen/20200714.065000~37 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F14%2F235514%2F6;p=platform%2Fcore%2Fapi%2Fmediastreamer.git Add new APIs to set/unset decoded ready callback of the node This callback will be invoked when the 'pad-added' signal of a gstreamer element for decoding something in the node is received. User can utilize this callback to link the decoded source pad with new node's sink pad for rendering something in the application. - int media_streamer_node_set_decoded_ready_cb(media_streamer_node_h node, media_streamer_node_decoded_ready_cb callback, void *user_data) - typedef void (*media_streamer_node_decoded_ready_cb)(media_streamer_node_h node, const char *src_pad_name, const char *media_types, void *user_data) - int media_streamer_node_unset_decoded_ready_cb(media_streamer_node_h node) Change-Id: I92199bae3b9c3e4028050e26a8ba7ab7b39df2d4 Signed-off-by: Sangchul Lee --- diff --git a/include/media_streamer.h b/include/media_streamer.h index 3f11575..4385790 100644 --- a/include/media_streamer.h +++ b/include/media_streamer.h @@ -530,6 +530,19 @@ typedef void (*media_streamer_position_changed_cb)(void *user_data); typedef void (*media_streamer_interrupted_cb)(media_streamer_interrupted_code_e code, void *user_data); /** + * @brief Called when the media streamer node is ready for decoded data. + * @since_tizen 6.0 + * @param[in] webrtc Media streamer node handle + * @param[in] src_pad_name The source pad name that can give decoded data to another one + * @param[in] media_type The media type of the data from the given source pad such as 'video/x-raw', 'audio/x-raw', and so on + * @param[in] user_data The user data passed from the callback registration function + * @see media_streamer_node_set_decoded_ready_cb() + * @see media_streamer_node_link() + * @see media_streamer_node_unset_decoded_ready_cb() + */ +typedef void (*media_streamer_node_decoded_ready_cb)(media_streamer_node_h node, const char *src_pad_name, const char *media_type, void *user_data); + +/** * @brief Called when the media streamer WebRTC node needs to send the message to the remote peer of WebRTC connection. * @since_tizen 6.0 * @remarks Two types will be delivered with @ message which is JSON string. @@ -1301,6 +1314,37 @@ int media_streamer_node_get_param(media_streamer_node_h node, const char *param_name, char **param_value); /** + * @brief Sets a callback function to be invoked when a source pad of @ node is ready to give decoded data. + * @remarks The available type of @a node for this function is #MEDIA_STREAMER_NODE_TYPE_WEBRTC. + * @since_tizen 6.0 + * @param[in] node Media streamer node handle + * @param[in] callback The decoded ready callback function to register + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value + * @retval #MEDIA_STREAMER_ERROR_NONE Successful + * @retval #MEDIA_STREAMER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_STREAMER_ERROR_INVALID_OPERATION Invalid operation + * @pre Create a media streamer node handle by calling media_streamer_node_create(). + * @post media_streamer_node_decoded_ready_cb() will be invoked. + * @see media_streamer_node_unset_decoded_ready_cb() + * @see media_streamer_node_decoded_ready_cb() + */ +int media_streamer_node_set_decoded_ready_cb(media_streamer_node_h node, media_streamer_node_decoded_ready_cb callback, void *user_data); + +/** + * @brief Unsets the decoded ready callback function. + * @since_tizen 6.0 + * @param[in] node Media streamer node handle + * @return @c 0 on success, + * otherwise a negative error value + * @retval #MEDIA_STREAMER_ERROR_NONE Successful + * @retval #MEDIA_STREAMER_ERROR_INVALID_PARAMETER Invalid parameter + * @see media_streamer_node_set_decoded_ready_cb() + */ +int media_streamer_node_unset_decoded_ready_cb(media_streamer_node_h node); + +/** * @brief Sets a callback function to be invoked when WebRTC node needs to send the message to the remote peer of WebRTC connection. * @details This function can be called only for #MEDIA_STREAMER_NODE_TYPE_WEBRTC type. * @since_tizen 6.0 diff --git a/include/media_streamer_priv.h b/include/media_streamer_priv.h index 15fe4fc..24085de 100644 --- a/include/media_streamer_priv.h +++ b/include/media_streamer_priv.h @@ -48,6 +48,14 @@ typedef struct { } media_streamer_sink_callbacks_s; /** + * @brief Media Streamer webrtc node callbacks structure. + */ +typedef struct { + media_streamer_callback_s message_cb; + media_streamer_callback_s decoded_ready_cb; +} media_streamer_webrtc_callbacks_s; + +/** * @brief Media Streamer param type handle. */ typedef struct { @@ -128,8 +136,6 @@ typedef struct { mm_resource_manager_res_h resource; device_policy_manager_h dpm_handle; int policy_changed_cb_id; - - media_streamer_callback_s user_cb; } media_streamer_node_s; /** diff --git a/src/media_streamer.c b/src/media_streamer.c index dbb28cb..60e3c66 100644 --- a/src/media_streamer.c +++ b/src/media_streamer.c @@ -938,17 +938,87 @@ int media_streamer_unset_interrupted_cb(media_streamer_h streamer) return MEDIA_STREAMER_ERROR_NONE; } +int media_streamer_node_set_decoded_ready_cb(media_streamer_node_h node, media_streamer_node_decoded_ready_cb callback, void *user_data) +{ + media_streamer_node_s *ms_node = (media_streamer_node_s *) node; + + ms_debug_fenter(); + + ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL"); + ms_retvm_if(callback == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "callback is NULL"); + + if (ms_node->type == MEDIA_STREAMER_NODE_TYPE_WEBRTC) { + media_streamer_webrtc_callbacks_s **_callbacks = (media_streamer_webrtc_callbacks_s **)&(ms_node->callbacks_structure); + + if (!*_callbacks) { + *_callbacks = (media_streamer_webrtc_callbacks_s *) calloc(1, sizeof(media_streamer_webrtc_callbacks_s)); + ms_retvm_if(*_callbacks == NULL, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "Error allocation memory"); + } + (*_callbacks)->decoded_ready_cb.callback = callback; + (*_callbacks)->decoded_ready_cb.user_data = user_data; + } else { + /* It can be accepted other types in the future. */ + ms_error("Not supported node type[%d]", ms_node->type); + return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + } + + ms_debug_fleave(); + + return MEDIA_STREAMER_ERROR_NONE; +} + +int media_streamer_node_unset_decoded_ready_cb(media_streamer_node_h node) +{ + media_streamer_node_s *ms_node = (media_streamer_node_s *) node; + + ms_debug_fenter(); + + ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL"); + + if (ms_node->type == MEDIA_STREAMER_NODE_TYPE_WEBRTC) { + media_streamer_webrtc_callbacks_s **_callbacks = (media_streamer_webrtc_callbacks_s **)&(ms_node->callbacks_structure); + + ms_retvm_if(*_callbacks == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "callbacks is NULL"); + ms_retvm_if((*_callbacks)->decoded_ready_cb.callback == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, + "decoded_ready_cb.callback is NULL"); + + (*_callbacks)->decoded_ready_cb.callback = NULL; + (*_callbacks)->decoded_ready_cb.user_data = NULL; + + if (!(*_callbacks)->message_cb.callback) { + free(ms_node->callbacks_structure); + ms_node->callbacks_structure = NULL; + } + } else { + /* It can be accepted other types in the future. */ + ms_error("Not supported node type[%d]", ms_node->type); + return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; + } + + ms_debug_fleave(); + + return MEDIA_STREAMER_ERROR_NONE; +} + int media_streamer_webrtc_node_set_message_cb(media_streamer_node_h webrtc, media_streamer_webrtc_message_cb callback, void *user_data) { media_streamer_node_s *ms_node = (media_streamer_node_s *) webrtc; + media_streamer_webrtc_callbacks_s **_callbacks; ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL"); + ms_retvm_if(callback == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "callback is NULL"); ms_retvm_if(ms_node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is not for WebRTC"); ms_debug_fenter(); - ms_node->user_cb.callback = callback; - ms_node->user_cb.user_data = user_data; + _callbacks = (media_streamer_webrtc_callbacks_s **)&(ms_node->callbacks_structure); + if (!*_callbacks) { + *_callbacks = (media_streamer_webrtc_callbacks_s *) calloc(1, sizeof(media_streamer_webrtc_callbacks_s)); + ms_retvm_if(*_callbacks == NULL, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "Error allocation memory"); + } + + (*_callbacks)->message_cb.callback = callback; + (*_callbacks)->message_cb.user_data = user_data; ms_debug_fleave(); @@ -958,16 +1028,27 @@ int media_streamer_webrtc_node_set_message_cb(media_streamer_node_h webrtc, medi int media_streamer_webrtc_node_unset_message_cb(media_streamer_node_h webrtc) { media_streamer_node_s *ms_node = (media_streamer_node_s *) webrtc; + media_streamer_webrtc_callbacks_s **_callbacks; + + ms_debug_fenter(); ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL"); ms_retvm_if(ms_node->type != MEDIA_STREAMER_NODE_TYPE_WEBRTC, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is not for WebRTC"); - ms_debug_fenter(); + _callbacks = (media_streamer_webrtc_callbacks_s **)&(ms_node->callbacks_structure); + + ms_retvm_if(*_callbacks == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "callbacks is NULL"); + ms_retvm_if((*_callbacks)->message_cb.callback == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "message_cb.callback is NULL"); - ms_node->user_cb.callback = NULL; - ms_node->user_cb.user_data = NULL; + (*_callbacks)->message_cb.callback = NULL; + (*_callbacks)->message_cb.user_data = NULL; + + if (!(*_callbacks)->decoded_ready_cb.callback) { + free(ms_node->callbacks_structure); + ms_node->callbacks_structure = NULL; + } ms_debug_fleave(); return MEDIA_STREAMER_ERROR_NONE; -} \ No newline at end of file +} diff --git a/src/media_streamer_gst_webrtc.c b/src/media_streamer_gst_webrtc.c index f19b0a8..353f629 100644 --- a/src/media_streamer_gst_webrtc.c +++ b/src/media_streamer_gst_webrtc.c @@ -79,14 +79,18 @@ static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc) static void __trigger_message_callback(media_streamer_node_s *webrtc_node, gchar *message) { + media_streamer_webrtc_callbacks_s *_callbacks; + ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); + ms_retm_if(message == NULL, "message is NULL"); ms_debug("message is : \n%s", message); - if (webrtc_node->user_cb.callback) { - ms_debug("=====> Now trigger user callback(%p)", webrtc_node->user_cb.callback); - ((media_streamer_webrtc_message_cb)(webrtc_node->user_cb.callback))(webrtc_node, message, webrtc_node->user_cb.user_data); - ms_debug("<===== End of the callback"); + _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure; + if (_callbacks->message_cb.callback) { + ms_debug("=====> invoke message callback(%p)", _callbacks->message_cb.callback); + ((media_streamer_webrtc_message_cb)(_callbacks->message_cb.callback))(webrtc_node, message, _callbacks->message_cb.user_data); + ms_debug("<===== end of the callback"); } else { ms_warning("message callback is NULL"); } @@ -419,18 +423,87 @@ void ms_webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channe ms_debug_fleave(); } +static void __trigger_decoded_ready_callback(media_streamer_node_s *webrtc_node, const gchar *new_pad_name, const gchar *media_type) +{ + media_streamer_webrtc_callbacks_s *_callbacks; + + ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); + ms_retm_if(new_pad_name == NULL, "new_pad_name is NULL"); + ms_retm_if(media_type == NULL, "media_type is NULL"); + + _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure; + if (!_callbacks->decoded_ready_cb.callback) { + ms_warning("decoded_ready_cb.callback is NULL"); + return; + } + + ms_debug("=====> invoke decoded ready callback(%p)", _callbacks->decoded_ready_cb.callback); + ((media_streamer_node_decoded_ready_cb)(_callbacks->decoded_ready_cb.callback))(webrtc_node, + (const char *)new_pad_name, + (const char *)media_type, + _callbacks->decoded_ready_cb.user_data); + ms_debug("<===== end of the callback"); +} + +static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data) +{ + media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data; + const gchar *new_pad_name; + const gchar *media_type; + + ms_retm_if(decodebin == NULL, "decodebin is NULL"); + ms_retm_if(new_pad == NULL, "new_pad is NULL"); + ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); + ms_retm_if(GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC, "new_pad is not for source"); + ms_retm_if(gst_pad_has_current_caps(new_pad) == FALSE, "new_pad does not have caps"); + + new_pad_name = GST_PAD_NAME(new_pad); + media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0)); + + ms_debug("new_pad_name[%s], media_type[%s]", new_pad_name, media_type); + + __trigger_decoded_ready_callback(webrtc_node, new_pad_name, media_type); +} + void ms_webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data) { - media_streamer_s *ms_streamer = (media_streamer_s *)user_data; + media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data; + media_streamer_webrtc_callbacks_s *_callbacks; + GstPad *sink_pad; + GstElement *decodebin; ms_retm_if(new_pad == NULL, "new_pad is NULL"); - ms_retm_if(ms_streamer == NULL, "ms_streamer is NULL"); + ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); + ms_retm_if(webrtc_node->callbacks_structure == NULL, "callbacks_structure is NULL"); ms_retm_if(GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC, "new_pad is not for source"); ms_debug_fenter(); ms_debug("Pad [%s] added on [%s]", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(webrtcbin)); + _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure; + if (!_callbacks->decoded_ready_cb.callback) { + ms_warning("decoded_ready_cb.callback is null, skip it"); + return; + } + + decodebin = ms_element_create(DEFAULT_DECODEBIN, NULL); + ms_retm_if(decodebin == NULL, "decodebin is NULL"); + + ms_retm_if(webrtc_node->parent_streamer == NULL, "parent_streamer is NULL"); + ms_retm_if(webrtc_node->parent_streamer->pipeline == NULL, "pipeline is NULL"); + gst_bin_add(GST_BIN(webrtc_node->parent_streamer->pipeline), decodebin); + + gst_element_sync_state_with_parent(decodebin); + + g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc_node); + + sink_pad = gst_element_get_static_pad(decodebin, "sink"); + ms_retm_if(sink_pad == NULL, "sink_pad is NULL"); + + gst_pad_link(new_pad, sink_pad); + gst_object_unref(sink_pad); + ms_debug_fleave(); } diff --git a/src/media_streamer_node.c b/src/media_streamer_node.c index cd1576f..b82ab05 100644 --- a/src/media_streamer_node.c +++ b/src/media_streamer_node.c @@ -1459,6 +1459,7 @@ int ms_webrtc_node_prepare(media_streamer_s *ms_streamer, media_streamer_node_s node_info_s *node_klass_type = NULL; GObject *send_channel = NULL; gboolean is_offerer = FALSE; + media_streamer_webrtc_callbacks_s *_callbacks = NULL; ms_debug_fenter(); @@ -1472,7 +1473,8 @@ int ms_webrtc_node_prepare(media_streamer_s *ms_streamer, media_streamer_node_s return MEDIA_STREAMER_ERROR_INVALID_OPERATION; } - if (!node->user_cb.callback) { + _callbacks = (media_streamer_webrtc_callbacks_s *) node->callbacks_structure; + if (!_callbacks || !(_callbacks->message_cb.callback)) { ms_error("message callback should be set before preparing"); return MEDIA_STREAMER_ERROR_INVALID_OPERATION; } @@ -1494,14 +1496,13 @@ int ms_webrtc_node_prepare(media_streamer_s *ms_streamer, media_streamer_node_s } g_signal_emit_by_name (webrtcbin, "create-data-channel", "channel", NULL, &send_channel); - if (send_channel) { + if (send_channel) ms_info("data channel(%p) for sending is created", send_channel); - } else { - ms_warning("Failed to create data channel, is usrsctp available?"); - } + else + ms_warning("Failed to create data channel"); ms_signal_create(&node->sig_list, webrtcbin, "on-data-channel", G_CALLBACK(ms_webrtcbin_on_data_channel_cb), ms_streamer); - ms_signal_create(&node->sig_list, webrtcbin, "pad-added", G_CALLBACK(ms_webrtcbin_pad_added_cb), ms_streamer); + ms_signal_create(&node->sig_list, webrtcbin, "pad-added", G_CALLBACK(ms_webrtcbin_pad_added_cb), node); ms_generate_dots(node->gst_element, "webrtc_prepared");