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, ×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 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, ×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 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 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