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 Intel’s 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 
mix_framemanager_init(MixFrameManager * self)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 
mix_framemanager_class_init(MixFrameManagerClass * klass)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 
mix_framemanager_new(void)59 MixFrameManager *mix_framemanager_new(void) {
60 	MixFrameManager *ret = g_object_new(MIX_TYPE_FRAMEMANAGER, NULL);
61 
62 	return ret;
63 }
64 
mix_framemanager_finalize(GObject * obj)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 
mix_framemanager_ref(MixFrameManager * fm)84 MixFrameManager *mix_framemanager_ref(MixFrameManager * fm) {
85 	return (MixFrameManager *) g_object_ref(G_OBJECT(fm));
86 }
87 
88 /* MixFrameManager class methods */
89 
mix_framemanager_initialize(MixFrameManager * fm,MixFrameOrderMode mode,gint framerate_numerator,gint framerate_denominator,gboolean timebased_ordering)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 }
mix_framemanager_deinitialize(MixFrameManager * fm)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 
mix_framemanager_set_framerate(MixFrameManager * fm,gint framerate_numerator,gint framerate_denominator)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 
mix_framemanager_get_framerate(MixFrameManager * fm,gint * framerate_numerator,gint * framerate_denominator)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 
mix_framemanager_get_frame_order_mode(MixFrameManager * fm,MixFrameOrderMode * mode)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 
mix_framemanager_flush(MixFrameManager * fm)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 
get_expected_frame_from_array(GPtrArray * array,guint64 expected,guint64 tolerance,guint64 * frametimestamp)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 
add_frame_into_array(GPtrArray * array,MixVideoFrame * mvf)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 
mix_framemanager_timestamp_based_enqueue(MixFrameManager * fm,MixVideoFrame * mvf)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 
mix_framemanager_frametype_based_enqueue(MixFrameManager * fm,MixVideoFrame * mvf)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 
mix_framemanager_enqueue(MixFrameManager * fm,MixVideoFrame * mvf)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 
mix_framemanager_dequeue(MixFrameManager * fm,MixVideoFrame ** mvf)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 
mix_framemanager_eos(MixFrameManager * fm)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