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, ×tamp)
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, ×tamp);
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 ×tamp_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 ×tamp_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, ×tamp);
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