From: Jeongmo Yang Date: Fri, 23 Apr 2021 07:01:29 +0000 (+0900) Subject: Add new internal APIs for media bridge X-Git-Tag: submit/tizen/20210506.034922~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F94%2F251694%2F18;p=platform%2Fcore%2Fapi%2Fmediatool.git Add new internal APIs for media bridge [Version] 0.1.46 [Issue Type] New feature Change-Id: If31421fb738d29fdb752ff1b64b3e3a4111218c4 Signed-off-by: Jeongmo Yang --- diff --git a/include/media_bridge_internal.h b/include/media_bridge_internal.h new file mode 100644 index 0000000..fbc6e1c --- /dev/null +++ b/include/media_bridge_internal.h @@ -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 +#include +#include + +#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 index 0000000..23bed56 --- /dev/null +++ b/include/media_bridge_private.h @@ -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 +#include + +#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__ */ diff --git a/packaging/capi-media-tool.spec b/packaging/capi-media-tool.spec index be0538d..82cc295 100755 --- a/packaging/capi-media-tool.spec +++ b/packaging/capi-media-tool.spec @@ -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 index 0000000..aeda57e --- /dev/null +++ b/src/media_bridge.c @@ -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 +#include +#include +#include + +#include +#include + + +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; +}