--- /dev/null
+/*
+ * 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__ */
} 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 {
GList *sig_list;
void *callbacks_structure;
+ GHashTable *probe_cb_table;
mm_resource_manager_res_h resource;
device_policy_manager_h dpm_handle;
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
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);
--- /dev/null
+/*
+ * 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
#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>
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;
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;
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");
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);
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");
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");
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;
_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;