--- /dev/null
+/*
+ * 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__*/
--- /dev/null
+/*
+ * 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;
+}