Add new internal APIs to media_streamer_internal.h 42/249242/26
authorHyunil <hyunil46.park@samsung.com>
Wed, 9 Dec 2020 07:34:29 +0000 (16:34 +0900)
committerHyunil <hyunil46.park@samsung.com>
Mon, 21 Dec 2020 05:58:23 +0000 (14:58 +0900)
- Add media_streamer_node_pad_probe_cb
  Called when data flows in the pad of media streamer node.
  This callback is used in pad of node by media_streamer_node_set_pad_probe_cb().
  The GstBuffer pointer flowing through the pad is passed as a void pointer and
  it is only readable.
- Add media_streamer_node_set_pad_probe_cb
  Sets a callback function to be invoked when data flows to a pad of node specified by pad_name.
  The callback can be registered with all pads in node using pad_name. However, only one per pad is possible.
- Add media_streamer_node_unset_pad_probe_cb
  Unsets the pad probe callback function.
  Unsets a callback function of a pad of @a node specified by pad name.
- Add test code

[Issue type] New feature
[Version] 0.1.130

Change-Id: I4ec3e91588edfe6185537530169ae9f7a4986dcc
Signed-off-by: Hyunil <hyunil46.park@samsung.com>
include/media_streamer_internal.h [new file with mode: 0644]
include/media_streamer_priv.h
packaging/capi-media-streamer.spec
src/media_streamer.c
src/media_streamer_internal.c [new file with mode: 0644]
test/media_streamer_test.c

diff --git a/include/media_streamer_internal.h b/include/media_streamer_internal.h
new file mode 100644 (file)
index 0000000..4feda55
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef __TIZEN_MEDIA_STREAMER_INTERNAL_H__
+#define __TIZEN_MEDIA_STREAMER_INTERNAL_H__
+
+#include "media_streamer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ * @brief Called when data flows in the pad of media streamer node.
+ * @details This callback is used in pad of @a node by media_streamer_node_set_pad_probe_cb().\n
+ *          The GstBuffer pointer flowing through the pad is passed as a void pointer and\n
+ *          it is only readable.
+ * @since_tizen 6.5
+ * @param[in] gst_buf     The void pointer to GstBuffer
+ * @param[in] user_data   The user data passed from the callback registration function
+ * @see media_streamer_node_set_pad_probe_cb()
+ * @see media_streamer_node_unset_pad_probe_cb()
+ */
+typedef void (*media_streamer_node_pad_probe_cb)(const void *gst_buf, void *user_data);
+
+/**
+ * @internal
+ * @brief Sets a callback function to be invoked when data flows to a pad of @a node specified by pad_name.
+ * @details The callback can be registered with all pads in @a node using pad_name.\n
+ *          However, only one per pad is possible.
+ * @since_tizen 6.5
+ * @param[in] node      Media streamer node handle
+ * @param[in] pad_name  Name of pad to register callback
+ * @param[in] callback  The pad probe 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_pad_probe_cb() will be invoked.
+ * @see media_streamer_node_unset_pad_probe_cb()
+ * @see media_streamer_node_pad_probe_cb()
+ */
+int media_streamer_node_set_pad_probe_cb(media_streamer_node_h node, const char *pad_name, media_streamer_node_pad_probe_cb callback, void *user_data);
+
+/**
+ * @internal
+ * @brief Unsets the pad probe callback function.
+ * @details Unsets a callback function of a pad of @a node specified by pad name.
+ * @since_tizen 6.5
+ * @param[in] node      Media streamer node handle
+ * @param[in] pad_name  Name of pad to register callback
+ * @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_pad_probe_cb()
+ */
+int media_streamer_node_unset_pad_probe_cb(media_streamer_node_h node, const char *pad_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_MEDIA_STREAMER_INTERNAL_H__ */
index d6b2206..c31615e 100644 (file)
@@ -48,6 +48,17 @@ typedef struct {
 } media_streamer_sink_callbacks_s;
 
 /**
+ * @brief Media Streamer probe callback structure.
+ */
+typedef struct {
+       void *node;
+       gchar *pad_name;
+       gulong probe_id;
+       void *callback;
+       void *user_data;
+} media_streamer_probe_callback_s;
+
+/**
  * @brief Media Streamer webrtc node callbacks structure.
  */
 typedef struct {
@@ -183,6 +194,7 @@ typedef struct {
        GList *sig_list;
 
        void *callbacks_structure;
+       GHashTable *probe_cb_table;
 
        mm_resource_manager_res_h resource;
        device_policy_manager_h dpm_handle;
index d2e8892..7481fd7 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-streamer
 Summary:    A Media Streamer API
-Version:    0.1.129
+Version:    0.1.130
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index e12de32..b168231 100644 (file)
@@ -125,6 +125,11 @@ int media_streamer_node_destroy(media_streamer_node_h node)
 
        ms_retvm_if(ms_node == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL");
 
+       if (ms_node->probe_cb_table) {
+               g_hash_table_destroy(ms_node->probe_cb_table);
+               ms_node->probe_cb_table = NULL;
+       }
+
        if (ms_node->parent_streamer == NULL) {
                /* This node was not added into any media streamer */
                ms_node_destroy(ms_node);
diff --git a/src/media_streamer_internal.c b/src/media_streamer_internal.c
new file mode 100644 (file)
index 0000000..fc6e5ca
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2020 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 <stdlib.h>
+#include <string.h>
+#include <dlog.h>
+#include "media_streamer.h"
+#include "media_streamer_priv.h"
+#include "media_streamer_node.h"
+#include "media_streamer_gst.h"
+#include "media_streamer_internal.h"
+
+//LCOV_EXCL_START
+static GstPadProbeReturn __gst_pad_probe_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
+{
+       media_streamer_probe_callback_s *probe_callback = NULL;
+
+       ms_retvm_if(!info->data, GST_PAD_PROBE_REMOVE, "data is NULL");
+       ms_retvm_if(!user_data, GST_PAD_PROBE_REMOVE, "user_data is NULL");
+
+       probe_callback = (media_streamer_probe_callback_s *)user_data;
+       ((media_streamer_node_pad_probe_cb)probe_callback->callback)((const GstBuffer *)gst_pad_probe_info_get_buffer(info), probe_callback->user_data);
+
+       return GST_PAD_PROBE_OK;
+}
+
+static void __remove_gst_pad_probe_cb(const char *pad_name, gulong probe_id, media_streamer_node_s *node)
+{
+       GstPad *pad = NULL;
+
+       ms_retm_if(!pad_name, "pad_name is NULL");
+       ms_retm_if(!node, "node is NULL");
+       ms_retm_if(!node->gst_element, "gst_element is NULL");
+
+       pad = gst_element_get_static_pad(node->gst_element, pad_name);
+       if (!pad)
+               pad = gst_element_get_request_pad(node->gst_element, pad_name);
+
+       if (!pad) {
+               ms_warning("pad[%s] is NULL, in case of force unset, request pad can be already disposed after unlink and\n"
+                               "probe was cleared by gst_pad_dispose()", pad_name);
+               return;
+       }
+
+       ms_debug("Remove probe[id:%lu], pad_name[%s]", probe_id, pad_name);
+       gst_pad_remove_probe(pad, probe_id);
+       gst_object_unref(GST_OBJECT(pad));
+}
+
+static void __free_probe_callback(media_streamer_probe_callback_s *probe_callback)
+{
+       ms_retm_if(!probe_callback, "probe_callback is NULL");
+       ms_retm_if(!probe_callback->pad_name, "pad_name is NULL");
+
+       ms_debug("Free probe_callback[%p], pad_name[%s]", probe_callback, probe_callback->pad_name);
+
+       g_free(probe_callback->pad_name);
+       probe_callback->pad_name = NULL;
+       probe_callback->callback = NULL;
+       probe_callback->user_data = NULL;
+       probe_callback->node = NULL;
+       g_free(probe_callback);
+}
+
+static void __ms_probe_callback_destroy(gpointer value)
+{
+       media_streamer_probe_callback_s *probe_callback = (media_streamer_probe_callback_s *)value;
+
+       ms_retm_if(!probe_callback, "probe_callback is NULL");
+       ms_retm_if(!probe_callback->callback, "user callback is NULL");
+       ms_retm_if(!probe_callback->pad_name, "pad_name is NULL");
+       ms_retm_if(!probe_callback->node, "node is NULL");
+
+       ms_debug_fenter();
+
+       __remove_gst_pad_probe_cb(probe_callback->pad_name, probe_callback->probe_id, probe_callback->node);
+       __free_probe_callback(probe_callback);
+
+       ms_debug_fleave();
+}
+
+int media_streamer_node_set_pad_probe_cb(media_streamer_node_h node, const char *pad_name, media_streamer_node_pad_probe_cb callback, void *user_data)
+{
+       media_streamer_node_s *ms_node = NULL;
+       media_streamer_probe_callback_s *probe_callback = NULL;
+       GstPad *pad = NULL;
+       gulong probe_id;
+
+       ms_retvm_if(!node || !pad_name || !callback, MEDIA_STREAMER_ERROR_INVALID_PARAMETER,
+                       "There is NULL among node[%p], pad_nad[%p] and callback[%p]", node, pad_name, callback);
+
+       ms_debug_fenter();
+
+       ms_node = (media_streamer_node_s *)node;
+       ms_retvm_if(ms_node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "gst_element is NULL");
+
+       if (!ms_node->probe_cb_table) {
+               ms_node->probe_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)__ms_probe_callback_destroy);
+               ms_retvm_if(!ms_node->probe_cb_table, MEDIA_STREAMER_ERROR_INVALID_OPERATION,"Failed to probe callback hash table");
+       } else {
+               ms_debug("probe_cb_table is already created");
+               if (g_hash_table_lookup(ms_node->probe_cb_table, (gconstpointer)pad_name)) {
+                       ms_error("callback is already set to pad[%s]", pad_name);
+                       return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+               }
+       }
+
+       pad = gst_element_get_static_pad(ms_node->gst_element, pad_name);
+       if (!pad) {
+               pad = gst_element_get_request_pad(ms_node->gst_element, pad_name);
+               if (!pad) {
+                       ms_error ("Failed to get pad from pad_name[%s]", pad_name);
+                       return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+               }
+       }
+
+       probe_callback = g_new0(media_streamer_probe_callback_s, 1);
+       probe_callback->callback = callback;
+       probe_callback->user_data = user_data;
+       probe_callback->node = ms_node;
+       probe_callback->pad_name = g_strdup(pad_name);
+
+       if (!(probe_id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, __gst_pad_probe_cb, (gpointer)probe_callback, NULL))) {
+               ms_error ("Failed to add probe callback");
+               gst_object_unref(GST_OBJECT(pad));
+               g_free(probe_callback);
+               return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
+       }
+       probe_callback->probe_id = probe_id;
+
+       if (g_hash_table_insert(ms_node->probe_cb_table, (gpointer)pad_name, (gpointer)probe_callback))
+               ms_debug("Add key[%s]: value[%p]", pad_name, probe_callback);
+
+       ms_debug("Add user callback[%p] to pad_name[%s]", callback, pad_name);
+       gst_object_unref(GST_OBJECT(pad));
+
+       ms_debug_fleave();
+
+       return MEDIA_STREAMER_ERROR_NONE;
+}
+
+int media_streamer_node_unset_pad_probe_cb(media_streamer_node_h node, const char *pad_name)
+{
+       media_streamer_node_s *ms_node = (media_streamer_node_s *)node;
+
+       ms_retvm_if(!ms_node, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "node is NULL");
+       ms_retvm_if(!pad_name, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "pad_name is NULL");
+       ms_retvm_if(!ms_node->probe_cb_table, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "probe_cb_table is NULL");
+
+       ms_debug_fenter();
+
+       if (!g_hash_table_remove(ms_node->probe_cb_table, (gpointer)pad_name)) {
+               ms_error("There is no callback to unset callback in the pad[%s]", pad_name);
+               return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
+       }
+       ms_debug("Remove key[%s] from probe callback table", pad_name);
+
+       ms_debug_fleave();
+
+       return MEDIA_STREAMER_ERROR_NONE;
+}
+//LCOV_EXCL_STOP
index 7ed8c11..8dba965 100644 (file)
@@ -23,6 +23,8 @@
 #include <Elementary.h>
 #include <appcore-efl.h>
 #include <media_streamer.h>
+#include <media_streamer_internal.h>
+#include <gst/gst.h>
 
 /* For WebRTC */
 #include <libsoup/soup.h>
@@ -191,6 +193,7 @@ media_streamer_node_h g_webrtc = NULL;
 media_streamer_node_h g_video_converter = NULL;
 media_streamer_node_h g_video_sink = NULL;
 media_streamer_node_h g_audio_sink = NULL;
+media_streamer_node_h g_qtdemux = NULL;
 
 media_format_h vfmt_vp8 = NULL;
 media_format_h vfmt_i420 = NULL;
@@ -224,6 +227,37 @@ struct appcore_ops ops = {
 
 appdata ad;
 
+static void node_src_pad_probe_cb(const void *gst_buf, void *user_data){
+       const GstBuffer *buf = GST_BUFFER_CAST(gst_buf);
+       GstBuffer *new_buf = NULL;
+
+       g_print("called node_src_pad_probe_cb, get gst_buf[%p],\n", gst_buf);
+       if (GST_IS_BUFFER(buf)){
+               g_printf("[%p] is a GstBuffer\n", buf);
+               /* gst_buf is not a writeable GstBuffer,
+                  if you need to modify it, create a new GstBuffer and release after use.
+                  If you only need to read the data in the GstBuffer, don't release it. */
+               new_buf = gst_buffer_copy(buf);
+               /* modify new_buf */
+
+               /* free */
+               g_printf("free new_buf[%p]\n", new_buf);
+               gst_buffer_unref(new_buf);
+       }
+}
+
+static void node_sink_pad_probe_cb(const void *gst_buf, void *user_data){
+       const GstBuffer *buf = GST_BUFFER_CAST(gst_buf);
+
+       g_print("called node_sink_pad_probe_cb, get gst_buf[%p],\n", gst_buf);
+       if (GST_IS_BUFFER(buf)) {
+               /* gst_buf is not a writeable GstBuffer,
+                  if you need to modify it, create a new GstBuffer and release after use.
+                  If you only need to read the data in the GstBuffer, don't release it. */
+               g_printf("[%p] is a GstBuffer\n", buf);
+       }
+}
+
 static void webrtc_decoded_ready_cb(media_streamer_node_h node, const char *src_pad_name, const char *media_type, void *user_data)
 {
        media_streamer_node_h video_converter = NULL;
@@ -413,6 +447,48 @@ static void _stop(void)
        g_print("== success stop \n");
 }
 
+static void _set_node_probe_callback(void)
+{
+       int ret = MEDIA_STREAMER_ERROR_NONE;
+       int src_pad_num = 0;
+       int sink_pad_num = 0;
+       char **src_pad_name = NULL;
+       char **sink_pad_name = NULL;
+
+       if (!g_qtdemux)
+               return;
+       media_streamer_node_get_pad_name(g_qtdemux, &src_pad_name, &src_pad_num, &sink_pad_name, &sink_pad_num);
+       g_printf("src_pad_name[%s], src_pad_num[%d], sink_pad_name[%s], sink_pad_num[%d]\n", src_pad_name[0], src_pad_num, sink_pad_name[0], sink_pad_num);
+
+       if (ret != media_streamer_node_set_pad_probe_cb(g_qtdemux, src_pad_name[0], node_src_pad_probe_cb, NULL))
+               return;
+       if (ret != media_streamer_node_set_pad_probe_cb(g_qtdemux, sink_pad_name[0], node_sink_pad_probe_cb, NULL))
+               return;
+
+       g_print("== success set node probe callback \n");
+}
+
+static void _unset_node_probe_callback(void)
+{
+       int ret = MEDIA_STREAMER_ERROR_NONE;
+       int src_pad_num = 0;
+       int sink_pad_num = 0;
+       char **src_pad_name = NULL;
+       char **sink_pad_name = NULL;
+
+       if (!g_qtdemux)
+               return;
+       media_streamer_node_get_pad_name(g_qtdemux, &src_pad_name, &src_pad_num, &sink_pad_name, &sink_pad_num);
+       g_printf("src_pad_name[%s], src_pad_num[%d], sink_pad_name[%s], sink_pad_num[%d]\n", src_pad_name[0], src_pad_num, sink_pad_name[0], sink_pad_num);
+
+       if (ret != media_streamer_node_unset_pad_probe_cb(g_qtdemux, src_pad_name[0]))
+               return;
+       if (ret != media_streamer_node_unset_pad_probe_cb(g_qtdemux, sink_pad_name[0]))
+               return;
+
+       g_print("== success unset node probe callback \n");
+}
+
 static void _destroy(media_streamer_h streamer)
 {
        g_print("== destroy \n");
@@ -1057,6 +1133,7 @@ static void _create_static_video_mp4_playing(void)
        media_streamer_node_create(MEDIA_STREAMER_NODE_TYPE_DEMUXER, cfmt_mp4, NULL, &qt_demux);
        media_streamer_node_add(current_media_streamer, qt_demux);
        APPEND_NODE(qt_demux);
+       g_qtdemux = qt_demux;
 
        media_streamer_node_h video_decoder = NULL;
        media_streamer_node_create(MEDIA_STREAMER_NODE_TYPE_VIDEO_DECODER, vfmt_h264, NULL, &video_decoder);
@@ -1785,7 +1862,7 @@ static void display_static_link_playing_scenario_select_menu(void)
        g_print("1. Appsrc -> Appsink\n");
        g_print("2. Audio File(aac) playing\n");
        g_print("3. Audio File(mp3) playing\n");
-       g_print("4. Video File(mp4) playing\n");
+       g_print("4. Video File(mp4) playing with node probe callback \n");
        g_print("b. back \n");
        g_print("----------------------------------------------------\n");
        g_print("====================================================\n");
@@ -1858,6 +1935,29 @@ static void display_preset_menu(void)
        g_print("====================================================\n");
 }
 
+static void display_static_video_mp4_preset_menu(void)
+{
+       g_print("\n");
+       g_print("====================================================\n");
+       g_print("   media streamer test: Static video mp4 preset menu\n");
+       g_print("----------------------------------------------------\n");
+       g_print("1. create media streamer \n");
+       g_print("2. set up current preset \n");
+       g_print("3. prepare \n");
+       g_print("4. unprepare \n");
+       g_print("5. play \n");
+       g_print("6. pause \n");
+       g_print("7. seek \n");
+       g_print("8. stop \n");
+       g_print("9. destroy media streamer \n");
+       g_print("s. set node probe callback to qtdemux  \n");
+       g_print("u. unset node probe callback to qtdemux \n\n");
+       g_print("b. back \n");
+       g_print("----------------------------------------------------\n");
+       g_print("====================================================\n");
+}
+
+
 static void display_voip_menu(void)
 {
        g_print("\n");
@@ -1921,13 +2021,19 @@ static void display_menu(void)
                        display_voip_menu();
                        break;
                case MENU_STATE_AUTOPLUG_PLAYING_MENU:
-               case MENU_STATE_STATIC_LINK_PLAYING_MENU:
                case MENU_STATE_PRESET_MENU:
                case MENU_STATE_ADAPTIVE_MENU:
                case MENU_STATE_VIDEO_TEST_MENU:
                case MENU_STATE_WEBRTC_MENU:
                        display_preset_menu();
                        break;
+               case MENU_STATE_STATIC_LINK_PLAYING_MENU:
+                       if (g_scenario_mode == SCENARIO_MODE_FILE_PLAY_MP4_VIDEO) {
+                               display_static_video_mp4_preset_menu();
+                               return;
+                       }
+                       display_preset_menu();
+                       break;
                default:
                        g_print("*** Unknown status.\n");
                        break;
@@ -2568,6 +2674,10 @@ void _interpret_preset_menu(char *cmd)
                        _stop();
                } else if (!strncmp(cmd, "9", len)) {
                        _destroy(current_media_streamer);
+               } else if (!strncmp(cmd, "s", len) && g_scenario_mode == SCENARIO_MODE_FILE_PLAY_MP4_VIDEO) {
+                       _set_node_probe_callback();
+               } else if (!strncmp(cmd, "u", len) && g_scenario_mode == SCENARIO_MODE_FILE_PLAY_MP4_VIDEO) {
+                       _unset_node_probe_callback();
                } else if (!strncmp(cmd, "b", len)) {
                        if (g_menu_preset & DOUBLE_STREAMER_MASK) {
                                g_menu_state = MENU_STATE_VOIP_MENU;