Home | History | Annotate | Download | only in src
      1 /*
      2  INTEL CONFIDENTIAL
      3  Copyright 2009 Intel Corporation All Rights Reserved.
      4  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 Intels prior express written permission.
      5 
      6  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.
      7  */
      8 #include <glib.h>
      9 
     10 #include "mixvideolog.h"
     11 #include "mixframemanager.h"
     12 #include "mixvideoframe_private.h"
     13 
     14 #define INITIAL_FRAME_ARRAY_SIZE 	16
     15 #define MIX_SECOND  (G_USEC_PER_SEC * G_GINT64_CONSTANT (1000))
     16 
     17 static GObjectClass *parent_class = NULL;
     18 
     19 static void mix_framemanager_finalize(GObject * obj);
     20 G_DEFINE_TYPE( MixFrameManager, mix_framemanager, G_TYPE_OBJECT);
     21 
     22 static void mix_framemanager_init(MixFrameManager * self) {
     23 	/* TODO: public member initialization */
     24 
     25 	/* TODO: private member initialization */
     26 
     27 	if (!g_thread_supported()) {
     28 		g_thread_init(NULL);
     29 	}
     30 
     31 	self->lock = g_mutex_new();
     32 
     33 	self->flushing = FALSE;
     34 	self->eos = FALSE;
     35 	self->frame_array = NULL;
     36 	self->frame_queue = NULL;
     37 	self->initialized = FALSE;
     38 
     39 	self->mode = MIX_FRAMEORDER_MODE_DISPLAYORDER;
     40 	self->framerate_numerator = 30;
     41 	self->framerate_denominator = 1;
     42 
     43 	self->is_first_frame = TRUE;
     44 
     45 	/* for vc1 in asf */
     46 	self->p_frame = NULL;
     47 	self->prev_timestamp = 0;
     48 }
     49 
     50 static void mix_framemanager_class_init(MixFrameManagerClass * klass) {
     51 	GObjectClass *gobject_class = (GObjectClass *) klass;
     52 
     53 	/* parent class for later use */
     54 	parent_class = g_type_class_peek_parent(klass);
     55 
     56 	gobject_class->finalize = mix_framemanager_finalize;
     57 }
     58 
     59 MixFrameManager *mix_framemanager_new(void) {
     60 	MixFrameManager *ret = g_object_new(MIX_TYPE_FRAMEMANAGER, NULL);
     61 
     62 	return ret;
     63 }
     64 
     65 void mix_framemanager_finalize(GObject * obj) {
     66 	/* clean up here. */
     67 
     68 	MixFrameManager *fm = MIX_FRAMEMANAGER(obj);
     69 
     70 	/* cleanup here */
     71 	mix_framemanager_deinitialize(fm);
     72 
     73 	if (fm->lock) {
     74 		g_mutex_free(fm->lock);
     75 		fm->lock = NULL;
     76 	}
     77 
     78 	/* Chain up parent */
     79 	if (parent_class->finalize) {
     80 		parent_class->finalize(obj);
     81 	}
     82 }
     83 
     84 MixFrameManager *mix_framemanager_ref(MixFrameManager * fm) {
     85 	return (MixFrameManager *) g_object_ref(G_OBJECT(fm));
     86 }
     87 
     88 /* MixFrameManager class methods */
     89 
     90 MIX_RESULT mix_framemanager_initialize(MixFrameManager *fm,
     91 		MixFrameOrderMode mode, gint framerate_numerator,
     92 		gint framerate_denominator, gboolean timebased_ordering) {
     93 
     94 	MIX_RESULT ret = MIX_RESULT_FAIL;
     95 
     96 	if (!MIX_IS_FRAMEMANAGER(fm) || (mode != MIX_FRAMEORDER_MODE_DISPLAYORDER
     97 			&& mode != MIX_FRAMEORDER_MODE_DECODEORDER) || framerate_numerator
     98 			<= 0 || framerate_denominator <= 0) {
     99 		return MIX_RESULT_INVALID_PARAM;
    100 	}
    101 
    102 	if (fm->initialized) {
    103 		return MIX_RESULT_ALREADY_INIT;
    104 	}
    105 
    106 	if (!g_thread_supported()) {
    107 		g_thread_init(NULL);
    108 	}
    109 
    110 	ret = MIX_RESULT_NO_MEMORY;
    111 	if (!fm->lock) {
    112 		fm->lock = g_mutex_new();
    113 		if (!fm->lock) {
    114 			goto cleanup;
    115 		}
    116 	}
    117 
    118 	if (mode == MIX_FRAMEORDER_MODE_DISPLAYORDER) {
    119 		fm->frame_array = g_ptr_array_sized_new(INITIAL_FRAME_ARRAY_SIZE);
    120 		if (!fm->frame_array) {
    121 			goto cleanup;
    122 		}
    123 	}
    124 
    125 	fm->frame_queue = g_queue_new();
    126 	if (!fm->frame_queue) {
    127 		goto cleanup;
    128 	}
    129 
    130 	fm->framerate_numerator = framerate_numerator;
    131 	fm->framerate_denominator = framerate_denominator;
    132 	fm->frame_timestamp_delta = fm->framerate_denominator * MIX_SECOND
    133 			/ fm->framerate_numerator;
    134 
    135 	fm->mode = mode;
    136 
    137 	fm->timebased_ordering = timebased_ordering;
    138 
    139 	fm->initialized = TRUE;
    140 
    141 	ret = MIX_RESULT_SUCCESS;
    142 
    143 	cleanup:
    144 
    145 	if (ret != MIX_RESULT_SUCCESS) {
    146 		if (fm->frame_array) {
    147 			g_ptr_array_free(fm->frame_array, TRUE);
    148 			fm->frame_array = NULL;
    149 		}
    150 		if (fm->frame_queue) {
    151 			g_queue_free(fm->frame_queue);
    152 			fm->frame_queue = NULL;
    153 		}
    154 	}
    155 	return ret;
    156 }
    157 MIX_RESULT mix_framemanager_deinitialize(MixFrameManager *fm) {
    158 
    159 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    160 		return MIX_RESULT_INVALID_PARAM;
    161 	}
    162 
    163 	if (!fm->lock) {
    164 		return MIX_RESULT_FAIL;
    165 	}
    166 
    167 	if (!fm->initialized) {
    168 		return MIX_RESULT_NOT_INIT;
    169 	}
    170 
    171 	mix_framemanager_flush(fm);
    172 
    173 	g_mutex_lock(fm->lock);
    174 
    175 	if (fm->frame_array) {
    176 		g_ptr_array_free(fm->frame_array, TRUE);
    177 		fm->frame_array = NULL;
    178 	}
    179 	if (fm->frame_queue) {
    180 		g_queue_free(fm->frame_queue);
    181 		fm->frame_queue = NULL;
    182 	}
    183 
    184 	fm->initialized = FALSE;
    185 
    186 	g_mutex_unlock(fm->lock);
    187 
    188 	return MIX_RESULT_SUCCESS;
    189 }
    190 
    191 MIX_RESULT mix_framemanager_set_framerate(MixFrameManager *fm,
    192 		gint framerate_numerator, gint framerate_denominator) {
    193 
    194 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    195 		return MIX_RESULT_INVALID_PARAM;
    196 	}
    197 
    198 	if (!fm->lock) {
    199 		return MIX_RESULT_FAIL;
    200 	}
    201 
    202 	if (framerate_numerator <= 0 || framerate_denominator <= 0) {
    203 		return MIX_RESULT_INVALID_PARAM;
    204 	}
    205 
    206 	g_mutex_lock(fm->lock);
    207 
    208 	fm->framerate_numerator = framerate_numerator;
    209 	fm->framerate_denominator = framerate_denominator;
    210 	fm->frame_timestamp_delta = fm->framerate_denominator * MIX_SECOND
    211 			/ fm->framerate_numerator;
    212 
    213 	g_mutex_unlock(fm->lock);
    214 
    215 	return MIX_RESULT_SUCCESS;
    216 }
    217 
    218 MIX_RESULT mix_framemanager_get_framerate(MixFrameManager *fm,
    219 		gint *framerate_numerator, gint *framerate_denominator) {
    220 
    221 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    222 		return MIX_RESULT_INVALID_PARAM;
    223 	}
    224 
    225 	if (!fm->lock) {
    226 		return MIX_RESULT_FAIL;
    227 	}
    228 
    229 	if (!framerate_numerator || !framerate_denominator) {
    230 		return MIX_RESULT_INVALID_PARAM;
    231 	}
    232 
    233 	g_mutex_lock(fm->lock);
    234 
    235 	*framerate_numerator = fm->framerate_numerator;
    236 	*framerate_denominator = fm->framerate_denominator;
    237 
    238 	g_mutex_unlock(fm->lock);
    239 
    240 	return MIX_RESULT_SUCCESS;
    241 }
    242 
    243 MIX_RESULT mix_framemanager_get_frame_order_mode(MixFrameManager *fm,
    244 		MixFrameOrderMode *mode) {
    245 
    246 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    247 		return MIX_RESULT_INVALID_PARAM;
    248 	}
    249 
    250 	if (!fm->lock) {
    251 		return MIX_RESULT_FAIL;
    252 	}
    253 
    254 	if (!mode) {
    255 		return MIX_RESULT_INVALID_PARAM;
    256 	}
    257 
    258 	/* no need to use lock */
    259 	*mode = fm->mode;
    260 
    261 	return MIX_RESULT_SUCCESS;
    262 }
    263 
    264 MIX_RESULT mix_framemanager_flush(MixFrameManager *fm) {
    265 
    266 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    267 		return MIX_RESULT_INVALID_PARAM;
    268 	}
    269 
    270 	if (!fm->initialized) {
    271 		return MIX_RESULT_NOT_INIT;
    272 	}
    273 
    274 	g_mutex_lock(fm->lock);
    275 
    276 	/* flush frame_array */
    277 	if (fm->frame_array) {
    278 		guint len = fm->frame_array->len;
    279 		if (len) {
    280 			guint idx = 0;
    281 			MixVideoFrame *frame = NULL;
    282 			for (idx = 0; idx < len; idx++) {
    283 				frame = (MixVideoFrame *) g_ptr_array_index(fm->frame_array,
    284 						idx);
    285 				if (frame) {
    286 					mix_videoframe_unref(frame);
    287 					g_ptr_array_index(fm->frame_array, idx) = NULL;
    288 				}
    289 			}
    290 			/* g_ptr_array_remove_range(fm->frame_array, 0, len); */
    291 		}
    292 	}
    293 
    294 	if (fm->frame_queue) {
    295 		guint len = fm->frame_queue->length;
    296 		if (len) {
    297 			MixVideoFrame *frame = NULL;
    298 			while ((frame = (MixVideoFrame *) g_queue_pop_head(fm->frame_queue))) {
    299 				mix_videoframe_unref(frame);
    300 			}
    301 		}
    302 	}
    303 
    304 	if(fm->p_frame) {
    305 		mix_videoframe_unref(fm->p_frame);
    306 		fm->p_frame = NULL;
    307 	}
    308 	fm->prev_timestamp = 0;
    309 
    310 	fm->eos = FALSE;
    311 
    312 	fm->is_first_frame = TRUE;
    313 
    314 	g_mutex_unlock(fm->lock);
    315 
    316 	return MIX_RESULT_SUCCESS;
    317 }
    318 
    319 MixVideoFrame *get_expected_frame_from_array(GPtrArray *array,
    320 		guint64 expected, guint64 tolerance, guint64 *frametimestamp) {
    321 
    322 	guint idx = 0;
    323 	guint len = 0;
    324 	guint64 timestamp = 0;
    325 	guint64 lowest_timestamp = (guint64)-1;
    326 	guint lowest_timestamp_idx = -1;
    327 
    328 	MixVideoFrame *frame = NULL;
    329 
    330 	if (!array || !expected || !tolerance || !frametimestamp || expected < tolerance) {
    331 
    332 		return NULL;
    333 	}
    334 
    335 	len = array->len;
    336 	if (!len) {
    337 		return NULL;
    338 	}
    339 
    340 	for (idx = 0; idx < len; idx++) {
    341 		MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, idx);
    342 		if (_frame) {
    343 
    344 			if (mix_videoframe_get_timestamp(_frame, &timestamp)
    345 					!= MIX_RESULT_SUCCESS) {
    346 
    347 				/*
    348 				 * Oops, this shall never happen!
    349 				 * In case it heppens, release the frame!
    350 				 */
    351 
    352 				mix_videoframe_unref(_frame);
    353 
    354 				/* make an available slot */
    355 				g_ptr_array_index(array, idx) = NULL;
    356 
    357 				break;
    358 			}
    359 
    360 			if (lowest_timestamp > timestamp)
    361 			{
    362 				lowest_timestamp = timestamp;
    363 				lowest_timestamp_idx = idx;
    364 			}
    365 		}
    366 	}
    367 
    368 	if (lowest_timestamp == (guint64)-1)
    369 	{
    370 		return NULL;
    371 	}
    372 
    373 
    374 	/* check if this is the expected next frame */
    375 	if (lowest_timestamp <= expected + tolerance)
    376 	{
    377 		MixVideoFrame *_frame = (MixVideoFrame *) g_ptr_array_index(array, lowest_timestamp_idx);
    378 		/* make this slot available */
    379 		g_ptr_array_index(array, lowest_timestamp_idx) = NULL;
    380 
    381 		*frametimestamp = lowest_timestamp;
    382 		frame = _frame;
    383 	}
    384 
    385 	return frame;
    386 }
    387 
    388 void add_frame_into_array(GPtrArray *array, MixVideoFrame *mvf) {
    389 
    390 	gboolean found_slot = FALSE;
    391 	guint len = 0;
    392 
    393 	if (!array || !mvf) {
    394 		return;
    395 	}
    396 
    397 	/* do we have slot for this frame? */
    398 	len = array->len;
    399 	if (len) {
    400 		guint idx = 0;
    401 		gpointer frame = NULL;
    402 		for (idx = 0; idx < len; idx++) {
    403 			frame = g_ptr_array_index(array, idx);
    404 			if (!frame) {
    405 				found_slot = TRUE;
    406 				g_ptr_array_index(array, idx) = (gpointer) mvf;
    407 				break;
    408 			}
    409 		}
    410 	}
    411 
    412 	if (!found_slot) {
    413 		g_ptr_array_add(array, (gpointer) mvf);
    414 	}
    415 
    416 }
    417 
    418 MIX_RESULT mix_framemanager_timestamp_based_enqueue(MixFrameManager *fm,
    419 		MixVideoFrame *mvf) {
    420 	/*
    421 	 * display order mode.
    422 	 *
    423 	 * if this is the first frame, we always push it into
    424 	 * output queue, if it is not, check if it is the one
    425 	 * expected, if yes, push it into the output queue.
    426 	 * if not, put it into waiting list.
    427 	 *
    428 	 * while the expected frame is pushed into output queue,
    429 	 * the expected next timestamp is also updated. with this
    430 	 * updated expected next timestamp, we search for expected
    431 	 * frame from the waiting list, if found, repeat the process.
    432 	 *
    433 	 */
    434 
    435 	MIX_RESULT ret = MIX_RESULT_FAIL;
    436 	guint64 timestamp = 0;
    437 
    438 	first_frame:
    439 
    440 	ret = mix_videoframe_get_timestamp(mvf, &timestamp);
    441 	if (ret != MIX_RESULT_SUCCESS) {
    442 		goto cleanup;
    443 	}
    444 
    445 	if (fm->is_first_frame) {
    446 
    447 		/*
    448 		 * for the first frame, we can always put it into the output queue
    449 		 */
    450 		g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    451 
    452 		/*
    453 		 * what timestamp of next frame shall be?
    454 		 */
    455 		fm->next_frame_timestamp = timestamp + fm->frame_timestamp_delta;
    456 
    457 		fm->is_first_frame = FALSE;
    458 
    459 	} else {
    460 
    461 		/*
    462 		 * is this the next frame expected?
    463 		 */
    464 
    465 		/* calculate tolerance */
    466 		guint64 tolerance = fm->frame_timestamp_delta / 4;
    467 		MixVideoFrame *frame_from_array = NULL;
    468 		guint64 timestamp_frame_array = 0;
    469 
    470 		/*
    471 		* timestamp may be associated with the second field, which
    472 		* will not fall between the tolerance range.
    473 		*/
    474 
    475 		if (timestamp <= fm->next_frame_timestamp + tolerance) {
    476 
    477 			/*
    478 			 * ok, this is the frame expected, push it into output queue
    479 			 */
    480 			g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    481 
    482 			/*
    483 			 * update next_frame_timestamp only if it falls within the tolerance range
    484 			 */
    485 			if (timestamp >= fm->next_frame_timestamp - tolerance)
    486 			{
    487 				fm->next_frame_timestamp = timestamp + fm->frame_timestamp_delta;
    488 			}
    489 
    490 			/*
    491 			 * since we updated next_frame_timestamp, there might be a frame
    492 			 * in the frame_array that satisfying this new next_frame_timestamp
    493 			 */
    494 
    495 			while ((frame_from_array = get_expected_frame_from_array(
    496 					fm->frame_array, fm->next_frame_timestamp, tolerance,
    497 					&timestamp_frame_array))) {
    498 
    499 				g_queue_push_tail(fm->frame_queue, (gpointer) frame_from_array);
    500 
    501 				/*
    502 			 	* update next_frame_timestamp only if it falls within the tolerance range
    503 			 	*/
    504 				if (timestamp_frame_array >= fm->next_frame_timestamp - tolerance)
    505 				{
    506 					fm->next_frame_timestamp = timestamp_frame_array
    507 							+ fm->frame_timestamp_delta;
    508 				}
    509 			}
    510 
    511 		} else {
    512 
    513 			/*
    514 			 * is discontinuity flag set for this frame ?
    515 			 */
    516 			gboolean discontinuity = FALSE;
    517 			ret = mix_videoframe_get_discontinuity(mvf, &discontinuity);
    518 			if (ret != MIX_RESULT_SUCCESS) {
    519 				goto cleanup;
    520 			}
    521 
    522 			/*
    523 			 * If this is a frame with discontinuity flag set, clear frame_array
    524 			 * and treat the frame as the first frame.
    525 			 */
    526 			if (discontinuity) {
    527 
    528 				guint len = fm->frame_array->len;
    529 				if (len) {
    530 					guint idx = 0;
    531 					MixVideoFrame *frame = NULL;
    532 					for (idx = 0; idx < len; idx++) {
    533 						frame = (MixVideoFrame *) g_ptr_array_index(
    534 								fm->frame_array, idx);
    535 						if (frame) {
    536 							mix_videoframe_unref(frame);
    537 							g_ptr_array_index(fm->frame_array, idx) = NULL;
    538 						}
    539 					}
    540 				}
    541 
    542 				fm->is_first_frame = TRUE;
    543 				goto first_frame;
    544 			}
    545 
    546 			/*
    547 			 * handle variable frame rate:
    548 			 * display any frame which time stamp is less than current one.
    549 			 *
    550 			 */
    551 			guint64 tolerance = fm->frame_timestamp_delta / 4;
    552 			MixVideoFrame *frame_from_array = NULL;
    553 			guint64 timestamp_frame_array = 0;
    554 
    555 			while ((frame_from_array = get_expected_frame_from_array(
    556 					fm->frame_array, timestamp, tolerance,
    557 					&timestamp_frame_array)))
    558 			{
    559 				g_queue_push_tail(fm->frame_queue, (gpointer) frame_from_array);
    560 
    561 				/*
    562 			 	* update next_frame_timestamp only if it falls within the tolerance range
    563 			 	*/
    564 				if (timestamp_frame_array >= fm->next_frame_timestamp - tolerance)
    565 				{
    566 					fm->next_frame_timestamp = timestamp_frame_array
    567 							+ fm->frame_timestamp_delta;
    568 				}
    569 			}
    570 			/*
    571 			 * this is not the expected frame, put it into frame_array
    572 			 */
    573 
    574 			add_frame_into_array(fm->frame_array, mvf);
    575 		}
    576 	}
    577 	cleanup:
    578 
    579 	return ret;
    580 }
    581 
    582 MIX_RESULT mix_framemanager_frametype_based_enqueue(MixFrameManager *fm,
    583 		MixVideoFrame *mvf) {
    584 
    585 	MIX_RESULT ret = MIX_RESULT_FAIL;
    586 	MixFrameType frame_type;
    587 	guint64 timestamp = 0;
    588 
    589 	ret = mix_videoframe_get_frame_type(mvf, &frame_type);
    590 	if (ret != MIX_RESULT_SUCCESS) {
    591 		goto cleanup;
    592 	}
    593 
    594 	ret = mix_videoframe_get_timestamp(mvf, &timestamp);
    595 	if (ret != MIX_RESULT_SUCCESS) {
    596 		goto cleanup;
    597 	}
    598 
    599 #ifdef MIX_LOG_ENABLE
    600 	if (frame_type == TYPE_I) {
    601 		LOG_I( "TYPE_I %"G_GINT64_FORMAT"\n", timestamp);
    602 	} else if (frame_type == TYPE_P) {
    603 		LOG_I( "TYPE_P %"G_GINT64_FORMAT"\n", timestamp);
    604 	} else if (frame_type == TYPE_B) {
    605 		LOG_I( "TYPE_B %"G_GINT64_FORMAT"\n", timestamp);
    606 	} else {
    607 		LOG_I( "TYPE_UNKNOWN %"G_GINT64_FORMAT"\n", timestamp);
    608 	}
    609 #endif
    610 
    611 	if (fm->is_first_frame) {
    612 		/*
    613 		 * The first frame is not a I frame, unexpected!
    614 		 */
    615 		if (frame_type != TYPE_I) {
    616 			goto cleanup;
    617 		}
    618 
    619 		g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    620 		fm->is_first_frame = FALSE;
    621 	} else {
    622 
    623 		/*
    624 		 * I P B B P B B ...
    625 		 */
    626 		if (frame_type == TYPE_I || frame_type == TYPE_P) {
    627 
    628 			if (fm->p_frame) {
    629 
    630 				ret = mix_videoframe_set_timestamp(fm->p_frame,
    631 						fm->prev_timestamp);
    632 				if (ret != MIX_RESULT_SUCCESS) {
    633 					goto cleanup;
    634 				}
    635 
    636 				g_queue_push_tail(fm->frame_queue, (gpointer) fm->p_frame);
    637 				fm->p_frame = NULL;
    638 			}
    639 
    640 			/* it is an I frame, push it into the out queue */
    641 			/*if (frame_type == TYPE_I) {
    642 
    643 			 g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    644 
    645 			 } else*/
    646 			{
    647 				/* it is a P frame, we can not push it to the out queue yet, save it */
    648 				fm->p_frame = mvf;
    649 				fm->prev_timestamp = timestamp;
    650 			}
    651 
    652 			ret = MIX_RESULT_SUCCESS;
    653 
    654 		} else {
    655 			/* it is a B frame, replace the timestamp with the previous one */
    656 			if (timestamp > fm->prev_timestamp) {
    657 				ret = mix_videoframe_set_timestamp(mvf, fm->prev_timestamp);
    658 				if (ret != MIX_RESULT_SUCCESS) {
    659 					goto cleanup;
    660 				}
    661 
    662 				/* save the timestamp */
    663 				fm->prev_timestamp = timestamp;
    664 			}
    665 			g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    666 			ret = MIX_RESULT_SUCCESS;
    667 		}
    668 	}
    669 
    670 	cleanup:
    671 
    672 	return ret;
    673 }
    674 
    675 MIX_RESULT mix_framemanager_enqueue(MixFrameManager *fm, MixVideoFrame *mvf) {
    676 
    677 	MIX_RESULT ret = MIX_RESULT_FAIL;
    678 
    679 	/*fm->mode = MIX_FRAMEORDER_MODE_DECODEORDER;*/
    680 
    681 	if (!mvf) {
    682 		return MIX_RESULT_INVALID_PARAM;
    683 	}
    684 
    685 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    686 		return MIX_RESULT_INVALID_PARAM;
    687 	}
    688 
    689 	if (!fm->initialized) {
    690 		return MIX_RESULT_NOT_INIT;
    691 	}
    692 
    693 	/*
    694 	 * This should never happen!
    695 	 */
    696 	if (fm->mode != MIX_FRAMEORDER_MODE_DISPLAYORDER && fm->mode
    697 			!= MIX_FRAMEORDER_MODE_DECODEORDER) {
    698 		return MIX_RESULT_FAIL;
    699 	}
    700 
    701 	g_mutex_lock(fm->lock);
    702 
    703 	ret = MIX_RESULT_SUCCESS;
    704 	if (fm->mode == MIX_FRAMEORDER_MODE_DECODEORDER) {
    705 		/*
    706 		 * decode order mode, push the frame into output queue
    707 		 */
    708 		g_queue_push_tail(fm->frame_queue, (gpointer) mvf);
    709 
    710 	} else {
    711 
    712 		if (fm->timebased_ordering) {
    713 			ret = mix_framemanager_timestamp_based_enqueue(fm, mvf);
    714 		} else {
    715 			ret = mix_framemanager_frametype_based_enqueue(fm, mvf);
    716 		}
    717 	}
    718 
    719 	g_mutex_unlock(fm->lock);
    720 
    721 	return ret;
    722 }
    723 
    724 MIX_RESULT mix_framemanager_dequeue(MixFrameManager *fm, MixVideoFrame **mvf) {
    725 
    726 	MIX_RESULT ret = MIX_RESULT_FAIL;
    727 
    728 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    729 		return MIX_RESULT_INVALID_PARAM;
    730 	}
    731 
    732 	if (!mvf) {
    733 		return MIX_RESULT_INVALID_PARAM;
    734 	}
    735 
    736 	if (!fm->initialized) {
    737 		return MIX_RESULT_NOT_INIT;
    738 	}
    739 
    740 	g_mutex_lock(fm->lock);
    741 
    742 	ret = MIX_RESULT_FRAME_NOTAVAIL;
    743 	*mvf = (MixVideoFrame *) g_queue_pop_head(fm->frame_queue);
    744 	if (*mvf) {
    745 		ret = MIX_RESULT_SUCCESS;
    746 	} else if (fm->eos) {
    747 		ret = MIX_RESULT_EOS;
    748 	}
    749 
    750 	g_mutex_unlock(fm->lock);
    751 
    752 	return ret;
    753 }
    754 
    755 MIX_RESULT mix_framemanager_eos(MixFrameManager *fm) {
    756 
    757 	MIX_RESULT ret = MIX_RESULT_FAIL;
    758 
    759 	if (!MIX_IS_FRAMEMANAGER(fm)) {
    760 		return MIX_RESULT_INVALID_PARAM;
    761 	}
    762 
    763 	if (!fm->initialized) {
    764 		return MIX_RESULT_NOT_INIT;
    765 	}
    766 
    767 	g_mutex_lock(fm->lock);
    768 
    769 	fm->eos = TRUE;
    770 
    771 	g_mutex_unlock(fm->lock);
    772 
    773 	return ret;
    774 }
    775 
    776