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
17static GObjectClass *parent_class = NULL;
18
19static void mix_framemanager_finalize(GObject * obj);
20G_DEFINE_TYPE( MixFrameManager, mix_framemanager, G_TYPE_OBJECT);
21
22static 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
50static 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
59MixFrameManager *mix_framemanager_new(void) {
60	MixFrameManager *ret = g_object_new(MIX_TYPE_FRAMEMANAGER, NULL);
61
62	return ret;
63}
64
65void 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
84MixFrameManager *mix_framemanager_ref(MixFrameManager * fm) {
85	return (MixFrameManager *) g_object_ref(G_OBJECT(fm));
86}
87
88/* MixFrameManager class methods */
89
90MIX_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}
157MIX_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
191MIX_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
218MIX_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
243MIX_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
264MIX_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
319MixVideoFrame *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
388void 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
418MIX_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
582MIX_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
675MIX_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
724MIX_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
755MIX_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