/*************************************************************************** * Copyright (c) 2002-2010, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: bcmplayer.c $ * $brcm_Revision: 54 $ * $brcm_Date: 6/21/10 4:31p $ * * Module Description: Transport Stream Index Player * * Revision History: * * Created: 02/09/2001 by Marcus Kellerman * * $brcm_Log: /BSEAV/lib/bcmplayer/src/bcmplayer.c $ * * 54 6/21/10 4:31p erickson * SW7405-4249: added BNAV_Version_TimestampOnly option for indexing * audio-only or scrambled streams * * 53 6/3/10 3:53p erickson * SW7400-2788: clean up BNAV_Player_FindIFrameFromIndex to avoid extra * logic * * 52 5/18/10 11:53a erickson * SW7405-4193: back out previous change * * 50 4/5/10 3:58p erickson * SW7405-4131: add bounds check when trying to skip resending the I frame * * 49 3/30/10 11:40a gmohile * SW7405-4079 : Check if nav file supports GOP trick modes before * trickplay * * 48 3/29/10 3:06p erickson * CDSTRMANA-294: re-add 0xb5 extension to dummy picture for field-encoded * MPEG2 brcm trick modes. * * 47 3/17/10 10:21a erickson * CDSTRMANA-294: add MSG for DISPLAY_PAST_BUFFER * * 46 1/8/10 11:02a erickson * SW7405-3041: fix overflow possibility * * 45 11/10/09 3:12p erickson * SW7400-2596: ensure that the first or last I frame is displayed when * doing an I frame trick mode with no looping * * 44 11/4/09 1:15p erickson * SW7400-2591: SkipP for AVC is not allowed * * 43 9/29/09 1:27p erickson * SW7405-3041: rework timestamp and file offset interpolation algos so * that overflow is not possible * * 42 9/4/09 3:47p erickson * SW7405-2974: fix off-by-one error in backward * BNAV_Player_FindIndexFromPts search * * 41 8/28/09 8:11p mphillip * SWDEPRECATED-3998: If index file has a non-zero lower bound, don't try * to read from before the start of file * * 40 7/30/09 12:12p jtna * PR54129: suppress error message in index lookup that are normal due to * race condition with trim * * 39 7/23/09 11:46a ahulse * PR56928: fix compiler warning * * 38 7/22/09 1:56p ahulse * PR56928: Allow user to set loose checking of index file in normal * playback * * 37 7/13/09 10:47a ahulse * PR56762: Report if B frame or not * * 36 6/25/09 11:00a erickson * PR56358: BNAV_Player_FindIndexFromPts should not update its cached read * ptr if the search fails * * 35 6/6/09 12:21p ahulse * PR55770: For field encoded streams return 1st field in frame not 2nd * * 34 6/4/09 4:03p erickson * PR54129: change DEFAULT_NORMAL_PLAY_BUFFER_SIZE to multiple of 188 and * 4096 * * 33 2/25/09 4:48p erickson * PR52471: added const keyword * * 32 1/27/09 9:05a erickson * PR51468: make global symbols static * * 31 4/9/08 4:07p erickson * PR41567: don't allow bcmplayer to run without a non-zero video pid * * 30 4/2/08 9:33a erickson * PR41171: add typecast to fix warning. fix misspelling. * * 29 3/27/08 2:28p ahulse * PR27566: Handle interpolated frames in host trick mode, stop searching * when close match * * 28 3/20/08 1:15p ahulse * PR40792: Fix loopback freeze in BCM trickmode * * 27 2/11/08 2:32p gmohile * PR 38980 : Use RAI as starting point for AVC DQT * * 26 1/3/08 6:21p erickson * PR36068: switch B_HAS_RAVE to opt-out. all new chips have RAVE. if you * are using a legacy chip with latest bcmplayer, please add your * BCHP_CHIP. * * 25 12/28/07 12:07p erickson * PR37038: any failure in BNAV_Player_GetNextPlayEntry needs to cause the * function to fail. this is needed for continuous record where the * beginning could be trimmed. also fixed infinite loop off-by-one bug in * BNAV_Player_FindIndexFromPts. * * 24 12/17/07 1:44p katrep * PR37217: Added 7335 support * * 23 12/6/07 10:24a ahulse * PR36547: On file trim, ensure we jump to next I frame * * 22 10/25/07 1:41p ahulse * PR36192: Account for file trimming when in continuous record mode, * AdjustCurrentIndex * * 21 10/22/07 5:23p vishk * PR 35237: SettopAPI-Coverity (CID 512): UNUSED_VALUE, * * 20 10/12/07 12:23p vishk * PR 35237: SettopAPI-Coverity (CID 512): UNUSED_VALUE, * * 19 10/10/07 10:52a btosi * PR30310: renamed TT_MODE_GENERIC_EOS to TT_MODE_INLINE_FLUSH * * 18 10/9/07 3:45p btosi * PR30310: modified BNAV_Player_AdvanceIndex() to use the new commands * TT_MODE_GENERIC_EOS and TT_MODE_PIC_OUTPUT_COUNT * * 17 10/4/07 6:58p vishk * PR 35280: SettopAPI-Coverity (CID 326): DEADCODE, * * 15 7/18/07 12:00p erickson * PR30310: implement skipGOP feature for eBpPlayDecoderGOPTrick * * 14 7/16/07 5:12p erickson * PR30310: don't allow eBpPlayDecoderGOPTrick with playModeModifier == 0 * * 13 7/16/07 4:46p erickson * PR30310: fixed support for field-encoded AVC streams for DQT * * 12 6/20/07 1:13p erickson * PR30310: convert to BNAV_Player_GenerateStartCodePacket and use for * EOS, SeqEnd as part of DQT impl. * * 11 6/12/07 10:46a erickson * PR30310: fix eBpPlayDecoderGOPTrick loop around at BOF * * 10 5/22/07 12:46p erickson * PR24374: improve BDBG_MSG for typical debug * * 9 4/30/07 1:32p erickson * PR30310: added eBpPlayDecoderGOPTrick * * 8 2/9/07 2:03p ahulse * PR27136: Increase max fifo entries to accomadate a GOP size up to 60 * * 7 2/9/07 11:57a katrep * PR26647: Rave has improved flush.Taken out the empty BPT packet * insertion workaround added in ver 5. * * 6 2/7/07 11:12a ahulse * PR27362: Downgrade near match PTS warning to a message * * 5 1/24/07 1:22p erickson * PR27274: send a harmless PROCESS BTP through the system so any pending * PROCESS is nullified. this is a workaround which appears 100% * effective. it should be removed when the root cause is understood. * * 4 11/30/06 6:36p erickson * PR26095: add eBpPlayNormalByFrames for IP settop support * * 3 11/30/06 12:03p erickson * PR26095: removed unused NEW_PLAY_MODE with normalForward state * variable. This was a modification of brcm trick modes with advance * count 1. It would confuse the feature about to go in. * * 2 11/27/06 1:59p erickson * PR25109: added 7403/7405 support * * Irvine_BSEAVSW_Devel/163 10/5/06 3:34p mward * PR21671: 7118 B_HAS_RAVE * * Irvine_BSEAVSW_Devel/162 9/12/06 5:59p ahulse * PR22600, PR22661: Increased PTS_CACHE_SIZE, reworked search algorithm * to avoid lengthy searches, interpolate PTS's if needed * * Irvine_BSEAVSW_Devel/161 8/14/06 2:00p ahulse * PR23214: use sizeof dummy_picture to calculate correct offsets * * Irvine_BSEAVSW_Devel/160 8/14/06 1:26p ahulse * PR23214: Remove picture coding extension when sending dummy picture * * Irvine_BSEAVSW_Devel/158 6/30/06 2:52p erickson * PR21941: fix warning * * Irvine_BSEAVSW_Devel/157 5/11/06 3:29p mphillip * PR20797: Add boundary shortcuts to IndexFromTimestamp to avoid lengthy * linear searches * * Irvine_BSEAVSW_Devel/156 5/4/06 4:47p erickson * PR21422: added improved debug for bad streams, bad PTS's * * Irvine_BSEAVSW_Devel/155 4/4/06 2:36p ahulse * PR20249: Clear needsDisplayPicture flag if we can't find entry in * index. Otherwise loops forever. * * Irvine_BSEAVSW_Devel/154 3/28/06 3:42p mphillip * PR19786: Handle version detection on trimmed files (mainline) * * Irvine_BSEAVSW_Devel/153 3/24/06 10:29a erickson * PR20220: added additional condition (I frame trick mode) to InsertEOS * test. * * Irvine_BSEAVSW_Devel/152 3/21/06 6:09p ahulse * PR18725: Put back in insertion of EOS before I frames * * Irvine_BSEAVSW_Devel/151 3/21/06 11:48a erickson * PR19848: use BTP's for host trick modes on 740x * * Irvine_BSEAVSW_Devel/150 3/17/06 2:00p erickson * PR19848: don't use BTP's for host trick modes on 740x yet because of * decoder hang * * Irvine_BSEAVSW_Devel/149 3/16/06 12:52p erickson * PR19848: added 740x BTP support * * Irvine_BSEAVSW_Devel/148 3/8/06 4:51p erickson * PR19853: fix off by one for VC1 * * Irvine_BSEAVSW_Devel/147 3/6/06 1:20p erickson * PR19853: added VC1 PES support * * Irvine_BSEAVSW_Devel/146 2/24/06 11:21a dlwin * PR 19611: Remove specific code for 7411D0, treat 7411D0 as a 7411C0. * * Irvine_BSEAVSW_Devel/145 2/15/06 4:54p ahulse * PR19373: No longer zero out data in packet, rely on BTP info for data * trim. * * Irvine_BSEAVSW_Devel/144 2/1/06 5:30p ahulse * PR17951: change bounds checking in * BNAV_Player_FindIndexFromOffset/Timestamp to be >= instead of > * * Irvine_BSEAVSW_Devel/143 1/26/06 12:29p rjlewis * PR18408: must declare storage at top with old C compilers. * * Irvine_BSEAVSW_Devel/142 1/18/06 5:35p ahulse * PR18408, PR18725:: Insert a valid PTS when adding SequenceHeader. Don't * send EOS before I frame in forward AVC trickmodes * * Irvine_BSEAVSW_Devel/140 1/8/06 10:36a dlwin * PR 18408: Corrected for incorrect PTS values. Checkin for Adrian, due * to user priivilege problems. * * Irvine_BSEAVSW_Devel/139 11/30/05 3:07p vsilyaev * PR 18183: Improved error reporting * * Irvine_BSEAVSW_Devel/138 9/14/05 2:24p erickson * PR17148: converted to BCHP_7411_REV * * Irvine_BSEAVSW_Devel/137 9/6/05 11:06a erickson * PR16794: BTP needs to include whole first packet only for legacy * platforms now * * Irvine_BSEAVSW_Devel/136 9/2/05 11:52a erickson * PR15861: use BTP's for host trick modes on 7038 * * Irvine_BSEAVSW_Devel/135 8/30/05 2:17p erickson * PR16208: for field encoded streams, don't send adjacent I pictures * * Irvine_BSEAVSW_Devel/134 8/26/05 3:48p erickson * PR16794: Use BTP's on 7411 host trick modes to trim last packet, but * not first packet, for a picture. This allows PES headers to get * through. Also, for PR 16803 I changed the logic on inserting EOS NAL * headers. They are now inserted before every I picture instead of after * every picture. * * Irvine_BSEAVSW_Devel/133 8/25/05 1:34p erickson * PR16208: make AVC EOS insertion permanent, and fix MPEG2 TS header for * EOS packet * * Irvine_BSEAVSW_Devel/132 8/19/05 3:54p erickson * PR16208: added EOS NAL unit after each frame during AVC trick modes * * Irvine_BSEAVSW_Devel/130 8/3/05 12:07p erickson * PR16138: improve comments, restore skip_count on BUILD_REFERENCE for * 7411 * * Irvine_BSEAVSW_Devel/129 7/18/05 11:35a erickson * PR16205: form correct PROCESS BTP's for 7411c and d * * Irvine_BSEAVSW_Devel/128 7/15/05 10:07p erickson * PR16138: added AVC support for SPS and PPS, refactored to allow more * shared code, implemented BTP's for 7411 host trick modes, reworked * GenerateDummyPicture in endian-neutral way * * Irvine_BSEAVSW_Devel/127 7/13/05 3:05p erickson * PR16207: added 7411D support * * Irvine_BSEAVSW_Devel/126 7/13/05 2:10p erickson * PR16138: added new NAV version for AVC support, PPS and SPS support * added, refactored player to better support this with less code * duplication * * Irvine_BSEAVSW_Devel/125 7/8/05 8:49a erickson * PR15861: added check for sending seqhdr for BTP-based host trick modes * * Irvine_BSEAVSW_Devel/124 7/6/05 4:18p erickson * PR16094: modified 7411C impl to use NULL packet before data when * data_end_packet is 1 or 3. This allows both data_start_byte and * data_end_byte to be used by hardware. * * Irvine_BSEAVSW_Devel/123 7/6/05 2:21p erickson * PR16102: increase PTS_CACHE_SIZE to support SD AVC in HD sized buffers. * Also improved debug messages. * * Irvine_BSEAVSW_Devel/122 7/1/05 5:55p erickson * PR15443: fixed non-7411c build * * Irvine_BSEAVSW_Devel/121 7/1/05 4:33p erickson * PR15443: fixed i386 compilation warnings * * Irvine_BSEAVSW_Devel/120 7/1/05 10:52a erickson * PR15443: Setting PTS based on each frame that is decoded. Firmware is * now reading PTS and this is required in order to get out correct * current PTS from decoder. * * Irvine_BSEAVSW_Devel/119 6/13/05 12:21p erickson * PR15849: set zeroByteCountBegin after adjusting for possible seqhdr * * Irvine_BSEAVSW_Devel/118 6/8/05 1:37p erickson * PR15126: converted BNAV_Player_FindIndexFromPts to use a cache of * recently sent pictures and their PTS values. No longer perform an * index search. * * Irvine_BSEAVSW_Devel/117 4/26/05 2:07p erickson * PR15052: removed PICTURE_START/END_PACKET/BYTE because they are unused. * Made usage of DISCARD_TAILEND consistent. Use SKIP_COUNT=0 for * BUILD_REFERNECE because it was incorrect and unused anyway. * * Irvine_BSEAVSW_Devel/116 3/9/05 12:29p erickson * PR13202: fixed zeroByteCountEnd logic for 7411C * * Irvine_BSEAVSW_Devel/115 3/8/05 3:43p erickson * PR13202: refactored common 7411C code into SendData, and added seqhdr * support * * Irvine_BSEAVSW_Devel/114 3/8/05 11:07a erickson * PR13202: commented out BCM7411C_BTP_FORMAT * * Irvine_BSEAVSW_Devel/113 3/7/05 9:39a erickson * PR13202: inc DATA_END_PACKET if value is 1 or 3, then add a dummy * packet * * Irvine_BSEAVSW_Devel/112 3/4/05 4:17p erickson * PR13202: modified 7411C_BTP_FORMAT based on new requirements from 7411 * * Irvine_BSEAVSW_Devel/111 3/3/05 12:52p erickson * PR8348: refactored BNAV_Player_AddFrameToFifo to support two things: * 1) interruption of the BuildReferenceFrame process - this allows a * smaller size FIFO to be used * for a large GOP. If the FIFO fills up, the function returns and resumes * its work later. * 2) subdivision of the GOP into multiple reference frames - this allows * large GOP streams to be * processed without a huge bandwidth spike on the trailing frames in the * GOP. * The main change was to change BNAV_Player_AddFrameToFifo so that it * doesn't recurse in order to * build a reference frame. There's now a separate function for that, and * so there isn't all the * extra conditionals to handle this. This makes handling these new * features manageable. * The remaining BNAV_Player_AddFrameToFifo function is only used to build * displayed frames, and it * can't be interrupted because the various optimizations are more * complex. * There are two new #define options for controlling this new behavior: * MAX_REFERENCE_FRAME_OFFSET * MAX_NUM_FIFO_ENTRIES-8 is used for max size (probably need another * #define here) * * Irvine_BSEAVSW_Devel/110 2/28/05 9:37a erickson * PR13202: added NEW_BTP_FORMAT support, defaulted off * * Irvine_BSEAVSW_Devel/109 2/18/05 12:38p erickson * PR14180: handle endianness of dummy picture BTP's * * Irvine_BSEAVSW_Devel/108 2/3/05 3:19p erickson * PR14017: must factor in timestampOffset when calculating BTP params * using packetSize * * Irvine_BSEAVSW_Devel/107 2/3/05 1:36p erickson * PR14017: fix for 192 byte packets * * Irvine_BSEAVSW_Devel/106 11/30/04 4:07p erickson * PR13303: commented out debug code * * Irvine_BSEAVSW_Devel/105 11/30/04 4:05p erickson * PR13303: added useReferenceFrame setting, defaulting to true, which * allows reduced memory/reduced performance mode * * Irvine_BSEAVSW_Devel/104 8/19/04 1:48p erickson * PR11582: brcm trick modes with mode_modified ==1 are invalid. fail it. * * Irvine_BSEAVSW_Devel/103 5/10/04 11:33a erickson * PR10065: fixed compiler warnings (most were unsigned/signed mismatches) * * Irvine_BSEAVSW_Devel/102 4/30/04 12:57p erickson * PR10909: default to magnum kernelinterface (debug or release) but * support legacy if specifically requested * * Irvine_BSEAVSW_Devel/101 4/14/04 5:03p erickson * PR10065: if BNAV_Player_AddNormalPlay fails, don't leave bad fifo entry * in the fifo * * Irvine_BSEAVSW_Devel/100 4/13/04 4:57p erickson * PR10292: allow sanity check to be skipped, also cleaned up some * compiler warnings and debug messages * * Irvine_BSEAVSW_Devel/99 3/18/04 3:38p erickson * PR10111: Null out bounds callback if SetBounds is called. Also NULL'd * handle when open fails and verified direction parameter is always 1/-1 * in a search algorithm. * * Irvine_BSEAVSW_Devel/98 1/23/04 12:20p bandrews * PR8956: Bad frame caused by missing sequence header just before short I * frame. * * Irvine_BSEAVSW_Devel/97 12/8/03 11:32a erickson * PR8879: added BNAV_Player_GetPositionInformation. doesn't impact * existing api. * * Irvine_BSEAVSW_Devel/96 11/10/03 2:33p erickson * PR8563: added transport timestamp support to bcmplayer * * Irvine_BSEAVSW_Devel/95 11/10/03 2:29p erickson * PR8563: added transport timestamp support to bcmplayer * * Irvine_BSEAVSW_Devel/94 10/8/03 4:25p erickson * need to rely on brcm_t.h for uint64_t. No more platform-specific * typedefs. * * Irvine_BSEAVSW_Devel/93 9/17/03 12:11p erickson * support both original and magnum debug interfaces * replaced kernel interface calls with ansi calls * * Irvine_BSEAVSW_Devel/92 9/8/03 5:01p erickson * added sanity check * this makes sure we're not trying to play completely corrupted, wrong- * endianness indexes * * Irvine_BSEAVSW_Devel/91 8/26/03 5:02p erickson * normal play should still feed in 188 byte chunks because of other * dependencies throughout the system * correct skip count for build reference. the firmware isn't checking it * anyway, but we wanted it to * mimic the skip count for decode. * * Irvine_BSEAVSW_Devel/90 8/25/03 12:30p erickson * Version 0 indexes need to fail. For a short time there were version 0 * indexes being produced and not getting labelled. False detects are * bad, and I don't want to leave the auto-detect code in there long- * term. So it's best to just fail them. You really shouldn't run version * 0 anyway because it's very inefficient. * I also fixed the skip count for BUILD_REFERENCE. * * Irvine_BSEAVSW_Devel/89 8/25/03 11:26a erickson * DISPLAY_PAST_BUFFER doesn't work with old index formats * * Irvine_BSEAVSW_Devel/88 8/22/03 9:22a erickson * fixed normal play * * Irvine_BSEAVSW_Devel/87 8/8/03 10:25a erickson * fixed variable declaration in middle of source * * Irvine_BSEAVSW_Devel/86 7/23/03 9:22a erickson * now setting or clearing the discontinuity_indicator in BTP packets (PR * 6380) * * Irvine_BSEAVSW_Devel/85 6/24/03 11:03a erickson * PR7218 - handled partially encrypted streams. Added maxFrameSize and * mpegSizeCallback. Had to change the * NAV table format (version 2) in order to handle I frames with reference * offset of 0. Bcmplayer * is backward compatible. * * Irvine_BSEAVSW_Devel/84 5/27/03 11:10a erickson * zeroByteCountBegin/End must be set to 0 for BTP packets * zeroByteCountBegin/End of 188 should be 0 * * Irvine_BSEAVSW_Devel/83 5/19/03 5:47p erickson * Fixed logic for zeroByteCountEnd. * * Irvine_BSEAVSW_Devel/82 5/19/03 1:16p marcusk * Specify how many bytes must be zeroed when performing trick modes and * frames are not aligned to transport packet boundaries. * * Irvine_BSEAVSW_Devel/81 4/25/03 1:48p erickson * used brcm_t.h for uint64_t * * Irvine_BSEAVSW_Devel/80 4/23/03 2:11p erickson * enabled DISPLAY_PAST_BUFFER and DUMMY_PICTURE optimizations for 7xxx * platforms. * * Irvine_BSEAVSW_Devel/79 4/23/03 10:07a erickson * win32 uint64_t definition, resolved warnings * * Irvine_BSEAVSW_Devel/78 4/22/03 5:16p erickson * New optimizations working, but not released yet * * Irvine_BSEAVSW_Devel/77 4/9/03 1:12p erickson * no video pid is allowed, but don't allow broadcom trick modes * * Irvine_BSEAVSW_Devel/76 4/4/03 1:41p erickson * converted bcmplayer to use uint64_t directly * * Irvine_BSEAVSW_Devel/75 3/31/03 2:42p erickson * added DUMMY_PICTURE_OPTIMIZATION, but left it commented out. * * Irvine_BSEAVSW_Devel/74 3/11/03 4:00p erickson * fixed debug interface usage * * Irvine_BSEAVSW_Devel/73 2/28/03 5:21p erickson * Normal play wraps now backup to the seqhdr and ts-packet * When doing trick modes after setCurrentIndex, we have to validate the * frame type and * can't completely skip the next advance. * * Irvine_BSEAVSW_Devel/71 2/14/03 2:57p erickson * fixed DISPLAY_PAST_BUFFER_OPTIMIZATION * * Irvine_BSEAVSW_Devel/70 2/14/03 1:57p erickson * New naming convention * Fixed looping from previous rework * Removed bcmindexer _getParam/_setParam * Did not change code shared between tsplayer and bcmplayer * Refactored settings, playmode and other structures * * Irvine_BSEAVSW_Devel/69 2/12/03 11:42a erickson * commented out PRINT_PACKETTYPES_SENT * defaulted the debugMode * initialized the lastOffset variable * * Irvine_BSEAVSW_Devel/68 2/11/03 11:46a erickson * loopMode wasn't allowed to be changed for normal play * normal playback now loops * * Irvine_BSEAVSW_Devel/67 2/10/03 5:23p erickson * win32 compilation warnings * * Irvine_BSEAVSW_Devel/66 2/10/03 5:15p erickson * bcmplayer rework: * 1) version 2.02 * 2) removed getParam/setParam API * 3) added various get/set functions and specific algorithmic functions. * No more typecasting * or strange parameter passing needed. Each function has a specified * execution time. * 4) added run-time debug modes * 5) normal play is no longer frame based. this enables 60 frames/sec * content. * * Irvine_BSEAVSW_Devel/65 10/30/02 10:16a erickson * Made floating point support optional, defaulting off. Define HAVE_FLOAT * to turn it back on. * * Irvine_HDDemo_Devel/64 9/26/02 3:4p erickson * Changed bcmindexer vchip from 3 bits to 16 bits packed into 12 bits. * The index is NOT backward compatible, but the 3 bit version had only * been released for 1 day. * * Irvine_HDDemo_Devel/63 9/25/02 9:36a erickson * ExtraB optimization disabled by default because venom2 doesn't support * it. * When changing eBpPlayMode, if it remains in normal play, do nothing. * This fixes a bug when unpausing normal play. * * Irvine_HDDemo_Devel/62 9/20/02 11:58a erickson * Enabled EXTRA_B optimization. This works for minititan but not venom2 * currently. * Removed old STUB_LAST_IP code because it will never be used. * Removed generate_NULL code because it isn't needed. * Added eBpIsHits and bcmplayer_isHits() * Enabled eBpPlayIP for HITS as well as GOP-based streams * Modified bcmplayer_findPictureIndex to detect mismatched searches in * HITS and GOP-based streams and to prevent long and unproductive * searches. * * Irvine_HDDemo_Devel/61 9/9/02 11:52a erickson * added vchip support * * Irvine_HDDemo_Devel/60 8/15/02 2:1p swindell * Fix VxWorks bcmkernel.h capitalization mismatch. * * Irvine_HDDemo_Devel/59 8/14/02 10:22a erickson * disabled the EXTRA_B_OPTIMIZATION until firmware supports it * reorganized some #defines * * Irvine_HDDemo_Devel/58 8/12/02 10:24a erickson * Fixed some compilation typecast warnings when using C++ compiler * Fixed rewind algorithm so if there's no skip frames in DISPLAY_REV, it * will resend the reference frame. * A few changes to the stub-frame code, which still doesn't work. * Fixed IndexFromPTS algorithm for adjacent I/P frames. * * Irvine_HDDemo_Devel\57 7/8/02 10:57a erickson * during 1x rewind, combine adjacent B's into single DISPLAY sequence. * 18% throughput savings. * Also found I was sending an extra I during rewind, so that's another 7 * %. * Total optimization from original algorithm: 50% * * Irvine_HDDemo_Devel\55 7/2/02 4:59p erickson * Updated to version 0201. This required bcmindexer version >= 0201. * Accelerates broadcom trick modes for GOP-based streams by 25% by using * a single I frame as the reference frame and then sending only what's * necessary. * * Irvine_HDDemo_Devel\54 6/19/02 4:1p erickson * complete rework of IndexFromPts algorithm. Works MUCH better. * Removed prevFrameEnd state variable. * Changed logic for sending sequence header on normal playback. * Made some macros to cleanup code. * * Irvine_HDDemo_Devel\52 6/17/02 10:9a erickson * fixed compiler warnings * * Irvine_HDDemo_Devel\51 5/17/02 9:47a marcusk * Added "" around BDBG_MODULE to support new debug routines. * * Irvine_HDDemo_Devel\50 4/17/02 1:24p erickson * Removed skip opengop b code (which never worked) * Added IndexFromTimestamp * Added readIndex() * Converted printf's to BDBG_MSG * Removed getPrevPlayEntry, which was the result of a misunderstanding of * customer requirements. * * Irvine_HDDemo_Devel\49 4/16/02 2:54p erickson * Removed unused variable * * Irvine_HDDemo_Devel\48 4/16/02 2:53p erickson * Implemented 2nd cache for IndexFromPts * Fixed IndexFromPts jitter problem * Convert get_frameOffset64 to macro * Added printCachePerformance * * \main\Irvine_HDDemo_Devel\45 3/15/02 6:35p erickson * Added #include for abs() on WIN32 * * \main\Irvine_HDDemo_Devel\44 3/15/02 6:31p erickson * Added skipNextAdvance so that trick modes would start on the I frame. * Removed some debug code. * * \main\Irvine_HDDemo_Devel\43 3/8/02 5:13p erickson * Removed #ifdef for SKIP_OPENGOP * Added some debug statements * Combined addForward and addFrame, and fixed a logic error * Added some code for new I Frame ref offset, but left it commented out. * * \main\Irvine_HDDemo_Devel\42 3/5/02 9:38a marcusk * Fixed VisualC++ compiler warnings. * * \main\Irvine_HDDemo_Devel\41 3/4/02 8:48p erickson * Implemented SkipB and SkipP Removed \n from debug statements Turned on * Skip Open GOP and USE_NEW_DISPLAY_MODES Fixed Skip Open GOP for trick * modes * * \main\Irvine_HDDemo_Devel\41 3/4/02 8:40p erickson * Removed unused members from structure Removed some redundant state * checking outside of setParam Fixed the skip open gop code to work for * play and trick modes Implemented SkipP and SkipB Removed \n from all * debug messages. * * \main\Irvine_HDDemo_Devel\40 2/27/02 12:35p erickson * Added PRINT_PACKETTYPES_SENT and PRINT_PACKETTYPES_ADDED debugging * modes. Added method to get fifo size. Removed PTS search threshold in * favor of first-fit algorithm. When setting PlayMode, I normalize the * mode, then check if there's really a change. When setting the PTS or * SCT, I seek to an I-frame, unless it's hits. No longer sending NULL * packet when flushing the fifo. It's not needed and doesn't belong * there. When playing normal, the startIndex shouldn't be incremented * until the first frame is sent, otherwise we lose the initial I frame. * Added SKIP_OPENGOP code, but left it commented out until later. Fixed * getLastSctIndex to return the correct index. * ***************************************************************************/ #ifndef USE_LEGACY_KERNAL_INTERFACE /* Magnum debug interface */ #include "bstd.h" #include "bchp.h" BDBG_MODULE(bcmplayer); #else /* Original debug interface */ #define BRCM_DBG_MODULE_NAME "bcmplayer" #include "brcm_dbg.h" #include "brcm_t.h" /* uint64_t */ #define BCHP_VER_C0 (0x00020000) /* emulate magnum */ #define BDBG_ERR(X) BRCM_DBG_ERR(X) #define BDBG_WRN(X) BRCM_DBG_WRN(X) #define BDBG_MSG(X) BRCM_DBG_MSG(X) #define BDBG_ASSERT(COND) \ do {if (!(COND)) BDBG_ERR(("BDBG_ASSERT failed on %d", __LINE__)); } while (0) #define BSTD_UNUSED(X) #endif #include "bcmindexer.h" #include "bcmindexerpriv.h" #include "bcmplayer.h" #include "mpeg2types.h" #include #include #include #define i_abs(X) ((X)<0?-(X):(X)) #define DEFAULT_CACHE_SIZE 100 #define BTP_PACKET_DATA_SIZE 10 #define MAX_NUM_FIFO_ENTRIES 250 /* set this to 4 x MAX GOP length if get "bcmplayer ERROR: fifo full!" errors */ #define MAX_REFERENCE_FRAME_OFFSET 30 #define DEFAULT_NORMAL_PLAY_BUFFER_SIZE (188*2*1024) /* enough to handle 720p, but should be tuned */ #define BDBG_MSG_PTSCACHE(X) /* BDBG_MSG(X) */ /* Set LOOSE_INDEX_CHECKING if you want bcmplayer to ignore certain errors found in the nav file. Disabled by default, as it is better to identify the source of the errors than ignore them. */ /* #define LOOSE_INDEX_CHECKING 1 */ /* PR 15861 - BTP's are used for host trick modes on 7038 (minititan+) and 7411 decoders. They are not supported on legacy platforms (minititan). */ #if BCHP_CHIP == 7320 || BCHP_CHIP == 7315 || BCHP_CHIP == 7110 || BCHP_CHIP == 7115 /* No RAVE. */ #else #define B_HAS_RAVE 1 #endif #if BCHP_CHIP == 7038 || B_HAS_RAVE #define USE_BTPS_FOR_HOST_TRICK_MODES #endif /** * There are numerous places where functions are called, and if they return * NULL, an error message is generated and the function returns immediately with * -1. This macro does that. **/ #define CHECKREAD(READ) \ if (!(READ)) { \ BDBG_ERR(("%s failed on line %d", #READ, __LINE__)); \ return -1; \ } #define CHECKREAD_SILENT(READ) \ if (!(READ)) { \ return -1; \ } typedef enum { eBpInsertNone = 0, /* No inserted packet. This is used for data that must be read from file. */ eBpInsertBTP, /* Broadcom Transport Packet, used for Broadcom trick modes and delimited data */ eBpInsertDummy, /* one packet picture, used for advancing the decoder pipeline but not being displayed */ eBpInsertNULL, /* NULL packet (pid 0x1fff) */ eBpInsertStartCode, /* insert packet with single start code */ TotalPacketTypes } eBpInsertPacketType; typedef struct BNAV_sPktFifoEntry { eBpInsertPacketType insertpackettype; unsigned long pktdata[BTP_PACKET_DATA_SIZE]; /* BTP payload */ uint64_t startByte; uint64_t endByte; unsigned long zeroByteCountBegin; unsigned long zeroByteCountEnd; unsigned startcode; struct BNAV_sPktFifoEntry *next; } BNAV_PktFifoEntry; struct BNAV_Player_HandleImpl { /* callbacks */ BP_READ_CB readCb; BP_TELL_CB tellCb; BP_SEEK_CB seekCb; BP_BOUNDS_CB boundsCb; void *filePointer; /* nav cache */ unsigned char *navCache; long navCacheIndexStart; long navCacheIndexEnd; /* nav cache metrics */ long navCacheMisses; long navCacheReads; long navCacheFails; /* nav cache parameters */ int navCacheSize; /* size in entries */ int navCacheBytes; /* size in bytes */ int navCacheOverlap; /* hardcoded to 1/4 of navCacheSize for now */ /* file format parameters */ long navFileIndexEntryStart; long navFileIndexEntrySize; /* size of each record in NAV table. Currently either sizeof(BNAV_Entry) or sizeof(BNAV_AVC_Entry) */ /* state */ eBpPlayModeParam playMode; eBpDirectionParam playDir; eBpLoopModeParam loopMode; long currentIndex; int skipCount; /* used for eBpPlaySkipB and eBpPlaySkipP */ int advanceCount; /* Number of frames to advance each picture (1=Normal) */ unsigned short pid; /* Current video PID */ char gotEntry; /* indicates that getNextEntry got an entry. */ long firstIndex; long lastIndex; BNAV_PktFifoEntry pktfifo[MAX_NUM_FIFO_ENTRIES]; long firstFifoEntry; long lastFifoEntry; BNAV_PktFifoEntry *pktfifo_tail; int curRefFrame; int skipNextAdvance; int iFrameRepeatCount; uint64_t lastSeqHdrOffset64; int disableExtraBOptimization; /* see note in playertypes.h */ BNAV_DecoderFeatures decoderFeatures; BNAV_Version navVersion; int useReferenceFrame; /* used for normal playback */ uint64_t currentOffset; unsigned long normalPlayBufferSize; BNAV_Player_DebugMode debugMode; int pastPred, futurePred; int timestampOffset; /* 0 or 4 */ unsigned packetSize; /* 188 or 192 */ /* Data needed to interrupt and resume BNAV_Player_BuildReferenceFrame_Process */ struct { int inProcess; /* if true, haven't finished reference frame */ int needDisplayPicture;/* if true, haven't sent display frame that necessitated the reference frame */ long curOffset; unsigned long lastFrameSize; long entry; } buildRef; /* array used to cache last displayed frames so that we can do efficient pts->index searches, even during fast trick modes. The value of PTS_CACHE_SIZE should be enough to hold all the pictures in even large playback and video FIFO's. 300 appears sufficient for MPEG2, but 600 is required for AVC. We need to handle SD decode in HD sized playback and decode fifos. */ #define PTS_CACHE_SIZE 1200 struct { uint32_t pts; long index; } ptscache[PTS_CACHE_SIZE]; int ptscache_wptr; /* index where next ptscache entry will be written */ int ptscache_rptr; /* index where we read ptscache entry from */ int doBackwardPtsSearch; /* If set search backwards instead of forwards */ /* used for eBpPlayDecoderGOPTrick */ struct { int currentStart, currentEnd; int pictureTag; int gopSkip; /* how many gops to skip between advances */ } gopTrick; }; /*--------------------------------------------------------------- - PRIVATE FUNCTIONS ---------------------------------------------------------------*/ static BNAV_Entry *BNAV_Player_ReadEntry(BNAV_Player_Handle handle, long index); static void BNAV_Player_SetCacheSize(BNAV_Player_Handle handle, int cacheSize); static int BNAV_Player_AddNormalPlay(BNAV_Player_Handle handle); static int BNAV_Player_AddNormalPlayByFrames(BNAV_Player_Handle handle); static int BNAV_Player_AddFrame(BNAV_Player_Handle handle, long entry); static int BNAV_Player_AddFrameToFifo(BNAV_Player_Handle handle, long entry); static int BNAV_Player_AdvanceIndex(BNAV_Player_Handle handle); static BNAV_PktFifoEntry *BNAV_Player_AddFifoEntry(BNAV_Player_Handle handle); static int BNAV_Player_AddSequenceHeader(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry, int btpMode, int display, int skip); static int BNAV_Player_AddData(BNAV_Player_Handle handle, uint64_t offset, unsigned long size, unsigned long pts, int btpMode, int display, int skip); static int BNAV_Player_AddFrameData(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry); static long BNAV_Player_P_FindPictureIndex(BNAV_Player_Handle handle, long startIndex, eSCType picCode, eBpDirectionParam dir); /* generate commands */ static void BNAV_Player_GenerateBTP(BNAV_Player_Handle handle, unsigned long *params, unsigned char *pkt, int discarding_tailend); static void BNAV_Player_GenerateDummyPicture(BNAV_Player_Handle handle, unsigned char *pkt); static void BNAV_Player_GenerateNullPacket(BNAV_Player_Handle handle, unsigned char *pkt); static void BNAV_Player_GenerateStartCodePacket(BNAV_Player_Handle handle, unsigned char *pkt, uint8_t startcode); static int BNAV_Player_GetFifoSize(const BNAV_Player_Handle handle); static int BNAV_Player_BuildReferenceFrame_Process(BNAV_Player_Handle handle); static int BNAV_Player_AddDummyFrame(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry); static int BNAV_Player_p_SanityCheck(BNAV_Player_Handle handle); /* 64 bit support */ #define getLo64(X) (unsigned long)((X)&0xFFFFFFFF) #define getHi64(X) (unsigned long)((X)>>32) #define create64(HI,LO) ((((uint64_t)(HI))<<32)|(unsigned long)(LO)) #define BNAV_Player_get_frameOffset64(PENTRY) \ create64(BNAV_get_frameOffsetHi(PENTRY),BNAV_get_frameOffsetLo(PENTRY)) /*! Function Definitions */ /** * Auto-detects the NAV table version. If successful, the version is set in bcmplayer's * state, and optionally returned through the version parameter (if not NULL). * * If BNAV_Player_detectNavTableVersion() is not called before * the first call to BNAV_Player_getNextPlayEntry(), it will be called * automatically. * * The reason it is not called by BNAV_Player_Allocate() and BNAV_Player_reset() * is that the user may want to use a NULL boundsCb and assert the firstIndex and * lastIndex. This must be done before auto-detecting the version. * * Return values: * -1 on failure * 0 on success **/ static int BNAV_Player_DetectNavTableVersion(BNAV_Player_Handle handle, int isPes); /* Important: when building general-purpose binaries, always default to the * lowest common denominator feature set. */ static const BNAV_DecoderFeatures g_defaultDecoderFeatures = {1,1,0,0}; void BNAV_Player_GetDefaultSettings(BNAV_Player_Settings *settings) { memset(settings, 0, sizeof(*settings)); settings->lastIndex = 0x7FFFFFFF; /* this allows autodetect to work, but you might get lots of error messages from readEntry(). */ memcpy(&settings->decoderFeatures, &g_defaultDecoderFeatures, sizeof(g_defaultDecoderFeatures)); settings->cacheSize = DEFAULT_CACHE_SIZE; settings->normalPlayBufferSize = DEFAULT_NORMAL_PLAY_BUFFER_SIZE; settings->debugMode = BNAV_Player_DebugFramesAndAllPacketsSent; settings->navVersion = BNAV_VersionUnknown; settings->boundsCb = BNAV_Player_DefaultGetBounds; settings->useReferenceFrame = 1; /* true */ } int BNAV_Player_Open(BNAV_Player_Handle *handle, const BNAV_Player_Settings *settings) { *handle = (BNAV_Player_Handle)malloc(sizeof(struct BNAV_Player_HandleImpl)); memset(*handle, 0, sizeof(**handle)); /* At this point, we are opened. If reset fails, a close must be performed. */ if (BNAV_Player_Reset(*handle, settings)) { /* Free everything */ BNAV_Player_Close(*handle); /* NULLing this is not required, but it may help prevent some application failures */ *handle = NULL; return -1; } return 0; } int BNAV_Player_Reset(BNAV_Player_Handle handle, const BNAV_Player_Settings *settings) { int result = 0; int i; if (!settings->readCb || !settings->seekCb || !settings->tellCb || !settings->filePointer || !settings->cacheSize) { BDBG_ERR(("Missing required BNAV_Player_Settings")); return -1; } if (!settings->videoPid) { BDBG_ERR(("Video PID required")); return -1; } handle->readCb = settings->readCb; handle->seekCb = settings->seekCb; handle->tellCb = settings->tellCb; handle->boundsCb = settings->boundsCb; handle->filePointer = settings->filePointer; switch (settings->navVersion) { case BNAV_Version_VC1_PES: case BNAV_Version_AVC: handle->navFileIndexEntrySize = sizeof(BNAV_AVC_Entry); break; default: handle->navFileIndexEntrySize = sizeof(BNAV_Entry); break; } handle->navFileIndexEntryStart = 0; handle->playMode = eBpPlayNormal; handle->playDir = eBpForward; handle->advanceCount = 1; handle->loopMode = eBpSinglePlay; handle->currentIndex = 0; handle->pid = settings->videoPid; handle->firstFifoEntry = 0; handle->lastFifoEntry = -1; handle->curRefFrame = -1; /* 0 is a valid refframe */ handle->skipNextAdvance = 0; handle->currentOffset = 0; handle->normalPlayBufferSize = settings->normalPlayBufferSize; handle->debugMode = settings->debugMode; handle->navVersion = settings->navVersion; handle->useReferenceFrame = settings->useReferenceFrame; memcpy(&handle->decoderFeatures, &settings->decoderFeatures, sizeof(settings->decoderFeatures)); handle->firstIndex = settings->firstIndex; handle->lastIndex = settings->lastIndex; handle->ptscache_wptr = 2; handle->ptscache_rptr = 0; handle->doBackwardPtsSearch = 1; handle->gopTrick.currentStart = -1; handle->gopTrick.pictureTag = 0; for (i=0;iptscache[i].index = -1; if (settings->transportTimestampEnabled) { handle->timestampOffset = 4; handle->packetSize = 192; } else { handle->timestampOffset = 0; handle->packetSize = 188; } /* videoPid is required for Broadcom trick modes. */ if (!settings->videoPid) handle->decoderFeatures.supportsBroadcomTrickModes = 0; handle->disableExtraBOptimization = 0; /* this only reallocates the cache if cacheSize or size changes. */ BNAV_Player_SetCacheSize(handle, settings->cacheSize); if (handle->navVersion == BNAV_VersionUnknown) { result = BNAV_Player_DetectNavTableVersion(handle, settings->isPes); if (result) { BDBG_ERR(("Unable to detect BNAV_Version")); return -1; } /* reset cache for changes */ switch (handle->navVersion) { case BNAV_Version_VC1_PES: case BNAV_Version_AVC: handle->navFileIndexEntrySize = sizeof(BNAV_AVC_Entry); break; default: handle->navFileIndexEntrySize = sizeof(BNAV_Entry); break; } BNAV_Player_SetCacheSize(handle, settings->cacheSize); } if (handle->navVersion >= BNAV_VersionUnknown) { BDBG_ERR(("Unknown BNAV_Version: %d", handle->navVersion)); BDBG_ERR(("Hint: Indexes should be in host endianness. If the index is byteswapped, the first symption is a bad version.")); return -1; } if (handle->navVersion == 0) { /* There were a couple months where version 1 indexes were being produced but we weren't labelling the indexes as such. And a false detect is really bad. If you need to use version 0 indexes and you know that they really are correct, feel free to delete this check, but by default it should fail. */ BDBG_ERR(("Unable to support version 0 indexes reliably.")); BDBG_ERR(("Hint: Indexes should be in host endianness. If the index is byteswapped, the first symption is a bad version.")); return -1; } if (handle->navVersion == BNAV_Version_VC1_PES) { /* PES data, no packets */ handle->timestampOffset = 0; handle->packetSize = 0; } /* no sanity check for otf */ if (!settings->skipSanityCheck && BNAV_Player_p_SanityCheck(handle)) { BDBG_ERR(("Internal check of bcm index failed. This is an invalid index. " "Is it the correct endianness? " "Please use the printindex utility to verify its integrity.")); return -1; } return result; } void BNAV_Player_Close(BNAV_Player_Handle handle) { if (handle->navCache) free(handle->navCache); free(handle); } int BNAV_Player_UpdateBounds(BNAV_Player_Handle handle) { if (handle->boundsCb) return (*handle->boundsCb)(handle, handle->filePointer, &handle->firstIndex, &handle->lastIndex); else return 0; } /** * This is the only place where the seekCb, tellCb and readCb callbacks are used. * The boundsCb calback is used by BNAV_Player_UpdateBounds(). **/ BNAV_Entry *BNAV_Player_ReadEntry(BNAV_Player_Handle handle, long index) { /* We don't want to update the bounds unless we have to (causes seeks that could be slow in some OS's) */ /* If the entries have not been set then set them */ /* We always update the bounds if continuous record (where first moves as buffer wraps) */ if (handle->firstIndex == handle->lastIndex || handle->firstIndex != 0) BNAV_Player_UpdateBounds(handle); /* If not in valid range then update the range values (we might be recording and the end has moved) */ if (index < handle->firstIndex || index > handle->lastIndex) BNAV_Player_UpdateBounds(handle); /* Test again, if we're still not in range then we're past the end of the file -- done! */ if (index < handle->firstIndex || index > handle->lastIndex) { BDBG_MSG(("BNAV_Player_ReadEntry failed, index %d, firstIndex %d, lastIndex %d", index, handle->firstIndex, handle->lastIndex)); return NULL; } /* Check if this naventry is in our cache */ if ((handle->navCacheIndexStart == -1) || !((index >= handle->navCacheIndexStart) && (index < handle->navCacheIndexEnd))) { unsigned long saveStart = handle->navCacheIndexStart; unsigned long saveEnd = handle->navCacheIndexEnd; unsigned long pos; long t; if (index < handle->navCacheIndexStart) { /* We are reading indexes backwards so cache backwards */ handle->navCacheIndexStart = index - handle->navCacheSize + handle->navCacheOverlap + 1; } else { handle->navCacheIndexStart = index - handle->navCacheOverlap; } /* need to handle -1 case */ if (handle->navCacheIndexStart < 0) { handle->navCacheIndexStart = 0; } /* need to handle being outside of trimmed bounds as well*/ if (handle->navCacheIndexStart < handle->firstIndex) { handle->navCacheIndexStart = handle->firstIndex; } pos = handle->navFileIndexEntryStart + (handle->navCacheIndexStart*handle->navFileIndexEntrySize); if ((*handle->seekCb)(handle->filePointer, pos, SEEK_SET)) t = -1; else { t = (*handle->tellCb)(handle->filePointer); if (t != -1) t = (t - handle->navFileIndexEntryStart) / handle->navFileIndexEntrySize; } /* We may not get exactly what we asked for, but it may work. */ if (t != -1 && t <= index && t < index + handle->navCacheSize) handle->navCacheIndexStart = t; else { BDBG_WRN(("Seek was invalidated: %ld, %ld", t, index)); handle->navCacheIndexStart = saveStart; handle->navCacheIndexEnd = saveEnd; handle->navCacheFails++; return 0; } handle->navCacheIndexEnd = handle->navCacheIndexStart; /* The end is equal to the start plus whatever we are able to read */ handle->navCacheIndexEnd += (*handle->readCb)(handle->navCache, 1, handle->navFileIndexEntrySize * handle->navCacheSize, handle->filePointer) / handle->navFileIndexEntrySize; if (index >= handle->navCacheIndexEnd || handle->navCacheIndexEnd == handle->navCacheIndexStart) { /* The index bounds should prevent this, therefore it is an ERR. */ BDBG_ERR(("Unable to read index: %ld in %ld..%ld", index, handle->navCacheIndexStart, handle->navCacheIndexEnd)); handle->navCacheFails++; return 0; } handle->navCacheMisses++; } handle->navCacheReads++; return (BNAV_Entry *) &(handle->navCache[(index-handle->navCacheIndexStart) * handle->navFileIndexEntrySize]); } void BNAV_Player_GetStatus(const BNAV_Player_Handle handle, BNAV_Player_Status *status) { status->fifoSize = BNAV_Player_GetFifoSize(handle); status->currentIndex = handle->currentIndex; } int BNAV_Player_GetPosition(BNAV_Player_Handle handle, BNAV_Player_Position *position) { return BNAV_Player_GetPositionInformation(handle, handle->currentIndex, position); } int BNAV_Player_GetPositionInformation(BNAV_Player_Handle handle, long index, BNAV_Player_Position *position) { BNAV_Entry *pEntry = BNAV_Player_ReadEntry(handle, index); if (!pEntry) return -1; position->index = index; position->pts = BNAV_get_framePts(pEntry); position->offsetHi = BNAV_get_frameOffsetHi(pEntry); position->offsetLo = BNAV_get_frameOffsetLo(pEntry); position->timestamp = BNAV_get_timestamp(pEntry); position->vchipState = BNAV_unpack_vchip(BNAV_get_packed_vchip(pEntry)); return 0; } int BNAV_Player_GetNextPlayEntry(BNAV_Player_Handle handle, BNAV_Player_PlayEntry *navEntry, unsigned char *pkt) { long startIndex; BNAV_PktFifoEntry *fifoEntry; if (BNAV_Player_UpdateBounds(handle)) return BNAV_GENERIC_IDX_FAIL; if (handle->currentIndex < handle->firstIndex ) { /* Index file has been trimmed ! */ BDBG_MSG(("currentIndex %lx, first %lx , last %lx , current offset %llx, firstFifoEntry %d, lastFifoEntry %d", handle->currentIndex, handle->firstIndex, handle->lastIndex, handle->currentOffset, handle->firstFifoEntry, handle->lastFifoEntry)); if( handle->playMode == eBpPlayNormal) { /* Tell caller, index has gone away from under us so they can take action */ return BNAV_GENERIC_IDX_FAIL; } } /* If the fifo is empty, allow the algorithm to fill it. If we have even one entry, allow the app to get it before processing more. Throughout this loop, any function may fail because of trimming away the beginning/end of file. If this happens, this function should fail. */ while (handle->firstFifoEntry > handle->lastFifoEntry) { /* reset the fifo */ handle->firstFifoEntry = 0; handle->lastFifoEntry = -1; /* If our BNAV_Player_BuildReferenceFrame_Process was interrupted, continue it */ if (handle->buildRef.inProcess) { if (BNAV_Player_BuildReferenceFrame_Process(handle)) return BNAV_GENERIC_IDX_FAIL; continue; } if (handle->playMode == eBpPlayNormal) { if (BNAV_Player_AddNormalPlay(handle)) return BNAV_GENERIC_IDX_FAIL; } else if (handle->playMode == eBpPlayNormalByFrames) { if (BNAV_Player_AddNormalPlayByFrames(handle)) return BNAV_GENERIC_IDX_FAIL; } else { /* needsDisplayPicture will be set if the last call to BNAV_Player_AddFrameToFifo results in a BuildReferenceFrame. In that case, another trip round is required to build the actual display picture, so we don't need to advance again. */ if (!handle->buildRef.needDisplayPicture) { if (BNAV_Player_AdvanceIndex(handle)) return BNAV_ADVANCE_IDX_FAIL; } /* now add it to the fifo */ startIndex = handle->currentIndex; if (handle->playMode == eBpPlayBrcm) { if (BNAV_Player_AddFrameToFifo(handle, startIndex)) return BNAV_GENERIC_IDX_FAIL; } else { /* I, IP, SkipB, SkipP - just add the current frame to the fifo */ if (BNAV_Player_AddFrame(handle, startIndex)) return BNAV_GENERIC_IDX_FAIL; } } } handle->gotEntry = 1; navEntry->zeroByteCountBegin = 0; navEntry->zeroByteCountEnd = 0; fifoEntry = &handle->pktfifo[handle->firstFifoEntry]; switch (fifoEntry->insertpackettype) { case eBpInsertStartCode: navEntry->isInsertedPacket = 1; BNAV_Player_GenerateStartCodePacket(handle, pkt, fifoEntry->startcode); break; case eBpInsertDummy: navEntry->isInsertedPacket = 1; BNAV_Player_GenerateDummyPicture(handle, pkt); break; case eBpInsertNULL: navEntry->isInsertedPacket = 1; BNAV_Player_GenerateNullPacket(handle, pkt); break; case eBpInsertBTP: /* Handle packet insertions */ navEntry->isInsertedPacket = fifoEntry->insertpackettype; /* For now... */ BNAV_Player_GenerateBTP(handle, fifoEntry->pktdata, pkt, /* Test if we're discarding the tailend */ (fifoEntry->pktdata[0] == TT_MODE_PROCESS && fifoEntry->pktdata[8] < 188)); break; case eBpInsertNone: /* Specify frame data to send */ navEntry->isInsertedPacket = 0; /* For now... */ navEntry->startOffsetHi = getHi64(fifoEntry->startByte); navEntry->startOffset = getLo64(fifoEntry->startByte); navEntry->zeroByteCountBegin = fifoEntry->zeroByteCountBegin; navEntry->zeroByteCountEnd = fifoEntry->zeroByteCountEnd; navEntry->byteCount = 1 + getLo64( fifoEntry->endByte - fifoEntry->startByte); break; default: BDBG_ERR(("Invalid HITS packet insertion type!")); return BNAV_GENERIC_IDX_FAIL; } /* tell host if this is the last piece of data in the fifo */ if (handle->firstFifoEntry >= handle->lastFifoEntry) navEntry->isLastEntry = 1; else navEntry->isLastEntry = 0; /* Get next entry */ handle->firstFifoEntry++; return 0; } void BNAV_Player_FlushFifo(BNAV_Player_Handle handle) { handle->firstFifoEntry = 0; handle->lastFifoEntry = -1; BDBG_MSG(("flush fifo")); } int BNAV_Player_GetFifoSize(const BNAV_Player_Handle handle) { return (handle->lastFifoEntry+1) - handle->firstFifoEntry; } static int BNAV_Player_AddCurrentToPTSCache(BNAV_Player_Handle handle) { BNAV_Entry *pEntry; pEntry = BNAV_Player_ReadEntry(handle, handle->currentIndex); if (!pEntry) return -1; handle->ptscache[handle->ptscache_wptr].pts = BNAV_get_framePts(pEntry); handle->ptscache[handle->ptscache_wptr].index = handle->currentIndex; BDBG_MSG_PTSCACHE(("add %ld, %#lx wptr=%d", handle->currentIndex, BNAV_get_framePts(pEntry), handle->ptscache_wptr )); if (++handle->ptscache_wptr == PTS_CACHE_SIZE) handle->ptscache_wptr = 0; return 0; } /** * In normal play, we read ALL data according to a specified size. * However, keep the current index state variable up-to-date. **/ static int BNAV_Player_AddNormalPlayByFrames(BNAV_Player_Handle handle) { uint64_t nextOffset; BNAV_PktFifoEntry *curFifoEntry; BNAV_Entry *pEntry; /* add to the fifo */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = handle->currentOffset; #ifdef LOOSE_INDEX_CHECKING retry: #endif /* find the end of the next frame */ ++handle->currentIndex; pEntry = BNAV_Player_ReadEntry(handle, handle->currentIndex); if (!pEntry) { if (handle->loopMode == eBpSinglePlay) return -1; handle->currentIndex = handle->firstIndex; pEntry = BNAV_Player_ReadEntry(handle, handle->currentIndex); if (!pEntry) return -1; } if (BNAV_get_frameSize(pEntry) == 0) { BDBG_ERR(("invalid frame size 0 at index %d", handle->currentIndex)); #ifdef LOOSE_INDEX_CHECKING goto retry; #else return -1; #endif } nextOffset = BNAV_Player_get_frameOffset64(pEntry) + BNAV_get_frameSize(pEntry); if (handle->packetSize) { nextOffset -= nextOffset % handle->packetSize; } /* feed everything from previous currentOffset to the next currentOffset. drop nothing. */ curFifoEntry->endByte = nextOffset - 1; handle->currentOffset = curFifoEntry->endByte + 1; BNAV_Player_AddCurrentToPTSCache(handle); return 0; } static int BNAV_Player_AddNormalPlay(BNAV_Player_Handle handle) { uint64_t nextOffset; BNAV_PktFifoEntry *curFifoEntry; BNAV_Entry *pEntry; /* add to the fifo */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = handle->currentOffset; /* find out how much we can read, up to normalPlayBufferSize */ nextOffset = handle->currentOffset; while ((pEntry = BNAV_Player_ReadEntry(handle, handle->currentIndex))) { if (BNAV_get_frameSize(pEntry) == 0) { BDBG_ERR(("invalid frame size 0 at index %d", handle->currentIndex)); #ifdef LOOSE_INDEX_CHECKING handle->currentIndex++; continue; #else return -1; #endif } nextOffset = BNAV_Player_get_frameOffset64(pEntry) + BNAV_get_frameSize(pEntry); if (handle->packetSize) { nextOffset -= nextOffset % handle->packetSize; } if (nextOffset > handle->currentOffset + handle->normalPlayBufferSize) { curFifoEntry->endByte = handle->currentOffset + handle->normalPlayBufferSize - 1; break; } curFifoEntry->endByte = nextOffset - 1; handle->currentIndex++; BNAV_Player_AddCurrentToPTSCache(handle); } /* do we have something to play? */ if (curFifoEntry->endByte > curFifoEntry->startByte) { handle->currentOffset = curFifoEntry->endByte + 1; return 0; } else { /* remove fifo entry - we should only have one entry, so flush will do it. */ BNAV_Player_FlushFifo(handle); if (handle->loopMode == eBpLoopForever) { if (!BNAV_Player_UpdateBounds(handle)) { BNAV_Player_SetCurrentIndex(handle, handle->firstIndex); return BNAV_Player_AddNormalPlay(handle); } } return -1; } } static int BNAV_Player_P_FindGOP(BNAV_Player_Handle handle, int num_gops) { long curIndex = handle->currentIndex; BNAV_Entry *pStartEntry; /* if we're starting, get on a GOP */ if (handle->gopTrick.currentStart == -1) { BNAV_Entry *onePrevEntry = NULL; BDBG_MSG(("finding GOP")); while ((pStartEntry = BNAV_Player_ReadEntry(handle, curIndex))) { if (handle->navVersion == BNAV_Version_AVC){ if (BNAV_get_RandomAccessIndicator((const BNAV_AVC_Entry *)pStartEntry)) break; } else { if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) { /* bcmplayer indexes field-encoded streams as two I's. back up and take the first if so. */ onePrevEntry = BNAV_Player_ReadEntry(handle, curIndex-1); if (onePrevEntry && BNAV_get_frameType(onePrevEntry) == eSCTypeIFrame) { pStartEntry = onePrevEntry; curIndex--; } else { onePrevEntry = NULL; } break; } } curIndex += handle->playDir; } BDBG_MSG(("got %p at %d", pStartEntry, curIndex)); if (!pStartEntry) return -1; /* hit end */ handle->gopTrick.currentStart = curIndex; /* find GopEnd */ BDBG_MSG(("finding end of GOP")); if (onePrevEntry) curIndex++; while ((pStartEntry = BNAV_Player_ReadEntry(handle, ++curIndex))) { if (handle->navVersion == BNAV_Version_AVC){ if (BNAV_get_RandomAccessIndicator((const BNAV_AVC_Entry *)pStartEntry)) break; } else { if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) break; } } BDBG_MSG(("got %p at %d", pStartEntry, curIndex-1)); if (pStartEntry) { handle->gopTrick.currentEnd = --curIndex; } else { /* if we got a start but not an end, we may be at the end of the file. */ handle->gopTrick.currentEnd = handle->gopTrick.currentStart; curIndex = handle->gopTrick.currentStart-1; /* go backward to find the start */ while ((pStartEntry = BNAV_Player_ReadEntry(handle, curIndex))) { if (handle->navVersion == BNAV_Version_AVC){ if (BNAV_get_RandomAccessIndicator((const BNAV_AVC_Entry *)pStartEntry)) break; } else { if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) break; } curIndex--; } if (!pStartEntry) { BDBG_ERR(("cannot do GOP trick mode on stream without one complete GOP")); return -1; } handle->gopTrick.currentStart = curIndex; } } BDBG_ASSERT(handle->gopTrick.currentStart != -1 && handle->gopTrick.currentEnd != -1); /* now skip gops */ while (num_gops--) { if (handle->playDir > 0) { curIndex = handle->gopTrick.currentEnd; while ((pStartEntry = BNAV_Player_ReadEntry(handle, ++curIndex))) { if (handle->navVersion == BNAV_Version_AVC){ if (BNAV_get_RandomAccessIndicator((const BNAV_AVC_Entry *)pStartEntry)) break; } else { if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) break; } } if (!pStartEntry) return -1; /* hit end */ handle->gopTrick.currentStart = handle->gopTrick.currentEnd; handle->gopTrick.currentEnd = curIndex; } else { curIndex = handle->gopTrick.currentStart; while ((pStartEntry = BNAV_Player_ReadEntry(handle, --curIndex))) { if (handle->navVersion == BNAV_Version_AVC){ if (BNAV_get_RandomAccessIndicator((const BNAV_AVC_Entry *)pStartEntry)) break; } else { if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) { /* bcmplayer indexes field-encoded streams as two I's. back up and take the first if so. */ BNAV_Entry *onePrevEntry = BNAV_Player_ReadEntry(handle, curIndex-1); if (onePrevEntry && BNAV_get_frameType(onePrevEntry) == eSCTypeIFrame) { pStartEntry = onePrevEntry; curIndex--; } break; } } } if (!pStartEntry) return -1; /* hit end */ handle->gopTrick.currentEnd = handle->gopTrick.currentStart; handle->gopTrick.currentStart = curIndex; } BDBG_MSG(("GOP at [%d,%d], %d", handle->gopTrick.currentStart, handle->gopTrick.currentEnd, num_gops)); } BDBG_ASSERT(handle->gopTrick.currentStart != -1 && handle->gopTrick.currentEnd != -1); return handle->gopTrick.currentStart; } int BNAV_Player_AdvanceIndex(BNAV_Player_Handle handle) { long startIndex; BNAV_Entry *pStartEntry; /* updateBounds already called from getNextPlayEntry. */ startIndex = handle->currentIndex; switch(handle->playMode) { case eBpPlayDecoderGOPTrick: /* now advance if we're in the middle of a GOP */ if (handle->gopTrick.currentStart != -1 && startIndex != handle->gopTrick.currentEnd ) { BDBG_MSG(("advance in GOP")); startIndex++; } else { BNAV_PktFifoEntry *curFifoEntry; /* find a starting GOP or the next GOP */ startIndex = BNAV_Player_P_FindGOP(handle, handle->gopTrick.currentStart == -1 ? 0 : (handle->gopTrick.gopSkip+1)); pStartEntry = BNAV_Player_ReadEntry(handle, startIndex); if (!pStartEntry) return -1; /* before sending next I, send a flush command to prevent prediction before I */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_INLINE_FLUSH; /* Insert timing marker indicating the beginning of the next GOP */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PICTURE_TAG; curFifoEntry->pktdata[9] = handle->gopTrick.pictureTag++; /* Picture Output "N" marker ** AVD will ouput the first 'handle->advanceCount' pictures of this GOP. */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PIC_OUTPUT_COUNT; curFifoEntry->pktdata[9] = handle->advanceCount; /* number of pictures specified by the application */ handle->lastSeqHdrOffset64 = 0; startIndex = handle->gopTrick.currentStart; } break; case eBpPlayBrcm: if (handle->playDir == eBpForward) { if (!handle->skipNextAdvance) startIndex = startIndex + handle->advanceCount; pStartEntry = BNAV_Player_ReadEntry(handle, startIndex); if (pStartEntry == NULL) { if ((handle->loopMode != eBpLoopForever) || (handle->gotEntry == 0)) { handle->gotEntry = 1; handle->currentIndex = startIndex - handle->advanceCount; return -1; } handle->gotEntry = 0; /* Ensure we don't loop more than once without returning */ startIndex = handle->firstIndex; /* Start over from the beginning. */ pStartEntry = BNAV_Player_ReadEntry(handle, startIndex); if (!pStartEntry) return -1; } } else { if (!handle->skipNextAdvance) startIndex = startIndex - handle->advanceCount; if (startIndex < 0) pStartEntry = NULL; else pStartEntry = BNAV_Player_ReadEntry(handle, startIndex); if (pStartEntry == NULL) { if ((handle->loopMode != eBpLoopForever) || (handle->gotEntry == 0)) { handle->gotEntry = 1; handle->currentIndex = startIndex + handle->advanceCount; return -1; } /* We've reached beginning of stream. Goto the end. */ handle->gotEntry = 0; /* Ensure we don't loop more than once without returning */ startIndex = handle->lastIndex; } } break; case eBpPlayI: case eBpPlayIP: case eBpPlaySkipB: case eBpPlaySkipP: { unsigned long tempCount = handle->advanceCount; int loopAround = 0; /* have we already looped the index */ int keepGoing = 1; /* should we look at the next frame */ long previousIFrame = -1; while (keepGoing) { eSCType frameType; if (handle->skipNextAdvance) { /* Only skip the first advance. We have to validate that we're on the right kind of frame. */ handle->skipNextAdvance = 0; } else { startIndex += handle->playDir; } if ((pStartEntry = BNAV_Player_ReadEntry(handle, startIndex)) == NULL) { if (handle->loopMode == eBpSinglePlay) { /* if we're not looping, then we need to push out the first/last I frame for a consistent stop */ if (handle->playMode == eBpPlayI) { if (previousIFrame != -1) { /* back up and send out the last i frame we skipped */ startIndex = previousIFrame; keepGoing = 0; continue; } else if (handle->iFrameRepeatCount++ < 2) { /* repeat the last I frame 2 more times to get through the decoder's pipeline */ startIndex = handle->currentIndex; keepGoing = 0; continue; } } /* we're done */ return -1; } /* prevent an infinite loop. */ if (loopAround) return -1; /* reset startIndex, taking into account the += playDir. */ if (handle->playDir > 0) startIndex = handle->firstIndex - 1; else startIndex = handle->lastIndex + 1; loopAround++; continue; } frameType = BNAV_get_frameType(pStartEntry); switch (handle->playMode) { case eBpPlayIP: if (frameType == eSCTypeRPFrame || frameType == eSCTypeIFrame || frameType == eSCTypePFrame) { if (!--tempCount) keepGoing = 0; } break; case eBpPlaySkipB: keepGoing = 0; if (frameType == eSCTypeBFrame) { if (handle->skipCount == handle->advanceCount) { handle->skipCount = 0; keepGoing = 1; } else handle->skipCount++; } break; case eBpPlaySkipP: if (frameType == eSCTypeIFrame) { keepGoing = 0; /* reset skipCount for the next P */ handle->skipCount = 0; } else if (frameType == eSCTypePFrame && handle->skipCount != -1) { if (handle->skipCount == handle->advanceCount) { /* keep going until we hit a I frame */ handle->skipCount = -1; } else { handle->skipCount++; keepGoing = 0; } } break; case eBpPlayI: if (frameType == eSCTypeIFrame) { previousIFrame = startIndex; if (!--tempCount) { /* For field encoded streams, you can get two I pictures which are adjacent to each other. For purposes of I frame advanceCount, we should ignore the second I picture. This will mess up I-only streams with advanceCount == 1, but that's not really a trick mode anyway. */ if (handle->navVersion == BNAV_Version_AVC && startIndex == handle->currentIndex+1) { /* readd to the tempCount and don't set keepGoing */ tempCount++; } else { keepGoing = 0; } } } break; default: break; } } } break; default: break; } handle->currentIndex = startIndex; handle->skipNextAdvance = 0; BNAV_Player_AddCurrentToPTSCache(handle); return 0; } /** Feed a frame for non-brcm (host only) based trick mode. **/ static int BNAV_Player_AddFrame(BNAV_Player_Handle handle, long entry) { BNAV_PktFifoEntry *curFifoEntry; BNAV_Entry *pStartEntry; uint64_t seqHdrOffset64; int result = 0; BDBG_MSG(("AddFrame %d", entry)); CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, entry)); /* For AVC, send an EOS before every I picture in I frame trick mode to avoid picture_order_count (poc) reordering. This cannot be used for IP or IPB host trick modes because the P's and B's might predict before the I. */ if (handle->navVersion == BNAV_Version_AVC && BNAV_get_frameType(pStartEntry) == eSCTypeIFrame && handle->playMode == eBpPlayI) { curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertStartCode; curFifoEntry->startcode = 0x0A; /* This will flush the SPS/PPS, so clear the state variable. */ handle->lastSeqHdrOffset64 = 0; } #ifdef USE_BTPS_FOR_HOST_TRICK_MODES /* This mode uses BTP packets for host trick modes. This makes for byte-accurate begin/end trims even with encrypted streams. In this mode, the video decoder must be set to Broadcom Trick Mode for all trick modes. */ if (handle->navVersion != BNAV_Version_VC1_PES) { /* send sequence header if different. for AVC this means just checking the PPS, which is sufficient. */ seqHdrOffset64 = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_seqHdrStartOffset(pStartEntry); if (handle->packetSize) { seqHdrOffset64 -= seqHdrOffset64 % handle->packetSize; } if (seqHdrOffset64 != handle->lastSeqHdrOffset64) { if (BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_PROCESS, 0, 0)) return -1; handle->lastSeqHdrOffset64 = seqHdrOffset64; } result = BNAV_Player_AddFrameData(handle, pStartEntry); if (result) return result; return result; } #endif /* Add fifo entry */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; /* back up to TS packet */ curFifoEntry->startByte = BNAV_Player_get_frameOffset64(pStartEntry); if (handle->packetSize) { curFifoEntry->startByte -= curFifoEntry->startByte % handle->packetSize; } if (handle->navVersion == BNAV_Version_AVC || handle->navVersion == BNAV_Version_VC1_PES) { BNAV_AVC_Entry *avcEntry = (BNAV_AVC_Entry *)pStartEntry; /* For AVC host trick modes w/o BTP's, we have to assume the SPS and PPS are adjacent and in SPS,PPS order. If this isn't true, we'll require BTP's. */ /* Calc the SPS offset */ seqHdrOffset64 = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_SPS_Offset(avcEntry); if (handle->packetSize) { seqHdrOffset64 -= seqHdrOffset64 % handle->packetSize; } } else { /* Calc the seqhdr offset */ seqHdrOffset64 = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_seqHdrStartOffset(pStartEntry); if (handle->packetSize) { seqHdrOffset64 -= seqHdrOffset64 % handle->packetSize; } } /* if there's a sequence header in the previous packet (or, for PES streams, close by), also send it */ if (seqHdrOffset64 != handle->lastSeqHdrOffset64 && curFifoEntry->startByte - seqHdrOffset64 <= 188 /* This 188 is meant to be hardcoded and not packetSize */) { handle->lastSeqHdrOffset64 = seqHdrOffset64; curFifoEntry->startByte = seqHdrOffset64; } /* Now that we know where the read must begin (including possible seqhdr), we can calc the zeroByteCountBegin so the host can zero out data the decoder shouldn't see */ if (handle->packetSize) { curFifoEntry->zeroByteCountBegin = (unsigned long)(curFifoEntry->startByte % handle->packetSize); } /* set the end feed byte based on actual picture end */ curFifoEntry->endByte = BNAV_Player_get_frameOffset64(pStartEntry) + BNAV_get_frameSize(pStartEntry); if (handle->packetSize) { /* Round end up to nearest transport packet, but tell caller to zero out any bytes after end of picture */ curFifoEntry->zeroByteCountEnd = handle->packetSize - (unsigned long)(curFifoEntry->endByte % handle->packetSize); if (curFifoEntry->zeroByteCountEnd == handle->packetSize) curFifoEntry->zeroByteCountEnd = 0; curFifoEntry->endByte += curFifoEntry->zeroByteCountEnd; } curFifoEntry->endByte -= 1; /* sub one since endByte is an offset to the last good byte */ return result; } /** Builds a reference frame. This function has some similar logic to BNAV_Player_AddFrameToFifo, but there are enough differences (especially in handling B frames and various optimizations), that the logic needs to be separated. Building a reference frame is a very simple process. Send all the frames needed to build a complete past predictor and load it into the reference frame buffer. **/ static int BNAV_Player_BuildReferenceFrame(BNAV_Player_Handle handle, long entry) { BNAV_Entry *pStartEntry; unsigned long rfo; unsigned long numFrames, lastFrameSize; int i; eSCType frametype; long startingentry; CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry)); frametype = BNAV_get_frameType(pStartEntry); rfo = BNAV_get_refFrameOffset(pStartEntry); if (handle->navVersion >= BNAV_VersionOpenGopBs && frametype == eSCTypeIFrame) { /* send the one I frame only, ignore its rfo because it points to previous GOP */ numFrames = 1; startingentry = entry; rfo = 0; /* fake it out */ } else { /* Calc how many frames will be sent */ numFrames = 0; for (i = rfo; i >= 1; i--) { CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - i)); if (BNAV_get_frameType(pStartEntry) != eSCTypeBFrame) numFrames++; } startingentry = entry - rfo; } CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, startingentry)); lastFrameSize = BNAV_get_frameSize(pStartEntry); BDBG_MSG(("BUILD_REFERENCE index=%ld %s, numFrames=%ld", entry, BNAV_frameTypeStr[BNAV_get_frameType(pStartEntry)], numFrames)); BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_BUILD_REFERENCE, 0, #if defined BCHP_7411_VER || B_HAS_RAVE /* the BUILDREF skip_count is needed for 7411. any picture sent after the skip_count will be decoded against the refframe and displayed. */ numFrames); #else /* the original BTP does not count packets, so use 0. Guarav reports that the reference frame is stored when the next BTP after this arrives. */ 0); #endif /* Add first frame after seqhdr (for I frames, this will be the only frame */ BNAV_Player_AddFrameData(handle, pStartEntry); /* Set up a process which be interrupted when the fifo fills */ handle->buildRef.inProcess = 1; handle->buildRef.curOffset = rfo-1; handle->buildRef.lastFrameSize = lastFrameSize; handle->buildRef.entry = entry; return BNAV_Player_BuildReferenceFrame_Process(handle); } /** This is the inner loop inside BNAV_Player_BuildReferenceFrame, but it's implemented using handle->buildRef so that it can be interrupted and resumed. This is necessary to handle large GOP sizes with a fixed size fifo. Otherwise you never know when the next stream might overflow your fifo. **/ static int BNAV_Player_BuildReferenceFrame_Process(BNAV_Player_Handle handle) { /* Now add the rest of the frames for the reference frame */ for (; handle->buildRef.curOffset >= 1; handle->buildRef.curOffset--) { BNAV_Entry *pStartEntry; int frametype; int index = handle->buildRef.entry - handle->buildRef.curOffset; /* If we're getting close to filling the fifo, abort. We'll resume later. The required gap must be >= the maximum number of entries that may be generated. That is: seqhdr = 4, frame = 4 */ if (handle->lastFifoEntry >= MAX_NUM_FIFO_ENTRIES - 8) { BDBG_MSG(("Interrupting BNAV_Player_BuildReferenceFrame_Process %ld, %ld...", handle->firstFifoEntry, handle->lastFifoEntry)); return 0; } CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, index)); frametype = BNAV_get_frameType(pStartEntry); if (handle->buildRef.lastFrameSize > BNAV_get_seqHdrStartOffset(pStartEntry)) { BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_PROCESS, 0, 0); } /* add only non-B frames until we get to the end */ if (frametype != eSCTypeBFrame) { BDBG_MSG((" %d: %s", index, BNAV_frameTypeStr[frametype])); BNAV_Player_AddFrameData(handle, pStartEntry); } handle->buildRef.lastFrameSize = BNAV_get_frameSize(pStartEntry); } /* This is now our current reference frame */ handle->curRefFrame = handle->buildRef.entry; handle->buildRef.inProcess = 0; return 0; } /** Add a frame for brcm trick mode. If a reference frame needs to be built, this function will exit early and state variables will be set to come back and complete the work on another pass. This adds command packets as needed. One call to this function will generate all of the fifo entries needed to display a frame. **/ static int BNAV_Player_AddFrameToFifo(BNAV_Player_Handle handle, long entry) { BNAV_Entry *pStartEntry; unsigned long rfo; unsigned long numFrames = 0, lastFrameSize; int i; eSCType frametype; int extraBFrames = 0; long startingentry; CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry)); rfo = BNAV_get_refFrameOffset(pStartEntry); frametype = BNAV_get_frameType(pStartEntry); BDBG_MSG(("%ld: %s", entry, BNAV_frameTypeStr[frametype])); /* Check if we need a reference frame. If we're restricted from building a reference, this must be handled differently later on. */ if (handle->useReferenceFrame) { /* Check if our GOP size is too large. If so, subdivide it. This keeps bandwidth requirements limited. Some GOP's are known to be 90 frames or more. */ if (rfo > MAX_REFERENCE_FRAME_OFFSET) { BDBG_MSG((" subdividing GOP, org rfo %ld, refframe %ld", rfo, entry-rfo)); rfo = rfo % MAX_REFERENCE_FRAME_OFFSET; if (!rfo) rfo = MAX_REFERENCE_FRAME_OFFSET; /* back up to nearest I or P frame */ while(1) { CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - rfo)); if (BNAV_get_frameType(pStartEntry) == eSCTypeBFrame) rfo++; else break; }; BDBG_MSG((" subdividing GOP, final rfo %ld, refframe %ld", rfo, entry-rfo)); } /* Causes playback to freeze in BCM mode, take out */ #if 0 /* initial I frames (rfo == 0) are useless to send for display. They are only good for references frames. */ if (!rfo) return -1; #endif /* build a reference frame if needed */ if (handle->curRefFrame != (int)(entry - rfo)) { handle->buildRef.needDisplayPicture = 1; if (BNAV_Player_BuildReferenceFrame(handle, entry - rfo) == -1) { handle->buildRef.needDisplayPicture = 0; return -1; } /* TODO: I'm using disableExtraBOptimization as a marker for when we're in host-based frame reverse mode. In this case, we need to build reference and display a picture in one pass. We should rename this variable. */ if (!handle->disableExtraBOptimization) { /* By always exiting, we make the processing path the same, whether the BuildReferenceFrame was interrupted or not. This also keeps fifo requirements smaller because the reference frame will be processed completely before the displayed picture will be processed. */ return 0; } } handle->buildRef.needDisplayPicture = 0; } /* Figure out our starting point. This may be from a reference frame or not. */ if (handle->navVersion >= BNAV_VersionOpenGopBs && handle->useReferenceFrame) { CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - rfo)); if (BNAV_get_frameType(pStartEntry) == eSCTypeIFrame) { /* Don't send the I frame again, so advance to the next non-B frame. */ do { rfo--; CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - rfo)); } while (BNAV_get_frameType(pStartEntry) == eSCTypeBFrame && rfo > 0); /* If there's no additional frames to send, then we (unfortunately) have to resend the reference frame. */ if (rfo == 0) rfo = BNAV_get_refFrameOffset(pStartEntry); } } else if (!handle->useReferenceFrame) { /* If we're doing no reference frame, we need to back up further to get a complete picture. This is essentially saving memory (one less buffer needed by decoder) at the price of more bandwidth. */ CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - rfo)); if (BNAV_get_frameType(pStartEntry) != eSCTypeIFrame) { rfo += BNAV_get_refFrameOffset(pStartEntry); } } /* Figure out how many frames were going to have to send after this BTP */ for (i = rfo; i >= 0; i--) { CHECKREAD_SILENT(pStartEntry = BNAV_Player_ReadEntry(handle, entry - i)); frametype = BNAV_get_frameType(pStartEntry); if (frametype != eSCTypeBFrame || i == 0) numFrames++; /** * See if there are extra B frames we can send because they predict off the * same frames. See playertypes.h for discussion. * NOTE: I don't support the forward direction because you would only realize * a gain with a GOP format of >2 B's between I/P's, but maybe someone could use it. * Likewise, I don't support >1x rewind because this would require the same GOP * format. Maybe someday. **/ if (frametype == eSCTypeBFrame && handle->playDir < 0 && handle->advanceCount == 1 && /* 1x rewind only */ handle->decoderFeatures.supportsCombiningBs && /* firmware must support */ !handle->disableExtraBOptimization /* must be enabled by app */ ) { if (i != 0) extraBFrames++; } else { extraBFrames = 0; } } /* startingentry is the first frame to be sent in this sequence */ startingentry = entry - rfo; CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, startingentry)); lastFrameSize = BNAV_get_frameSize(pStartEntry); frametype = BNAV_get_frameType(pStartEntry); /*BDBG_ASSERT(frametype != eSCTypeBFrame);*/ if (handle->playDir > 0) { BDBG_MSG(("DISPLAY %d, %ld", 1, numFrames - 1)); BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_DISPLAY_FORWARD, 1, numFrames - 1); } else { /** * If we're going in reverse and our current entry matches what's in the future * prediction buffer, then we can display the past prediction buffer. **/ if (handle->decoderFeatures.supportsDisplayPastBuffer && handle->navVersion >= BNAV_VersionOpenGopBs && handle->playDir == eBpReverse && /* TODO: This is hanging the driver/firmware for 3x rewind where alignment is such that no B frames are sent. */ /* (handle->futurePred == entry || frametype == eSCTypeIFrame)) */ handle->futurePred == entry) { rfo = 0; /* this will force AddDummyFrame, and that's all we need */ /* TODO: Why is a dummy frame needed after DISPLAY_PAST_BUFFER? */ if (!handle->decoderFeatures.supportsDummyPicture) { BDBG_ERR(("DisplayPastBuffer requires DummyPicture")); } /* the one display frame isn't really needed. But we'll do that later. */ BDBG_MSG(("DISPLAY_PAST_BUFFER %d, %d", 1 + extraBFrames, 0)); BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_DISPLAY_PAST_BUFFER, 1 + extraBFrames, 0); } else { BDBG_MSG(("DISPLAY %d, %ld", 1 + extraBFrames, numFrames - 1)); BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_DISPLAY_REWIND, 1 + extraBFrames, numFrames - 1); } } /* the prediction frames are invalid at this point */ handle->pastPred = handle->futurePred = -1; /* The reason we have this unconditional AddFrame is that we've already sent the PROCESS BTP with the AddSequenceHeader. The possible subsequent AddFrame's in the for loop will generate new PROCESS BTP's. */ if (rfo == 0 && handle->decoderFeatures.supportsDummyPicture) { BDBG_MSG((" %ld: %s (dummy)", startingentry, BNAV_frameTypeStr[frametype])); BNAV_Player_AddDummyFrame(handle, pStartEntry); } else { BDBG_MSG((" %ld: %s", startingentry, BNAV_frameTypeStr[frametype])); BNAV_Player_AddFrameData(handle, pStartEntry); } for (i = rfo-1; i >= 0; i--) { CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, entry - i)); frametype = BNAV_get_frameType(pStartEntry); if (lastFrameSize > BNAV_get_seqHdrStartOffset(pStartEntry)) { BNAV_Player_AddSequenceHeader(handle, pStartEntry, TT_MODE_PROCESS, 0, 0); } lastFrameSize = BNAV_get_frameSize(pStartEntry); /** * If we're in buildmode, add only non-B frames. * If we're not in buildmode, add only non-B frames until we get to the end, * then add a B-frame. **/ if (frametype != eSCTypeBFrame || i == 0) { if (i == 0 && frametype != eSCTypeBFrame && handle->decoderFeatures.supportsDummyPicture) { BDBG_MSG((" %ld: %s (dummy)", entry-i, BNAV_frameTypeStr[frametype])); BNAV_Player_AddDummyFrame(handle, pStartEntry); } else { BDBG_MSG((" %ld: %s", entry-i, BNAV_frameTypeStr[frametype])); BNAV_Player_AddFrameData(handle, pStartEntry); } /* remember what's in the prediction frames */ /* TODO: The dummy frame goes into the future prediction buffer. If it didn't, then we could use that past prediction buffer. */ if (frametype != eSCTypeBFrame) { handle->pastPred = handle->futurePred; handle->futurePred = entry-i; } } } /* send any previous B's that predict against the same frames. */ for (i=1; i<=extraBFrames; i++) { CHECKREAD(pStartEntry = BNAV_Player_ReadEntry(handle, entry - i)); BNAV_Player_AddFrameData(handle, pStartEntry); } /* advance the current index because we sent some extra B's */ if (extraBFrames) handle->currentIndex -= extraBFrames; return 0; } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + INPUTS: None. + OUTPUTS: None. + RETURNS: new FIFO entry for playback, NULL if failure + FUNCTION: This function allocates a new FIFO entry and adds it to the end + of the FIFO. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ static BNAV_PktFifoEntry *BNAV_Player_AddFifoEntry(BNAV_Player_Handle handle) { BNAV_PktFifoEntry *p_fifo; handle->lastFifoEntry++; if (handle->lastFifoEntry >= MAX_NUM_FIFO_ENTRIES) { BDBG_ERR(("bcmplayer ERROR: fifo full!")); return NULL; } p_fifo = &(handle->pktfifo[handle->lastFifoEntry]); /* Initialize values that may not otherwise get initialized in code */ p_fifo->zeroByteCountBegin = 0; p_fifo->zeroByteCountEnd = 0; #if defined BCHP_7411_VER memset(p_fifo->pktdata, 0xFF, sizeof(p_fifo->pktdata)); #else memset(p_fifo->pktdata, 0x00, sizeof(p_fifo->pktdata)); #endif return p_fifo; } static int BNAV_Player_SendData(BNAV_Player_Handle handle, unsigned long picStartByte, /* index of first byte in first packet for this picture */ unsigned long picEndByte, /* index of last byte in last packet for this picture */ uint64_t feedStartByte, /* packet-aligned absolute offset to start of data */ uint64_t feedEndByte, /* packet-aligned absolute offset to last byte of data */ unsigned long pts, int mode, int display, int skip) { #if defined BCHP_7411_VER /** For 7411C/D, the PROCESS command is always used to send all mpeg data. **/ int data_end_packet; int data_end_byte = picEndByte + 1 - handle->timestampOffset; /* convert to total bytes to keep in packet */ int send_dummy_packet = 0; BNAV_PktFifoEntry *curFifoEntry; BSTD_UNUSED(pts); BSTD_UNUSED(mode); BSTD_UNUSED(skip); BSTD_UNUSED(display); /* Send a command packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ curFifoEntry->pktdata[4] = 0xFFFF0000 | (picStartByte - handle->timestampOffset); /* DATA_START_BYTE */ data_end_packet = feedEndByte/handle->packetSize - feedStartByte/handle->packetSize + 1; /* If DATA_END_PACKET is 1 or 3, we have to change it to 2 or 4 and add a NULL packet. This is to prevent incorrect start code detection by the 7411C hardware. We add the NULL packet before the payload because of a nice fluke in the implementation. If we add the NULL packet after the data, the data_end_byte will not apply, and the host is unable to clear the end of the packet to the exact byte because of possible PVR encryption. If we add the NULL packet before the data, we would expect the same problem to shift to the beginning, but it doesn't because the hardware happens to only apply the data_start_byte to the next packet with the same PID. So it just happens to work fine. */ if (data_end_packet == 1 || data_end_packet == 3) { data_end_packet++; send_dummy_packet = 1; } /* DATA_END_PACKET - this is count of total packets. */ curFifoEntry->pktdata[5] = 0xFF000000 | data_end_packet; /* DATA_END_BYTE - this is a count of total bytes in the last packet */ curFifoEntry->pktdata[6] = 0xFFFF0000 | data_end_byte; curFifoEntry->pktdata[7] = 0xFFFF0000; /* content type */ if ( ((pts>>8)&0xFF) == 0x01 || ((pts>>8)&0xFF) == 0x03) pts += 0x00000100; curFifoEntry->pktdata[9] = pts; /* PTS */ if (send_dummy_packet) { curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertNULL; } /* Send the entire frame data */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedStartByte; curFifoEntry->endByte = feedEndByte; return 0; #elif B_HAS_RAVE /** Original BTP format Different BTP modes are used to send data. Multi-packet blocks require two BTP's, one for the block minus one packet, and one for the last packet. This is because the video transport engine does not count packets. **/ BNAV_PktFifoEntry *curFifoEntry; BSTD_UNUSED(mode); BSTD_UNUSED(skip); BSTD_UNUSED(display); /* Send a command packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ if (feedEndByte - feedStartByte < handle->packetSize) { curFifoEntry->pktdata[7] = picStartByte - handle->timestampOffset; /* DISCARD_HEADEND */ #if BCHP_CHIP == 7401 && BCHP_VER == BCHP_VER_A0 /* 7401 A0 PROCESS BTP is broken for discard_tailend field. Instead, zero out trailing data. This will be fixed on B0. This workaround will not work for encrypted streams. */ curFifoEntry->pktdata[8] = 188; #else curFifoEntry->pktdata[8] = picEndByte + 1 - handle->timestampOffset; /* DISCARD_TAILEND */ #endif curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the single-packet frame data */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedStartByte; #if BCHP_CHIP == 7401 && BCHP_VER == BCHP_VER_A0 curFifoEntry->zeroByteCountEnd = handle->packetSize - (picEndByte + 1) - handle->timestampOffset; #endif curFifoEntry->endByte = feedEndByte; return 0; } curFifoEntry->pktdata[7] = picStartByte - handle->timestampOffset; /* DISCARD_HEADEND */ curFifoEntry->pktdata[8] = 188; /* DISCARD_TAILEND */ curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the frame data minus the final packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedStartByte; curFifoEntry->endByte = feedEndByte - handle->packetSize; if (picEndByte == handle->packetSize - 1) { /* we're sending more than one packet, but our last byte comes at the end of the packet. so we're done */ curFifoEntry->endByte = curFifoEntry->endByte + handle->packetSize; return 0; } /* Fragmented final packet-->send a command packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ curFifoEntry->pktdata[7] = 0; /* DISCARD_HEADEND */ #if BCHP_CHIP == 7401 && BCHP_VER == BCHP_VER_A0 curFifoEntry->pktdata[8] = 188; #else curFifoEntry->pktdata[8] = picEndByte + 1 - handle->timestampOffset; /* DISCARD_TAILEND */ #endif curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the final packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedEndByte - (handle->packetSize - 1); #if BCHP_CHIP == 7401 && BCHP_VER == BCHP_VER_A0 curFifoEntry->zeroByteCountEnd = handle->packetSize - (picEndByte + 1) - handle->timestampOffset; #endif curFifoEntry->endByte = feedEndByte; return 0; #else /** Original BTP format Different BTP modes are used to send data. Multi-packet blocks require two BTP's, one for the block minus one packet, and one for the last packet. This is because the video transport engine does not count packets. **/ BNAV_PktFifoEntry *curFifoEntry; /* Send a command packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = mode; /* BTP type */ curFifoEntry->pktdata[1] = skip; /* SKIP_COUNT */ curFifoEntry->pktdata[2] = display; /* DISPLAY_COUNT */ if (feedEndByte - feedStartByte < handle->packetSize) { curFifoEntry->pktdata[7] = picStartByte - handle->timestampOffset; /* DISCARD_HEADEND */ curFifoEntry->pktdata[8] = picEndByte + 1 - handle->timestampOffset; /* DISCARD_TAILEND */ curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the single-packet frame data */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedStartByte; curFifoEntry->endByte = feedEndByte; return 0; } curFifoEntry->pktdata[7] = picStartByte - handle->timestampOffset; /* DISCARD_HEADEND */ curFifoEntry->pktdata[8] = 188; /* DISCARD_TAILEND */ curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the frame data minus the final packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedStartByte; curFifoEntry->endByte = feedEndByte - handle->packetSize; if (picEndByte == handle->packetSize - 1) { /* we're sending more than one packet, but our last byte comes at the end of the packet. so we're done */ curFifoEntry->endByte = curFifoEntry->endByte + handle->packetSize; return 0; } /* Fragmented final packet-->send a command packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ curFifoEntry->pktdata[7] = 0; /* DISCARD_HEADEND */ curFifoEntry->pktdata[8] = picEndByte + 1 - handle->timestampOffset; /* DISCARD_TAILEND */ curFifoEntry->pktdata[9] = pts; /* PTS */ /* Send the final packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertNone; curFifoEntry->startByte = feedEndByte - (handle->packetSize - 1); curFifoEntry->endByte = feedEndByte; return 0; #endif } /* take a byte-accurate offset and size and compute the packet-aligned offsets and the starting point in the first packet and ending point in the last packet. then call SendData which is responsible for creating the BTP's to get the work done. */ static int BNAV_Player_AddData(BNAV_Player_Handle handle, uint64_t offset, unsigned long size, unsigned long pts, int btpMode, int display, int skip) { unsigned long packet_start_byte, packet_end_byte; uint64_t end_offset = offset + size - 1; /* index of last byte */ /* handle transport packet alignment */ packet_start_byte = (unsigned long)(offset % handle->packetSize); packet_end_byte = (unsigned long)(end_offset % handle->packetSize); offset -= packet_start_byte; /* set start byte of data to feed */ end_offset += handle->packetSize - packet_end_byte - 1; /* set end byte of data to feed */ #ifndef USE_BTPS_FOR_HOST_TRICK_MODES /* This is a helpful technique, but should be unnecessary. Instead of trimming the beginning of the first packet, we can chose to send the whole packet. For many (but not all) streams, this will allow other data like the PES header, MPEG2 seqhdr or AVC SPS/PPS to be sent. This shouldn't be needed because the PTS should be set via the BTP and the seqhdr should be indexed and sent separately. PR 16794 - This is still required for legacy platforms. */ if (handle->playMode != eBpPlayBrcm) { packet_start_byte = 0; } #endif BDBG_ASSERT((end_offset + 1) % handle->packetSize == 0); BDBG_ASSERT(offset % handle->packetSize == 0); return BNAV_Player_SendData(handle, packet_start_byte, packet_end_byte, offset, end_offset, pts, btpMode, display, skip); } /** Add a single frame (picture) to the fifo. Use whatever BTP's are necessary to get it decoded. **/ static int BNAV_Player_AddFrameData(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry) { return BNAV_Player_AddData(handle, BNAV_Player_get_frameOffset64(pStartEntry), BNAV_get_frameSize(pStartEntry), BNAV_get_framePts(pStartEntry), TT_MODE_PROCESS, 0, 0); } static int BNAV_Player_AddDummyFrame(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry) { BNAV_PktFifoEntry *curFifoEntry; unsigned long pts = BNAV_get_framePts(pStartEntry); /* PROCESS BTP - only one is needed because the picture is only one packet */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ #if defined BCHP_7411_VER curFifoEntry->pktdata[4] = 0xFFFF0000|0; /* DATA_START_BYTE */ curFifoEntry->pktdata[5] = 0xFF000000|2; /* DATA_END_PACKET */ curFifoEntry->pktdata[6] = 0xFFFF0000|188; /* DATA_END_BYTE */ curFifoEntry->pktdata[7] = 0xFFFF0000|0; /* CONTENT_TYPE */ if ( ((pts>>8)&0xFF) == 0x01 || ((pts>>8)&0xFF) == 0x03) pts += 0x00000100; curFifoEntry->pktdata[9] = pts; /* PTS */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertNULL; #else curFifoEntry->pktdata[7] = 0; /* DISCARD_HEADEND */ curFifoEntry->pktdata[8] = 188; /* DISCARD_TAILEND */ curFifoEntry->pktdata[9] = pts; #endif /* one packet picture */ curFifoEntry = BNAV_Player_AddFifoEntry(handle); curFifoEntry->insertpackettype = eBpInsertDummy; return 0; } /** Send a BTP mode command which is always followed by a seqhdr. If mode == TT_MODE_PROCESS, then this is not really a BTP mode command. It is just sending a seqhdr. **/ int BNAV_Player_AddSequenceHeader(BNAV_Player_Handle handle, BNAV_Entry *pStartEntry, int btpMode, int display, int skip) { if (handle->navVersion == BNAV_Version_AVC || handle->navVersion == BNAV_Version_VC1_PES) { /* send SPS and PPS, for now, every time */ uint64_t sps_offset, pps_offset; unsigned long sps_size, pps_size, pts; BNAV_AVC_Entry *avcEntry = (BNAV_AVC_Entry *)pStartEntry; sps_offset = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_SPS_Offset(avcEntry); sps_size = BNAV_get_SPS_Size(avcEntry); pps_offset = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_seqHdrStartOffset(pStartEntry); pps_size = BNAV_get_seqHdrSize(pStartEntry); /* at the beginning of a stream, we might have some SPS or PPS indexes with no data. We can't decode these frames. */ if (!sps_size || !pps_size) return -1; pts = BNAV_get_framePts(pStartEntry); if (sps_offset + sps_size == pps_offset) { /* send both together */ BNAV_Player_AddData(handle, sps_offset, sps_size + pps_size, pts, 0, 0, 0); } else { /* send separately */ BNAV_Player_AddData(handle, sps_offset, sps_size, pts, 0, 0, 0); BNAV_Player_AddData(handle, pps_offset, pps_size, pts, 0, 0, 0); } } else { uint64_t offset = BNAV_Player_get_frameOffset64(pStartEntry) - BNAV_get_seqHdrStartOffset(pStartEntry); unsigned long size = BNAV_get_seqHdrSize(pStartEntry); if (!size) return -1; #if defined BCHP_7411_VER if (btpMode != TT_MODE_PROCESS) { BNAV_PktFifoEntry *curFifoEntry; /* If not a PROCESS command, then send the BTP command separately from the seqhdr data. */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = btpMode; /* MODE */ if (btpMode != TT_MODE_DISPLAY_PAST_BUFFER) { curFifoEntry->pktdata[1] = 0xFFFF0000 | skip; /* SKIP_COUNT */ if (btpMode != TT_MODE_BUILD_REFERENCE) { curFifoEntry->pktdata[2] = 0xFFFF0000 | display; /* DISPLAY_COUNT */ curFifoEntry->pktdata[9] = BNAV_get_framePts(pStartEntry); } } } #elif B_HAS_RAVE if (btpMode != TT_MODE_PROCESS) { BNAV_PktFifoEntry *curFifoEntry; /* If not a PROCESS command, then send the BTP command separately from the seqhdr data. */ CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = btpMode; /* MODE */ curFifoEntry->pktdata[1] = skip; /* SKIP_COUNT */ curFifoEntry->pktdata[2] = display; /* DISPLAY_COUNT */ curFifoEntry->pktdata[9] = BNAV_get_framePts(pStartEntry); } #endif BNAV_Player_AddData(handle, offset, size, BNAV_get_framePts(pStartEntry), btpMode, display, skip); } return 0; } /**************** The following "Generate" functions are called at GetNextPlayEntry time when the internal fifo is consumed. **/ /** Generate a 1 packet dummy picture. Write it to memory pointed to by pkt parameter. **/ static void BNAV_Player_GenerateDummyPicture(BNAV_Player_Handle handle, unsigned char *pkt) { static const unsigned char dummy_picture[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x57, 0xa8, 0x00, 0x00, 0x01, 0xb5, 0x8f, 0xff, 0xff, 0x1c, /* The 0xB5 extension is required for field-encoded MPEG2 brcm trick modes. It will cause a problem with MPEG1 streams. If we encounter MPEG1 streams, we may need to extend the API to get bcmplayer more information. */ 0x00, 0x00, 0x01, 0x01, 0x2a, 0x8b, 0xf0, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; pkt[handle->timestampOffset+0] = 0x47; /* SYNC BYTE */ pkt[handle->timestampOffset+1] = (handle->pid >> 8) & 0x1f; pkt[handle->timestampOffset+2] = handle->pid & 0xff; /* PID */ pkt[handle->timestampOffset+3] = 0x30; /* not scrambled, adaptation_field then payload, 0 continuity counter */ pkt[handle->timestampOffset+4] = 188 - 5 - sizeof( dummy_picture ); /* leave only 32 bytes of payload */ pkt[handle->timestampOffset+5] = 0x82; memset(&pkt[handle->timestampOffset+6], 0, 188-6-sizeof(dummy_picture) ); /* zero out adaptation field */ memcpy(&pkt[handle->timestampOffset+188-sizeof(dummy_picture)], dummy_picture, sizeof(dummy_picture)); /* copy dummy picture */ } /** Generate a null packet. Write it to memory pointed to by pkt parameter. **/ static void BNAV_Player_GenerateNullPacket(BNAV_Player_Handle handle, unsigned char *pkt) { memset(pkt, 0, handle->packetSize); pkt[handle->timestampOffset+0] = 0x47; /* SYNC BYTE */ pkt[handle->timestampOffset+1] = 0; pkt[handle->timestampOffset+2] = 0; /* zero pid so it doesn't get filtered out */ pkt[handle->timestampOffset+3] = 0x10; /* not scrambled, not adaptation field, 0 continuity counter */ } /** Generate a packet with a single start code. **/ static void BNAV_Player_GenerateStartCodePacket(BNAV_Player_Handle handle, unsigned char *pkt, uint8_t startcode) { pkt[handle->timestampOffset+0] = 0x47; /* SYNC BYTE */ pkt[handle->timestampOffset+1] = (handle->pid >> 8) & 0x1f; pkt[handle->timestampOffset+2] = handle->pid & 0xff; /* PID */ pkt[handle->timestampOffset+3] = 0x30; /* not scrambled, adaptation_field then payload, 0 continuity counter */ pkt[handle->timestampOffset+4] = 188-5-4; /* leave only 4 bytes of payload */ memset(&pkt[handle->timestampOffset+5], 0, 188-5-4); /* zero out adaptation field */ pkt[handle->timestampOffset+184] = 0x00; pkt[handle->timestampOffset+185] = 0x00; pkt[handle->timestampOffset+186] = 0x01; pkt[handle->timestampOffset+187] = startcode; } /** Generate a BTP packet. Write it to memory pointed to by pkt parameter. **/ void BNAV_Player_GenerateBTP(BNAV_Player_Handle handle, unsigned long *params, unsigned char *pkt, int discarding_tailend) { unsigned i; pkt[handle->timestampOffset+0] = 0x47; /* SYNC BYTE */ pkt[handle->timestampOffset+1] = (handle->pid >> 8) & 0x1f; pkt[handle->timestampOffset+2] = handle->pid & 0xff; /* PID */ pkt[handle->timestampOffset+3] = 0x20; /* not scrambled, adaptation_field and no payload, 0 continuity counter */ pkt[handle->timestampOffset+4] = 0xb7; /* adaptation field length is 183 - the remainder of the packet */ if (discarding_tailend) pkt[handle->timestampOffset+5] = 0x02; /* don't set discontinuity_indicator in this condition */ else pkt[handle->timestampOffset+5] = 0x82; pkt[handle->timestampOffset+6] = 45; /* Number of relevant bytes */ pkt[handle->timestampOffset+7] = 0x00; /* Align byte */ pkt[handle->timestampOffset+8] = 0x42; /* B */ pkt[handle->timestampOffset+9] = 0x52; /* R */ pkt[handle->timestampOffset+10] = 0x43; /* C */ pkt[handle->timestampOffset+11] = 0x4d; /* M */ for(i=0; i<10; ++i) { int base = handle->timestampOffset + 12 + i*4; pkt[base] = (unsigned char) ((params[i] & 0xff000000) >> 24); pkt[base+1] = (unsigned char) ((params[i] & 0x00ff0000) >> 16); pkt[base+2] = (unsigned char) ((params[i] & 0x0000ff00) >> 8); pkt[base+3] = (unsigned char) (params[i] & 0x000000ff); } for(i=handle->timestampOffset+52; ipacketSize; ++i) { pkt[i] = 0x00; } } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + INPUTS: handle = pointer to a previously allocated sBcmPlayer structure + startIndex = index in stream to begin search + startCode = start code type to search for + dir = direction to search + OUTPUTS: None. + RETURNS: SCT index matching this start code type + FUNCTION: This function searches the SCT table for a matching start code. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ long BNAV_Player_FindStartCodeIndex(BNAV_Player_Handle handle, long startIndex, eSCType startCode, eBpDirectionParam dir) { BNAV_Entry *pEntry; char startCodeFound = 0; while (!startCodeFound) { CHECKREAD(pEntry = BNAV_Player_ReadEntry(handle, startIndex)); if (startCode == eSCTypeUnknown) { startCodeFound = 1; } else if (BNAV_get_frameType(pEntry) == startCode) { startCodeFound = 1; } else { startIndex += dir; } } return startIndex; } static long BNAV_Player_P_FindPictureIndex(BNAV_Player_Handle handle, long startIndex, eSCType picCode, eBpDirectionParam dir) { eBpDirectionParam searchDir = dir; while (1) { BNAV_Entry *pEntry; eSCType frametype; pEntry = BNAV_Player_ReadEntry(handle, startIndex); if (!pEntry) { /* TODO: please consider moving this switch of direction to the app. I don't think it's general case. */ if (searchDir == dir) { startIndex -= dir; /* go back one and switch directions */ searchDir = (eBpDirectionParam)(dir * -1); continue; } else { return -1; /* no match found */ } } frametype = BNAV_get_frameType(pEntry); if ((picCode == eSCTypeIFrame && frametype == eSCTypeRPFrame) || (picCode == eSCTypeRPFrame && frametype == eSCTypeIFrame)) { /* If we're looking for I and find RP, or vice versa, bail out because we may never find the right one. This isn't good for a mixed HITS/GOP-based stream, but there's no way to handle this well. */ BDBG_WRN(("BNAV_Player_P_FindPictureIndex: detected HITS/GOP-based mismatch.")); return -1; } if (picCode == eSCTypeUnknown || frametype == picCode) { break; } else { startIndex += dir; } } return startIndex; } #define DISPLAY_ORDER_SEARCH_THRESHOLD 7 /* Set to value that exceeds number of B frames between I and P */ /* Seven will work with up to 5 B's between the I and P */ /* Need this because when searching forward for a P frame, we */ /* will go past B's that are in the future before the P. If we */ /* set "read ptr" to position of P, the B's behind it will no */ /* longer be searchable, resulting in a full cache search */ #define PTS_JITTER 0x110 /* Decoder may return PTS value +/- X of the value to be found in the cache */ /* if this value is too low, cache search will reject the best candidate */ /* and potentially end up searching the whole cache. */ /* Empirical testing has shown a PTS jitter of 0x100 occurs occaisionally */ #define MIN_GOP_THRESHHOLD 1800*18 /* target window for identifying an all same PTS GOP - setto 18 PAL frames */ #define BRCM_CONVERGE_THRESHHOLD 1800*4 /* -X BRCM trick modes often return PTS values that were not added to the */ /* cache. This will cause a full ( time consuming ) cache search and still */ /* no matching entry will be found. */ /* Set this to max PTS difference between targetPTS and PTS found in cache */ /* you are willing to accept as a close match. */ /* Change to higher value if long cache searches are found in -Brcm trick */ #define BRCM_DIVERGE_THRESHHOLD 1800*9 /* Similar to BRCM_CONVERGE_THRESHHOLD above. */ /* But, this is a "moving away" watermark. That is mindiff holds how close */ /* we got to targetPts, current diff holds how far we have moved away from */ /* targetPTS. Set this value too low, and it will falsely find close matches*/ /* when a better match does exist. Too high and it "wastes" time searching */ /* away from close match. Initially best to set high */ #define ABS_DIFF( a, b ) ( (a) > (b) ? (a) - (b) : (b) - (a) ) void BNAV_Player_SetBackwardPtsCacheSearch( BNAV_Player_Handle handle ) { handle->doBackwardPtsSearch = 1; } static int countFramesWithSamePts( BNAV_Player_Handle handle, int endPtr, int directionInc, int inRangeIndex, uint32_t mindiff, int *j, uint32_t *gopDiff ) { int samePtsFrames = 0; int gopLoops=1; *gopDiff = -1; oneMoreTime: while( handle->ptscache[*j].pts == handle->ptscache[inRangeIndex].pts ) { samePtsFrames++; if( *j == endPtr ) break; *j+=directionInc; if( *j < 0 || *j >= PTS_CACHE_SIZE ) *j = directionInc==1?0:PTS_CACHE_SIZE-1; } if( gopLoops && handle->ptscache[*j].pts != handle->ptscache[inRangeIndex].pts ) { *gopDiff = ABS_DIFF( handle->ptscache[*j].pts , handle->ptscache[inRangeIndex].pts ); } if( directionInc < 0 && gopLoops ) { gopLoops--; if( mindiff > *gopDiff ) { /* When searching backwards through same PTS gop's, mindiff */ /* will bring us within range 1 gop too soon, we need to keep */ /* Keep traversing gop(s) until mindiff is less than gopDiff */ mindiff -= *gopDiff; gopLoops++; } inRangeIndex = *j; samePtsFrames=0; goto oneMoreTime; } else { *j = inRangeIndex; /* point back to beginning of GOP */ } if( !gopLoops && handle->ptscache[*j].pts != handle->ptscache[inRangeIndex].pts ) { /* gopLoops will go to zero on a reverse search, at which time "*j" */ /* will point to the next GOP, so back up one */ *j = *j+(directionInc*-1); /* coverity[dead_error_line: FALSE] */ /* coverity[dead_error_condition: FALSE] */ if( *j < 0 || *j >= PTS_CACHE_SIZE ) *j = directionInc==1?0:PTS_CACHE_SIZE-1; } return samePtsFrames; } long BNAV_Player_FindIndexFromPts(BNAV_Player_Handle handle, unsigned long targetPts, int searchWindow) { uint32_t mindiff = 0xFFFFFFFF; /* max value */ long index = -1; int i,directionInc; int samePtsFrames=0; int inRangeIndex=-1,backOff; int startPtr=0, endPtr=0; uint32_t diff; int oldrptr = handle->ptscache_rptr; BSTD_UNUSED(searchWindow); if( handle->doBackwardPtsSearch ) { handle->doBackwardPtsSearch = 0; /* Search backwards after decoder flush */ directionInc = -1; startPtr = handle->ptscache_wptr - 1; if( startPtr < 0 ) startPtr = PTS_CACHE_SIZE-1; endPtr = handle->ptscache_wptr; } else { directionInc = 1; endPtr = handle->ptscache_wptr - 1; if( endPtr < 0 ) endPtr = PTS_CACHE_SIZE-1; startPtr = handle->ptscache_rptr; } backOff = DISPLAY_ORDER_SEARCH_THRESHOLD; if( handle->playMode == eBpPlayBrcm ) backOff += 3; for (i = startPtr; i != endPtr; i+=directionInc ) { if ( i < 0 || i >= PTS_CACHE_SIZE ) { i = directionInc==1?-1:PTS_CACHE_SIZE; continue; } if (handle->ptscache[i].index == -1) continue; /* abs diff */ diff = ABS_DIFF( handle->ptscache[i].pts , targetPts ); /* look for min and remember the index */ if (diff < mindiff) { mindiff = diff; index = handle->ptscache[i].index; if( mindiff <= MIN_GOP_THRESHHOLD ) inRangeIndex = i; /* if close match, we're done now */ if (mindiff < PTS_JITTER ) { handle->ptscache_rptr = i - backOff; if( handle->ptscache_rptr < 0 ) handle->ptscache_rptr += PTS_CACHE_SIZE; break; } } else if ( inRangeIndex != -1 ) { /* diff is no longer less than mindiff and we have previously got within MIN_GOP_THRESHHOLD of */ /* targetPTS. But haven't got close enough of a match ( < PTS_JITTER ) to stop searching. */ /* This happens in the following cases : */ /* All frames in the GOP have been encoded with the same PTS */ /* Avc streams which have duplicate PTS I/P frames etc */ /* in -X Brcm trickmodes, decoder returns a PTS not in the cache. */ if( handle->playMode == eBpPlayI ) { /* Don't attempt interpolation - we are assuming at least I frames have different PTS's */ /* - exception to this is field encoded AVC streams which may have an odd and even */ /* I frame encoded with same PTS. */ if ( mindiff <= BRCM_CONVERGE_THRESHHOLD && diff > BRCM_DIVERGE_THRESHHOLD ) { /* This is needed for case where interpolated PTS are coming back from decoder */ handle->ptscache_rptr = i - backOff; if( handle->ptscache_rptr < 0 ) handle->ptscache_rptr += PTS_CACHE_SIZE; break; } continue; } if( handle->ptscache[i].pts == handle->ptscache[inRangeIndex].pts ) { uint32_t ptsDiff=0xffffffff, gopDiff=0; /* We have a GOP with all frames having same PTS, need to interpolate */ /* count Nr of frames in same PTS GOP, and return start of GOP in "i" */ samePtsFrames = countFramesWithSamePts( handle, endPtr, directionInc, inRangeIndex, mindiff, &i, &gopDiff ); index = handle->ptscache[i].index; ptsDiff = ABS_DIFF( handle->ptscache[i].pts , targetPts ); if( ptsDiff ) index += ptsDiff / ( gopDiff/samePtsFrames ); handle->ptscache_rptr = i - backOff; if( handle->ptscache_rptr < 0 ) handle->ptscache_rptr += PTS_CACHE_SIZE; mindiff = 0; /* we've interpolated so, suppress any warning's that could arise */ break; } else { /* This is not a same PTS GOP, but we don't have a close match */ /* Will normally hit this code in -X BRCM modes when the decoder outputs PTS's */ /* which haven't been added to the cache, but are X frames away from the closest */ /* match we have in the cache. */ /* This code will trap those values and stop searching once the current difference */ /* has exceeded the set threshhold. */ if ( mindiff <= BRCM_CONVERGE_THRESHHOLD && diff > BRCM_DIVERGE_THRESHHOLD ) { handle->ptscache_rptr = i - backOff; if( handle->ptscache_rptr < 0 ) handle->ptscache_rptr += PTS_CACHE_SIZE; break; } } } } #if BDBG_DEBUG_BUILD { int depth = i - handle->ptscache_rptr; if (depth < 0) depth += PTS_CACHE_SIZE; BDBG_MSG_PTSCACHE(("Got idx: %ld rp: %d wp: %d tgt PTS: (0x%8x), diff: %#x, cache depth: %d ", index, handle->ptscache_rptr, handle->ptscache_wptr, targetPts ,mindiff, depth )); } #endif /* If things don't work out right, put out some error messages. */ if (mindiff == 0xFFFFFFFF) { BDBG_ERR(("No PTS in cache")); goto error; } else if (mindiff >= 100000) { BDBG_ERR(("Invalid PTS %#x. %d Nearest PTS was %#x.", targetPts, mindiff, targetPts+mindiff)); goto error; } else if (mindiff > ( MIN_GOP_THRESHHOLD + PTS_JITTER ) ) { BDBG_ERR(("Couldn't find PTS %#x. %d Nearest was %#x. Are all PTS's in GOP the same ? May need to adjust MIN_GOP_THRESHHOLD", targetPts, mindiff, targetPts+mindiff)); goto error; } else if (mindiff >= (BRCM_CONVERGE_THRESHHOLD + PTS_JITTER ) ) { BDBG_MSG_PTSCACHE(("Couldn't find PTS %#x. %d Nearest was %#x. In -x Brcm mode ? May need to adjust CONVERGE_THRESHHOLD", targetPts, mindiff, targetPts+mindiff)); goto error; } return index; error: handle->ptscache_rptr = oldrptr; return -1; } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + INPUTS: handle = pointer to a previously allocated sTsPlayer structure + offsetHi = upper 32 bits of the search offset + offset = lower 32 bits of the search offset + OUTPUTS: None. + RETURNS: SCT index covering this byte offset. -1 if seeks fail. + FUNCTION: This function searches the SCT table for an entry that corresponding + to the specified byte offset. If the byte offset is less than the + offset of the first entry if will return the first entry. If the + offset is greater than the offset of the last entry, it will return + the last entry. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ long BNAV_Player_FindIndexFromOffset(BNAV_Player_Handle handle, unsigned long offsetHi, unsigned long offsetLow) { BNAV_Entry *pEntry; char entryFound = 0; long index; int dir = 0; uint64_t offset, firstByteOffset, lastByteOffset, testOffset = 0; BNAV_Entry *first, *last; int count = 0; offset = create64(offsetHi, offsetLow); /* get first and last byte offset of mpeg */ if (BNAV_Player_UpdateBounds(handle)) return -1; CHECKREAD(first = BNAV_Player_ReadEntry(handle, handle->firstIndex)) firstByteOffset = BNAV_Player_get_frameOffset64(first); CHECKREAD(last = BNAV_Player_ReadEntry(handle, handle->lastIndex)) lastByteOffset = BNAV_Player_get_frameOffset64(last); /* check bounds */ if (firstByteOffset >= offset) return handle->firstIndex; if (offset >= lastByteOffset) return handle->lastIndex; /** * interpolate to an index * * Here's the straightforward math for clarity: * * index = (long)(((offset - firstByteOffset) / (float)(lastByteOffset - firstByteOffset) * * (handle->lastIndex - handle->firstIndex)) + handle->firstIndex; * * Here's support for no native 64 bit type: **/ #ifdef HAVE_FLOAT { uint64_t temp1, temp2; float temp3; temp1 = lastByteOffset - firstByteOffset; temp2 = offset - firstByteOffset; temp3 = ((float)temp2) / temp1; index = (long)(temp3 * (handle->lastIndex - handle->firstIndex)) + handle->firstIndex; } #else { uint64_t temp1, temp2, temp3; temp1 = lastByteOffset - firstByteOffset; temp2 = offset - firstByteOffset; temp3 = handle->lastIndex - handle->firstIndex; /* reduce the terms before doing the interpolation to prevent overflow. */ while (temp2 > 100000000) { temp2 /= 2; temp1 /= 2; } while (temp3 > 100000000) { temp3 /= 2; temp1 /= 2; } if (temp1 == 0) { temp3 = 0; } else { temp3 = temp3 * temp2 / temp1; } index = (unsigned long)temp3 + handle->firstIndex; } #endif /* linear search */ while (!entryFound) { CHECKREAD_SILENT(pEntry = BNAV_Player_ReadEntry(handle, index)) testOffset = BNAV_Player_get_frameOffset64(pEntry); if (testOffset > offset) { if (dir == 0) dir = -1; if (dir > 0) { entryFound = 1; /* back up one */ index -= dir; } else index += dir; } else { if (dir == 0) dir = 1; if (dir < 0) entryFound = 1; else index += dir; } count++; } BDBG_MSG(("IndexFromOffset, %d loops, diff %ld", count, (unsigned long)(testOffset - offset))); return index; } /** * IndexFromTimestamp has identical logic to IndexFromOffset **/ long BNAV_Player_FindIndexFromTimestamp(BNAV_Player_Handle handle, unsigned long timestamp) { BNAV_Entry *pEntry; char entryFound = 0; long index; int dir = 0; unsigned long firstTimestamp, lastTimestamp, testTimestamp = 0; BNAV_Entry *first, *last; int count = 0; /* get first and last timestamp of mpeg */ if (BNAV_Player_UpdateBounds(handle)) return -1; CHECKREAD(first = BNAV_Player_ReadEntry(handle, handle->firstIndex)) firstTimestamp = BNAV_get_timestamp(first); CHECKREAD(last = BNAV_Player_ReadEntry(handle, handle->lastIndex)) lastTimestamp = BNAV_get_timestamp(last); BDBG_MSG(("IndexFromTimestamp: timestamps %ld,%ld,%ld", firstTimestamp,timestamp,lastTimestamp)); /* check bounds */ if (firstTimestamp >= timestamp) return handle->firstIndex; if (timestamp >= lastTimestamp) return handle->lastIndex; /* interpolate to an index */ #ifdef HAVE_FLOAT index = (long)(((timestamp - firstTimestamp) / (float)(lastTimestamp - firstTimestamp)) * (handle->lastIndex - handle->firstIndex)) + handle->firstIndex; #else { /* a 1 hour recording will have a lastTimestamp of 1000 * 60 * 60 = 3,600,000. at 30 fps, a 1 hour recording will have a lastIndex of 30 * 60 * 60 = 108,000. we can reduce these terms before doing the interpolation to prevent overflow. */ unsigned timestampPartDiff = timestamp - firstTimestamp; unsigned timestampWholeDiff = lastTimestamp - firstTimestamp; unsigned indexDiff = handle->lastIndex - handle->firstIndex; /* reduce timestamp first because it increases faster */ while (timestampPartDiff > 65535) { timestampPartDiff /= 2; timestampWholeDiff /= 2; } while (indexDiff > 100000) { indexDiff /= 2; timestampWholeDiff /= 2; } if (timestampWholeDiff == 0) { index = handle->firstIndex; } else { index = (timestampPartDiff * indexDiff / timestampWholeDiff) + handle->firstIndex; } } #endif BDBG_MSG(("IndexFromTimestamp: indexes %ld,%ld,%ld, cur %ld", handle->firstIndex,index,handle->lastIndex,handle->currentIndex)); if ((index == handle->firstIndex) || (index == handle->lastIndex)) { /* Shortcut if this is a boundary condition */ return index; } /* linear search */ while (!entryFound) { CHECKREAD(pEntry = BNAV_Player_ReadEntry(handle, index)) testTimestamp = BNAV_get_timestamp(pEntry); if (testTimestamp > timestamp) { if (dir == 0) dir = -1; if (dir > 0) { entryFound = 1; /* back up one */ index -= dir; } else index += dir; } else if (testTimestamp == timestamp) { entryFound = 1; } else { if (dir == 0) dir = 1; if (dir < 0) entryFound = 1; else index += dir; } count++; } BDBG_MSG(("IndexFromTimestamp, %d loops, diff %ld, index %ld", count, testTimestamp - timestamp, index)); return index; } void BNAV_Player_PrintCachePerformance(const BNAV_Player_Handle handle) { int hits = handle->navCacheReads-handle->navCacheMisses; printf("cache: %d hits, %ld misses, %ld fails. %0.1f%% hit ratio\n", hits, handle->navCacheMisses, handle->navCacheFails, 100.0*hits/handle->navCacheReads); } int BNAV_Player_ReadIndex(BNAV_Player_Handle handle, int index, BNAV_Entry *entry) { BNAV_Entry *i; CHECKREAD(i = BNAV_Player_ReadEntry(handle, index)); memcpy(entry, i, handle->navFileIndexEntrySize); return 0; } int BNAV_Player_IsHits(BNAV_Player_Handle handle, int searchRange) { int index = 0; while (searchRange--) { int frametype; BNAV_Entry *i = BNAV_Player_ReadEntry(handle, index++); if (!i) break; frametype = BNAV_get_frameType(i); if (frametype == eSCTypeRPFrame) return 1; else if (frametype == eSCTypeIFrame) return 0; } return -1; } static int BNAV_Player_DetectNavTableVersion(BNAV_Player_Handle handle, int isPes) { BNAV_Entry *pEntry; BSTD_UNUSED(isPes); BNAV_Player_UpdateBounds(handle); pEntry = BNAV_Player_ReadEntry(handle, handle->firstIndex); if (!pEntry) { BDBG_ERR(("BNAV_Player_DetectNavTableVersion: no entry 0")); return -1; } handle->navVersion = BNAV_get_version(pEntry); return 0; } void BNAV_Player_GetPlayMode(const BNAV_Player_Handle handle, BNAV_Player_PlayMode *mode) { mode->playMode = handle->playMode; mode->playModeModifier = handle->advanceCount * handle->playDir; mode->loopMode = handle->loopMode; mode->disableExtraBOptimization = handle->disableExtraBOptimization; } int BNAV_Player_SetPlayMode(BNAV_Player_Handle handle, const BNAV_Player_PlayMode *mode) { int playModeModifier = mode->playModeModifier; BDBG_MSG(("setPlayMode mode=%d,mode_modifier=%d,loop=%d", mode->playMode, mode->playModeModifier, mode->loopMode)); if (handle->navVersion == BNAV_Version_TimestampOnly) { if (mode->playMode != eBpPlayNormal) { BDBG_ERR(("BNAV_Version_TimestampOnly only supports eBpPlayNormal")); return -1; } } /* Validate the parameters. */ if (mode->playMode == eBpPlayIP || mode->playMode == eBpPlayNormal || mode->playMode == eBpPlayNormalByFrames) { playModeModifier = 1; } else if (mode->playMode == eBpPlayDecoderGOPTrick) { if (playModeModifier == 0) { BDBG_ERR(("eBpPlayDecoderGOPTrick cannot have playModeModifier == 0.")); return -1; } /* Check if DQT can be supported using the nav file */ if (handle->navVersion == BNAV_Version_AVC){ long startIndex; startIndex = BNAV_Player_P_FindGOP(handle, 0); if(!BNAV_Player_ReadEntry(handle, startIndex)){ BDBG_WRN(("Current index file does not support GOP trickmodes. Please recreate the index to support GOP trickmodes")); return -1; } } handle->gopTrick.gopSkip = i_abs(playModeModifier)/100; playModeModifier = playModeModifier % 100; } else if (playModeModifier == 0) { BDBG_ERR(("PlayMode cannot have playModeModifier == 0.")); return -1; } else if (playModeModifier < 0 && mode->playMode != eBpPlayI && mode->playMode != eBpPlayBrcm) { BDBG_ERR(("PlayModeModifier cannot be <0 for this mode.")); return -1; } if (handle->navVersion == BNAV_Version_VC1_PES || handle->navVersion == BNAV_Version_AVC) { if (mode->playMode == eBpPlayBrcm) { BDBG_ERR(("VC1/AVC Broadcom Trick Modes not supported")); return -1; } } if (handle->navVersion == BNAV_Version_AVC) { if (mode->playMode == eBpPlaySkipP) { /* AVC P frames don't have unidirectional prediction, so we can't drop some P's. */ BDBG_ERR(("SkipP does not work with the AVC codec")); return -1; } } if (mode->playMode == eBpPlayBrcm && !handle->decoderFeatures.supportsBroadcomTrickModes) { BDBG_ERR(("Decoder does not support Broadcom Trick Modes.")); return -1; } if (mode->playMode == eBpPlayBrcm && playModeModifier == 1) { BDBG_ERR(("Broadcom Trick Modes cannot have mode modifier of 1.")); return -1; } /* loop mode can change */ handle->loopMode = mode->loopMode; /* if we're staying in playmode, don't change anything past this point */ if (mode->playMode == handle->playMode && handle->playMode == eBpPlayNormal) { return 0; } handle->playMode = mode->playMode; handle->playDir = playModeModifier>0?eBpForward:eBpReverse; handle->advanceCount = playModeModifier>0?playModeModifier:-playModeModifier; handle->curRefFrame = -1; handle->disableExtraBOptimization = mode->disableExtraBOptimization; BNAV_Player_SetCurrentIndex(handle, handle->currentIndex); return 0; } long BNAV_Player_FindIFrameFromIndex(BNAV_Player_Handle handle, long index, eBpDirectionParam dir) { if (index == -1) index = handle->currentIndex; if (handle->navVersion == BNAV_Version_TimestampOnly) { return index; } /* verify that dir is always 1/-1, because this algorithm breaks if you pass in 0. */ dir = (dir > 0)?1:-1; index = BNAV_Player_P_FindPictureIndex(handle, index, eSCTypeIFrame, dir); /* TODO: bcmindexer now indexes MPEG field encoded streams as a single NAV entry per field pair. if we can do the same for AVC, then this code can be removed. this would be best because backing up one I picture is basically a guess. */ if (dir < 0) { long previousIndex; previousIndex = BNAV_Player_P_FindPictureIndex(handle, index-1, eSCTypeIFrame, dir); if (index-previousIndex == 1) { /* When searching backwards in field encoded streams, return the 1st field in the field pair, not the 2nd */ BDBG_MSG(("1st field index=%d, 2nd field index=%d", previousIndex, index)); index = previousIndex; } } return index; } bool BNAV_Player_IndexIsBFrame( BNAV_Player_Handle handle, long index ) { BNAV_Entry *pEntry = 0; eSCType frametype; bool rc=false; pEntry = BNAV_Player_ReadEntry(handle, index); if( pEntry ) { frametype = BNAV_get_frameType(pEntry); if ( frametype == eSCTypeBFrame ) rc = true; } return rc; } int BNAV_Player_SetCurrentIndex(BNAV_Player_Handle handle, long index) { BNAV_Entry *pEntry; BDBG_MSG(("SetCurrentIndex %ld", index)); if (BNAV_Player_UpdateBounds(handle)) { BDBG_WRN(("SetCurrentIndex failed: %ld UpdateBounds", index)); return -1; } pEntry = BNAV_Player_ReadEntry(handle, index); if (!pEntry) { BDBG_WRN(("SetCurrentIndex failed: %ld ReadEntry", index)); return -1; } handle->currentIndex = index; if (handle->playMode == eBpPlayNormal) { /* Back up to the TS-packet aligned sequence header. From this point, bcmplayer sends all data. */ uint64_t temp = BNAV_Player_get_frameOffset64(pEntry); temp = temp - BNAV_get_seqHdrStartOffset(pEntry); if (handle->packetSize) { handle->currentOffset = temp - temp % handle->packetSize; } else { handle->currentOffset = temp; } handle->lastSeqHdrOffset64 = handle->currentOffset; } else { handle->lastSeqHdrOffset64 = 0; } BNAV_Player_FlushFifo(handle); #if 0 /* no longer need empty btp packet,RAVE has improved flush,keeping this for debugging */ /* PR 27274 - send a harmless PROCESS BTP through the system so any pending PROCESS is nullified. Also see PR's 25759, 26647, 20997. */ { BNAV_PktFifoEntry *curFifoEntry; CHECKREAD(curFifoEntry = BNAV_Player_AddFifoEntry(handle)); curFifoEntry->insertpackettype = eBpInsertBTP; curFifoEntry->pktdata[0] = TT_MODE_PROCESS; /* MODE */ curFifoEntry->pktdata[7] = 0; /* don't trim anything. */ curFifoEntry->pktdata[8] = 188; /* don't trim anything. */ } #endif handle->skipCount = 0; handle->skipNextAdvance = 1; handle->iFrameRepeatCount = 0; handle->buildRef.inProcess = 0; handle->buildRef.needDisplayPicture = 0; handle->gopTrick.currentStart = -1; handle->gopTrick.pictureTag = 0; return 0; } /* resize buffer and clear cache. even if not resizing, always reset for consistent behavior. */ static void BNAV_Player_SetCacheSize(BNAV_Player_Handle handle, int cacheSize) { int cacheBytes = handle->navFileIndexEntrySize * cacheSize; if (handle->navCacheSize == cacheSize && handle->navCacheBytes == cacheBytes) goto reset; handle->navCacheSize = cacheSize; handle->navCacheBytes = cacheBytes; handle->navCacheOverlap = cacheSize/4; /* realloc memory */ if (handle->navCache) free(handle->navCache); handle->navCache = (unsigned char *)malloc(handle->navCacheBytes); reset: handle->navCacheFails = 0; handle->navCacheMisses = 0; handle->navCacheReads = 0; handle->navCacheIndexStart = -1; handle->navCacheIndexEnd = -1; } int BNAV_Player_SetBounds(BNAV_Player_Handle handle, long firstIndex, long lastIndex) { if (firstIndex > lastIndex || firstIndex < 0) return -1; handle->firstIndex = firstIndex; handle->lastIndex = lastIndex; /* If you ever call SetBounds, then no longer use the callback. */ handle->boundsCb = NULL; /*TODO: should we check SetCurrentIndex and force it within bounds?*/ return 0; } void BNAV_Player_GetSettings(BNAV_Player_Handle handle, BNAV_Player_Settings *settings) { settings->cacheSize = handle->navCacheSize; settings->normalPlayBufferSize = handle->normalPlayBufferSize; settings->debugMode = handle->debugMode; memcpy(&settings->decoderFeatures, &handle->decoderFeatures, sizeof(handle->decoderFeatures)); settings->videoPid = handle->pid; settings->navVersion = handle->navVersion; settings->readCb = handle->readCb; settings->tellCb = handle->tellCb; settings->seekCb = handle->seekCb; settings->boundsCb = handle->boundsCb; settings->filePointer = handle->filePointer; settings->firstIndex = handle->firstIndex; settings->lastIndex = handle->lastIndex; /*settings->isPes = handle->isPes;*/ } void BNAV_Player_SetDebugMode(BNAV_Player_Handle handle, BNAV_Player_DebugMode debugMode) { handle->debugMode = debugMode; } int BNAV_Player_DefaultGetBounds(BNAV_Player_Handle handle, void *filePointer, long *firstIndex, long *lastIndex) { long size; if ((*handle->seekCb)(filePointer, 0, SEEK_END)) return -1; size = (*handle->tellCb)(filePointer); if (size == -1) return -1; *firstIndex = 0; *lastIndex = (size/handle->navFileIndexEntrySize) - 1; return 0; } #define TOTAL_SANITY_CHECKS 100 static int BNAV_Player_p_SanityCheck(BNAV_Player_Handle handle) { int i; uint64_t last = 0; int version = -1; if (BNAV_Player_UpdateBounds(handle)) return -1; for (i=0;i