Add new internal APIs for media bridge 94/251694/18
authorJeongmo Yang <jm80.yang@samsung.com>
Fri, 23 Apr 2021 07:01:29 +0000 (16:01 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Mon, 26 Apr 2021 11:26:06 +0000 (20:26 +0900)
[Version] 0.1.46
[Issue Type] New feature

Change-Id: If31421fb738d29fdb752ff1b64b3e3a4111218c4
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
include/media_bridge_internal.h [new file with mode: 0644]
include/media_bridge_private.h [new file with mode: 0644]
packaging/capi-media-tool.spec
src/media_bridge.c [new file with mode: 0644]

diff --git a/include/media_bridge_internal.h b/include/media_bridge_internal.h
new file mode 100644 (file)
index 0000000..fbc6e1c
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+* Copyright (c) 2021 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_BRIDGE_INTERNAL_H__
+#define __TIZEN_MEDIA_BRIDGE_INTERNAL_H__
+
+#include <stdint.h>
+#include <tizen.h>
+#include <media_packet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file media_bridge_internal.h
+ * @brief This file contains the media bridge internal API.
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_TOOL_MEDIA_BRIDGE_INTERNAL_MODULE
+ * @{
+ */
+
+/**
+ * @internal
+ * @brief Media Bridge handle type.
+ * @since_tizen 6.5
+ */
+typedef struct media_bridge_s *media_bridge_h;
+
+/**
+ * @internal
+ * @brief Enumerations of media bridge error.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       MEDIA_BRIDGE_ERROR_NONE = TIZEN_ERROR_NONE,                             /**< Successful */
+       MEDIA_BRIDGE_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY,           /**< Out of memory */
+       MEDIA_BRIDGE_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER,   /**< Invalid parameter */
+       MEDIA_BRIDGE_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION,   /**< Invalid operation */
+       MEDIA_BRIDGE_ERROR_INVALID_STATE = TIZEN_ERROR_MEDIA_TOOL | 0x11,       /**< Invalid state */
+       MEDIA_BRIDGE_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED,           /**< The feature is not supported */
+} media_bridge_error_e;
+
+/**
+ * @internal
+ * @brief Enumeration for the media bridge state.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       MEDIA_BRIDGE_STATE_NONE,          /**< None */
+       MEDIA_BRIDGE_STATE_CREATED,       /**< Created */
+       MEDIA_BRIDGE_STATE_STREAMING      /**< Now streaming */
+} media_bridge_state_e;
+
+/**
+ * @internal
+ * @brief Enumeration for source of sink module of the media bridge.
+ * @since_tizen 6.5
+ */
+typedef enum {
+       MEDIA_BRIDGE_MODULE_CAMERA,     /**< Media camera */
+       MEDIA_BRIDGE_MODULE_CODEC,      /**< Media codec */
+       MEDIA_BRIDGE_MODULE_PLAYER,     /**< Media player */
+       MEDIA_BRIDGE_MODULE_VISION,     /**< Media vision */
+       MEDIA_BRIDGE_MODULE_WEBRTC,     /**< Media webrtc */
+       MEDIA_BRIDGE_MODULE_NUM
+} media_bridge_module_e;
+
+
+/**
+ * @internal
+ * @brief The function prototype to set media bridge for source module.
+ * @since_tizen 6.5
+ * @remarks The source module should have the implementation for this function.
+ * @param[in] module_handle The handle to the module
+ * @param[in] bridge The handle to the media bridge
+ * @see media_bridge_set_source()
+ */
+typedef int (*module_media_bridge_set_bridge_func)(void *module_handle, media_bridge_h bridge);
+
+/**
+ * @internal
+ * @brief The function prototype to unset media bridge for source module.
+ * @since_tizen 6.5
+ * @remarks The source module should have the implementation for this function.
+ * @param[in] module_handle The handle to the module
+ * @see media_bridge_unset_source()
+ */
+typedef int (*module_media_bridge_unset_bridge_func)(void *module_handle);
+
+/**
+ * @internal
+ * @brief The function prototype to push media packet to sink module.
+ * @since_tizen 6.5
+ * @remarks The sink module should have the implementation for this function.
+ * @param[in] module_handle The handle to the module
+ * @param[in] bridge The handle to the media bridge
+ * @param[in] sink_id The id of the added sink
+ * @param[in] packet The media packet from source module
+ * @see media_bridge_unset_source()
+ */
+typedef int (*module_media_bridge_push_packet_func)(void *module_handle, media_bridge_h bridge, int sink_id, media_packet_h packet);
+
+
+/**
+ * @internal
+ * @brief Creates a new media bridge handle.
+ * @since_tizen 6.5
+ * @param[out] bridge A newly returned handle to the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @post If it succeeds, the state will be #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_destroy()
+ */
+int media_bridge_create(media_bridge_h *bridge);
+
+/**
+ * @internal
+ * @brief Destroys the media bridge handle and releases all its resources.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_create()
+ */
+int media_bridge_destroy(media_bridge_h bridge);
+
+/**
+ * @internal
+ * @brief Sets the source module for media bridge.
+ * @since_tizen 6.5
+ * @remarks This function should be called before streaming(see media_bridge_start()).
+ * @param[in] bridge The handle to the media bridge
+ * @param[in] module The module that provides buffers
+ * @param[in] module_handle The handle to the module
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIA_BRIDGE_ERROR_NOT_SUPPORTED The feature is not supported
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_create()
+ * @see media_bridge_unset_source()
+ */
+int media_bridge_set_source(media_bridge_h bridge, media_bridge_module_e module, void *module_handle);
+
+/**
+ * @internal
+ * @brief Unsets the source module for media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_OPERATION Invalid operation
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_set_source()
+ */
+int media_bridge_unset_source(media_bridge_h bridge);
+
+/**
+ * @internal
+ * @brief Adds the sink module for media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @param[in] module The module that consumes buffers
+ * @param[in] module_handle The handle to the module
+ * @param[out] sink_id The id of the added sink
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIA_BRIDGE_ERROR_NOT_SUPPORTED The feature is not supported
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_create()
+ * @see media_bridge_remove_sink()
+ */
+int media_bridge_add_sink(media_bridge_h bridge, media_bridge_module_e module, void *module_handle, int *sink_id);
+
+/**
+ * @internal
+ * @brief Removes the sink module for media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @param[in] sink_id The id of the added sink
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @see media_bridge_create()
+ * @see media_bridge_add_sink()
+ */
+int media_bridge_remove_sink(media_bridge_h bridge, int sink_id);
+
+/**
+ * @internal
+ * @brief Starts the streaming of media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_OPERATION Invalid operation
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_CREATED.
+ * @pre The source module should be set through media_bridge_set_source() \n
+        and the sink module should be added through media_bridge_add_sink().
+ * @post If it succeeds, the state will be #MEDIA_BRIDGE_STATE_STREAMING and \n
+ *       buffers will be delivered from source module to sink modules automatically.
+ * @see media_bridge_stop()
+ */
+int media_bridge_start(media_bridge_h bridge);
+
+/**
+ * @internal
+ * @brief Stops the streaming of media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_STREAMING.
+ * @post If it succeeds, the state will be #MEDIA_BRIDGE_STATE_CREATED and \n
+ *       it's stopped to deliver buffers from source module to sink modules.
+ * @see media_bridge_start()
+ */
+int media_bridge_stop(media_bridge_h bridge);
+
+/**
+ * @internal
+ * @brief Gets the state of media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @param[out] state The current state of the media bridge
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int media_bridge_get_state(media_bridge_h bridge, media_bridge_state_e *state);
+
+/**
+ * @internal
+ * @brief Pushes media packet to media bridge.
+ * @since_tizen 6.5
+ * @param[in] bridge The handle to the media bridge
+ * @param[in] packet The handle to the media packet
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_BRIDGE_ERROR_NONE Successful
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIA_BRIDGE_ERROR_INVALID_OPERATION Invalid operation
+ * @pre The state must be set to #MEDIA_BRIDGE_STATE_STREAMING.
+ * @see media_bridge_start()
+ */
+int media_bridge_push_packet(media_bridge_h bridge, media_packet_h packet);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_MEDIA_BRIDGE_INTERNAL_H__ */
diff --git a/include/media_bridge_private.h b/include/media_bridge_private.h
new file mode 100644 (file)
index 0000000..23bed56
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+* Copyright (c) 2021 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_BRIDGE_PRIVATE_H__
+#define __TIZEN_MEDIA_BRIDGE_PRIVATE_H__
+
+#include <media_bridge_internal.h>
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "TIZEN_N_MEDIA_BRIDGE"
+
+#define MEDIA_BRIDGE_CHECK_CONDITION(condition, error, msg)     \
+do { \
+       if (!(condition)) { \
+               LOGE("%s(0x%08x)", msg, error); \
+               return error; \
+       } \
+} while (0)
+
+#define MEDIA_BRIDGE_NULL_CHECK(arg)   \
+       MEDIA_BRIDGE_CHECK_CONDITION(arg, MEDIA_BRIDGE_ERROR_INVALID_PARAMETER, "MEDIA_BRIDGE_ERROR_INVALID_PARAMETER")
+
+#define MEDIA_BRIDGE_SINK_MAX   10
+
+
+typedef struct _media_bridge_source_s {
+       media_bridge_module_e module;
+       void *dl_handle;
+       void *module_handle;
+       module_media_bridge_set_bridge_func set_bridge_func;
+       module_media_bridge_unset_bridge_func unset_bridge_func;
+} media_bridge_source_s;
+
+typedef struct _media_bridge_sink_s {
+       media_bridge_module_e module;
+       void *dl_handle;
+       void *module_handle;
+       module_media_bridge_push_packet_func push_packet_func;
+} media_bridge_sink_s;
+
+typedef struct _media_bridge_s {
+       media_bridge_state_e state;
+       GMutex lock;
+       GCond cond;
+       GQueue *packet_queue;
+       GThread *push_packet_thread;
+
+       media_bridge_source_s src;
+       media_bridge_sink_s sink[MEDIA_BRIDGE_SINK_MAX];
+       int sink_count;
+} media_bridge_s;
+
+typedef struct _media_bridge_module_name_table {
+       const char *library_path;
+       const char *set_bridge;
+       const char *unset_bridge;
+       const char *push_packet;
+} media_bridge_module_name_table;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_MEDIA_BRIDGE_PRIVATE_H__ */
index be0538d2a1fd876aa72bef492523ac8b88c80ebd..82cc2955c08980454981d4c89209c0b20a7548a0 100755 (executable)
@@ -1,6 +1,6 @@
 Name:       capi-media-tool
 Summary:    A Core API media tool library in Tizen Native API
-Version:    0.1.45
+Version:    0.1.46
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
@@ -49,6 +49,7 @@ export CFLAGS+=" -fprofile-arcs -ftest-coverage"
 export CXXFLAGS+=" -fprofile-arcs -ftest-coverage"
 export LDFLAGS+=" -lgcov"
 %endif
+export CFLAGS+=" -DPATH_LIBDIR=\\\"%{_libdir}\\\""
 
 MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
 %cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DFULLVER=%{version} -DMAJORVER=${MAJORVER} \
diff --git a/src/media_bridge.c b/src/media_bridge.c
new file mode 100644 (file)
index 0000000..aeda57e
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+* Copyright (c) 2021 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <dlog.h>
+#include <media_bridge_private.h>
+
+
+static media_bridge_module_name_table g_media_bridge_module_name_table[MEDIA_BRIDGE_MODULE_NUM] = {
+       /* MEDIA_BRIDGE_MODULE_CAMERA */
+       {PATH_LIBDIR"/libcapi-media-camera.so.0",
+               "camera_media_bridge_set_bridge",
+               "camera_media_bridge_unset_bridge",
+               NULL},
+       /* MEDIA_BRIDGE_MODULE_PLAYER */
+       {PATH_LIBDIR"/libcapi-media-player.so.0",
+               NULL,
+               NULL,
+               NULL},
+       /* MEDIA_BRIDGE_MODULE_VISION */
+       {PATH_LIBDIR"/libmv_common.so",
+               NULL,
+               NULL,
+               NULL},
+       /* MEDIA_BRIDGE_MODULE_CODEC */
+       {PATH_LIBDIR"/libcapi-media-codec.so.0",
+               NULL,
+               NULL,
+               NULL},
+       /* MEDIA_BRIDGE_MODULE_WEBRTC */
+       {PATH_LIBDIR"/libcapi-media-webrtc.so.0",
+               NULL,
+               NULL,
+               "webrtc_media_bridge_push_packet"}
+};
+
+
+static void __media_bridge_packet_queue_release(gpointer data)
+{
+       media_packet_h packet = (media_packet_h)data;
+
+       if (!packet) {
+               LOGE("NULL packet");
+               return;
+       }
+
+       LOGI("release remained packet[%p]", packet);
+
+       media_packet_unref(packet);
+}
+
+
+static int __media_bridge_unset_source_no_lock(media_bridge_s *handle)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       if (!handle->src.dl_handle || !handle->src.module_handle ||
+               !handle->src.unset_bridge_func) {
+               LOGE("invalid source[%d:%p,%p,%p]",
+                       handle->src.module, handle->src.module_handle,
+                       handle->src.dl_handle, handle->src.unset_bridge_func);
+               return MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+       }
+
+       ret = handle->src.unset_bridge_func(handle->src.module_handle);
+       if (ret != MEDIA_BRIDGE_ERROR_NONE) {
+               LOGE("unset media bridge failed[0x%x]", ret);
+               return ret;
+       }
+
+       dlclose(handle->src.dl_handle);
+       memset(&handle->src, 0x0, sizeof(media_bridge_source_s));
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+static int __media_bridge_remove_all_sink_no_lock(media_bridge_s *handle)
+{
+       int id = 0;
+       media_bridge_sink_s *sink = NULL;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       for (id = 0 ; id < MEDIA_BRIDGE_SINK_MAX ; id++) {
+               sink = &handle->sink[id];
+               if (sink->dl_handle) {
+                       LOGI("remove sink[id:%d,t:%d,%p,%p]",
+                               id, sink->module, sink->module_handle, sink->dl_handle);
+                       dlclose(sink->dl_handle);
+                       memset(sink, 0x0, sizeof(media_bridge_sink_s));
+               }
+       }
+
+       handle->sink_count = 0;
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+static gpointer __media_bridge_push_packet_func(gpointer data)
+{
+       int id = 0;
+       int push_count = 0;
+       int ret = 0;
+       media_bridge_s *handle = (media_bridge_s *)data;
+       media_packet_h packet = NULL;
+       media_bridge_sink_s *sink = NULL;
+
+       if (!handle) {
+               LOGE("NULL handle");
+               return NULL;
+       }
+
+       LOGI("push packet thread[%p,%p]", handle, handle->push_packet_thread);
+
+       g_mutex_lock(&handle->lock);
+
+       while (handle->state != MEDIA_BRIDGE_STATE_NONE) {
+               if (g_queue_is_empty(handle->packet_queue))
+                       g_cond_wait(&handle->cond, &handle->lock);
+
+               packet = g_queue_pop_head(handle->packet_queue);
+               if (!packet) {
+                       LOGW("NULL packet, bridge state[%d]", handle->state);
+                       continue;
+               }
+
+               g_mutex_unlock(&handle->lock);
+
+               LOGD("[%p] push packet[%p] to sink[count:%d]", handle, packet, handle->sink_count);
+
+               push_count = 0;
+
+               for (id = 0 ; id < MEDIA_BRIDGE_SINK_MAX ; id++) {
+                       sink = &handle->sink[id];
+
+                       if (!sink->push_packet_func)
+                               continue;
+
+                       ret = media_packet_ref(packet);
+                       if (ret != MEDIA_PACKET_ERROR_NONE) {
+                               LOGE("failed[0x%x] to ref packet[%p] for sink[%d,t:%d,h:%p]",
+                                       ret, packet, id, sink->module, sink->module_handle);
+                               continue;
+                       }
+
+                       ret = sink->push_packet_func(sink->module_handle, (media_bridge_h)handle, id, packet);
+                       if (ret != MEDIA_BRIDGE_ERROR_NONE) {
+                               LOGE("failed[0x%x] to push media packet[module:%d,%p, bridge:%p, id:%d]",
+                                       ret, sink->module, sink->module_handle, handle, id);
+                               media_packet_unref(packet);
+                               continue;
+                       }
+
+                       push_count++;
+               }
+
+               media_packet_unref(packet);
+               packet = NULL;
+
+               if (handle->sink_count != push_count)
+                       LOGW("something wrong[sink count:%d vs push count:%d]", handle->sink_count, push_count);
+
+               g_mutex_lock(&handle->lock);
+       }
+
+       g_mutex_unlock(&handle->lock);
+
+       LOGI("leave: push packet thread[%p,%p]", handle, handle->push_packet_thread);
+
+       return NULL;
+}
+
+
+int media_bridge_create(media_bridge_h *bridge)
+{
+       media_bridge_s *new_handle = NULL;
+
+       MEDIA_BRIDGE_NULL_CHECK(bridge);
+
+       new_handle = g_new0(media_bridge_s, 1);
+
+       new_handle->state = MEDIA_BRIDGE_STATE_CREATED;
+       new_handle->packet_queue = g_queue_new();
+
+       g_mutex_init(&new_handle->lock);
+       g_cond_init(&new_handle->cond);
+
+       new_handle->push_packet_thread = g_thread_try_new("media_bridge_push_packet_thread",
+               __media_bridge_push_packet_func, (gpointer)new_handle, NULL);
+       if (!new_handle->push_packet_thread) {
+               LOGE("push_packet_thread failed");
+               goto _CREATE_FAILED;
+       }
+
+       LOGI("The new bridge handle[%p]", new_handle);
+
+       *bridge = (media_bridge_h)new_handle;
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+
+_CREATE_FAILED:
+       g_cond_clear(&new_handle->cond);
+       g_mutex_clear(&new_handle->lock);
+       g_queue_free_full(new_handle->packet_queue, __media_bridge_packet_queue_release);
+       g_free(new_handle);
+
+       return MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+}
+
+
+int media_bridge_destroy(media_bridge_h bridge)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               g_mutex_unlock(&handle->lock);
+               return MEDIA_BRIDGE_ERROR_INVALID_STATE;
+       }
+
+       LOGI("Release bridge handle[%p]", handle);
+
+       handle->state = MEDIA_BRIDGE_STATE_NONE;
+
+       g_cond_signal(&handle->cond);
+       g_mutex_unlock(&handle->lock);
+
+       LOGI("join push_packet_thread[%p]", handle->push_packet_thread);
+
+       g_thread_join(handle->push_packet_thread);
+       handle->push_packet_thread = NULL;
+
+       LOGI("join done");
+
+       g_cond_clear(&handle->cond);
+       g_mutex_clear(&handle->lock);
+       g_queue_free_full(handle->packet_queue, __media_bridge_packet_queue_release);
+
+       if (handle->src.dl_handle) {
+               ret = __media_bridge_unset_source_no_lock(handle);
+               LOGW("__media_bridge_unset_source_no_lock ret[0x%x]", ret);
+       }
+
+       __media_bridge_remove_all_sink_no_lock(handle);
+
+       memset(handle, 0x0, sizeof(media_bridge_s));
+
+       g_free(handle);
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+int media_bridge_set_source(media_bridge_h bridge, media_bridge_module_e module, void *module_handle)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+       void *dl_handle = NULL;
+       module_media_bridge_set_bridge_func set_bridge_func = NULL;
+       module_media_bridge_unset_bridge_func unset_bridge_func = NULL;
+       const char *library_path = NULL;
+       const char *set_bridge = NULL;
+       const char *unset_bridge = NULL;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+       MEDIA_BRIDGE_NULL_CHECK(module_handle);
+
+       if (module < MEDIA_BRIDGE_MODULE_CAMERA || module >= MEDIA_BRIDGE_MODULE_NUM) {
+               LOGE("invalid module[%d]", module);
+               return MEDIA_BRIDGE_ERROR_INVALID_PARAMETER;
+       }
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_STATE;
+               goto _SET_SOURCE_OUT;
+       }
+
+       if (handle->src.module_handle) {
+               LOGE("source[%p] is already set", handle->src.module_handle);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+               goto _SET_SOURCE_OUT;
+       }
+
+       library_path = g_media_bridge_module_name_table[module].library_path;
+       set_bridge = g_media_bridge_module_name_table[module].set_bridge;
+       unset_bridge = g_media_bridge_module_name_table[module].unset_bridge;
+
+       if (!library_path || !set_bridge || !unset_bridge) {
+               LOGE("module[%d] is not supported[%s,%s,%s] for source",
+                       module, library_path, set_bridge, unset_bridge);
+               ret = MEDIA_BRIDGE_ERROR_NOT_SUPPORTED;
+               goto _SET_SOURCE_OUT;
+       }
+
+       LOGI("open[%s]", library_path);
+
+       dl_handle = dlopen(library_path, RTLD_NOW);
+       if (!dl_handle) {
+               LOGE("open[%s] failed[errno:%d]", library_path, errno);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+               goto _SET_SOURCE_OUT;
+       }
+
+       set_bridge_func = dlsym(dl_handle, set_bridge);
+       unset_bridge_func = dlsym(dl_handle, unset_bridge);
+       if (!set_bridge_func || !unset_bridge_func) {
+               LOGE("module[%d] symbol[%s,%s] failed[%s]",
+                       module, set_bridge, unset_bridge, dlerror());
+               ret = MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+               goto _SET_SOURCE_OUT;
+       }
+
+       ret = set_bridge_func(module_handle, bridge);
+       if (ret != MEDIA_BRIDGE_ERROR_NONE) {
+               LOGE("set media bridge failed[0x%x]", ret);
+               goto _SET_SOURCE_OUT;
+       }
+
+       handle->src.module = module;
+       handle->src.dl_handle = dl_handle;
+       handle->src.module_handle = module_handle;
+       handle->src.set_bridge_func = set_bridge_func;
+       handle->src.unset_bridge_func = unset_bridge_func;
+
+       LOGI("bridge[%p] set source[module:%d,%p] done", bridge, module, module_handle);
+
+_SET_SOURCE_OUT:
+       if (ret != MEDIA_BRIDGE_ERROR_NONE && dl_handle)
+               dlclose(dl_handle);
+
+       g_mutex_unlock(&handle->lock);
+
+       return ret;
+}
+
+
+int media_bridge_unset_source(media_bridge_h bridge)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_STATE;
+               goto _UNSET_SOURCE_OUT;
+       }
+
+       LOGI("[%p] unset source[%d:%p]",
+               bridge, handle->src.module, handle->src.module_handle);
+
+       ret = __media_bridge_unset_source_no_lock(handle);
+
+       if (ret == MEDIA_BRIDGE_ERROR_NONE)
+               LOGI("[%p] unset source done", handle);
+
+_UNSET_SOURCE_OUT:
+       g_mutex_unlock(&handle->lock);
+
+       return ret;
+}
+
+
+int media_bridge_add_sink(media_bridge_h bridge, media_bridge_module_e module, void *module_handle, int *sink_id)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       int id = 0;
+       void *dl_handle = NULL;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+       media_bridge_sink_s *sink = NULL;
+       module_media_bridge_push_packet_func push_packet_func = NULL;
+       const char *library_path = NULL;
+       const char *push_packet = NULL;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+       MEDIA_BRIDGE_NULL_CHECK(module_handle);
+       MEDIA_BRIDGE_NULL_CHECK(sink_id);
+
+       if (module < MEDIA_BRIDGE_MODULE_CAMERA || module >= MEDIA_BRIDGE_MODULE_NUM) {
+               LOGE("invalid module[%d]", module);
+               return MEDIA_BRIDGE_ERROR_INVALID_PARAMETER;
+       }
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_STATE;
+               goto _ADD_SINK_OUT;
+       }
+
+       for (id = 0 ; id < MEDIA_BRIDGE_SINK_MAX ; id++) {
+               if (handle->sink[id].push_packet_func == NULL) {
+                       LOGI("found empty slot[%d] for sink", id);
+                       sink = &handle->sink[id];
+                       break;
+               }
+       }
+
+       if (!sink) {
+               LOGE("no empty slot for sink");
+               ret = MEDIA_BRIDGE_ERROR_OUT_OF_MEMORY;
+               goto _ADD_SINK_OUT;
+       }
+
+       library_path = g_media_bridge_module_name_table[module].library_path;
+       push_packet = g_media_bridge_module_name_table[module].push_packet;
+
+       if (!library_path || !push_packet) {
+               LOGE("module[%d] is not supported[%s,%s] for sink", module, library_path, push_packet);
+               ret = MEDIA_BRIDGE_ERROR_NOT_SUPPORTED;
+               goto _ADD_SINK_OUT;
+       }
+
+       LOGI("open[%s]", library_path);
+
+       dl_handle = dlopen(library_path, RTLD_NOW);
+       if (!dl_handle) {
+               LOGE("open[%s] failed[errno:%d]", library_path, errno);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+               goto _ADD_SINK_OUT;
+       }
+
+       push_packet_func = dlsym(dl_handle, push_packet);
+       if (!push_packet_func) {
+               LOGE("module[%d] symbol[%s] failed[%s]", module, push_packet, dlerror());
+               ret = MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+               goto _ADD_SINK_OUT;
+       }
+
+       sink->dl_handle = dl_handle;
+       sink->module = module;
+       sink->module_handle = module_handle;
+       sink->push_packet_func = push_packet_func;
+
+       handle->sink_count++;
+
+       *sink_id = id;
+
+       LOGI("[%p] sink added[module:%d,%p, id:%d] count[%d]",
+               handle, module, module_handle, id, handle->sink_count);
+
+_ADD_SINK_OUT:
+       if (ret != MEDIA_BRIDGE_ERROR_NONE && dl_handle)
+               dlclose(dl_handle);
+
+       g_mutex_unlock(&handle->lock);
+
+       return ret;
+}
+
+
+int media_bridge_remove_sink(media_bridge_h bridge, int sink_id)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       int id = 0;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       if (sink_id < 0 || sink_id >= MEDIA_BRIDGE_SINK_MAX) {
+               LOGE("invalid sink id[%d]", sink_id);
+               return MEDIA_BRIDGE_ERROR_INVALID_PARAMETER;
+       }
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_STATE;
+               goto _REMOVE_SINK_OUT;
+       }
+
+       if (handle->sink[sink_id].dl_handle == NULL) {
+               LOGE("sink_id[%d] is empty", sink_id);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_PARAMETER;
+               goto _REMOVE_SINK_OUT;
+       }
+
+       dlclose(handle->sink[sink_id].dl_handle);
+
+       memset(&handle->sink[sink_id], 0x0, sizeof(media_bridge_sink_s));
+
+       handle->sink_count--;
+
+       LOGI("[%p] sink removed[id:%d,count:%d]", sink_id, handle->sink_count);
+
+_REMOVE_SINK_OUT:
+       g_mutex_unlock(&handle->lock);
+
+       return ret;
+}
+
+
+int media_bridge_start(media_bridge_h bridge)
+{
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_CREATED) {
+               LOGE("state[%d] is not CREATED", handle->state);
+               g_mutex_unlock(&handle->lock);
+               return MEDIA_BRIDGE_ERROR_INVALID_STATE;
+       }
+
+       if (handle->sink_count < 1) {
+               LOGE("no sink is added");
+               g_mutex_unlock(&handle->lock);
+               return MEDIA_BRIDGE_ERROR_INVALID_OPERATION;
+       }
+
+       handle->state = MEDIA_BRIDGE_STATE_STREAMING;
+
+       LOGI("[%p] set state to [STREAMING]", handle);
+
+       g_mutex_unlock(&handle->lock);
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+int media_bridge_stop(media_bridge_h bridge)
+{
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_STREAMING) {
+               LOGE("state[%d] is not STREAMING", handle->state);
+               g_mutex_unlock(&handle->lock);
+               return MEDIA_BRIDGE_ERROR_INVALID_STATE;
+       }
+
+       handle->state = MEDIA_BRIDGE_STATE_CREATED;
+
+       LOGI("[%p] set state to [CREATED]", handle);
+
+       g_mutex_unlock(&handle->lock);
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+int media_bridge_get_state(media_bridge_h bridge, media_bridge_state_e *state)
+{
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+       MEDIA_BRIDGE_NULL_CHECK(state);
+
+       *state = handle->state;
+
+       return MEDIA_BRIDGE_ERROR_NONE;
+}
+
+
+int media_bridge_push_packet(media_bridge_h bridge, media_packet_h packet)
+{
+       int ret = MEDIA_BRIDGE_ERROR_NONE;
+       int packet_ret = MEDIA_PACKET_ERROR_NONE;
+       int id = 0;
+       int push_count = 0;
+       media_bridge_s *handle = (media_bridge_s *)bridge;
+
+       MEDIA_BRIDGE_NULL_CHECK(handle);
+
+       g_mutex_lock(&handle->lock);
+
+       if (handle->state != MEDIA_BRIDGE_STATE_STREAMING) {
+               LOGE("state[%d] is not STREAMING", handle->state);
+               ret = MEDIA_BRIDGE_ERROR_INVALID_STATE;
+               goto _PUSH_PACKET_OUT;
+       }
+
+       if (handle->sink_count < 1) {
+               LOGW("no sink is added");
+               goto _PUSH_PACKET_OUT;
+       }
+
+       LOGD("[%p] push packet[%p] to queue", handle, packet);
+
+       g_queue_push_tail(handle->packet_queue, (gpointer)packet);
+       g_cond_signal(&handle->cond);
+
+_PUSH_PACKET_OUT:
+       g_mutex_unlock(&handle->lock);
+
+       if (ret == MEDIA_BRIDGE_ERROR_NONE) {
+               media_packet_unref(packet);
+               LOGI("[%p] push packet[%p] done", handle, packet);
+       } else {
+               LOGE("[%p] push packet[%p] failed[0x%x]", handle, packet, ret);
+       }
+
+       return ret;
+}