Add media codec sync APIs for internal 16/214216/5 accepted/tizen/unified/20190919.220158 submit/tizen/20190919.104212
authorJeongmo Yang <jm80.yang@samsung.com>
Thu, 19 Sep 2019 05:33:56 +0000 (14:33 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Thu, 19 Sep 2019 07:56:48 +0000 (16:56 +0900)
[Version] 0.6.1
[Profile] Common
[Issue Type] New Internal API

Change-Id: Ibd04deb782ecacc227fd85f7996bd989c34b3a7a
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
include/media_codec.h [changed mode: 0755->0644]
include/media_codec_port.h [changed mode: 0755->0644]
include/media_codec_port_gst.h [changed mode: 0755->0644]
include/media_codec_private.h
include/media_codec_sync_internal.h [new file with mode: 0644]
include/media_codec_sync_private.h [new file with mode: 0644]
packaging/capi-media-codec.spec
src/media_codec_port.c [changed mode: 0755->0644]
src/media_codec_port_gst.c [changed mode: 0755->0644]
src/media_codec_sync_internal.c [new file with mode: 0644]

old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index c574563..0057fa0 100644 (file)
@@ -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 (file)
index 0000000..c193bb0
--- /dev/null
@@ -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 <media_codec.h>
+
+#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 (file)
index 0000000..2f6fa53
--- /dev/null
@@ -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 <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <gst/gst.h>
+#include <gst/allocators/gsttizenmemory.h>
+#include <gst/app/gstappsrc.h>
+#include <media_codec_private.h>
+#include <media_codec_sync_internal.h>
+
+#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__ */
index ad913b6..455600a 100755 (executable)
@@ -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
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/src/media_codec_sync_internal.c b/src/media_codec_sync_internal.c
new file mode 100644 (file)
index 0000000..5d95d6f
--- /dev/null
@@ -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 <inttypes.h>
+#include <dlog.h>
+#include <media_codec_sync_private.h>
+
+#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;
+}