blob: 97618c55b7c2484cf591d128c6fb8c9748658eb3 [file] [log] [blame]
/*
INTEL CONFIDENTIAL
Copyright 2009 Intel Corporation All Rights Reserved.
The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material contains trade secrets and proprietary and confidential information of Intel or its suppliers and licensors. The Material is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel’s prior express written permission.
No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing.
*/
#include <glib.h>
#include "mixvideolog.h"
#include "mixframemanager.h"
#include "mixvideoframe_private.h"
#define INITIAL_FRAME_ARRAY_SIZE 16
#define MIX_SECOND (G_USEC_PER_SEC * G_GINT64_CONSTANT (1000))
static GObjectClass *parent_class = NULL;
static void mix_framemanager_finalize(GObject * obj);
G_DEFINE_TYPE( MixFrameManager, mix_framemanager, G_TYPE_OBJECT);
static void mix_framemanager_init(MixFrameManager * self) {
/* TODO: public member initialization */
/* TODO: private member initialization */
if (!g_thread_supported()) {
g_thread_init(NULL);
}
self->lock = g_mutex_new();
self->flushing = FALSE;
self->eos = FALSE;
self->frame_array = NULL;
self->frame_queue = NULL;
self->initialized = FALSE;
self->mode = MIX_FRAMEORDER_MODE_DISPLAYORDER;
self->framerate_numerator = 30;
self->framerate_denominator = 1;
self->is_first_frame = TRUE;
/* for vc1 in asf */
self->p_frame = NULL;
self->prev_timestamp = 0;
#ifdef ANDROID
self->timestamp_storage = NULL;
#endif
}
static void mix_framemanager_class_init(MixFrameManagerClass * klass) {
GObjectClass *gobject_class = (GObjectClass *) klass;
/* parent class for later use */
parent_class = g_type_class_peek_parent(klass);
gobject_class->finalize = mix_framemanager_finalize;
}
MixFrameManager *mix_framemanager_new(void) {
MixFrameManager *ret = g_object_new(MIX_TYPE_FRAMEMANAGER, NULL);
return ret;
}
void mix_framemanager_finalize(GObject * obj) {
/* clean up here. */
MixFrameManager *fm = MIX_FRAMEMANAGER(obj);
/* cleanup here */
mix_framemanager_deinitialize(fm);
if (fm->lock) {
g_mutex_free(fm->lock);
fm->lock = NULL;
}
/* Chain up parent */
if (parent_class->finalize) {
parent_class->finalize(obj);
}
}
MixFrameManager *mix_framemanager_ref(MixFrameManager * fm) {
return (MixFrameManager *) g_object_ref(G_OBJECT(fm));
}
/* MixFrameManager class methods */
MIX_RESULT mix_framemanager_initialize(MixFrameManager *fm,
MixFrameOrderMode mode, gint framerate_numerator,
gint framerate_denominator, gboolean timebased_ordering) {
MIX_RESULT ret = MIX_RESULT_FAIL;
if (!MIX_IS_FRAMEMANAGER(fm) || (mode != MIX_FRAMEORDER_MODE_DISPLAYORDER
&& mode != MIX_FRAMEORDER_MODE_DECODEORDER) || framerate_numerator
<= 0 || framerate_denominator <= 0) {
return MIX_RESULT_INVALID_PARAM;
}
if (fm->initialized) {
return MIX_RESULT_ALREADY_INIT;
}
if (!g_thread_supported()) {
g_thread_init(NULL);
}
ret = MIX_RESULT_NO_MEMORY;
if (!fm->lock) {
fm->lock = g_mutex_new();
if (!fm->lock) {
goto cleanup;
}
}
if (mode == MIX_FRAMEORDER_MODE_DISPLAYORDER) {
fm->frame_array = g_ptr_array_sized_new(INITIAL_FRAME_ARRAY_SIZE);
if (!fm->frame_array) {
goto cleanup;
}
#ifdef ANDROID
fm->timestamp_storage = g_array_sized_new(FALSE, TRUE,
sizeof(guint64), INITIAL_FRAME_ARRAY_SIZE);
if (!fm->timestamp_storage) {
goto cleanup;
}
#endif
}
fm->frame_queue = g_queue_new();
if (!fm->frame_queue) {
goto cleanup;
}
fm->framerate_numerator = framerate_numerator;
fm->framerate_denominator = framerate_denominator;
fm->frame_timestamp_delta = fm->framerate_denominator * MIX_SECOND
/ fm->framerate_numerator;
fm->mode = mode;
fm->timebased_ordering = timebased_ordering;
fm->initialized = TRUE;
ret = MIX_RESULT_SUCCESS;
cleanup:
if (ret != MIX_RESULT_SUCCESS) {
if (fm->frame_array) {
g_ptr_array_free(fm->frame_array, TRUE);
fm->frame_array = NULL;
}
if (fm->frame_queue) {
g_queue_free(fm->frame_queue);
fm->frame_queue = NULL;
}
}
return ret;
}
MIX_RESULT mix_framemanager_deinitialize(MixFrameManager *fm) {
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->lock) {
return MIX_RESULT_FAIL;
}
if (!fm->initialized) {
return MIX_RESULT_NOT_INIT;
}
mix_framemanager_flush(fm);
g_mutex_lock(fm->lock);
if (fm->frame_array) {
g_ptr_array_free(fm->frame_array, TRUE);
fm->frame_array = NULL;
}
if (fm->frame_queue) {
g_queue_free(fm->frame_queue);
fm->frame_queue = NULL;
}
#ifdef ANDROID
if (fm->timestamp_storage) {
g_array_free(fm->timestamp_storage, TRUE);
fm->timestamp_storage = NULL;
}
#endif
fm->initialized = FALSE;
g_mutex_unlock(fm->lock);
return MIX_RESULT_SUCCESS;
}
MIX_RESULT mix_framemanager_set_framerate(MixFrameManager *fm,
gint framerate_numerator, gint framerate_denominator) {
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->lock) {
return MIX_RESULT_FAIL;
}
if (framerate_numerator <= 0 || framerate_denominator <= 0) {
return MIX_RESULT_INVALID_PARAM;
}
g_mutex_lock(fm->lock);
fm->framerate_numerator = framerate_numerator;
fm->framerate_denominator = framerate_denominator;
fm->frame_timestamp_delta = fm->framerate_denominator * MIX_SECOND
/ fm->framerate_numerator;
g_mutex_unlock(fm->lock);
return MIX_RESULT_SUCCESS;
}
MIX_RESULT mix_framemanager_get_framerate(MixFrameManager *fm,
gint *framerate_numerator, gint *framerate_denominator) {
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->lock) {
return MIX_RESULT_FAIL;
}
if (!framerate_numerator || !framerate_denominator) {
return MIX_RESULT_INVALID_PARAM;
}
g_mutex_lock(fm->lock);
*framerate_numerator = fm->framerate_numerator;
*framerate_denominator = fm->framerate_denominator;
g_mutex_unlock(fm->lock);
return MIX_RESULT_SUCCESS;
}
MIX_RESULT mix_framemanager_get_frame_order_mode(MixFrameManager *fm,
MixFrameOrderMode *mode) {
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->lock) {
return MIX_RESULT_FAIL;
}
if (!mode) {
return MIX_RESULT_INVALID_PARAM;
}
/* no need to use lock */
*mode = fm->mode;
return MIX_RESULT_SUCCESS;
}
MIX_RESULT mix_framemanager_flush(MixFrameManager *fm) {
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->initialized) {
return MIX_RESULT_NOT_INIT;
}
g_mutex_lock(fm->lock);
/* flush frame_array */
if (fm->frame_array) {
guint len = fm->frame_array->len;
if (len) {
guint idx = 0;
MixVideoFrame *frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = (MixVideoFrame *) g_ptr_array_index(fm->frame_array,
idx);
if (frame) {
mix_videoframe_unref(frame);
g_ptr_array_index(fm->frame_array, idx) = NULL;
}
}
/* g_ptr_array_remove_range(fm->frame_array, 0, len); */
}
}
#ifdef ANDROID
if(fm->timestamp_storage) {
g_array_remove_range(fm->timestamp_storage, 0, fm->timestamp_storage->len);
}
#endif
if (fm->frame_queue) {
guint len = fm->frame_queue->length;
if (len) {
MixVideoFrame *frame = NULL;
while ((frame = (MixVideoFrame *) g_queue_pop_head(fm->frame_queue))) {
mix_videoframe_unref(frame);
}
}
}
if(fm->p_frame) {
mix_videoframe_unref(fm->p_frame);
fm->p_frame = NULL;
}
fm->prev_timestamp = 0;
fm->eos = FALSE;
fm->is_first_frame = TRUE;
g_mutex_unlock(fm->lock);
return MIX_RESULT_SUCCESS;
}
MixVideoFrame *get_expected_frame_from_array(GPtrArray *array,
guint64 expected, guint64 tolerance, guint64 *frametimestamp) {
guint idx = 0;
guint len = 0;
guint64 timestamp = 0;
guint64 lowest_timestamp = (guint64)-1;
guint lowest_timestamp_idx = -1;
MixVideoFrame *frame = NULL;
if (!array || !expected || !tolerance || !frametimestamp || expected < tolerance) {
return NULL;
}
len = array->len;
if (!len) {
return NULL;
}
for (idx = 0; idx < len; idx++) {
MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, idx);
if (_frame) {
if (mix_videoframe_get_timestamp(_frame, &timestamp)
!= MIX_RESULT_SUCCESS) {
/*
* Oops, this shall never happen!
* In case it heppens, release the frame!
*/
mix_videoframe_unref(_frame);
/* make an available slot */
g_ptr_array_index(array, idx) = NULL;
break;
}
if (lowest_timestamp > timestamp)
{
lowest_timestamp = timestamp;
lowest_timestamp_idx = idx;
}
}
}
if (lowest_timestamp == (guint64)-1)
{
return NULL;
}
/* check if this is the expected next frame */
if (lowest_timestamp <= expected + tolerance)
{
MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, lowest_timestamp_idx);
/* make this slot available */
g_ptr_array_index(array, lowest_timestamp_idx) = NULL;
*frametimestamp = lowest_timestamp;
frame = _frame;
}
return frame;
}
#ifdef ANDROID
MixVideoFrame *get_expected_frame_from_array_DO(GPtrArray *array,
guint32 expected, guint32 *framedisplayorder) {
guint idx = 0;
guint len = 0;
guint32 displayorder = 0;
guint32 lowest_displayorder = (guint32)-1;
guint lowest_displayorder_idx = -1;
MixVideoFrame *frame = NULL;
if (!array || !expected || !framedisplayorder) {
return NULL;
}
len = array->len;
if (!len) {
return NULL;
}
for (idx = 0; idx < len; idx++) {
MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, idx);
if (_frame) {
if (mix_videoframe_get_displayorder(_frame, &displayorder)
!= MIX_RESULT_SUCCESS) {
/*
* Oops, this shall never happen!
* In case it heppens, release the frame!
*/
mix_videoframe_unref(_frame);
/* make an available slot */
g_ptr_array_index(array, idx) = NULL;
break;
}
if (lowest_displayorder > displayorder)
{
lowest_displayorder = displayorder;
lowest_displayorder_idx = idx;
}
}
}
if (lowest_displayorder == (guint32)-1)
{
return NULL;
}
/* check if this is the expected next frame */
if (lowest_displayorder <= expected)
{
MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, lowest_displayorder_idx);
/* make this slot available */
g_ptr_array_index(array, lowest_displayorder_idx) = NULL;
*framedisplayorder = lowest_displayorder;
frame = _frame;
}
return frame;
}
#endif /* ANDROID */
void add_frame_into_array(GPtrArray *array, MixVideoFrame *mvf) {
gboolean found_slot = FALSE;
guint len = 0;
if (!array || !mvf) {
return;
}
/* do we have slot for this frame? */
len = array->len;
if (len) {
guint idx = 0;
gpointer frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = g_ptr_array_index(array, idx);
if (!frame) {
found_slot = TRUE;
g_ptr_array_index(array, idx) = (gpointer) mvf;
break;
}
}
}
if (!found_slot) {
g_ptr_array_add(array, (gpointer) mvf);
}
}
#ifdef ANDROID
gint frame_sorting_func_DO(gconstpointer a, gconstpointer b) {
MixVideoFrame *fa = *((MixVideoFrame **) a);
MixVideoFrame *fb = *((MixVideoFrame **) b);
guint32 ta, tb;
if (!fa && !fb) {
return 0;
}
if (fa && !fb) {
return 1;
}
if (!fa && fb) {
return -1;
}
mix_videoframe_get_displayorder(fa, &ta);
mix_videoframe_get_displayorder(fb, &tb);
if (ta > tb) {
return 1;
}
if (ta == tb) {
return 0;
}
return -1;
}
MIX_RESULT mix_framemanager_displayorder_based_enqueue(MixFrameManager *fm,
MixVideoFrame *mvf) {
/*
* display order mode.
*
* if this is the first frame, we always push it into
* output queue, if it is not, check if it is the one
* expected, if yes, push it into the output queue.
* if not, put it into waiting list.
*
* while the expected frame is pushed into output queue,
* the expected next timestamp is also updated. with this
* updated expected next timestamp, we search for expected
* frame from the waiting list, if found, repeat the process.
*
*/
MIX_RESULT ret = MIX_RESULT_FAIL;
guint32 displayorder = 0;
first_frame:
ret = mix_videoframe_get_displayorder(mvf, &displayorder);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
if (fm->is_first_frame) {
/*
* for the first frame, we can always put it into the output queue
*/
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
/*
* what displayorder of next frame shall be?
*/
fm->next_displayorder = displayorder + 1;
fm->is_first_frame = FALSE;
} else {
/*
* If displayorder is 0, send all the frames in the array to the queue
*/
if(displayorder == 0) {
if (fm->frame_array) {
guint len = fm->frame_array->len;
if (len) {
/* sorting frames in the array by displayorder */
g_ptr_array_sort(fm->frame_array, frame_sorting_func_DO);
guint idx = 0;
MixVideoFrame *frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = (MixVideoFrame *) g_ptr_array_index(
fm->frame_array, idx);
if (frame) {
g_ptr_array_index(fm->frame_array, idx) = NULL;
g_queue_push_tail(fm->frame_queue, (gpointer) frame);
}
}
}
}
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
/*
* what displayorder of next frame shall be?
*/
fm->next_displayorder = displayorder + 1;
} else {
/*
* is this the next frame expected?
*/
/* calculate tolerance */
MixVideoFrame *frame_from_array = NULL;
guint32 displayorder_frame_array = 0;
/*
* timestamp may be associated with the second field, which
* will not fall between the tolerance range.
*/
if (displayorder <= fm->next_displayorder) {
/*
* ok, this is the frame expected, push it into output queue
*/
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
/*
* update next_frame_timestamp only if it falls within the tolerance range
*/
if (displayorder == fm->next_displayorder)
{
fm->next_displayorder = displayorder + 1;
}
/*
* since we updated next_displayorder, there might be a frame
* in the frame_array that satisfying this new next_displayorder
*/
while ((frame_from_array = get_expected_frame_from_array_DO(
fm->frame_array, fm->next_displayorder,
&displayorder_frame_array))) {
g_queue_push_tail(fm->frame_queue, (gpointer) frame_from_array);
/*
* update next_frame_timestamp only if it falls within the tolerance range
*/
if (displayorder_frame_array >= fm->next_displayorder)
{
fm->next_displayorder = displayorder_frame_array + 1;
}
}
} else {
/*
* is discontinuity flag set for this frame ?
*/
gboolean discontinuity = FALSE;
ret = mix_videoframe_get_discontinuity(mvf, &discontinuity);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
/*
* If this is a frame with discontinuity flag set, clear frame_array
* and treat the frame as the first frame.
*/
if (discontinuity) {
guint len = fm->frame_array->len;
if (len) {
guint idx = 0;
MixVideoFrame *frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = (MixVideoFrame *) g_ptr_array_index(
fm->frame_array, idx);
if (frame) {
mix_videoframe_unref(frame);
g_ptr_array_index(fm->frame_array, idx) = NULL;
}
}
}
fm->is_first_frame = TRUE;
goto first_frame;
}
/*
* this is not the expected frame, put it into frame_array
*/
add_frame_into_array(fm->frame_array, mvf);
}
}
}
cleanup:
return ret;
}
#endif /* ANDROID */
MIX_RESULT mix_framemanager_timestamp_based_enqueue(MixFrameManager *fm,
MixVideoFrame *mvf) {
/*
* display order mode.
*
* if this is the first frame, we always push it into
* output queue, if it is not, check if it is the one
* expected, if yes, push it into the output queue.
* if not, put it into waiting list.
*
* while the expected frame is pushed into output queue,
* the expected next timestamp is also updated. with this
* updated expected next timestamp, we search for expected
* frame from the waiting list, if found, repeat the process.
*
*/
MIX_RESULT ret = MIX_RESULT_FAIL;
guint64 timestamp = 0;
first_frame:
ret = mix_videoframe_get_timestamp(mvf, &timestamp);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
if (fm->is_first_frame) {
/*
* for the first frame, we can always put it into the output queue
*/
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
/*
* what timestamp of next frame shall be?
*/
fm->next_frame_timestamp = timestamp + fm->frame_timestamp_delta;
fm->is_first_frame = FALSE;
} else {
/*
* is this the next frame expected?
*/
/* calculate tolerance */
guint64 tolerance = fm->frame_timestamp_delta / 4;
MixVideoFrame *frame_from_array = NULL;
guint64 timestamp_frame_array = 0;
/*
* timestamp may be associated with the second field, which
* will not fall between the tolerance range.
*/
if (timestamp <= fm->next_frame_timestamp + tolerance) {
/*
* ok, this is the frame expected, push it into output queue
*/
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
/*
* update next_frame_timestamp only if it falls within the tolerance range
*/
if (timestamp >= fm->next_frame_timestamp - tolerance)
{
fm->next_frame_timestamp = timestamp + fm->frame_timestamp_delta;
}
/*
* since we updated next_frame_timestamp, there might be a frame
* in the frame_array that satisfying this new next_frame_timestamp
*/
while ((frame_from_array = get_expected_frame_from_array(
fm->frame_array, fm->next_frame_timestamp, tolerance,
&timestamp_frame_array))) {
g_queue_push_tail(fm->frame_queue, (gpointer) frame_from_array);
/*
* update next_frame_timestamp only if it falls within the tolerance range
*/
if (timestamp_frame_array >= fm->next_frame_timestamp - tolerance)
{
fm->next_frame_timestamp = timestamp_frame_array
+ fm->frame_timestamp_delta;
}
}
} else {
/*
* is discontinuity flag set for this frame ?
*/
gboolean discontinuity = FALSE;
ret = mix_videoframe_get_discontinuity(mvf, &discontinuity);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
/*
* If this is a frame with discontinuity flag set, clear frame_array
* and treat the frame as the first frame.
*/
if (discontinuity) {
guint len = fm->frame_array->len;
if (len) {
guint idx = 0;
MixVideoFrame *frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = (MixVideoFrame *) g_ptr_array_index(
fm->frame_array, idx);
if (frame) {
mix_videoframe_unref(frame);
g_ptr_array_index(fm->frame_array, idx) = NULL;
}
}
}
fm->is_first_frame = TRUE;
goto first_frame;
}
/*
* handle variable frame rate:
* display any frame which time stamp is less than current one.
*
*/
guint64 tolerance = fm->frame_timestamp_delta / 4;
MixVideoFrame *frame_from_array = NULL;
guint64 timestamp_frame_array = 0;
while ((frame_from_array = get_expected_frame_from_array(
fm->frame_array, timestamp, tolerance,
&timestamp_frame_array)))
{
g_queue_push_tail(fm->frame_queue, (gpointer) frame_from_array);
/*
* update next_frame_timestamp only if it falls within the tolerance range
*/
if (timestamp_frame_array >= fm->next_frame_timestamp - tolerance)
{
fm->next_frame_timestamp = timestamp_frame_array
+ fm->frame_timestamp_delta;
}
}
/*
* this is not the expected frame, put it into frame_array
*/
add_frame_into_array(fm->frame_array, mvf);
}
}
cleanup:
return ret;
}
MIX_RESULT mix_framemanager_frametype_based_enqueue(MixFrameManager *fm,
MixVideoFrame *mvf) {
MIX_RESULT ret = MIX_RESULT_FAIL;
MixFrameType frame_type;
guint64 timestamp = 0;
ret = mix_videoframe_get_frame_type(mvf, &frame_type);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
ret = mix_videoframe_get_timestamp(mvf, &timestamp);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
#ifdef MIX_LOG_ENABLE
if (frame_type == TYPE_I) {
LOG_I( "TYPE_I %"G_GINT64_FORMAT"\n", timestamp);
} else if (frame_type == TYPE_P) {
LOG_I( "TYPE_P %"G_GINT64_FORMAT"\n", timestamp);
} else if (frame_type == TYPE_B) {
LOG_I( "TYPE_B %"G_GINT64_FORMAT"\n", timestamp);
} else {
LOG_I( "TYPE_UNKNOWN %"G_GINT64_FORMAT"\n", timestamp);
}
#endif
if (fm->is_first_frame) {
/*
* The first frame is not a I frame, unexpected!
*/
if (frame_type != TYPE_I) {
goto cleanup;
}
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
fm->is_first_frame = FALSE;
} else {
/*
* I P B B P B B ...
*/
if (frame_type == TYPE_I || frame_type == TYPE_P) {
if (fm->p_frame) {
ret = mix_videoframe_set_timestamp(fm->p_frame,
fm->prev_timestamp);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
g_queue_push_tail(fm->frame_queue, (gpointer) fm->p_frame);
fm->p_frame = NULL;
}
/* it is an I frame, push it into the out queue */
/*if (frame_type == TYPE_I) {
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
} else*/
{
/* it is a P frame, we can not push it to the out queue yet, save it */
fm->p_frame = mvf;
fm->prev_timestamp = timestamp;
}
ret = MIX_RESULT_SUCCESS;
} else {
/* it is a B frame, replace the timestamp with the previous one */
if (timestamp > fm->prev_timestamp) {
ret = mix_videoframe_set_timestamp(mvf, fm->prev_timestamp);
if (ret != MIX_RESULT_SUCCESS) {
goto cleanup;
}
/* save the timestamp */
fm->prev_timestamp = timestamp;
}
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
ret = MIX_RESULT_SUCCESS;
}
}
cleanup:
return ret;
}
MIX_RESULT mix_framemanager_enqueue(MixFrameManager *fm, MixVideoFrame *mvf) {
MIX_RESULT ret = MIX_RESULT_FAIL;
/*fm->mode = MIX_FRAMEORDER_MODE_DECODEORDER;*/
if (!mvf) {
return MIX_RESULT_INVALID_PARAM;
}
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->initialized) {
return MIX_RESULT_NOT_INIT;
}
/*
* This should never happen!
*/
if (fm->mode != MIX_FRAMEORDER_MODE_DISPLAYORDER && fm->mode
!= MIX_FRAMEORDER_MODE_DECODEORDER) {
return MIX_RESULT_FAIL;
}
g_mutex_lock(fm->lock);
ret = MIX_RESULT_SUCCESS;
if (fm->mode == MIX_FRAMEORDER_MODE_DECODEORDER) {
/*
* decode order mode, push the frame into output queue
*/
g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
} else {
#ifdef ANDROID
guint64 timestamp = 0;
mix_videoframe_get_timestamp(mvf, &timestamp);
/* add timestamp into timestamp storage */
if(fm->timestamp_storage) {
gint idx = 0;
gboolean found = FALSE;
if(fm->timestamp_storage->len) {
for(idx = 0; idx < fm->timestamp_storage->len; idx ++) {
if(timestamp == g_array_index(fm->timestamp_storage, guint64, idx)) {
found = TRUE;
break;
}
}
}
if(!found) {
g_array_append_val(fm->timestamp_storage, timestamp);
}
}
#endif
if (fm->timebased_ordering) {
#ifndef ANDROID
ret = mix_framemanager_timestamp_based_enqueue(fm, mvf);
#else
ret = mix_framemanager_displayorder_based_enqueue(fm, mvf);
#endif
} else {
ret = mix_framemanager_frametype_based_enqueue(fm, mvf);
}
}
g_mutex_unlock(fm->lock);
return ret;
}
#ifdef ANDROID
gint timestamp_storage_sorting_func(gconstpointer a, gconstpointer b) {
guint64 ta = *((guint64 *)a);
guint64 tb = *((guint64 *)b);
if(ta > tb) {
return +1;
} else if(ta == tb) {
return 0;
}
return -1;
}
#endif
MIX_RESULT mix_framemanager_dequeue(MixFrameManager *fm, MixVideoFrame **mvf) {
MIX_RESULT ret = MIX_RESULT_FAIL;
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!mvf) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->initialized) {
return MIX_RESULT_NOT_INIT;
}
g_mutex_lock(fm->lock);
ret = MIX_RESULT_FRAME_NOTAVAIL;
*mvf = (MixVideoFrame *) g_queue_pop_head(fm->frame_queue);
if (*mvf) {
#ifdef ANDORID
if(fm->timestamp_storage && fm->mode == MIX_FRAMEORDER_MODE_DISPLAYORDER) {
if(fm->timestamp_storage->len) {
guint64 ts;
g_array_sort(fm->timestamp_storage, timestamp_storage_sorting_func);
ts = g_array_index(val_array, guint64, 0)
mix_videoframe_set_timestamp(*mvf, ts);
g_array_remove_index_fast(fm->timestamp_storage, 0);
}
}
#endif
ret = MIX_RESULT_SUCCESS;
} else if (fm->eos) {
ret = MIX_RESULT_EOS;
}
g_mutex_unlock(fm->lock);
return ret;
}
gint frame_sorting_func(gconstpointer a, gconstpointer b) {
MixVideoFrame *fa = *((MixVideoFrame **) a);
MixVideoFrame *fb = *((MixVideoFrame **) b);
guint64 ta, tb;
if (!fa && !fb) {
return 0;
}
if (fa && !fb) {
return 1;
}
if (!fa && fb) {
return -1;
}
mix_videoframe_get_timestamp(fa, &ta);
mix_videoframe_get_timestamp(fb, &tb);
if (ta > tb) {
return 1;
}
if (ta == tb) {
return 0;
}
return -1;
}
MIX_RESULT mix_framemanager_eos(MixFrameManager *fm) {
MIX_RESULT ret = MIX_RESULT_FAIL;
if (!MIX_IS_FRAMEMANAGER(fm)) {
return MIX_RESULT_INVALID_PARAM;
}
if (!fm->initialized) {
return MIX_RESULT_NOT_INIT;
}
g_mutex_lock(fm->lock);
if (fm->mode == MIX_FRAMEORDER_MODE_DISPLAYORDER) {
/* Do we have frames that are not in the output queue?
* MixVideoFormat* must guarantee that when this
* function called, the last frame is already enqueued!
*/
/* In case it is frame type based enqueue, p_frame is the
* only frame that is not in the output queue
*/
if (fm->p_frame && fm->frame_queue) {
g_queue_push_tail(fm->frame_queue, (gpointer) fm->p_frame);
fm->p_frame = NULL;
}
/* In case it is timestamp based enqueue, throw all the frames
* in the array into the output queue by the order of timestamp
*/
if (fm->frame_array) {
guint len = fm->frame_array->len;
if (len) {
#ifndef ANDROID
/* sorting frames in the array by timestamp */
g_ptr_array_sort(fm->frame_array, frame_sorting_func);
#else
/* sorting frames is the array by displayorder */
g_ptr_array_sort(fm->frame_array, frame_sorting_func_DO);
#endif
guint idx = 0;
MixVideoFrame *frame = NULL;
for (idx = 0; idx < len; idx++) {
frame = (MixVideoFrame *) g_ptr_array_index(
fm->frame_array, idx);
if (frame) {
g_ptr_array_index(fm->frame_array, idx) = NULL;
g_queue_push_tail(fm->frame_queue, (gpointer) frame);
}
}
}
}
}
fm->eos = TRUE;
g_mutex_unlock(fm->lock);
return ret;
}