From 33cea5135f54206e7e2aa7719b42babfdd76351f Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Thu, 19 Sep 2019 14:33:56 +0900 Subject: [PATCH] Add media codec sync APIs for internal [Version] 0.6.1 [Profile] Common [Issue Type] New Internal API Change-Id: Ibd04deb782ecacc227fd85f7996bd989c34b3a7a Signed-off-by: Jeongmo Yang --- include/media_codec.h | 0 include/media_codec_port.h | 0 include/media_codec_port_gst.h | 0 include/media_codec_private.h | 3 +- include/media_codec_sync_internal.h | 215 ++++++++ include/media_codec_sync_private.h | 88 ++++ packaging/capi-media-codec.spec | 4 +- src/media_codec_port.c | 0 src/media_codec_port_gst.c | 0 src/media_codec_sync_internal.c | 955 ++++++++++++++++++++++++++++++++++++ 10 files changed, 1261 insertions(+), 4 deletions(-) mode change 100755 => 100644 include/media_codec.h mode change 100755 => 100644 include/media_codec_port.h mode change 100755 => 100644 include/media_codec_port_gst.h create mode 100644 include/media_codec_sync_internal.h create mode 100644 include/media_codec_sync_private.h mode change 100755 => 100644 src/media_codec_port.c mode change 100755 => 100644 src/media_codec_port_gst.c create mode 100644 src/media_codec_sync_internal.c diff --git a/include/media_codec.h b/include/media_codec.h old mode 100755 new mode 100644 diff --git a/include/media_codec_port.h b/include/media_codec_port.h old mode 100755 new mode 100644 diff --git a/include/media_codec_port_gst.h b/include/media_codec_port_gst.h old mode 100755 new mode 100644 diff --git a/include/media_codec_private.h b/include/media_codec_private.h index c574563..0057fa0 100644 --- a/include/media_codec_private.h +++ b/include/media_codec_private.h @@ -37,8 +37,7 @@ extern "C" { #define MEDIACODEC_CHECK_CONDITION(condition, error, msg) \ - if (condition) {} else \ - {LOGE("[%s] %s(0x%08x)", __FUNCTION__, msg, error); return error; }; \ + if (!(condition)) {LOGE("%s(0x%08x)", msg, error); return error; } #define MEDIACODEC_INSTANCE_CHECK(mediacodec) \ MEDIACODEC_CHECK_CONDITION(mediacodec != NULL, MEDIACODEC_ERROR_INVALID_PARAMETER, "MEDIACODEC_ERROR_INVALID_PARAMETER") diff --git a/include/media_codec_sync_internal.h b/include/media_codec_sync_internal.h new file mode 100644 index 0000000..c193bb0 --- /dev/null +++ b/include/media_codec_sync_internal.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2019 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_CODEC_SYNC_INTERNAL_H__ +#define __TIZEN_MEDIA_CODEC_SYNC_INTERNAL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file media_codec_sync_internal.h +* @brief This file contains the internal capi media codec sync API. +*/ + +/** +* @addtogroup CAPI_MEDIA_CODEC_SYNC_MODULE +* @{ +*/ + +/** + * @brief Media Codec Sync type handle. + * @since_tizen 5.5 + */ +typedef struct mediacodecsync_s *mediacodecsync_h; + + +typedef enum { + MEDIACODECSYNC_STATE_CREATED = 0, /**< Media codec sync is created */ + MEDIACODECSYNC_STATE_READY, /**< Media codec sync is ready to run */ + MEDIACODECSYNC_STATE_RUNNING, /**< Media codec sync is running */ + MEDIACODECSYNC_STATE_PAUSED /**< Media codec sync is paused */ +} mediacodecsync_state_e; + +typedef enum { + MEDIACODECSYNC_PACKET_TYPE_AUDIO = 0, + MEDIACODECSYNC_PACKET_TYPE_VIDEO +} mediacodecsync_packet_type_e; + +typedef void (*mediacodecsync_buffer_used_cb)(media_packet_h packet, mediacodecsync_packet_type_e type, void *user_data); + +/** + * @brief Creates a mediacodecsync handle for synchronous AV rendering. + * @since_tizen 5.5 + * @remarks you must release @a mediacodecsync using mediacodecsync_destroy().\n + * Although you can create multiple mediacodecsync handles at the same time, + * the mediacodec cannot guarantee proper operation because of limited resources, like + * audio or display device. + * + * @param[in] callback The callback function to register + * @param[in] user_data The user data to be passed to the callback function + * @param[out] mediacodecsync A new handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_OUT_OF_MEMORY Out of memory + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_CREATED. + */ +int mediacodecsync_create(mediacodecsync_buffer_used_cb callback, void *userdata, mediacodecsync_h *mediacodecsync); + +/** + * @brief Destroys the mediacodecsync handle and releases all its resources. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync to be destroyed. + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_CREATED. + */ +int mediacodecsync_destroy(mediacodecsync_h mediacodecsync); + +/** + * @brief Sets the media format to be rendered. + * @since_tizen 5.5 + * @param[in] mediacodecsync The mediacodecsync handle + * @param[in] audio_format The #media_format_h of audio data + * @param[in] video_format The #media_format_h of video data + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_NOT_SUPPORTED_ON_DEVICE Not supported on device + * @pre The state should be #MEDIACODECSYNC_STATE_CREATED. + * @pre The media format has been created and the required values have been set. + * @see media_format_set_video_mime() + * @see media_format_set_audio_mime() + * @see media_format_set_video_width() + * @see media_format_set_video_height() + * @see media_format_set_video_avg_bps() + * @see media_format_set_video_frame_rate() + * @see media_format_set_audio_channel() + * @see media_format_set_audio_samplerate() + * @see media_format_set_audio_bit() + * @see media_format_set_audio_avg_bps() + */ +int mediacodecsync_set_format(mediacodecsync_h mediacodecsync, media_format_h audio_format, media_format_h video_format); + +/** + * @brief Prepares @a mediacodecsync for synchronous rendering. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_OUT_OF_MEMORY Out of memory + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @retval #MEDIACODEC_ERROR_RESOURCE_OVERLOADED Exceed the instance limits + * @retval #MEDIACODEC_ERROR_INTERNAL Internal error + * @pre The mediacodecsync should call mediacodecsync_set_format() before calling mediacodec_prepare(). + * @pre The state should be #MEDIACODECSYNC_STATE_CREATED. + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_READY. + */ +int mediacodecsync_prepare(mediacodecsync_h mediacodecsync); + +/** + * @brief Unprepares @a mediacodecsync. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_READY. + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_CREATED. + */ +int mediacodecsync_unprepare(mediacodecsync_h mediacodecsync); + +/** + * @brief Runs @a mediacodecsync. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_READY or #MEDIACODECSYNC_STATE_PAUSED. + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_RUNNING. + */ +int mediacodecsync_run(mediacodecsync_h mediacodecsync); + +/** + * @brief Stops @a mediacodecsync. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_RUN or #MEDIACODECSYNC_STATE_PAUSED. + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_READY. + */ +int mediacodecsync_stop(mediacodecsync_h mediacodecsync); + +/** + * @brief Pauses @a mediacodecsync. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_RUN. + * @post If it succeeds, the state will be #MEDIACODECSYNC_STATE_PAUSED. + */ +int mediacodecsync_pause(mediacodecsync_h mediacodecsync); + +/** + * @brief Pushes packets to render. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @param[in] packet The packet to render + * @param[in] type The type of packet + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + * @pre The state should be #MEDIACODECSYNC_STATE_RUN. + */ +int mediacodecsync_push_packet(mediacodecsync_h mediacodecsync, media_packet_h packet, mediacodecsync_packet_type_e type); + +/** + * @brief Gets state of @a mediacodecsync. + * @since_tizen 5.5 + * @param[in] mediacodecsync The handle to mediacodecsync + * @param[out] state The state of mediacodecsync + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIACODEC_ERROR_NONE Successful + * @retval #MEDIACODEC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIACODEC_ERROR_INVALID_OPERATION Invalid operation + */ +int mediacodecsync_get_state(mediacodecsync_h mediacodecsync, mediacodecsync_state_e *state); + +/** + * @} + */ +#ifdef __cplusplus +} +#endif +#endif /*__TIZEN_MEDIA_CODEC_INTERNAL_H__*/ diff --git a/include/media_codec_sync_private.h b/include/media_codec_sync_private.h new file mode 100644 index 0000000..2f6fa53 --- /dev/null +++ b/include/media_codec_sync_private.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 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_CODEC_SYNC_PRIVATE_H__ +#define __TIZEN_MEDIA_CODEC_SYNC_PRIVATE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + MEDIACODECSYNC_PIPELINE = 0, + MEDIACODECSYNC_ELEMENT_APPSRC, + MEDIACODECSYNC_ELEMENT_CAPS, + MEDIACODECSYNC_ELEMENT_QUE, + MEDIACODECSYNC_ELEMENT_SINK, + MEDIACODECSYNC_ELEMENT_NUM +}; + +#define AUDIO_FORMAT_TABLE_SIZE 15 +#define VIDEO_FORMAT_TABLE_SIZE 8 + +typedef struct _format_table { + media_format_mimetype_e mime_type; + char *format_string; +} format_table; + +typedef struct _packet_info { + bool has_tbm_surface; + void *data; + uint64_t data_size; + uint64_t dts; + uint64_t pts; + uint64_t duration; +} packet_info; + +typedef struct _mediacodecsync_s { + GMutex lock; + media_format_h audio_format; + media_format_h video_format; + + GstAllocator *allocator; + GstClock *clock; + GstElement *audio_pipe[MEDIACODECSYNC_ELEMENT_NUM]; + GstElement *video_pipe[MEDIACODECSYNC_ELEMENT_NUM]; + GstCaps *video_caps; + GstVideoInfo video_info; + + mediacodecsync_buffer_used_cb buffer_used_cb; + void *buffer_used_cb_userdata; + + mediacodecsync_state_e state; +} mediacodecsync_s; + +typedef struct _GstMCSBuffer { + GstBuffer *gst_buffer; + int type; + mediacodecsync_s *handle; + media_packet_h packet; +} GstMCSBuffer; + +#ifdef __cplusplus +} +#endif + +#endif /* __TIZEN_MEDIA_CODEC_SYNC_PRIVATE_H__ */ diff --git a/packaging/capi-media-codec.spec b/packaging/capi-media-codec.spec index ad913b6..455600a 100755 --- a/packaging/capi-media-codec.spec +++ b/packaging/capi-media-codec.spec @@ -4,8 +4,8 @@ Name: capi-media-codec Summary: A Media Codec library in Tizen Native API -Version: 0.6.0 -Release: 1 +Version: 0.6.1 +Release: 0 Group: Multimedia/API License: Apache-2.0 Source0: %{name}-%{version}.tar.gz diff --git a/src/media_codec_port.c b/src/media_codec_port.c old mode 100755 new mode 100644 diff --git a/src/media_codec_port_gst.c b/src/media_codec_port_gst.c old mode 100755 new mode 100644 diff --git a/src/media_codec_sync_internal.c b/src/media_codec_sync_internal.c new file mode 100644 index 0000000..5d95d6f --- /dev/null +++ b/src/media_codec_sync_internal.c @@ -0,0 +1,955 @@ +/* + * Copyright (c) 2019 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 + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "TIZEN_N_MEDIACODECSYNC" + +#define PIPELINE_NAME_AUDIO "MEDIACODECSYNC_AUDIO" +#define PIPELINE_NAME_VIDEO "MEDIACODECSYNC_VIDEO" + +#define _MEDIACODECSYNC_PIPELINE_MAKE(handle, pipeline, name) \ + do { \ + if (pipeline) { \ + gst_object_unref(pipeline); \ + pipeline = NULL; \ + } \ + pipeline = gst_pipeline_new(name); \ + if (!pipeline) { \ + LOGE("create pipeline[%s] failed", name); \ + goto _CREATE_PIPELINE_FAILED; \ + } \ + g_object_weak_ref(G_OBJECT(pipeline), (GWeakNotify)__mediacodecsync_element_release_noti, handle); \ + LOGD("create pipeline [%s] done", name); \ + } while (0) + +#define _MEDIACODECSYNC_ELEMENT_MAKE(handle, element, name, prefix, nick) \ + do { \ + char nick_name[24]; \ + if (element) { \ + gst_object_unref(element); \ + element = NULL; \ + } \ + snprintf(nick_name, sizeof(nick_name), "%s%s", prefix, nick); \ + element = gst_element_factory_make(name, nick_name); \ + if (!element) { \ + LOGE("create element[%s,%s] failed", name, nick_name); \ + goto _CREATE_PIPELINE_FAILED; \ + } \ + g_object_weak_ref(G_OBJECT(element), (GWeakNotify)__mediacodecsync_element_release_noti, handle); \ + LOGD("create element [%s,%s] done", name, nick_name); \ + } while (0) + +#define _MEDIACODECSYNC_ELEMENT_REMOVE(element) \ + do { \ + if (element != NULL) \ + gst_object_unref(element); \ + } while (0) + + + +static void __mediacodecsync_need_audio_data_cb(GstElement *appsrc, guint size, gpointer data) +{ + /*mediacodecsync_s *handle = (mediacodecsync_s *)data;*/ + + LOGI("audio data is needed"); +} + + +static void __mediacodecsync_enough_audio_data_cb(GstElement *appsrc, gpointer data) +{ + /*mediacodecsync_s *handle = (mediacodecsync_s *)data;*/ + + LOGI("audio data is enough"); +} + + +static void __mediacodecsync_need_video_data_cb(GstElement *appsrc, guint size, gpointer data) +{ + /*mediacodecsync_s *handle = (mediacodecsync_s *)data;*/ + + LOGI("video data is needed"); +} + + +static void __mediacodecsync_enough_video_data_cb(GstElement *appsrc, gpointer data) +{ + /*mediacodecsync_s *handle = (mediacodecsync_s *)data;*/ + + LOGI("video data is enough"); +} + + +static int _mediacodecsync_get_packet_info(media_packet_h packet, packet_info *info) +{ + int ret = MEDIACODEC_ERROR_NONE; + bool has_tbm_surface = false; + void *data = NULL; + uint64_t data_size = 0; + uint64_t dts = 0; + uint64_t pts = 0; + uint64_t duration = 0; + + if (!packet || !info) { + LOGE("NULL params %p %p", packet, info); + return MEDIACODEC_ERROR_INVALID_PARAMETER; + } + + /* get packet info */ + ret = media_packet_has_tbm_surface_buffer(packet, &has_tbm_surface); + ret |= media_packet_get_buffer_data_ptr(packet, &data); + ret |= media_packet_get_buffer_size(packet, &data_size); + ret |= media_packet_get_dts(packet, &dts); + ret |= media_packet_get_pts(packet, &pts); + ret |= media_packet_get_duration(packet, &duration); + + if (ret == MEDIACODEC_ERROR_NONE) { + info->has_tbm_surface = has_tbm_surface; + info->data = data; + info->data_size = data_size; + info->dts = dts; + info->pts = pts; + info->duration = duration; + + LOGI("packet info[%u], %p, size %"PRIu64", dts %"PRIu64", pts %"PRIu64", duration %"PRIu64, + has_tbm_surface, data, data_size, dts, pts, duration); + } + + return ret; +} + + +static void _mediacodecsync_gst_buffer_finalize(GstMCSBuffer *mcs_buffer) +{ + mediacodecsync_s *handle = NULL; + + if (!mcs_buffer || !mcs_buffer->handle) { + LOGE("NULL buffer(%p) or handle", mcs_buffer); + return; + } + + handle = mcs_buffer->handle; + + LOGI("[type %d] mcs_buffer %p, gst buffer %p, packet %p", + mcs_buffer->type, mcs_buffer, mcs_buffer->gst_buffer, mcs_buffer->packet); + + if (mcs_buffer->packet) { + if (handle->buffer_used_cb) + handle->buffer_used_cb(mcs_buffer->packet, mcs_buffer->type, handle->buffer_used_cb_userdata); + else + LOGE("No buffer used callback"); + } + + free(mcs_buffer); +} + + +static GstMCSBuffer *_mediacodecsync_gst_buffer_new(mediacodecsync_s *handle, media_packet_h packet, int type) +{ + GstMCSBuffer *new_buffer = NULL; + GstMemory *memory = NULL; + tbm_surface_h tbm_surface = NULL; + packet_info info; + + if (!handle || !packet) { + LOGE("invalid params %p, %p", handle, packet); + return NULL; + } + + /* get packet info */ + if (_mediacodecsync_get_packet_info(packet, &info) != MEDIA_PACKET_ERROR_NONE) { + LOGE("_mediacodecsync_get_packet_info failed"); + return NULL; + } + + new_buffer = (GstMCSBuffer *)calloc(1, sizeof(GstMCSBuffer)); + if (!new_buffer) { + LOGE("GstMCSBuffer alloc failed"); + return NULL; + } + + new_buffer->gst_buffer = gst_buffer_new(); + new_buffer->type = type; + new_buffer->packet = packet; + new_buffer->handle = handle; + + if (info.has_tbm_surface) { + /* get tbm surface from packet */ + if (media_packet_get_tbm_surface(packet, &tbm_surface) != MEDIA_PACKET_ERROR_NONE) { + LOGE("get tbm surface failed"); + goto _BUFFER_NEW_FAILED; + } + + /* create tizen memory for gst buffer with tbm surface */ + memory = gst_tizen_allocator_alloc_surface(handle->allocator, + &handle->video_info, tbm_surface, (gpointer)new_buffer, + (GDestroyNotify)_mediacodecsync_gst_buffer_finalize); + } else { + /* If tbm is not used, the data from packet will be used. */ + memory = gst_memory_new_wrapped(0, info.data, (gsize)info.data_size, + 0, (gsize)info.data_size, (gpointer)new_buffer, + (GDestroyNotify)_mediacodecsync_gst_buffer_finalize); + } + + if (!memory) { + LOGE("GstMemory failed"); + goto _BUFFER_NEW_FAILED; + } + + gst_buffer_append_memory(new_buffer->gst_buffer, memory); + + /* set buffer meta */ + gst_buffer_set_size(new_buffer->gst_buffer, info.data_size); + GST_BUFFER_DTS(new_buffer->gst_buffer) = info.dts; + GST_BUFFER_PTS(new_buffer->gst_buffer) = info.pts; + GST_BUFFER_DURATION(new_buffer->gst_buffer) = info.duration; + GST_BUFFER_OFFSET(new_buffer->gst_buffer) = 0; + GST_BUFFER_OFFSET_END(new_buffer->gst_buffer) = info.data_size; + + return new_buffer; + +_BUFFER_NEW_FAILED: + gst_buffer_unref(new_buffer->gst_buffer); + free(new_buffer); + return NULL; +} + +static void __mediacodecsync_element_release_noti(gpointer data, GObject *obj) +{ + int i = 0; + mediacodecsync_s *handle = (mediacodecsync_s *)data; + + if (!handle) { + LOGE("NULL handle"); + return; + } + + for (i = 0 ; i < MEDIACODECSYNC_ELEMENT_NUM ; i++) { + if (handle->audio_pipe[i] && G_OBJECT(handle->audio_pipe[i]) == obj) { + LOGD("audio element[%d] was released", i); + handle->audio_pipe[i] = NULL; + return; + } + } + + for (i = 0 ; i < MEDIACODECSYNC_ELEMENT_NUM ; i++) { + if (handle->video_pipe[i] && G_OBJECT(handle->video_pipe[i]) == obj) { + LOGD("video element[%d] was released", i); + handle->video_pipe[i] = NULL; + return; + } + } + + LOGW("no matched element [%p]", obj); +} + + +static GstCaps *_mediacodecsync_make_caps_from_mediaformat(media_format_h format) +{ + int i = 0; + GstCaps *new_caps = NULL; + media_format_type_e type = MEDIA_FORMAT_NONE; + media_format_mimetype_e mime_type = 0; + + format_table audio_table[AUDIO_FORMAT_TABLE_SIZE] = { + {MEDIA_FORMAT_PCM, "S16LE"}, + {MEDIA_FORMAT_PCM_S16LE, "S16LE"}, + {MEDIA_FORMAT_PCM_S24LE, "S24LE"}, + {MEDIA_FORMAT_PCM_S32LE, "S32LE"}, + {MEDIA_FORMAT_PCM_S16BE, "S16BE"}, + {MEDIA_FORMAT_PCM_S24BE, "S24BE"}, + {MEDIA_FORMAT_PCM_S32BE, "S32BE"}, + {MEDIA_FORMAT_PCM_F32LE, "F32LE"}, + {MEDIA_FORMAT_PCM_F32BE, "F32BE"}, + {MEDIA_FORMAT_PCM_U16LE, "U16LE"}, + {MEDIA_FORMAT_PCM_U24LE, "U24LE"}, + {MEDIA_FORMAT_PCM_U32LE, "U32LE"}, + {MEDIA_FORMAT_PCM_U16BE, "U16BE"}, + {MEDIA_FORMAT_PCM_U24BE, "U24BE"}, + {MEDIA_FORMAT_PCM_U32BE, "U32BE"} + }; + format_table video_table[VIDEO_FORMAT_TABLE_SIZE] = { + {MEDIA_FORMAT_I420, "I420"}, + {MEDIA_FORMAT_NV12, "SN12"}, + {MEDIA_FORMAT_NV12T, "NV12T"}, + {MEDIA_FORMAT_YV12, "YV12"}, + {MEDIA_FORMAT_NV21, "NV21"}, + {MEDIA_FORMAT_NV16, "NV16"}, + {MEDIA_FORMAT_YUYV, "YUYV"}, + {MEDIA_FORMAT_UYVY, "UYVY"} + }; + + if (media_format_get_type(format, &type) != MEDIA_FORMAT_ERROR_NONE) { + LOGE("get type failed"); + return NULL; + } + + if (type & MEDIA_FORMAT_AUDIO) { + int channel; + int samplerate; + int bit; + + if (media_format_get_audio_info(format, &mime_type, &channel, + &samplerate, &bit, NULL) != MEDIA_FORMAT_ERROR_NONE) { + LOGE("get audio info failed"); + return NULL; + } + + for (i = 0 ; i < AUDIO_FORMAT_TABLE_SIZE ; i++) { + if (audio_table[i].mime_type == mime_type) { + new_caps = gst_caps_new_simple("audio/x-raw", + "rate", G_TYPE_INT, samplerate, + "channels", G_TYPE_INT, channel, + "format", G_TYPE_STRING, audio_table[i].format_string, + NULL); + break; + } + } + } else if (type & MEDIA_FORMAT_VIDEO) { + int width; + int height; + + if (media_format_get_video_info(format, &mime_type, + &width, &height, NULL, NULL) != MEDIA_FORMAT_ERROR_NONE) { + LOGE("get video info failed"); + return NULL; + } + + for (i = 0 ; i < VIDEO_FORMAT_TABLE_SIZE ; i++) { + if (video_table[i].mime_type == mime_type) { + new_caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, video_table[i].format_string, + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "framerate", GST_TYPE_FRACTION, 30, 1, + NULL); + break; + } + } + } + + if (new_caps) { + gchar *caps_string = gst_caps_to_string(new_caps); + if (caps_string) { + LOGD("caps [%s]", caps_string); + g_free(caps_string); + } + } else { + LOGE("new caps failed [type:0x%x,0x%x]", type, mime_type); + } + + return new_caps; +} + + +static void _mediacodecsync_destroy_pipeline(GstElement *pipeline[MEDIACODECSYNC_ELEMENT_NUM]) +{ + GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; + + if (!pipeline[MEDIACODECSYNC_PIPELINE]) { + LOGW("NULL pipeline"); + return; + } + + result = gst_element_set_state(pipeline[MEDIACODECSYNC_PIPELINE], GST_STATE_NULL); + + LOGD("set state NULL : %d", result); + + _MEDIACODECSYNC_ELEMENT_REMOVE(pipeline[MEDIACODECSYNC_PIPELINE]); + _MEDIACODECSYNC_ELEMENT_REMOVE(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC]); + _MEDIACODECSYNC_ELEMENT_REMOVE(pipeline[MEDIACODECSYNC_ELEMENT_CAPS]); + _MEDIACODECSYNC_ELEMENT_REMOVE(pipeline[MEDIACODECSYNC_ELEMENT_QUE]); + _MEDIACODECSYNC_ELEMENT_REMOVE(pipeline[MEDIACODECSYNC_ELEMENT_SINK]); +} + + +static int _mediacodecsync_create_pipeline(mediacodecsync_s *handle, media_format_h format, + GstElement *pipeline[MEDIACODECSYNC_ELEMENT_NUM], const char *pipe_name, const char *nick_prefix, + GCallback need_data_cb, GCallback enough_data_cb, GstCaps **out_caps) +{ + GstCaps *caps = NULL; + + LOGD("name : %s", pipe_name); + + /* main pipeline */ + _MEDIACODECSYNC_PIPELINE_MAKE(handle, pipeline[MEDIACODECSYNC_PIPELINE], pipe_name); + + /* elements */ + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_APPSRC], "appsrc", nick_prefix, "src"); + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_CAPS], "capsfilter", nick_prefix, "caps"); + + if (!strncmp(nick_prefix, "audio", 5)) { + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_QUE], "queue", nick_prefix, "queue"); + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_SINK], "pulsesink", nick_prefix, "sink"); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "qos", 1, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "provide-clock", 0, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "slave-method", 2, NULL); /* GST_AUDIO_BASE_SINK_SLAVE_NONE */ + } else { + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_QUE], "queue", nick_prefix, "queue"); + _MEDIACODECSYNC_ELEMENT_MAKE(handle, pipeline[MEDIACODECSYNC_ELEMENT_SINK], "tizenwlsink", nick_prefix, "sink"); + + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "use-tbm", 1, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "enable-last-sample", 0, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "show-preroll-frame", 0, NULL); + } + + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC]), "max-bytes", (guint64)0, NULL); /* unlimited */ + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_QUE]), "max-size-time", 0, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_QUE]), "max-size-bytes", 0, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_QUE]), "max-size-buffers", 0, NULL); + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC]), "format", 3, NULL); /* GST_FORMAT_TIME */ + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_SINK]), "sync", 1, NULL); + + /* add elements to pipeline */ + gst_bin_add_many(GST_BIN(pipeline[MEDIACODECSYNC_PIPELINE]), + pipeline[MEDIACODECSYNC_ELEMENT_APPSRC], + pipeline[MEDIACODECSYNC_ELEMENT_CAPS], + pipeline[MEDIACODECSYNC_ELEMENT_QUE], + pipeline[MEDIACODECSYNC_ELEMENT_SINK], + NULL); + + /* link elements */ + if (!(gst_element_link_many(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC], + pipeline[MEDIACODECSYNC_ELEMENT_CAPS], + pipeline[MEDIACODECSYNC_ELEMENT_QUE], + pipeline[MEDIACODECSYNC_ELEMENT_SINK], + NULL))) { + LOGE("link elements failed"); + goto _CREATE_PIPELINE_FAILED; + } + + /* set capsfilter */ + caps = _mediacodecsync_make_caps_from_mediaformat(format); + if (!caps) { + LOGE("pipeline caps failed"); + goto _CREATE_PIPELINE_FAILED; + } + + g_object_set(G_OBJECT(pipeline[MEDIACODECSYNC_ELEMENT_CAPS]), "caps", caps, NULL); + + if (out_caps) { + if (*out_caps) { + LOGD("unref previous video_caps %p", *out_caps); + gst_caps_unref(*out_caps); + *out_caps = NULL; + } + + *out_caps = caps; + } else { + gst_caps_unref(caps); + caps = NULL; + } + + g_signal_connect(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC], "need-data", need_data_cb, handle); + g_signal_connect(pipeline[MEDIACODECSYNC_ELEMENT_APPSRC], "enough-data", enough_data_cb, handle); + + LOGD("done"); + + return MEDIACODEC_ERROR_NONE; + +_CREATE_PIPELINE_FAILED: + LOGE("[%s] failed", pipe_name); + + _mediacodecsync_destroy_pipeline(pipeline); + + return MEDIACODEC_ERROR_INTERNAL; +} + + +static int _mediacodecsync_prepare_pipeline(mediacodecsync_s *handle) +{ + int ret = MEDIACODEC_ERROR_NONE; + GstClock *clock = NULL; + GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; + + LOGD("enter"); + + if (handle->audio_format) { + /* create audio pipeline */ + ret = _mediacodecsync_create_pipeline(handle, handle->audio_format, + handle->audio_pipe, PIPELINE_NAME_AUDIO, "audio", + (GCallback)__mediacodecsync_need_audio_data_cb, + (GCallback)__mediacodecsync_enough_audio_data_cb, + NULL); + if (ret != MEDIACODEC_ERROR_NONE) + return ret; + + /* set state */ + result = gst_element_set_state(handle->audio_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_PAUSED); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - VIDEO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _PREPARE_FAILED; + } + + LOGD("audio pipeline done"); + } + + if (handle->video_format) { + /* create video pipeline */ + ret = _mediacodecsync_create_pipeline(handle, handle->video_format, + handle->video_pipe, PIPELINE_NAME_VIDEO, "video", + (GCallback)__mediacodecsync_need_video_data_cb, + (GCallback)__mediacodecsync_enough_video_data_cb, + &handle->video_caps); + if (ret != MEDIACODEC_ERROR_NONE) + goto _PREPARE_FAILED; + + /* get video info */ + if (!gst_video_info_from_caps(&handle->video_info, handle->video_caps)) { + LOGE("failed to get video info"); + goto _PREPARE_FAILED; + } + + /* set state */ + result = gst_element_set_state(handle->video_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_PAUSED); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - VIDEO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _PREPARE_FAILED; + } + + LOGD("video pipeline done"); + + /* share clock to synchronize */ + if (handle->audio_pipe[MEDIACODECSYNC_PIPELINE]) { + LOGD("set clock of audio pipeline to video pipeline"); + + clock = gst_pipeline_get_clock(GST_PIPELINE(handle->audio_pipe[MEDIACODECSYNC_PIPELINE])); + + if (!gst_pipeline_set_clock(GST_PIPELINE(handle->video_pipe[MEDIACODECSYNC_PIPELINE]), clock)) + LOGW("failed to set clock to video pipeline"); + + gst_object_unref(clock); + clock = NULL; + } + } + + LOGD("done"); + + return ret; + +_PREPARE_FAILED: + _mediacodecsync_destroy_pipeline(handle->audio_pipe); + _mediacodecsync_destroy_pipeline(handle->video_pipe); + + return ret; +} + + +static void _mediacodecsync_unprepare_pipeline(mediacodecsync_s *handle) +{ + _mediacodecsync_destroy_pipeline(handle->audio_pipe); + _mediacodecsync_destroy_pipeline(handle->video_pipe); +} + + + +int mediacodecsync_create(mediacodecsync_buffer_used_cb callback, void *userdata, mediacodecsync_h *mediacodecsync) +{ + mediacodecsync_s *new_handle = NULL; + + MEDIACODEC_NULL_ARG_CHECK(mediacodecsync); + MEDIACODEC_NULL_ARG_CHECK(callback); + + LOGD("enter"); + + new_handle = (mediacodecsync_s *)malloc(sizeof(mediacodecsync_s)); + if (new_handle == NULL) { + LOGE("handle allocation failed"); + return MEDIACODEC_ERROR_OUT_OF_MEMORY; + } + + memset(new_handle, 0x0, sizeof(mediacodecsync_s)); + + new_handle->buffer_used_cb = callback; + new_handle->buffer_used_cb_userdata = userdata; + new_handle->state = MEDIACODECSYNC_STATE_CREATED; + new_handle->allocator = gst_tizen_allocator_new(); + g_mutex_init(&new_handle->lock); + + *mediacodecsync = (mediacodecsync_h)new_handle; + + LOGD("new handle : %p", *mediacodecsync); + + return MEDIACODEC_ERROR_NONE; +} + + +int mediacodecsync_destroy(mediacodecsync_h mediacodecsync) +{ + mediacodecsync_s *handle = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + LOGD("handle to destroy : %p", handle); + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_CREATED) { + LOGE("invalid state %d", handle->state); + g_mutex_unlock(&handle->lock); + return MEDIACODEC_ERROR_INVALID_STATE; + } + + if (handle->audio_format) { + media_format_unref(handle->audio_format); + handle->audio_format = NULL; + } + + if (handle->video_format) { + media_format_unref(handle->video_format); + handle->video_format = NULL; + } + + if (handle->video_caps) { + gst_caps_unref(handle->video_caps); + handle->video_caps = NULL; + } + + g_mutex_unlock(&handle->lock); + + g_mutex_clear(&handle->lock); + + free(handle); + + LOGD("done"); + + return MEDIACODEC_ERROR_NONE; +} + + +int mediacodecsync_set_format(mediacodecsync_h mediacodecsync, media_format_h audio_format, media_format_h video_format) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + if (!audio_format && !video_format) { + LOGE("all formats are NULL"); + return MEDIACODEC_ERROR_INVALID_PARAMETER; + } + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_CREATED) { + LOGE("invalid state %d", handle->state); + ret = MEDIACODEC_ERROR_INVALID_STATE; + goto _SET_FORMAT_DONE; + } + + if (audio_format) { + LOGD("set audio format : %p", audio_format); + + if (media_format_ref(audio_format) != MEDIACODEC_ERROR_NONE) { + LOGE("audio format ref failed"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _SET_FORMAT_DONE; + } + + if (handle->audio_format) + media_format_unref(handle->audio_format); + + handle->audio_format = audio_format; + } + + if (video_format) { + LOGD("set video format : %p", audio_format); + + if (media_format_ref(video_format) != MEDIACODEC_ERROR_NONE) { + LOGE("video format ref failed"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _SET_FORMAT_DONE; + } + + if (handle->video_format) + media_format_unref(handle->video_format); + + handle->video_format = video_format; + } + +_SET_FORMAT_DONE: + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_prepare(mediacodecsync_h mediacodecsync) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_CREATED) { + LOGE("invalid state %d", handle->state); + g_mutex_unlock(&handle->lock); + return MEDIACODEC_ERROR_INVALID_STATE; + } + + ret = _mediacodecsync_prepare_pipeline(handle); + if (ret == MEDIACODEC_ERROR_NONE) + handle->state = MEDIACODECSYNC_STATE_READY; + + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_unprepare(mediacodecsync_h mediacodecsync) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_READY) { + LOGE("invalid state %d", handle->state); + g_mutex_unlock(&handle->lock); + return MEDIACODEC_ERROR_INVALID_STATE; + } + + _mediacodecsync_unprepare_pipeline(handle); + handle->state = MEDIACODECSYNC_STATE_CREATED; + + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_run(mediacodecsync_h mediacodecsync) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_READY && + handle->state != MEDIACODECSYNC_STATE_PAUSED) { + LOGE("invalid state %d", handle->state); + ret = MEDIACODEC_ERROR_INVALID_STATE; + goto _RUN_DONE; + } + + /* set state */ + if (handle->audio_pipe[MEDIACODECSYNC_PIPELINE]) { + result = gst_element_set_state(handle->audio_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_PLAYING); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - AUDIO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _RUN_DONE; + } + } + + if (handle->video_pipe[MEDIACODECSYNC_PIPELINE]) { + result = gst_element_set_state(handle->video_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_PLAYING); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - VIDEO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _RUN_DONE; + } + } + + LOGD("pipeline is now playing"); + handle->state = MEDIACODECSYNC_STATE_RUNNING; + +_RUN_DONE: + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_stop(mediacodecsync_h mediacodecsync) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state < MEDIACODECSYNC_STATE_RUNNING) { + LOGE("invalid state %d", handle->state); + g_mutex_unlock(&handle->lock); + return MEDIACODEC_ERROR_INVALID_STATE; + } + + /* set state */ + result = gst_element_set_state(handle->video_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_READY); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - VIDEO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + } else { + LOGD("pipeline is ready"); + handle->state = MEDIACODECSYNC_STATE_READY; + } + + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_pause(mediacodecsync_h mediacodecsync) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_RUNNING) { + LOGE("invalid state %d", handle->state); + g_mutex_unlock(&handle->lock); + return MEDIACODEC_ERROR_INVALID_STATE; + } + + /* set state */ + result = gst_element_set_state(handle->video_pipe[MEDIACODECSYNC_PIPELINE], GST_STATE_PAUSED); + if (result == GST_STATE_CHANGE_FAILURE) { + LOGE("state change failed - VIDEO pipeline"); + ret = MEDIACODEC_ERROR_INTERNAL; + } else { + LOGD("pipeline is paused"); + handle->state = MEDIACODECSYNC_STATE_PAUSED; + } + + g_mutex_unlock(&handle->lock); + + return ret; +} + + +static int _mediacodecsync_push_packet_to_pipeline(GstElement *appsrc, GstBuffer *buffer) +{ + GstFlowReturn gst_ret = GST_FLOW_OK; + + if (!appsrc || !buffer) { + LOGE("NULL params %p,%p", appsrc, buffer); + return MEDIACODEC_ERROR_INTERNAL; + } + + gst_ret = gst_app_src_push_buffer((GstAppSrc *)appsrc, buffer); + if (gst_ret != GST_FLOW_OK) { + LOGE("appsrc push failed %d", gst_ret); + return MEDIACODEC_ERROR_INTERNAL; + } + + return MEDIACODEC_ERROR_NONE; +} + + +int mediacodecsync_push_packet(mediacodecsync_h mediacodecsync, media_packet_h packet, mediacodecsync_packet_type_e type) +{ + int ret = MEDIACODEC_ERROR_NONE; + mediacodecsync_s *handle = NULL; + GstMCSBuffer *mcs_buffer = NULL; + GstElement *appsrc = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + + handle = (mediacodecsync_s *)mediacodecsync; + + g_mutex_lock(&handle->lock); + + if (handle->state != MEDIACODECSYNC_STATE_RUNNING) { + LOGE("invalid state %d", handle->state); + ret = MEDIACODEC_ERROR_INVALID_STATE; + goto _PUSH_PACKET_DONE; + } + + mcs_buffer = _mediacodecsync_gst_buffer_new(handle, packet, type); + if (!mcs_buffer) { + LOGE("mediacodecsync new buffer failed"); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _PUSH_PACKET_DONE; + } + + LOGI("type %u, new buffer %p [gst %p][packet %p]", + type, mcs_buffer, mcs_buffer->gst_buffer, packet); + + switch (type) { + case MEDIACODECSYNC_PACKET_TYPE_AUDIO: + appsrc = handle->audio_pipe[MEDIACODECSYNC_ELEMENT_APPSRC]; + break; + case MEDIACODECSYNC_PACKET_TYPE_VIDEO: + appsrc = handle->video_pipe[MEDIACODECSYNC_ELEMENT_APPSRC]; + break; + default: + LOGE("unhandled packet type %u, release created buffer", type); + gst_buffer_unref(mcs_buffer->gst_buffer); + ret = MEDIACODEC_ERROR_INTERNAL; + goto _PUSH_PACKET_DONE; + } + + ret = _mediacodecsync_push_packet_to_pipeline(appsrc, mcs_buffer->gst_buffer); + +_PUSH_PACKET_DONE: + g_mutex_unlock(&handle->lock); + + return ret; +} + + +int mediacodecsync_get_state(mediacodecsync_h mediacodecsync, mediacodecsync_state_e *state) +{ + mediacodecsync_s *handle = NULL; + + MEDIACODEC_INSTANCE_CHECK(mediacodecsync); + MEDIACODEC_NULL_ARG_CHECK(state); + + handle = (mediacodecsync_s *)mediacodecsync; + + *state = handle->state; + + LOGI("state %u", *state); + + return MEDIACODEC_ERROR_NONE; +} -- 2.7.4