source: svn/newcon3bcm2_21bu/nexus/modules/playback/src/nexus_playback.c @ 66

Last change on this file since 66 was 66, checked in by megakiss, 11 years ago

키패드 기능 연결

전원키 누르고 리모콘 누르면 학습
CH+ 간격 1초 증가 MAX:15
CH- 간격 1초 감소 MIN :1

  • Property svn:executable set to *
File size: 86.8 KB
Line 
1/***************************************************************************
2 *     (c)2007-2011 Broadcom Corporation
3 *
4 *  This program is the proprietary software of Broadcom Corporation and/or its licensors,
5 *  and may only be used, duplicated, modified or distributed pursuant to the terms and
6 *  conditions of a separate, written license agreement executed between you and Broadcom
7 *  (an "Authorized License").  Except as set forth in an Authorized License, Broadcom grants
8 *  no license (express or implied), right to use, or waiver of any kind with respect to the
9 *  Software, and Broadcom expressly reserves all rights in and to the Software and all
10 *  intellectual property rights therein.  IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU
11 *  HAVE NO RIGHT TO USE THIS SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY
12 *  NOTIFY BROADCOM AND DISCONTINUE ALL USE OF THE SOFTWARE.
13 *
14 *  Except as expressly set forth in the Authorized License,
15 *
16 *  1.     This program, including its structure, sequence and organization, constitutes the valuable trade
17 *  secrets of Broadcom, and you shall use all reasonable efforts to protect the confidentiality thereof,
18 *  and to use this information only in connection with your use of Broadcom integrated circuit products.
19 *
20 *  2.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
21 *  AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, REPRESENTATIONS OR
22 *  WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
23 *  THE SOFTWARE.  BROADCOM SPECIFICALLY DISCLAIMS ANY AND ALL IMPLIED WARRANTIES
24 *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE,
25 *  LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION
26 *  OR CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT OF
27 *  USE OR PERFORMANCE OF THE SOFTWARE.
28 *
29 *  3.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR ITS
30 *  LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR
31 *  EXEMPLARY DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO YOUR
32 *  USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF
33 *  THE POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT
34 *  ACTUALLY PAID FOR THE SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
35 *  LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF
36 *  ANY LIMITED REMEDY.
37 *
38 * $brcm_Workfile: nexus_playback.c $
39 * $brcm_Revision: 118 $
40 * $brcm_Date: 12/20/11 1:43p $
41 *
42 * Module Description:
43 *
44 * Revision History:
45 *
46 * $brcm_Log: /nexus/modules/playback/src/nexus_playback.c $
47 *
48 * 118   12/20/11 1:43p vsilyaev
49 * SW7425-2034: Used 32-bit type for program id (pid) types
50 *
51 * 117   12/9/11 10:07a erickson
52 * SWDEPRECATED-1003: reset force_source_frame_rate on normal play
53 *
54 * 116   10/19/11 3:52p erickson
55 * SW7335-1368: don't allow non-zero rate to round down to zero (pause)
56 *
57 * 115   10/12/11 11:44a erickson
58 * SW7425-1264: convert decode_rate to abs before passing to
59 *  nexus_playback_trick.c. if trick params must be adjusted, actually
60 *  modify a copy of the struct for better debug.
61 *
62 * 114   10/12/11 9:32a erickson
63 * SW7425-1264: improve param checking for automatic and custom trick
64 *  modes
65 *
66 * 113   10/7/11 11:16a erickson
67 * SW7125-823: removing check for video pid channel when starting
68 *  timeshifting. valid usage modes exist where this is not true.
69 *
70 * 112   9/22/11 4:43p erickson
71 * SW7420-1965: add simple decoder support to nexus playback module
72 *
73 * 111   9/7/11 11:41a erickson
74 * SW7405-5059: fail stc trick if any decoder not in TSM mode
75 *
76 * 110   8/31/11 2:44p vsilyaev
77 * SW7420-1978: Add selection of index type for playback of MPEG-2 TS
78 *  files
79 *
80 * 109   8/30/11 12:46p vsilyaev
81 * SWDTV-7867: Use proper scale factor when converting rate to time
82 *
83 * 108   8/12/11 9:40a erickson
84 * SW7358-83: add NEXUS_PlaybackTrickModeSettings.brcmTrickMode
85 *
86 * 107   6/16/11 3:38p erickson
87 * SW7125-823: if playback lacks decoder, allow NEXUS_Playback_Seek to
88 *  succeed but issue a WRN
89 *
90 * 106   5/26/11 7:32p vsilyaev
91 * SW7425-646: Added configurable 'gap' for timeshifting
92 *
93 * 105   5/13/11 5:12p erickson
94 * SW7125-823: add support for decoder-less host trick modes with
95 *  nexus_playback
96 *
97 * 104   5/13/11 4:13p erickson
98 * SWDTV-6386: add "nx_" namespace prefix for internal nexus threads
99 *
100 * 103   4/27/11 10:34a erickson
101 * SW7405-4249: all audio-only timeshifting
102 *
103 * 102   4/14/11 3:31p jtna
104 * SWDTV-6321: fix build warnings for 2.6.37 kernel
105 *
106 * 101   3/28/11 3:45p vsilyaev
107 * SW7335-1214: Added NEXUS_CallbackHandler framework
108 *
109 * 100   2/24/11 1:36p vsilyaev
110 * SWDTV-5485: Differentiates ES and ES->PES types
111 *
112 * 99   11/24/10 3:10p vsilyaev
113 * SW35230-2195: Better support of 'start_paused'->pause transition and
114 *  cleaned error reporting when PTS can't be obtained
115 *
116 * 98   11/11/10 5:47p vsilyaev
117 * SW7405-4995: Use BKNI_Memcmp to preinitialize structure used in
118 *  GetDefaultSettings
119 *
120 * 97   11/2/10 7:10p mward
121 * SW7400-2920: playback should not flush when transitioning decoder skip
122 *  modes if there's no index.
123 *
124 * 96   10/22/10 11:46a vsilyaev
125 * SW35230-1600: When decrypting payload clear file buffer on transitions.
126 *
127 * 95   9/28/10 1:57p erickson
128 * SWDEPRECATED-1003: pass force source frame rate from media framework to
129 *  VideoDecoder
130 *
131 * 93   6/29/10 3:20p erickson
132 * SW7405-4367: playback should flush when transitioning decoder skip
133 *  modes. also added NEXUS_PlaybackTrickModeSettings.avoidFlush option to
134 *  disable this behavior.
135 *
136 * 92   6/1/10 3:07p vsilyaev
137 * SW7400-2787: Improving handling of 'start_paused'
138 *
139 * 91   4/29/10 11:55a vsilyaev
140 * SW7400-2749: If in 'start.paused' state don't start playback on
141 *  GetStatus
142 *
143 * 90   4/14/10 5:43p jtna
144 *  SW7125-317: Coverity Defect ID:20335 CHECKED_RETURN
145 *
146 * 89   3/11/10 5:26p vsilyaev
147 * SW3556-913: Always use header file to declare external functions
148 *
149 * 88   3/5/10 7:12p jtna
150 * SW3556-913: support for ErrorHandlingMode_eAbort
151 *
152 * 87   2/23/10 4:58p erickson
153 * SW7400-2684: add state variable so we only fire beginningOfStreamAction
154 *  or endOfStreamAction once when paused
155 *
156 * 86   2/22/10 6:55p vsilyaev
157 * SW3556-913: Don't call  b_play_check_buffer before reading status
158 *
159 * 85   2/22/10 6:50p vsilyaev
160 * SW3556-913: Return playback state even if in 'Aborted' or 'Paused'
161 *  state
162 *
163 * 84   2/18/10 12:05p vsilyaev
164 * SW3556-913: Differentiate between index error and end of stream
165 *
166 * 83   1/13/10 5:41p vsilyaev
167 * SW3556-913: Added option control error handling for Play/Seek/SetTrick
168 *  operations
169 *
170 * 82   1/12/10 8:13p vsilyaev
171 * SW3556-913: Don't sync pause with the playback data flow (file I/O)
172 *
173 * 81   12/17/09 6:16p vsilyaev
174 * SW3556-913: Added option to specify timeout on asynchronous I/O
175 *
176 * 80   12/17/09 5:52p vsilyaev
177 * SW3548-2677: Added configuration that allows user to choose what
178 *  context should be used for synchronous I/O
179 *
180 * 79   11/16/09 10:31a vsilyaev
181 * SW7405-3393 SW7400-2593: Use signed decode_rate for the host_paced mode
182 *
183 * 78   11/5/09 7:32p vsilyaev
184 * SW7400-2593: Updated lookup table and handling of slow rewind modes
185 *
186 * 77   11/5/09 11:37a vsilyaev
187 * SW7400-2593: Set trickModeSettings.rate to 0 while in the pause state
188 *
189 * 76   11/5/09 9:25a vsilyaev
190 * SW7400-2593: Populate trickModeSettings inside NEXUS_Playback_GetStatus
191 *
192 * 75   10/14/09 10:24a jtna
193 * SW7405-3205: fix typo in BDBG_ERR
194 *
195 * 74   9/30/09 7:12p vsilyaev
196 * SW7405-2732: Fixed communication between media player and nexux
197 *  playback when entering into the frame reverse mode
198 *
199 * 73   9/30/09 6:48p vsilyaev
200 * SW7405-2732: Added fragmented mode, for the decoder side it set using
201 *  Reoorder and filtering I frames only
202 *
203 * 72   9/9/09 12:04p erickson
204 * SW7400-2507: NEXUS_Playback_CloseAllPidChannels should call
205 *  NEXUS_Playback_ClosePidChannel so that cleanup logic doesn't have to
206 *  be duplicated
207 *
208 * 71   8/19/09 3:25p erickson
209 * PR57840: remove incorrect comment
210 *
211 * 70   8/10/09 3:36p ahulse
212 * PR56762: firstPts/Passed callbacks not registered in settop API mode,
213 *  move registration into b_play_trick_set_pid
214 *
215 * 69   7/31/09 4:29p erickson
216 * PR57235: rename Settop API names, remove unused code
217 *
218 * 68   7/31/09 12:36p katrep
219 * PR57193: stc_trick flag should be restored to requested value when
220 *  resuming playback from trickmodes
221 *
222 * 67   7/30/09 7:15p vsilyaev
223 * PR 55989: Do UnlockModule only after all playback componets where
224 *  shutdown, this prevents from stray IO callback to reach into closed
225 *  media player
226 *
227 * 66   7/21/09 5:57p vsilyaev
228 * PR 56977: Nexus playback always control audio and video decoder.
229 *  Provided way for the user to control the decoder slowmotion factor
230 *  using the playback trick API.
231 *
232 * 65   7/13/09 3:51p jtna
233 * PR56423: fix condition to set decoder trick state
234 *
235 * 64   7/8/09 3:25p vsilyaev
236 * PR 55989: Used size of the video decoder CDB to control size of the
237 *  data chunk
238 *
239 * 63   7/8/09 11:59a vsilyaev
240 * PR 55989: Added support for OTF trickmodes
241 *
242 * 62   6/19/09 4:56p vsilyaev
243 * PR 56169: Allow fast decode trickmodes even without STC trickmodes
244 *
245 * 61   6/19/09 4:11p vsilyaev
246 * PR 56169: PR 53824: Allow user to sepcify max decodder rate. This
247 *  superceeds overrideStcTrick.
248 *
249 * 60   6/11/09 4:31p jtna
250 * PR55817: make check on duplicate channels backward-compatible
251 *
252 * 59   6/8/09 3:47p erickson
253 * PR55817: relax the check on duplicate pid channels. they are allowed,
254 *  as long as they are simultaneously enabled. we still prevent duplicate
255 *  NEXUS_PidChannelHandle entries.
256 *
257 * 58   4/16/09 1:29p erickson
258 * PR54129: failed Seek is not an ERR. prevent eWaitingRecord loop at
259 *  beginning of circular record.
260 *
261 * 57   4/3/09 12:56p erickson
262 * PR53824: reenable stc trick modes. add
263 *  NEXUS_PlaybackTrickModeSettings.overrideStcTrick for additional app
264 *  control.
265 *
266 * 56   3/27/09 12:51p vsilyaev
267 * PR 50311: Return readPosition in the Nexus playback status. In
268 *  combination with the position it could be used to measure depth of
269 *  decoder and playback buffers in units of time.
270 *
271 * 55   3/6/09 1:55p erickson
272 * PR52891: remove unnecessary call to b_play_trick_set_pid inside
273 *  NEXUS_Playback_OpenPidChannel
274 *
275 * 54   3/5/09 5:46p vsilyaev
276 * PR 52579: Updated circular FIFO/timeshifting code
277 *
278 * 53   2/26/09 9:03a erickson
279 * PR52099: block all TrickState calls to AudioDecoder and VideoDecoder if
280 *  NEXUS_PlaybackHostTrickMode_eNormal is set. also removed some old,
281 *  unused code.
282 *
283 * 52   2/25/09 11:19a katrep
284 * PR52099: Need to take into account decoder trick modes. Audio needs to
285 *  be muted.
286 *
287 * 51   2/19/09 11:52a erickson
288 * PR52290: Playback should fail on certain operations if there's no
289 *  eAudio or eVideo pid set
290 *
291 * 50   2/13/09 11:49a erickson
292 * PR52107: fix NEXUS_Playback_Seek when in startPaused state
293 *
294 * 49   2/5/09 4:48p erickson
295 * PR51870: fix uninitialized variable
296 *
297 * 48   2/2/09 3:35p gmohile
298 * PR 47815 : Add manual control of trickmodes
299 *
300 * 47   2/2/09 2:53p gmohile
301 * PR 47815 : Add manual control of trickmodes
302 *
303 * 46   1/27/09 4:04p erickson
304 * PR51377: NEXUS_Playback_Start or NEXUS_Playback_OpenPidChannel should
305 *  reset VideoDecoder's trick state
306 *
307 * 45   1/26/09 11:38a vsilyaev
308 * PR 51579: Added ES to PES packetization and support for capturing of
309 *  streams produced by the playback module
310 *
311 * 44   1/12/09 5:45p vsilyaev
312 * PR 50763: Improved seek to position 0 after rewind reached begining of
313 *  the file
314 *
315 * 43   1/9/09 1:55a erickson
316 * PR49294: don't allow NEXUS_PlaybackSettings.stcTrick to change while
317 *  started
318 *
319 * 42   12/19/08 5:59p vsilyaev
320 * PR 50214: Added callbacks and counters for parsing index files
321 *
322 * 41   12/15/08 2:41p erickson
323 * PR50369: if b_play_trick_set fails, we can't abort all Playback state
324 *  changes otherwise we end up with a stray NEXUS_Playpump_ReadComplete.
325 *
326 * 40   12/10/08 4:42p erickson
327 * PR49930: add internal accurate seek algorithm for driving audio through
328 *  the transition
329 *
330 * 39   12/10/08 11:11a erickson
331 * PR49930: added NEXUS_Playback_AccurateSeekInProcess test api, added
332 *  required flush before calling NEXUS_VideoDecoder_SetStartPts
333 *
334 * 38   12/10/08 2:06a vsilyaev
335 * PR 48760: Using ES player for accurate progress monitor of MP3 streams
336 *
337 * 37   12/8/08 11:19a erickson
338 * PR49930: implement NEXUS_PlaybackSettings.accurateSeek
339 *
340 * 36   11/21/08 12:06p erickson
341 * PR49531: corrected comment
342 *
343 * 35   11/21/08 8:54a erickson
344 * PR49531: cancel playback timer before stopping Playpump so that no
345 *  additional calls to Playpump are made after stop
346 *
347 * 34   11/19/08 1:50p erickson
348 * PR48744: don't require
349 *  NEXUS_PlaybackPidChannelSettings.pidTypeSettings.video.index for
350 *  decoder trick modes
351 *
352 * 33   11/18/08 4:58p erickson
353 * PR47854: call NEXUS_FilePlay_Cancel if waiting for io times out. allow
354 *  Playback to recover from eAborted state.
355 *
356 * 32   11/5/08 2:37p erickson
357 * PR47407: fix warning
358 *
359 * 31   11/4/08 5:33p erickson
360 * PR47407: remove call to b_play_trick_set and replace with direct
361 *  assignment
362 *
363 * 30   10/23/08 3:52p erickson
364 * PR48149: fix NEXUS_Playback_P_CheckWaitingIo state, don't force
365 *  eStopped state
366 *
367 * 29   10/21/08 2:14p erickson
368 * PR48149: implement algo to get out of eWaitingIo state
369 *
370 * 28   10/14/08 5:01p erickson
371 * PR47407: only reapply audio decoder trick modes if already started
372 *
373 * 27   10/14/08 3:36p erickson
374 * PR47407: reapply audio decoder trick mode settings when pid channel is
375 *  opened or set
376 *
377 * 26   10/9/08 2:54p erickson
378 * PR47608: move location of stcChannel test
379 *
380 * 25   10/9/08 2:23p erickson
381 * PR47608: NEXUS_PlaybackSettings.stcChannel is now required for TSM
382 *  playback. NEXUS_PlaybackSettings.stcTrick was added for stc trick
383 *  modes.
384 *
385 * 24   10/6/08 11:48a gmohile
386 * PR 47608 : Invalidate STC channel during flush
387 *
388 * 23   9/18/08 6:28p katrep
389 * PR45880: Add support for FSPB(80/120 playback)
390 *
391 * 22   7/28/08 3:34p katrep
392 * PR43242: Allow changes of timeshifting flag while playback is in
393 *  progress
394 *
395 * 21   7/24/08 4:34p erickson
396 * PR45124: allow duplicate calls to NEXUS_Playback_OpenPidChannel and
397 *  ref_cnt internally
398 *
399 * 20   7/3/08 1:07p vsilyaev
400 * PR 44381: Added code to limi nestendess of recursive calls
401 *
402 * 19   6/24/08 4:07p erickson
403 * PR44107: rename to TrickMode
404 *
405 * 18   6/12/08 3:02p erickson
406 * PR43606: rename NEXUS_PlaybackStreamSettings to
407 *  NEXUS_PlaybackStartSettings
408 *
409 * 17   5/30/08 2:56p erickson
410 * PR43000: fix default frame advance state at Start
411 *
412 * 16   5/22/08 7:25p vsilyaev
413 * PR 42984: Wait for I/O completion shall be unconditional
414 *
415 * 15   5/21/08 2:37p erickson
416 * PR41960: NEXUS_Playback_Start wasn't resetting its state to allow for
417 *  the next Start.
418 *
419 * 14   5/15/08 4:38p erickson
420 * PR42119: default trickmode_params correctly
421 *
422 * 13   5/7/08 4:20p erickson
423 * PR42329: unlock playback inside NEXUS_Playback_Stop to avoid deadlock
424 *  with playpump callbacks
425 *
426 * 12   5/5/08 5:42p vsilyaev
427 * PR 42355: Fixed transition from trickmode to frame advance/reverse
428 *
429 * 11   5/5/08 4:46p vsilyaev
430 * PR 42355: Fixed rewind for AVI files
431 *
432 * 10   4/25/08 1:24p erickson
433 * PR41951: NEXUS_Playback_ClosePidChannel should return void
434 *
435 * 9   4/17/08 4:26p vsilyaev
436 * PR 41845: Hold playpump callbacks if not playing data
437 *
438 * 8   4/10/08 5:08p erickson
439 * PR40832: fix eWaitingRecord state
440 *
441 * 7   4/10/08 9:16a erickson
442 * PR41589: check rc
443 *
444 * 6   4/9/08 1:09p jgarrett
445 * PR 41557: Fixing GetDefaultPidChannelSettings
446 *
447 * 5   4/7/08 12:04p erickson
448 * PR41431: only fail stcChannel change during trick mode
449 *
450 * 4   3/17/08 4:09p vsilyaev
451 * PR 40554: In broadcom trikcmodes use decode_rate from the media player
452 *
453 * 3   3/8/08 7:45a erickson
454 * PR40103: convert to NEXUS_TaskCallback
455 *
456 * 2   3/7/08 5:34p erickson
457 * PR40307: don't return error on success
458 *
459 * 1   1/18/08 2:36p jgarrett
460 * PR 38808: Merging to main branch
461 *
462 * Nexus_Devel/26   1/8/08 2:23p vsilyaev
463 * PR 35824: Fixed resync on transition between modes
464 *
465 * Nexus_Devel/25   1/7/08 5:09p erickson
466 * PR35824: add new continuous record support
467 *
468 * Nexus_Devel/24   12/28/07 5:06p erickson
469 * PR37574: return state in status
470 *
471 * Nexus_Devel/23   12/21/07 5:55p vsilyaev
472 * PR 38073: Fixed fa -> fb -> play transitions
473 *
474 * Nexus_Devel/22   12/20/07 5:24p vsilyaev
475 * PR 38073: Improved handling of trickmodes
476 *
477 * Nexus_Devel/21   12/20/07 3:41p vsilyaev
478 * PR 38073: Improving use of bmedia_player
479 *
480 * Nexus_Devel/20   12/20/07 10:29a vsilyaev
481 * PR 38073: Updated to work with new bmedia_player
482 *
483 * Nexus_Devel/PR38073/3   12/19/07 5:01p vsilyaev
484 * PR 38073: Moved time tracking code for host paced mode into the
485 * bmedia_player
486 *
487 * Nexus_Devel/PR38073/2   12/18/07 6:45p vsilyaev
488 * PR 38073: Added handling of no-index streams
489 *
490 * Nexus_Devel/PR38073/1   12/18/07 4:57p vsilyaev
491 * PR 38073: Updated playback module to work exclusively with media player
492 *
493 * Nexus_Devel/19   12/6/07 5:24p vsilyaev
494 * PR 37574: Added handling of segmented feed
495 *
496 * Nexus_Devel/18   12/5/07 3:09p vsilyaev
497 * PR 37934: Implemented BLST_S_DICT
498 *
499 * Nexus_Devel/17   12/3/07 4:43p erickson
500 * PR36404: restore start_paused code. fix status fifoSize.
501 *
502 * Nexus_Devel/16   11/16/07 12:38p vsilyaev
503 * PR 35824: Added STC trickmodes
504 *
505 * Nexus_Devel/15   11/13/07 3:31p vsilyaev
506 * PR 37015: At start Seek index to 0 offset
507 *
508 * Nexus_Devel/14   11/13/07 12:21p erickson
509 * PR36788: fixed frameadvance. disabled start_paused check.
510 *
511 * Nexus_Devel/13   11/9/07 6:02p vsilyaev
512 * PR 36788: Added timeshifting
513 *
514 * Nexus_Devel/12   11/1/07 9:41a erickson
515 * PR36633: base enum changes
516 *
517 * Nexus_Devel/11   10/30/07 5:05p vsilyaev
518 * PR 36404: Added audio tracking for slow motion modes
519 *
520 * Nexus_Devel/10   10/19/07 11:21a vsilyaev
521 * PR 35824: Fixed data corruption on trick modes
522 *
523 * Nexus_Devel/9   10/17/07 5:38p vsilyaev
524 * PR 35824: Added trickmode test
525 *
526 * Nexus_Devel/8   10/17/07 1:05p vsilyaev
527 * PR 35824: Added playpump_trick
528 *
529 * Nexus_Devel/7   10/16/07 4:59p vsilyaev
530 * PR 35824: Rearranged code
531 *
532 * Nexus_Devel/6   10/16/07 12:59p vsilyaev
533 * PR 35824: Splitting playback into managable piecies
534 *
535 * Nexus_Devel/5   10/15/07 5:12p vsilyaev
536 * PR 35824: Added module initialization
537 *
538 * Nexus_Devel/4   10/15/07 2:55p vsilyaev
539 * PR 35824: Added synchronization thunk layer
540 *
541 * Nexus_Devel/2   10/11/07 6:25p vsilyaev
542 * PR 35824: Added more playback code
543 *
544 * Nexus_Devel/1   10/10/07 4:54p vsilyaev
545 * PR 35824: Playback module
546 *
547 * $copied_brcm_Log: /BSEAV/api/src/pvr/bsettop_playback.c $
548 * $copied_brcm_Revision: 172 $
549 * $copied_brcm_Date: 10/1/07 11:13a $
550 **************************************************************************/
551#include "nexus_playback_module.h"
552#include "nexus_playback_impl.h"
553#include "nexus_core_utils.h"
554
555BDBG_MODULE(nexus_playback);
556
557/* We have a separate dbg macro for data flow. When you want to see it, uncomment
558the following */
559#define BDBG_MSG_FLOW(X) /* BDBG_MSG(X) */
560
561
562
563BDBG_OBJECT_ID(NEXUS_Playback);
564
565/*
566 * the following function is used to control playback dataflow
567 * and handle various transition between trickmodes
568 */
569static NEXUS_Error bplay_p_frameadvance(NEXUS_PlaybackHandle p, bool forward, bool *restart);
570static void b_play_playpump_read_callback_locked(void *context);
571static NEXUS_Error b_play_playpump_read_callback_guard(void *context);
572
573void
574NEXUS_Playback_GetDefaultSettings( NEXUS_PlaybackSettings *pSettings)
575{
576    BKNI_Memset(pSettings, 0, sizeof(*pSettings));
577    pSettings->playpump = NULL;
578    NEXUS_Playpump_GetDefaultSettings(&pSettings->playpumpSettings);
579    pSettings->endOfStreamAction = NEXUS_PlaybackLoopMode_eLoop;
580    pSettings->stcChannel = NULL;
581    pSettings->beginningOfStreamAction = NEXUS_PlaybackLoopMode_eLoop;
582    NEXUS_CallbackDesc_Init(&pSettings->endOfStreamCallback);
583    NEXUS_CallbackDesc_Init(&pSettings->beginningOfStreamCallback);
584    NEXUS_CallbackDesc_Init(&pSettings->errorCallback);
585    NEXUS_CallbackDesc_Init(&pSettings->parsingErrorCallback);
586    pSettings->startPaused = false;
587    pSettings->timeshifting = false;
588    pSettings->stcTrick = false;
589    pSettings->accurateSeek = false;
590    pSettings->enableStreamProcessing = false;
591    pSettings->ioTimeout = 5000; /* 5 sec */
592    pSettings->playErrorHandling = NEXUS_PlaybackErrorHandlingMode_eEndOfStream;
593    pSettings->seekErrorHandling = NEXUS_PlaybackErrorHandlingMode_eIgnore;
594    pSettings->trickErrorHandling = NEXUS_PlaybackErrorHandlingMode_eIgnore;
595    /* milliseconds to gap between end of playback and record which will not cause decode stuttering */
596    pSettings->timeshiftingSettings.endOfStreamGap = 3000;
597    /* milliseconds to gap between beginning of playback and truncating record which will not cause excess beginningOfStream events */
598    pSettings->timeshiftingSettings.beginningOfStreamGap = 5000;
599
600    return;
601}
602
603NEXUS_PlaybackHandle
604NEXUS_Playback_Create( void )
605{
606    NEXUS_PlaybackHandle playback;
607    NEXUS_Error rc;
608
609    playback = BKNI_Malloc(sizeof(*playback));
610    if (!playback) { rc = BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY); goto err_alloc; }
611
612    BDBG_OBJECT_INIT(playback,NEXUS_Playback);
613    BKNI_Memset(&playback->state, 0, sizeof(playback->state));
614
615    playback->state.state = eStopped;
616    BLST_S_INIT(&playback->pid_list);
617    playback->file = NULL;
618    playback->media_player = NULL;
619    playback->thread_terminate = false;
620    playback->playpump_thread_event = NULL;
621    playback->playpump_thread = NULL;
622    playback->playpump_event_callback = NULL;
623    playback->playpump_event = NULL;
624    playback->index_file_mode = NEXUS_PlaybackIndexFileIo_eCallback;
625    playback->actualTransportType = NEXUS_TransportType_eTs;
626    NEXUS_CallbackHandler_Init(playback->dataCallbackHandler, b_play_playpump_read_callback_locked, playback);
627    NEXUS_CallbackHandler_SetGuard(playback->dataCallbackHandler, b_play_playpump_read_callback_guard);
628    NEXUS_CallbackHandler_Init(playback->videoDecoderFirstPts, NEXUS_Playback_P_VideoDecoderFirstPts, playback);
629    NEXUS_CallbackHandler_Init(playback->videoDecoderFirstPtsPassed, NEXUS_Playback_P_VideoDecoderFirstPtsPassed, playback);
630
631    NEXUS_Thread_GetDefaultSettings(&playback->playpump_thread_settings);
632    NEXUS_Playback_GetDefaultTrickModeSettings(&playback->state.trickmode_params);
633    playback->errorCallback = NEXUS_TaskCallback_Create(playback, NULL);
634    if(!playback->errorCallback) {rc=BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);goto err_error_callback;}
635    playback->endOfStreamCallback = NEXUS_TaskCallback_Create(playback, NULL);
636    if(!playback->endOfStreamCallback) {rc=BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);goto err_end_of_stream_callback;}
637    playback->beginningOfStreamCallback = NEXUS_TaskCallback_Create(playback, NULL);
638    if(!playback->beginningOfStreamCallback) {rc=BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);goto err_beginning_of_stream_callback;}
639    playback->parsingErrorCallback = NEXUS_TaskCallback_Create(playback, NULL);
640    if(!playback->parsingErrorCallback) {rc=BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);goto err_parsing_error_callback;}
641
642    NEXUS_Playback_GetDefaultSettings(&playback->params);
643
644    rc = BKNI_CreateEvent(&playback->ack_event);
645    if(rc!=BERR_SUCCESS) { rc = BERR_TRACE(rc); goto err_ack_event;}
646
647    playback->playpump_event = NULL;
648    playback->playpump_event_callback = NULL;
649
650    b_play_media_time_test();
651#if B_PLAYBACK_CAPTURE
652    playback->capture.has_data = false;
653    playback->capture.no = 0;
654    playback->capture.fout = NULL;
655#endif
656
657    return playback;
658
659err_ack_event:
660    NEXUS_TaskCallback_Destroy(playback->parsingErrorCallback);
661err_parsing_error_callback:
662    NEXUS_TaskCallback_Destroy(playback->beginningOfStreamCallback);
663err_beginning_of_stream_callback:
664    NEXUS_TaskCallback_Destroy(playback->endOfStreamCallback);
665err_end_of_stream_callback:
666    NEXUS_TaskCallback_Destroy(playback->errorCallback);
667err_error_callback:
668    BKNI_Free(playback);
669err_alloc:
670    return NULL;
671}
672
673
674void
675NEXUS_Playback_ClosePidChannel(NEXUS_PlaybackHandle playback, NEXUS_PidChannelHandle pidChannel)
676{
677    NEXUS_Error rc;
678    NEXUS_Playback_P_PidChannel *play_pid;
679
680    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
681
682    BLST_S_DICT_FIND(&playback->pid_list, play_pid, pidChannel, pidChn, link);
683    if(play_pid==NULL) {
684        BDBG_WRN(("NEXUS_Playback_ClosePidChannel: %#lx can't found pid:%#lx", (unsigned long)playback, (unsigned long)pidChannel));
685        rc = BERR_TRACE(NEXUS_INVALID_PARAMETER);
686        return;
687    }
688
689    if (play_pid->cfg.pidSettings.pidType == NEXUS_PidType_eVideo) {
690        NEXUS_VideoDecoderPlaybackSettings playbackSettings;
691        NEXUS_P_Playback_VideoDecoder_GetPlaybackSettings(play_pid, &playbackSettings);
692        playbackSettings.firstPts.callback = NULL;
693        playbackSettings.firstPtsPassed.callback = NULL;
694        (void)NEXUS_P_Playback_VideoDecoder_SetPlaybackSettings(play_pid, &playbackSettings);
695        NEXUS_CallbackHandler_Stop(playback->videoDecoderFirstPts);
696        NEXUS_CallbackHandler_Stop(playback->videoDecoderFirstPtsPassed);
697    }
698
699    BDBG_ASSERT(play_pid->ref_cnt>0);
700    play_pid->ref_cnt--;
701    if(play_pid->ref_cnt==0) {
702        BLST_S_DICT_REMOVE(&playback->pid_list, play_pid, pidChannel, NEXUS_Playback_P_PidChannel, pidChn, link);
703        BDBG_ASSERT(play_pid);
704        BDBG_ASSERT(playback->params.playpump);
705        rc = NEXUS_Playpump_ClosePidChannel(playback->params.playpump, play_pid->pidChn);
706        if(rc!=NEXUS_SUCCESS) {rc = BERR_TRACE(rc);}
707        BKNI_Free(play_pid);
708    }
709    return;
710}
711
712void
713NEXUS_Playback_CloseAllPidChannels(NEXUS_PlaybackHandle playback)
714{
715    NEXUS_Playback_P_PidChannel *pid;
716
717    /* clear all pid channels */
718    while(NULL!=(pid = BLST_S_FIRST(&playback->pid_list))) {
719        /* use NEXUS_Playback_ClosePidChannel so that other cleanup will happen */
720        NEXUS_Playback_ClosePidChannel(playback, pid->pidChn);
721    }
722    return;
723}
724
725void
726NEXUS_Playback_GetDefaultPidChannelSettings(NEXUS_PlaybackPidChannelSettings *pSettings)
727{
728    BDBG_ASSERT(pSettings);
729    BKNI_Memset(pSettings, 0, sizeof(*pSettings));
730    NEXUS_Playpump_GetDefaultOpenPidChannelSettings(&pSettings->pidSettings);
731    BDBG_ASSERT(pSettings->pidSettings.pidType == NEXUS_PidType_eUnknown);
732    return;
733}
734
735NEXUS_PidChannelHandle
736NEXUS_Playback_OpenPidChannel(NEXUS_PlaybackHandle playback, unsigned pidNo, const NEXUS_PlaybackPidChannelSettings *pSettings)
737{
738    NEXUS_PlaybackPidChannelSettings settings;
739    NEXUS_Playback_P_PidChannel *play_pid;
740    NEXUS_Error rc;
741    unsigned playpump_pidNo;
742
743    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
744    if(pSettings==NULL) {
745        NEXUS_Playback_GetDefaultPidChannelSettings(&settings);
746        pSettings = &settings;
747    }
748
749    /* if pidChannelIndex is specified, this is a special case that requires the app to manage its pid channels more. */
750    if (pSettings->pidSettings.pidSettings.pidChannelIndex != -1) {
751        NEXUS_PidChannelStatus pidStatus;
752        /* look up existing pid channels for the same pidChannelIndex */
753        for(play_pid=BLST_S_FIRST(&playback->pid_list); play_pid; play_pid=BLST_S_NEXT(play_pid, link)) {
754            rc = NEXUS_PidChannel_GetStatus(play_pid->pidChn, &pidStatus);
755            if(rc==NEXUS_SUCCESS && (int)pidStatus.pidChannelIndex == pSettings->pidSettings.pidSettings.pidChannelIndex) {
756                if(play_pid->pid != pidNo) {
757                    BDBG_ERR(("Cannot open the same pidChannelIndex (%d) with different pids (0x%x and 0x%x)", pSettings->pidSettings.pidSettings.pidChannelIndex, play_pid->pid, pidNo));
758                    return NULL;
759                }
760                play_pid->ref_cnt++;
761                goto done;
762            }
763        }
764    /* else, allow the new NEXUS_PidChannelHandle to be opened, even if a NEXUS_PidChannelHandle already exists for this pid on a different pidChannelIndex.
765       This is needed to support playpump multiplexing. */
766    }
767    else {
768        for(play_pid=BLST_S_FIRST(&playback->pid_list); play_pid; play_pid=BLST_S_NEXT(play_pid, link)) {
769            if(play_pid->pid == pidNo) {
770                BDBG_MSG(("NEXUS_Playback_OpenPidChannel: %#lx detected duplicated pid %u (%#lx:%u)", (unsigned long)playback, (unsigned)pidNo, (unsigned long)play_pid, play_pid->ref_cnt));
771                if(BKNI_Memcmp(pSettings, &play_pid->cfg, sizeof(*pSettings))!=0) {
772                    BDBG_WRN(("NEXUS_Playback_OpenPidChannel: %#lx detected duplicated pid %u (%#lx:%u) with non-compatible settings", (unsigned long)playback, (unsigned)pidNo, (unsigned long)play_pid, play_pid->ref_cnt));
773                }
774                play_pid->ref_cnt++;
775                goto done;
776            }
777        }
778    }
779
780    if(playback->params.playpump==NULL) {
781        BDBG_WRN(("NEXUS_Playback_OpenPidChannel: %#lx pid could only be open if playpump is attached", (unsigned long)playback));
782        rc = BERR_TRACE(BERR_NOT_SUPPORTED);
783        goto err_playpump;
784    }
785    play_pid = BKNI_Malloc(sizeof(*play_pid));
786    if(play_pid==NULL) { rc=BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);goto err_alloc;}
787    playpump_pidNo = pidNo;
788
789    switch(playback->params.playpumpSettings.transportType) {
790    default:
791        break;
792#if 0
793    case NEXUS_TransportType_eMp4:
794    case NEXUS_TransportType_eMkv:
795        break;
796#endif
797    case NEXUS_TransportType_eEs:
798        if(playback->params.enableStreamProcessing) {
799            playpump_pidNo = 0xC0;
800        }
801        break;
802    }
803
804    BDBG_MSG(("NEXUS_Playback_OpenPidChannel: %#lx pid %#lx mapped %u->%#x", (unsigned long)playback, (unsigned long)play_pid, (unsigned)pidNo, (unsigned)playpump_pidNo));
805    play_pid->pidChn = NEXUS_Playpump_OpenPidChannel(playback->params.playpump, playpump_pidNo, &pSettings->pidSettings);
806    if(!play_pid->pidChn) { rc = BERR_TRACE(BERR_NOT_SUPPORTED);goto err_open_pid;}
807    play_pid->pid = pidNo;
808    play_pid->ref_cnt = 1;
809    play_pid->cfg = *pSettings;
810
811    BLST_S_DICT_ADD(&playback->pid_list, play_pid, NEXUS_Playback_P_PidChannel, pidChn, link, err_duplicate);
812
813done:
814    return play_pid->pidChn;
815
816err_duplicate:
817    rc = BERR_TRACE(BERR_INVALID_PARAMETER);
818    NEXUS_Playpump_ClosePidChannel(playback->params.playpump, play_pid->pidChn);
819err_open_pid:
820    BKNI_Free(play_pid);
821err_alloc:
822err_playpump:
823    return NULL;
824
825}
826
827NEXUS_Error
828NEXUS_Playback_GetPidChannelSettings(NEXUS_PlaybackHandle playback, NEXUS_PidChannelHandle pidChannel, NEXUS_PlaybackPidChannelSettings *pSettings)
829{
830    const NEXUS_Playback_P_PidChannel *pid;
831    NEXUS_Error rc;
832    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
833    BDBG_ASSERT(pSettings);
834
835    BLST_S_DICT_FIND(&playback->pid_list, pid, pidChannel, pidChn, link);
836    if(!pid) {
837        rc = BERR_TRACE(NEXUS_INVALID_PARAMETER);
838        goto err_invalid_pid;
839    }
840    *pSettings = pid->cfg;
841    return BERR_SUCCESS;
842err_invalid_pid:
843    return rc;
844}
845
846NEXUS_Error
847NEXUS_Playback_SetPidChannelSettings( NEXUS_PlaybackHandle playback, NEXUS_PidChannelHandle pidChannel, const NEXUS_PlaybackPidChannelSettings *pSettings)
848{
849    NEXUS_Playback_P_PidChannel *pid;
850    NEXUS_Error rc;
851    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
852    BDBG_ASSERT(pSettings);
853
854    BLST_S_DICT_FIND(&playback->pid_list, pid, pidChannel, pidChn, link);
855    if(!pid) { rc = BERR_TRACE(NEXUS_INVALID_PARAMETER); goto err_invalid_pid; }
856
857    if(BKNI_Memcmp(&pid->cfg.pidSettings, &pSettings->pidSettings, sizeof(pSettings->pidSettings))!=0) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);goto err_settings; } /* we can't change playpump pid configuration */
858    pid->cfg.pidTypeSettings = pSettings->pidTypeSettings;
859
860    if (playback->state.state != eStopped) {
861        /* reapply current decoder settings */
862        rc = b_play_trick_set_pid(playback, pid, NULL);
863        if (rc) {rc = BERR_TRACE(rc); goto err_settings;}
864    }
865
866    return BERR_SUCCESS;
867
868err_settings:
869err_invalid_pid:
870    return rc;
871}
872
873static void
874NEXUS_Playback_P_KillPlaypumpThread(NEXUS_PlaybackHandle playback)
875{
876    playback->thread_terminate = true;
877    BDBG_ASSERT(playback->playpump_thread_event);
878    BKNI_SetEvent(playback->playpump_thread_event);
879    NEXUS_UnlockModule();
880    BDBG_MSG(("killing playpump_thread ..."));
881    NEXUS_Thread_Destroy(playback->playpump_thread);
882    BDBG_MSG(("killing playpump_thread ... done"));
883    NEXUS_LockModule();
884    playback->playpump_thread = NULL;
885    return;
886}
887
888
889void
890NEXUS_Playback_Destroy( NEXUS_PlaybackHandle playback)
891{
892    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
893
894    if(playback->state.state != eStopped) {
895        BDBG_WRN(("NEXUS_Playback_Destroy: destroying playback without stopping"));
896        NEXUS_Playback_Stop(playback);
897    }
898    NEXUS_Playback_CloseAllPidChannels(playback);
899
900    if(playback->playpump_thread) {
901        NEXUS_Playback_P_KillPlaypumpThread(playback);
902    }
903    if(playback->playpump_thread_event) {
904        BKNI_DestroyEvent(playback->playpump_thread_event);
905    }
906    if(playback->playpump_event) {
907        BDBG_ASSERT(playback->playpump_event_callback);
908        NEXUS_UnregisterEvent(playback->playpump_event_callback);
909        BKNI_DestroyEvent(playback->playpump_event);
910    }
911    BKNI_DestroyEvent(playback->ack_event);
912    NEXUS_CallbackHandler_Shutdown(playback->dataCallbackHandler);
913    NEXUS_CallbackHandler_Shutdown(playback->videoDecoderFirstPts);
914    NEXUS_CallbackHandler_Shutdown(playback->videoDecoderFirstPtsPassed);
915    NEXUS_TaskCallback_Destroy(playback->parsingErrorCallback);
916    NEXUS_TaskCallback_Destroy(playback->errorCallback);
917    NEXUS_TaskCallback_Destroy(playback->endOfStreamCallback);
918    NEXUS_TaskCallback_Destroy(playback->beginningOfStreamCallback);
919
920    BDBG_OBJECT_DESTROY(playback, NEXUS_Playback);
921    BKNI_Free(playback);
922    return;
923}
924
925
926static int
927NEXUS_P_Playback_GetPriority(void *cntx)
928{
929    NEXUS_PlaybackHandle playback=cntx;
930    NEXUS_PlaypumpStatus status;
931    NEXUS_Error rc;
932    int factor;
933
934    /* no b_lock here because the state check is atomic and bplaypump_get_status
935    has its own synchronization. */
936    if (playback->state.mode == NEXUS_PlaybackState_ePlaying) {
937        BDBG_ASSERT(playback->params.playpump);
938
939        rc = NEXUS_Playpump_GetStatus(playback->params.playpump, &status);
940        if(rc==NEXUS_SUCCESS) {
941            factor  = (status.fifoSize - status.fifoDepth)/(status.fifoSize/256); /* priority is 0 .. 256 */
942            /* make read priority grow linearly with space in the fifo*/
943            BDBG_MSG_FLOW(("[%#x] level %d%% priority %d", playback, status.fifoDepth*100/status.fifoSize, factor));
944     } else {
945            factor = 0;
946        }
947    } else {
948        factor = 32;
949    }
950
951    return factor;
952}
953
954
955/**
956Relocate BNAV_Player to the current decode location.
957Call these before changing pvr state in order to get the current pts.
958**/
959void
960b_play_update_location(NEXUS_PlaybackHandle p)
961{
962    NEXUS_Error rc;
963    uint32_t pts;
964
965    rc = b_play_getpts(p, &pts);
966    if(rc==NEXUS_SUCCESS) {
967        p->state.validPts = true;
968        bmedia_player_update_position(p->media_player, pts);
969    } else {
970        p->state.validPts = false;
971        BDBG_MSG(("Can't reset location because no pts."));
972    }
973    return;
974}
975
976const NEXUS_Playback_P_PidChannel *
977b_play_get_video_decoder(NEXUS_PlaybackHandle playback)
978{
979    const NEXUS_Playback_P_PidChannel *pid;
980    for(pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
981        if(pid->cfg.pidSettings.pidType == NEXUS_PidType_eVideo) {
982            if (pid->cfg.pidTypeSettings.video.decoder || pid->cfg.pidTypeSettings.video.simpleDecoder) return pid;
983        }
984    }
985    return NULL;
986}
987
988static bool
989b_play_has_audio_decoder(NEXUS_PlaybackHandle playback)
990{
991    const NEXUS_Playback_P_PidChannel *pid;
992    for(pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
993        if(pid->cfg.pidSettings.pidType == NEXUS_PidType_eAudio) {
994            if (pid->cfg.pidTypeSettings.audio.primary || pid->cfg.pidTypeSettings.audio.secondary || pid->cfg.pidTypeSettings.audio.simpleDecoder) {
995                return true;
996            }
997        }
998    }
999    return false;
1000}
1001
1002bool
1003b_play_decoders_in_tsm_mode(NEXUS_PlaybackHandle playback)
1004{
1005    NEXUS_Error rc;
1006    const NEXUS_Playback_P_PidChannel *pid;
1007    for(pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
1008        if(pid->cfg.pidSettings.pidType == NEXUS_PidType_eVideo) {
1009            NEXUS_VideoDecoderStatus status;
1010            rc = NEXUS_P_Playback_VideoDecoder_GetStatus(pid, &status);
1011            if (rc) return false;
1012            if (!status.tsm) return false;
1013        }
1014        else if(pid->cfg.pidSettings.pidType == NEXUS_PidType_eAudio) {
1015            NEXUS_AudioDecoderStatus status;
1016            rc = NEXUS_P_Playback_AudioDecoder_GetStatus(pid, &status);
1017            if (rc) return false;
1018            if (!status.tsm) return false;
1019        }
1020    }
1021    return true;
1022}
1023
1024NEXUS_Error
1025b_play_getpts(NEXUS_PlaybackHandle p, uint32_t *pts)
1026{
1027    NEXUS_Error rc;
1028    NEXUS_Error result=NEXUS_NOT_SUPPORTED;
1029    const NEXUS_Playback_P_PidChannel *pid;
1030
1031    BDBG_ASSERT(pts);
1032    *pts = 0;
1033
1034    /* check video before checking audio */
1035    pid = b_play_get_video_decoder(p);
1036    if (pid) {
1037        NEXUS_VideoDecoderStatus status;
1038        rc = NEXUS_P_Playback_VideoDecoder_GetStatus(pid, &status);
1039        if(rc==NEXUS_SUCCESS) {
1040            if(status.ptsType == NEXUS_PtsType_eCoded || status.ptsType == NEXUS_PtsType_eInterpolatedFromValidPTS || p->actualTransportType==NEXUS_TransportType_eEs) {
1041                *pts = status.pts;
1042                result = NEXUS_SUCCESS;
1043            }
1044        }
1045    } else {
1046        bool found = false;
1047        for(pid = BLST_S_FIRST(&p->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
1048            if(pid->cfg.pidSettings.pidType == NEXUS_PidType_eAudio) {
1049                NEXUS_AudioDecoderStatus audioStatus;
1050                found = true;
1051                rc = NEXUS_P_Playback_AudioDecoder_GetStatus(pid, &audioStatus);
1052                if(rc==BERR_SUCCESS) {
1053                    if(audioStatus.ptsType == NEXUS_PtsType_eCoded || audioStatus.ptsType == NEXUS_PtsType_eInterpolatedFromValidPTS || p->actualTransportType==NEXUS_TransportType_eEs) {
1054                        *pts = audioStatus.pts;
1055                        result = NEXUS_SUCCESS;
1056                        break;
1057                    }
1058                }
1059            }
1060        }
1061        if(!found) {
1062            BDBG_WRN(("Unable to look up position because no audio decoder was provided in any NEXUS_PlaybackPidChannelSettings."));
1063        }
1064    }
1065    return result;
1066}
1067
1068
1069
1070void
1071NEXUS_Playback_GetDefaultTrickModeSettings(NEXUS_PlaybackTrickModeSettings *trickmode)
1072{
1073    BKNI_Memset(trickmode, 0, sizeof(*trickmode));
1074    trickmode->mode = NEXUS_PlaybackHostTrickMode_eNone;
1075    trickmode->rate = NEXUS_NORMAL_PLAY_SPEED;
1076    trickmode->mode_modifier = 1;
1077    trickmode->maxDecoderRate = 1.2 * NEXUS_NORMAL_PLAY_SPEED;
1078    trickmode->rateControl = NEXUS_PlaybackRateControl_eDecoder;
1079    trickmode->skipControl = NEXUS_PlaybackSkipControl_eHost;
1080    trickmode->brcmTrickMode = true;
1081    return;
1082}
1083
1084static int bplay_p_accurate_seek(NEXUS_PlaybackHandle p, bmedia_player_pos position)
1085{
1086    uint32_t pts = 0;
1087    const NEXUS_Playback_P_PidChannel *pid = b_play_get_video_decoder(p);
1088    NEXUS_VideoDecoderHandle decode;
1089    int rc;
1090    b_trick_settings settings;
1091
1092    /* requires regular decoder. does not support simpleDecoder */
1093    if (!pid) {
1094        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1095    }
1096    decode = pid->cfg.pidTypeSettings.video.decoder;
1097    if (!decode) {
1098        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1099    }
1100
1101    /* must flush before NEXUS_VideoDecoder_SetStartPts is called to avoid a false detect
1102    with existing pictures in the queue */
1103    b_play_flush(p);
1104
1105    switch (p->params.playpumpSettings.transportType) {
1106    case NEXUS_TransportType_eTs:
1107    case NEXUS_TransportType_eAsf:
1108    case NEXUS_TransportType_eAvi:
1109        break;
1110    default:
1111        /* not supported for other containers
1112        for MKV and MP4, get_next_frame is an asynchronous function. we will need to devise a different technique. */
1113        return -1;
1114    }
1115
1116    b_play_trick_get(p, &settings);
1117    if (settings.state != b_trick_state_normal) {
1118        /* only applies to normal play mode */
1119        return 0;
1120    }
1121
1122    if (!bmedia_player_lookup_pts(p->media_player, position, &pts)) {
1123
1124        switch (p->params.playpumpSettings.transportType) {
1125        case NEXUS_TransportType_eAsf:
1126        case NEXUS_TransportType_eAvi:
1127            {
1128            bmedia_player_decoder_mode mode;
1129            bmedia_player_entry entry;
1130            /* jump back to the previous key frame */
1131            bmedia_player_set_direction(p->media_player, -33, -BMEDIA_TIME_SCALE_BASE, &mode);
1132            do {
1133                if (bmedia_player_next(p->media_player, &entry)) {
1134                    entry.timestamp = position;
1135                    break;
1136                }
1137            }
1138            while (entry.type == bmedia_player_entry_type_file && entry.start == 0);
1139            bmedia_player_set_direction(p->media_player, 0, BMEDIA_TIME_SCALE_BASE, &mode);
1140            bmedia_player_seek(p->media_player, entry.timestamp);
1141            BDBG_WRN(("accurate seek: from timestamp %d to %d (pts %#x)", entry.timestamp, position, pts));
1142            }
1143            break;
1144        default:
1145            /* bcmplayer already seeks back to previous I frame */
1146            BDBG_WRN(("accurate seek: to %d (pts %#x)", position, pts));
1147            break;
1148        }
1149
1150        /* tell decoder to discard until this pts */
1151        rc = NEXUS_VideoDecoder_SetStartPts(decode, pts);
1152        if (rc) return BERR_TRACE(rc);
1153    }
1154    else {
1155        BDBG_WRN(("Unable to do accurateSeek position=%d(%#x), decode=%p", position, pts, decode));
1156    }
1157
1158    /* start state machine to allow audio to chase video's PTS. This keeps a full audio CDB from
1159    hanging video as it seeks for its start PTS. the state machine is:
1160    1) wait for video first PTS
1161    2) start audio pause (which launches the rap_monitor_timer)
1162    3) wait for video first PTS passed, which means we've found the start pts
1163    4) stop audio pause
1164    */
1165    p->state.inAccurateSeek = true;
1166    return 0;
1167}
1168
1169/*
1170Resume normal playback. This should not be called immediately after bplay_start because it's
1171not needed.
1172
1173We may come from the following states:
11741) stc trick modes.
11752) decoder pause from decoder trick or normal play.
11763) decoder pause from host trick. requires reset_location.
11774) decoder trick.
11785) host trick. requires reset_location.
1179
1180The way to distinguish 2 & 3 is to use the BNAV_Player state. See below.
1181*/
1182static NEXUS_Error
1183bplay_p_play_impl(NEXUS_PlaybackHandle p, bool *restart, bool flush, bmedia_player_pos position)
1184{
1185    NEXUS_Error rc = NEXUS_SUCCESS;
1186    b_trick_settings settings;
1187    bmedia_player_decoder_mode mode;
1188    bmedia_player_decoder_config config;
1189
1190    *restart = false;
1191
1192    if (flush) {
1193        b_play_flush(p);
1194    }
1195
1196    /* XXX this should be avoided in favor of calling bmedia_player_set_direction, and then handling bmedia_player_decoder_mode */
1197    b_play_trick_get(p, &settings);
1198    settings.forward = true;
1199    settings.state = b_trick_state_normal;
1200    settings.decode_rate = NEXUS_NORMAL_PLAY_SPEED;
1201    settings.decode_mode = NEXUS_VideoDecoderDecodeMode_eAll;
1202    settings.fragmented = false;
1203    settings.reordering_mode = NEXUS_VideoDecoderReorderingMode_eNone;
1204    settings.stc_trick = p->params.stcTrick;
1205    settings.force_source_frame_rate = 0;
1206    NEXUS_Playback_GetDefaultTrickModeSettings(&p->state.trickmode_params);
1207    rc = b_play_trick_set(p, &settings);
1208    if (rc != NEXUS_SUCCESS) { return BERR_TRACE(rc); }
1209
1210    bmedia_player_get_decoder_config(p->media_player, &config);
1211    config.host_mode = bmedia_player_host_trick_mode_auto;
1212    config.mode_modifier = 1; /* unused */
1213    rc = bmedia_player_set_decoder_config(p->media_player, &config);
1214    if (rc) return BERR_TRACE(rc);
1215
1216    if (p->state.decoder_flushed) {
1217        /* resync player */
1218        bmedia_player_set_direction(p->media_player, 0, BMEDIA_TIME_SCALE_BASE, &mode); /* normal decode */
1219
1220        if (p->params.accurateSeek) {
1221            bplay_p_accurate_seek(p, position);
1222        }
1223
1224        *restart = true;
1225    }
1226
1227    p->state.direction = 1;
1228    p->state.mode = NEXUS_PlaybackState_ePlaying;
1229    p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Forward;
1230    return rc;
1231}
1232
1233NEXUS_Error
1234bplay_p_play(NEXUS_PlaybackHandle p, bool *restart, bool flush)
1235{
1236    bmedia_player_pos position;
1237
1238    BDBG_MSG(("resume play"));
1239    b_play_update_location(p);
1240    bmedia_player_tell(p->media_player, &position);
1241    return bplay_p_play_impl(p, restart, flush, position);
1242}
1243
1244NEXUS_Error
1245bplay_p_play_from(NEXUS_PlaybackHandle p, bmedia_player_pos position)
1246{
1247    bool restart;
1248    bmedia_player_seek(p->media_player, position);
1249    return bplay_p_play_impl(p, &restart, true, position);
1250}
1251
1252
1253NEXUS_Error
1254bplay_p_pause(NEXUS_PlaybackHandle p)
1255{
1256    b_trick_settings settings;
1257    BDBG_MSG(("going to a pause, state.state %d, state.mode %d", p->state.state, p->state.mode));
1258    if (p->state.mode == NEXUS_PlaybackState_ePaused) {
1259        return NEXUS_SUCCESS;
1260    }
1261    p->state.mode = NEXUS_PlaybackState_ePaused;
1262    p->state.loopedDuringPause = false;
1263
1264    /* during pause, we have a timer so we can notify the caller of internal changes trim continuous record trim */
1265    if (!p->state.timer) {
1266        p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME * 5, b_play_timer, p);   /* schedule another call into the same function after while */
1267    }
1268
1269    b_play_trick_get(p, &settings);
1270    settings.decode_rate = 0;
1271    return b_play_trick_set(p, &settings);
1272}
1273
1274/**
1275Callback from brecord
1276**/
1277void
1278NEXUS_Playback_RecordProgress_priv(NEXUS_PlaybackHandle p)
1279{
1280    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1281    NEXUS_ASSERT_MODULE();
1282
1283    if (p->state.start_paused) {
1284        return;
1285    }
1286
1287    if (p->state.state != eWaitingRecord) {
1288        BDBG_MSG_FLOW(("Got record event, but not waiting for record(%d), ignore", p->state.state));
1289        if (p->state.state == eWaitingPlayback) {
1290            goto restart; /* Kickstart playback */
1291        }
1292        return;
1293    }
1294
1295    BDBG_MSG(("NEXUS_Playback_RecordProgress_priv %d", p->state.read_size));
1296    if (p->state.read_size==0) {
1297        bmedia_player_pos pos;
1298        bmedia_player_status status;
1299
1300        bmedia_player_tell(p->media_player, &pos);
1301        bmedia_player_get_status(p->media_player, &status);
1302        if (pos > status.bounds.first) {
1303            /* we are waiting for an index, try to get new entry */
1304            b_play_next_frame(p);
1305        }
1306        else {
1307            /* if we've fallen off the beginning of a continuous record, we need to reposition */
1308            p->state.state = eWaitingPlayback;
1309        }
1310    }
1311    else {
1312       /* we are waiting for a data */
1313        p->state.state = eWaitingIo;
1314        NEXUS_File_AsyncRead(p->file->file.data, p->state.buf, p->state.read_size, NEXUS_MODULE_SELF, b_play_frame_data, p);
1315    }
1316
1317restart:
1318    b_play_check_buffer(p);
1319    return;
1320}
1321
1322/**
1323Whatever state you're in, get into a paused state that can be
1324used for frame advance. This means paused-from-play.
1325**/
1326static NEXUS_Error
1327bplay_p_get_into_paused_play_state(NEXUS_PlaybackHandle p, bool *restart)
1328{
1329    NEXUS_Error rc;
1330    rc = bplay_p_play(p, restart, true);
1331    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);}
1332    rc = bplay_p_pause(p);
1333    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);}
1334    *restart = true; /* we've already flushed, but we need to restart playback after this */
1335    p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Forward;
1336    return rc;
1337}
1338
1339/* set b_trick_settings based on bmedia_player_decoder_mode and (optionally) a NEXUS_PlaybackTrickModeSettings override */
1340static void
1341NEXUS_Playback_P_ConvertPlayerDecodeMode(NEXUS_PlaybackHandle p, const bmedia_player_decoder_mode *mode,  b_trick_settings *trick_settings, const NEXUS_PlaybackTrickModeSettings *params)
1342{
1343    unsigned abs_time_scale;
1344    BDBG_CASSERT(NEXUS_VideoDecoderDecodeMode_eAll==(NEXUS_VideoDecoderDecodeMode)bmedia_player_decoder_frames_all);
1345    BDBG_CASSERT(NEXUS_VideoDecoderDecodeMode_eIP==(NEXUS_VideoDecoderDecodeMode)bmedia_player_decoder_frames_IP);
1346    BDBG_CASSERT(NEXUS_VideoDecoderDecodeMode_eI==(NEXUS_VideoDecoderDecodeMode)bmedia_player_decoder_frames_I);
1347    trick_settings->decode_mode = (NEXUS_VideoDecoderDecodeMode)mode->display_frames;
1348
1349    BDBG_CASSERT(NEXUS_VideoDecoderReorderingMode_eNone==(NEXUS_VideoDecoderReorderingMode)bmedia_player_reordering_mode_none);
1350    BDBG_CASSERT(NEXUS_VideoDecoderReorderingMode_eSequential==(NEXUS_VideoDecoderReorderingMode)bmedia_player_reordering_mode_sequential);
1351    BDBG_CASSERT(NEXUS_VideoDecoderReorderingMode_eInterleaved==(NEXUS_VideoDecoderReorderingMode)bmedia_player_reordering_mode_interleaved);
1352    BDBG_CASSERT(NEXUS_VideoDecoderReorderingMode_eChunkForward==(NEXUS_VideoDecoderReorderingMode)bmedia_player_reordering_mode_forward);
1353    BDBG_CASSERT(NEXUS_VideoDecoderReorderingMode_eChunkBackward==(NEXUS_VideoDecoderReorderingMode)bmedia_player_reordering_mode_backward);
1354    trick_settings->reordering_mode = (NEXUS_VideoDecoderReorderingMode)mode->reordering_mode;
1355    abs_time_scale = mode->time_scale >= 0 ? mode->time_scale : -mode->time_scale;
1356    if(mode->continuous) {
1357        trick_settings->decode_rate = abs_time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1358        trick_settings->state = b_trick_state_normal;
1359    } else {
1360        trick_settings->decode_rate = NEXUS_NORMAL_PLAY_SPEED;
1361        if(mode->brcm) {
1362             trick_settings->state = b_trick_state_brcm_trick_mode;
1363             trick_settings->decode_rate = (abs_time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE));
1364        } else if(mode->dqt) {
1365             trick_settings->state = b_trick_state_display_queue_trick_mode;
1366        } else if(mode->tsm) {
1367            trick_settings->decode_rate = mode->time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1368            trick_settings->state = b_trick_state_host_paced;
1369        } else if(mode->fragmented) {
1370            trick_settings->decode_rate = abs_time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1371            trick_settings->state = b_trick_state_host_trick_mode;
1372            trick_settings->reordering_mode = trick_settings->forward ? NEXUS_VideoDecoderReorderingMode_eChunkForward : NEXUS_VideoDecoderReorderingMode_eChunkBackward;
1373        } else if(mode->reordering_mode!=bmedia_player_reordering_mode_none) {
1374            trick_settings->decode_rate = abs_time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1375            trick_settings->state = b_trick_state_host_trick_mode;
1376        } else {
1377            trick_settings->decode_rate = abs_time_scale * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1378            trick_settings->state = b_trick_state_host_trick_mode;
1379        }
1380    }
1381    /* apply custom modifications */
1382    if (params && params->mode!=NEXUS_PlaybackHostTrickMode_eNone) {
1383        /* override rate control */
1384        /* decode_rate is unsigned. decoder and STC rate can only be positive. in the future, SW STC could support negative rates. */
1385        trick_settings->decode_rate = params->rate>0?params->rate:-params->rate;
1386        trick_settings->stc_trick = (params->rateControl == NEXUS_PlaybackRateControl_eStream && p->params.stcChannel);
1387        switch(params->mode) {
1388        case NEXUS_PlaybackHostTrickMode_ePlayI:
1389            trick_settings->decode_mode = NEXUS_VideoDecoderDecodeMode_eI;
1390            break;
1391        case NEXUS_PlaybackHostTrickMode_ePlayIP:
1392            trick_settings->decode_mode = NEXUS_VideoDecoderDecodeMode_eIP;
1393            break;
1394        default:
1395            break;
1396        }
1397    } else {
1398        trick_settings->stc_trick = p->params.stcTrick;
1399    }
1400    return;
1401}
1402
1403/**
1404The type of frame advance depends on the type of pause used. You must always be paused
1405before doing frame advance/reverse.
1406
1407Frame advance (i.e. forward) is can be decoder-based or stc based.
1408Frame reverse is host-based.
1409**/
1410static NEXUS_Error
1411bplay_p_frameadvance(NEXUS_PlaybackHandle p, bool forward, bool *restart)
1412{
1413    NEXUS_Error rc;
1414
1415    BDBG_MSG(("frame %s", forward?"advance":"reverse"));
1416    *restart = false;
1417
1418    /* fifoMarkerCounter tells the wait_for_end routine to not check for a certain amount of time after a frame advance.
1419    If your system checks for static video PTS to detect EOF, this will give it time to move. */
1420    if (p->state.fifoMarkerCounter == 0) {
1421        unsigned cdbDepth;
1422
1423        bplay_get_decode_mark(p, &p->state.fifoMarker, &cdbDepth);
1424        p->state.fifoMarkerCounter = 5;
1425    }
1426
1427    if(forward && p->state.frame_advance != NEXUS_Playback_P_FrameAdvance_Forward) {
1428        rc = bplay_p_get_into_paused_play_state(p, restart);
1429        if(rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);}
1430    } else if(!forward && p->state.frame_advance != NEXUS_Playback_P_FrameAdvance_Reverse) {
1431        bmedia_player_decoder_mode mode;
1432        b_trick_settings settings;
1433
1434        b_play_trick_get(p, &settings);
1435        b_play_update_location(p);
1436        bmedia_player_set_direction(p->media_player, -33 /* 33 ms */, -BMEDIA_TIME_SCALE_BASE, &mode);
1437        NEXUS_Playback_P_ConvertPlayerDecodeMode(p, &mode,  &settings, NULL);
1438        if(mode.discontinuity) {
1439            b_play_flush(p);
1440        }
1441        p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Reverse;
1442        *restart = true;
1443        settings.decode_rate = 0;
1444        settings.forward = false;
1445        b_play_trick_set(p, &settings);
1446    } else {
1447        BDBG_MSG(("continuing frame %s", forward?"advance":"reverse"));
1448    }
1449
1450    p->state.direction = forward?1:-1;
1451
1452    rc = b_play_trick_frameadvance(p);
1453    if(rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);}
1454    p->state.mode = NEXUS_PlaybackState_ePaused;
1455    return rc;
1456}
1457
1458static NEXUS_Error NEXUS_Playback_P_CheckWaitingIo(NEXUS_PlaybackHandle playback, bool stopping)
1459{
1460    unsigned i = 0;
1461    unsigned ioTimeout = playback->params.ioTimeout; /* exposed in the NEXUS_PlaybackSettings if we support recovery from io error by killing the File i/o thread. */
1462
1463/* period of 1/10th of a second */
1464#define IO_TIMEOUT_PERIOD 100
1465    if (playback->state.state == eWaitingIo) { /* WaitingIo it's non cancelable state, so we should wait for I/O to complete */
1466        playback->state.state = stopping ? eStopping : eCancelIo;
1467        BDBG_MSG(("NEXUS_Playback_P_CheckWaitingIo %#lx waiting for I/O to complete", playback));
1468        do {
1469            NEXUS_Error rc;
1470            NEXUS_UnlockModule();
1471            rc = BKNI_WaitForEvent(playback->ack_event, IO_TIMEOUT_PERIOD);
1472            NEXUS_LockModule();
1473            if (rc == BERR_TIMEOUT) {
1474                BDBG_WRN(("NEXUS_Playback_P_CheckWaitingIo %#lx timed out, keep waiting", (unsigned long)playback));
1475            }
1476            else if (rc!=0) {
1477                return BERR_TRACE(rc);
1478            }
1479            if (++i >= ioTimeout / IO_TIMEOUT_PERIOD) {
1480                BDBG_ERR(("NEXUS_Playback_P_CheckWaitingIo %#lx timed out. Canceling File thread.", (unsigned long)playback));
1481                NEXUS_FilePlay_Cancel(playback->file);
1482                i = 0;
1483                /* we keep looping. File will spawn a new thread and will invoke the callback with an error. Playback should go to eAborted. */
1484            }
1485        } while (!(playback->state.state == eIoCanceled || playback->state.state == eStopped || playback->state.state == eAborted));
1486    }
1487    if (playback->state.state == eAborted) {
1488        return -1;
1489    }
1490    return 0;
1491}
1492
1493
1494/*
1495* This function is used to synchronize control with
1496* data pump. it return b_ok if synchronization suceeded,
1497* false otherwise.
1498*/
1499static NEXUS_Error
1500bplay_sync_playback(NEXUS_PlaybackHandle p)
1501{
1502    BERR_Code rc;
1503
1504    if (p->state.start_paused) {
1505        /* if playback wasn't started yet, just do nothing. we should be in WaitingPlayback state. */
1506        BDBG_ASSERT(p->state.state == eWaitingPlayback);
1507        return NEXUS_SUCCESS;
1508    }
1509    if (p->state.state == eStopped) {
1510        return BERR_TRACE(NEXUS_NOT_SUPPORTED);
1511    }
1512    if (p->state.state == eAborted) {
1513        /* something went wrong. we need the app to stop. */
1514        return BERR_TRACE(NEXUS_UNKNOWN);
1515    }
1516
1517    rc = NEXUS_Playback_P_CheckWaitingIo(p, false);
1518    if (rc) return BERR_TRACE(rc);
1519
1520    p->state.reset = false;
1521
1522    /* still locked */
1523    return NEXUS_SUCCESS;
1524}
1525
1526/* this function is used to jumpstart data pump if it was cancelled because of bplay_sync_playback */
1527static void
1528bplay_restart_playback(NEXUS_PlaybackHandle p, bool restart_data_flow)
1529{
1530    if (p->state.start_paused) {
1531        /* if playback wasn't started yet, just do nothing. we should be in WaitingPlayback state. */
1532        BDBG_ASSERT(p->state.state == eWaitingPlayback);
1533        return;
1534    }
1535
1536    /* if restart_data_flow is false, then either the API has failed or we're in a paused state and
1537    don't want to restart. in all other cases, it's true. */
1538    if (restart_data_flow) {
1539        /* 3 states override this */
1540        restart_data_flow = (
1541            p->state.state != eIoCanceled &&
1542            p->state.state != eWaitingPlayback &&
1543            p->state.state != eWaitingRecord);
1544    }
1545
1546    if (p->state.reset) {
1547        BDBG_MSG(("I/O reset was requested"));
1548        if(p->state.media.entry.atom) {
1549            batom_release(p->state.media.entry.atom);
1550            p->state.media.entry.atom = NULL;
1551        }
1552        b_play_flush(p);
1553        bplay_p_clear_buffer(p);
1554        p->state.state = eTransition;
1555        /* start data flowing */
1556        b_play_next_frame(p);
1557    }
1558    /* PR 21643 - The eWaitingRecord exception was added because if you pause immediately after starting a timeshift record,
1559    you could be in waiting record state. we need to restart. */
1560    else if (restart_data_flow)
1561    {
1562        BDBG_MSG(("Restart: start data flowing"));
1563        b_play_next_frame(p);
1564    }
1565    else if (p->state.state == eWaitingRecord)
1566    {
1567        BDBG_MSG(("Restart: start data flowing in eWaitingRecord"));
1568        if (p->state.read_size==0) {
1569            /* we are waiting for an index, try to get new entry */
1570            b_play_next_frame(p);
1571        }
1572        else {
1573           /* we are waiting for a data */
1574            p->state.state = eWaitingIo;
1575            NEXUS_File_AsyncRead(p->file->file.data, p->state.buf, p->state.read_size, NEXUS_MODULE_SELF, b_play_frame_data, p);
1576        }
1577    }
1578    else if (p->state.state == eIoCanceled)  {
1579        BDBG_MSG(("Restart: I/O was canceled, reviving it"));
1580        p->state.state = eWaitingIo;
1581        switch(p->state.io_size) {
1582        case B_MEDIA_ATOM_MAGIC:
1583            b_play_media_send_meta(p);
1584            break;
1585        case B_MEDIA_NEST_MAGIC:
1586            b_play_next_frame(p);
1587            break;
1588       default:
1589            b_play_frame_data(p, p->state.io_size);
1590            break;
1591        }
1592    }
1593    else if (p->state.state == eWaitingPlayback ||
1594             p->state.state == eTimer)
1595    {
1596        /* This section of code needs to handle all normal states that require no change. */
1597        BDBG_MSG(("Restart: no state change state=%d,mode=%d", p->state.state, p->state.mode));
1598    }
1599    else {
1600        /* If this occurs, something is broken in the state machine and must be fixed immediately. This
1601        is complex code and small changes can lead to disastrous lockups and reentrancy. */
1602        BDBG_ERR(("Restart: unknown state %d", p->state.state));
1603    }
1604
1605    p->state.decoder_flushed = false;
1606    b_play_check_buffer(p);
1607    return;
1608}
1609
1610/* this function is used to abort playback if trick/play/seek functions have failed, it's should be called instead of bplay_restart_playback */
1611void
1612NEXUS_Playback_P_AbortPlayback(NEXUS_PlaybackHandle p)
1613{
1614    BDBG_WRN(("NEXUS_Playback_P_AbortPlayback: %#x aborting", (unsigned)p));
1615    if (p->state.start_paused) {
1616        /* if playback wasn't started yet, just do nothing. we should be in WaitingPlayback state. */
1617        BDBG_ASSERT(p->state.state == eWaitingPlayback);
1618        return;
1619    }
1620
1621    /* save playback position */
1622    b_play_update_location(p);
1623    bmedia_player_tell(p->media_player, &p->state.abortPosition);
1624
1625    b_play_flush(p);
1626    if(p->state.media.entry.atom) {
1627        batom_release(p->state.media.entry.atom);
1628        p->state.media.entry.atom = NULL;
1629    }
1630    p->state.state = eAborted;
1631    p->state.decoder_flushed = false;
1632    return;
1633}
1634
1635
1636/* rest of functions is a public API to control playback */
1637NEXUS_Error
1638NEXUS_Playback_Play(NEXUS_PlaybackHandle p)
1639{
1640    NEXUS_Error rc;
1641    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1642
1643    if (p->state.start_paused) {
1644        BDBG_MSG(("NEXUS_Playback_Play: %#lx starting playback from paused state", (unsigned long)p));
1645        p->state.start_paused = false;
1646        p->state.data_source(p);
1647        return NEXUS_SUCCESS;
1648    }
1649
1650    if (p->state.mode == NEXUS_PlaybackState_ePlaying) {
1651        BDBG_WRN(("Already in the playback mode"));
1652        return NEXUS_SUCCESS;
1653    }
1654
1655    rc = bplay_sync_playback(p);
1656    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc); goto done; }
1657
1658    rc = bplay_p_play(p, &p->state.reset, false);
1659    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);} /* fall through so bplay_restart_playback is called */
1660
1661    if(rc==NEXUS_SUCCESS || p->params.seekErrorHandling==NEXUS_PlaybackErrorHandlingMode_eIgnore) {
1662        bplay_restart_playback(p, rc==NEXUS_SUCCESS);
1663    } else {
1664        NEXUS_Playback_P_AbortPlayback(p);
1665        rc = BERR_TRACE(rc);
1666    }
1667done:
1668    return rc;
1669}
1670
1671
1672NEXUS_Error
1673NEXUS_Playback_Pause(NEXUS_PlaybackHandle p)
1674{
1675    NEXUS_Error rc=NEXUS_SUCCESS;
1676
1677    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1678
1679    if (p->state.start_paused) {
1680        BDBG_MSG(("NEXUS_Playback_Play: %#lx starting playback from paused state", (unsigned long)p));
1681        p->state.start_paused = false;
1682        p->state.data_source(p);
1683    } else if (p->state.mode == NEXUS_PlaybackState_ePaused) {
1684        BDBG_WRN(("Already in pause mode"));
1685        goto done;
1686    }
1687
1688    rc = bplay_p_pause(p);
1689    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);} /* fall through */
1690
1691done:
1692    return rc;
1693}
1694
1695NEXUS_Error
1696NEXUS_Playback_FrameAdvance(NEXUS_PlaybackHandle p, bool forward)
1697{
1698    NEXUS_Error rc;
1699
1700    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1701
1702
1703    if (p->state.mode != NEXUS_PlaybackState_ePaused) {
1704        BDBG_WRN(("Not in paused state"));
1705        rc = BERR_TRACE(BERR_NOT_SUPPORTED);
1706        goto done;
1707    }
1708
1709    rc = bplay_sync_playback(p);
1710    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc); goto done; }
1711
1712    rc = bplay_p_frameadvance(p, forward, &p->state.reset);
1713    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc);} /* fall through so bplay_restart_playback is called */
1714
1715    bplay_restart_playback(p, rc==BERR_SUCCESS);
1716
1717done:
1718    return rc;
1719}
1720
1721
1722static NEXUS_Error
1723b_play_set_media_player_config(NEXUS_PlaybackHandle p, const NEXUS_PlaybackTrickModeSettings *params)
1724{
1725    NEXUS_Error rc;
1726    bmedia_player_decoder_config config;
1727
1728    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_eNone == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_auto);
1729    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_eNormal ==  (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_normal);
1730    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlayI == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_I);
1731    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlaySkipB == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_skipB);
1732    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlayIP == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_IP);
1733    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlaySkipP == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_skipP);
1734    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlayBrcm == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_brcm);
1735    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_ePlayGop == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_gop);
1736    BDBG_CASSERT( NEXUS_PlaybackHostTrickMode_eTimeSkip == (NEXUS_PlaybackHostTrickMode)bmedia_player_host_trick_mode_time_skip);
1737
1738    bmedia_player_get_decoder_config(p->media_player, &config);
1739    b_play_update_media_player_config(p, &config);
1740
1741    if (params->mode != NEXUS_PlaybackHostTrickMode_eNone && params->skipControl == NEXUS_PlaybackSkipControl_eDecoder) {
1742        switch(params->mode) {
1743        case NEXUS_PlaybackHostTrickMode_eNormal:
1744        case NEXUS_PlaybackHostTrickMode_ePlayI:
1745        case NEXUS_PlaybackHostTrickMode_ePlayIP:
1746            config.host_mode = bmedia_player_host_trick_mode_auto;
1747            break;
1748        default:
1749            /* decoder doesn't support other skip modes */
1750            return BERR_TRACE(BERR_NOT_SUPPORTED);
1751        }
1752    }
1753    else {
1754        config.host_mode = params->mode;
1755    }
1756
1757    config.mode_modifier = params->mode_modifier;
1758    config.max_decoder_rate = params->maxDecoderRate/(NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1759    if (params->mode != NEXUS_PlaybackHostTrickMode_eNone) {
1760        config.stc = (params->rateControl == NEXUS_PlaybackRateControl_eStream && p->params.stcChannel);
1761    }
1762    else {
1763        /* p->params.stcTrick is only used for automatic trick modes */
1764        config.stc = p->params.stcTrick;
1765    }
1766    config.brcm = params->brcmTrickMode;
1767    rc = bmedia_player_set_decoder_config(p->media_player, &config);
1768    if (rc) return BERR_TRACE(rc);
1769
1770    return rc;
1771}
1772
1773
1774NEXUS_Error
1775NEXUS_Playback_TrickMode(NEXUS_PlaybackHandle p, const NEXUS_PlaybackTrickModeSettings *params)
1776{
1777    NEXUS_Error rc;
1778    int new_direction;
1779    b_trick_settings trick_settings;
1780    bmedia_player_decoder_mode mode;
1781    NEXUS_PlaybackTrickModeSettings modified_params;
1782
1783    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1784    if (params->mode >= NEXUS_PlaybackHostTrickMode_eMax) {
1785        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1786    }
1787   
1788    if (params->skipControl == NEXUS_PlaybackSkipControl_eDecoder || params->rateControl == NEXUS_PlaybackRateControl_eDecoder) {
1789        if (!b_play_get_video_decoder(p) && !b_play_has_audio_decoder(p)) {
1790            BDBG_ERR(("NEXUS_Playback_TrickMode failed because no video or audio decoder was provided in any NEXUS_PlaybackPidChannelSettings."));
1791            return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1792        }
1793    }
1794
1795    /* validate params based on automatic or custom mode */
1796    if (params->mode == NEXUS_PlaybackHostTrickMode_eNone) {
1797        /* in automatic mode, validate all custom trick params are left at default values */
1798        if (params->rateControl != NEXUS_PlaybackRateControl_eDecoder ||
1799            params->skipControl != NEXUS_PlaybackSkipControl_eHost ||
1800            params->mode_modifier != 1)
1801        {
1802            return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1803        }
1804        new_direction = (params->rate >= 0)?1:-1;
1805    }
1806    else {
1807        /* verify custom mode params */
1808        if (params->skipControl == NEXUS_PlaybackSkipControl_eDecoder &&
1809            params->mode_modifier != 1) {
1810            /* decoder can't do anything with the mode modifier, so ensure its default */
1811            return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1812        }
1813        if (params->mode_modifier >= 0 && params->rate < 0) {
1814            /* do not make this conversion. fix the app. */
1815            return BERR_TRACE(NEXUS_INVALID_PARAMETER);
1816        }
1817        if (params->mode_modifier < 0 && params->rate >= 0) {
1818            /* grandfather this conversion */
1819            BDBG_WRN(("changing trick rate from %d to %d because mode_modifier is negative", params->rate, -1*params->rate));
1820            modified_params = *params;
1821            modified_params.rate = -modified_params.rate;
1822            params = &modified_params;
1823        }
1824        /* we may support negative eStream rates in the future using SW STC. */
1825        new_direction = (params->rate >= 0)?1:-1;
1826    }
1827
1828    rc = bplay_sync_playback(p);
1829    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc); goto done; }
1830
1831    BDBG_CASSERT( (256 * NEXUS_NORMAL_PLAY_SPEED)/BMEDIA_TIME_SCALE_BASE == 256 * (NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE));
1832
1833    b_play_update_location(p);
1834
1835    rc = b_play_set_media_player_config(p, params);
1836    if(rc!=BERR_SUCCESS) {rc=BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error;}
1837
1838    /* set host player rate */
1839    {
1840        bmedia_player_step direction;
1841        bmedia_time_scale time_scale;
1842        int player_rate;
1843       
1844        if (params->skipControl == NEXUS_PlaybackSkipControl_eDecoder) {
1845            /* if skipControl is eDecoder, then we aren't skipping in the host. */
1846            player_rate = NEXUS_NORMAL_PLAY_SPEED;
1847        }
1848        else {
1849            player_rate = params->rate;
1850        }
1851
1852        direction = (33*player_rate)/NEXUS_NORMAL_PLAY_SPEED;
1853        if (player_rate && !direction) { /* prevent rounding down to zero */
1854            direction = player_rate>0?1:-1;
1855        }
1856        time_scale = player_rate/(NEXUS_NORMAL_PLAY_SPEED/BMEDIA_TIME_SCALE_BASE);
1857        if (player_rate && !time_scale) { /* prevent rounding down to zero */
1858            time_scale = player_rate>0?1:-1;
1859        }
1860       
1861        rc = bmedia_player_set_direction(p->media_player, direction, time_scale, &mode);
1862        if(rc!=BERR_SUCCESS) {rc=BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error;}
1863    }
1864
1865    /* set decoder/stc rate */
1866    b_play_trick_get(p, &trick_settings);
1867
1868    NEXUS_Playback_P_ConvertPlayerDecodeMode(p, &mode, &trick_settings, params);
1869    trick_settings.forward = (new_direction==1);
1870    trick_settings.avoid_flush = (params->avoidFlush || !p->file->file.index );
1871    trick_settings.force_source_frame_rate = mode.force_source_frame_rate;
1872
1873    rc = b_play_trick_set(p, &trick_settings);
1874    if (rc!=NEXUS_SUCCESS) {rc=BERR_TRACE(rc); } /* fall through. we need to maintain internal Playback state, even on a bad trick mode setting. */
1875
1876    if(mode.discontinuity) {
1877        b_play_flush(p);
1878    }
1879
1880    if (p->state.decoder_flushed) {
1881        p->state.reset = true;
1882    }
1883
1884    if (rc==NEXUS_SUCCESS) {
1885        p->state.trickmode_params = *params;
1886        p->state.direction = new_direction;
1887        p->state.mode = NEXUS_PlaybackState_eTrickMode;
1888    }
1889
1890error:
1891    if(rc==NEXUS_SUCCESS || p->params.seekErrorHandling==NEXUS_PlaybackErrorHandlingMode_eIgnore) {
1892        bplay_restart_playback(p, rc==NEXUS_SUCCESS);
1893    } else {
1894        NEXUS_Playback_P_AbortPlayback(p);
1895        rc = BERR_TRACE(rc);
1896    }
1897
1898done:
1899    if(params->rate == -NEXUS_NORMAL_PLAY_SPEED) {
1900        p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Reverse;
1901    } else if(params->rate >=0 && params->rate <= NEXUS_NORMAL_PLAY_SPEED) {
1902        p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Forward;
1903    } else {
1904        p->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Invalid;
1905    }
1906    return rc;
1907}
1908
1909NEXUS_Error
1910NEXUS_Playback_Seek(NEXUS_PlaybackHandle p, NEXUS_PlaybackPosition position)
1911{
1912    NEXUS_Error rc;
1913    int result;
1914
1915    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1916    rc = bplay_sync_playback(p);
1917    if(rc!=NEXUS_SUCCESS) {rc = BERR_TRACE(rc); goto done;}
1918
1919    if (!b_play_get_video_decoder(p) && !b_play_has_audio_decoder(p)) {
1920        /* no-decoder playback is valid, so this is only a WRN */
1921        BDBG_WRN(("No video or audio decoder was provided with NEXUS_PlaybackPidChannelSettings; therefore NEXUS_Playback_Seek will not be precise."));
1922    }
1923
1924    result = bmedia_player_seek(p->media_player, position);
1925
1926    if (result!=-1 && p->params.accurateSeek && !p->state.start_paused) {
1927        bplay_p_accurate_seek(p, position);
1928    }
1929
1930    if (result!=-1) {
1931        p->state.reset = true;
1932    }
1933    else {
1934        /* don't use BERR_TRACE. failed seek is normal. */
1935        rc = NEXUS_UNKNOWN;
1936    }
1937    if(rc!=NEXUS_SUCCESS) {
1938        if(p->params.seekErrorHandling==NEXUS_PlaybackErrorHandlingMode_eAbort) {
1939            NEXUS_Playback_P_AbortPlayback(p);
1940            rc = BERR_TRACE(rc);
1941            goto done;
1942        }
1943    }
1944
1945    bplay_restart_playback(p, false);
1946done:
1947    return rc;
1948}
1949
1950NEXUS_Error
1951NEXUS_Playback_GetStatus(NEXUS_PlaybackHandle p, NEXUS_PlaybackStatus *status)
1952{
1953    NEXUS_PlaypumpStatus pumpstatus;
1954    NEXUS_Error rc=NEXUS_SUCCESS;
1955    bmedia_player_status player_status;
1956    bmedia_player_pos position;
1957    bool need_checkbuffer = false;
1958
1959    BKNI_Memset(status, 0, sizeof(*status));
1960    NEXUS_Playback_GetDefaultTrickModeSettings(&status->trickModeSettings);
1961    switch(p->state.state) {
1962    case eStopped:
1963        status->state = NEXUS_PlaybackState_eStopped;
1964        break;
1965    case eAborted:
1966        status->state = NEXUS_PlaybackState_eAborted;
1967        break;
1968    default:
1969        status->state = p->state.mode;
1970        status->trickModeSettings = p->state.trickmode_params;
1971        if(!p->state.start_paused) {
1972            need_checkbuffer = true;
1973        } else {
1974            status->state = NEXUS_PlaybackState_ePaused;
1975        }
1976        if(status->state == NEXUS_PlaybackState_ePaused) {
1977            status->trickModeSettings.rate = 0;
1978        }
1979        break;
1980    }
1981
1982    if (p->state.state != eStopped) {
1983        BDBG_ASSERT(p->params.playpump);
1984        rc = NEXUS_Playpump_GetStatus(p->params.playpump, &pumpstatus);
1985        if(rc==NEXUS_SUCCESS) {
1986            status->fifoDepth = pumpstatus.fifoDepth;
1987            status->fifoSize = pumpstatus.fifoSize;
1988            status->bytesPlayed = pumpstatus.bytesPlayed;
1989        }
1990        bmedia_player_get_status(p->media_player, &player_status);
1991        status->first = player_status.bounds.first;
1992        status->last = player_status.bounds.last;
1993        status->indexErrors = p->state.index_error_cnt + player_status.index_error_cnt;
1994        status->dataErrors = p->state.data_error_cnt + player_status.data_error_cnt;
1995        status->readPosition = player_status.position;
1996
1997        if (p->state.state != eAborted) {
1998            if(!p->state.start_paused) {
1999                b_play_update_location(p);
2000            }
2001            bmedia_player_tell(p->media_player, &position);
2002            status->position = position;
2003        }
2004        else {
2005            status->position = p->state.abortPosition;
2006        }
2007    }
2008    if(need_checkbuffer) {
2009        b_play_check_buffer(p);
2010    }
2011
2012    return rc;
2013}
2014
2015void
2016NEXUS_Playback_GetDefaultStartSettings(NEXUS_PlaybackStartSettings *pSettings)
2017{
2018    BKNI_Memset(pSettings, 0, sizeof(*pSettings));
2019    pSettings->mode = NEXUS_PlaybackMode_eIndexed;
2020    pSettings->bitrate = 10 * 1000 * 1000;
2021    pSettings->mpeg2TsIndexType = NEXUS_PlaybackMpeg2TsIndexType_eAutoDetect;
2022    pSettings->indexFileIo.mode = NEXUS_PlaybackIndexFileIo_eCallback;
2023    NEXUS_Thread_GetDefaultSettings(&pSettings->indexFileIo.threadSettings);
2024    return;
2025}
2026
2027static void
2028NEXUS_Playback_P_PlaypumpThread(void *context)
2029{
2030    NEXUS_PlaybackHandle playback = context;
2031    for(;;) {
2032        BERR_Code rc;
2033        BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2034        BDBG_ASSERT(playback->playpump_thread_event);
2035        rc=BKNI_WaitForEvent(playback->playpump_thread_event, 100);
2036        if(rc!=BERR_SUCCESS || rc!=BERR_TIMEOUT) {
2037            rc=BERR_TRACE(rc);
2038            break;
2039        }
2040        NEXUS_LockModule();
2041        BDBG_ASSERT(playback->playpump_thread_event);
2042        if(playback->thread_terminate) {
2043            NEXUS_UnlockModule();
2044            break;
2045        }
2046        if(rc==BERR_SUCCESS && playback->state.state != eStopped) {
2047            b_play_read_callback(playback);
2048        }
2049        NEXUS_UnlockModule();
2050    }
2051    BDBG_MSG(("NEXUS_Playback_P_PlaypumpThread: %#x terminated", (unsigned)playback));
2052    return;
2053}
2054
2055NEXUS_Error
2056NEXUS_Playback_Start(NEXUS_PlaybackHandle playback, NEXUS_FilePlayHandle file, const NEXUS_PlaybackStartSettings *pSettings)
2057{
2058    NEXUS_PlaypumpStatus playpump_status;
2059    NEXUS_Error rc=NEXUS_SUCCESS;
2060    NEXUS_PlaypumpSettings pumpCfg;
2061    NEXUS_PlaybackStartSettings defaultStartSettings;
2062
2063    if (!file) {
2064        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2065    }
2066    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2067
2068    if(!pSettings) {
2069        NEXUS_Playback_GetDefaultStartSettings(&defaultStartSettings);
2070        pSettings = &defaultStartSettings;
2071    }
2072    b_play_capture_open(playback);
2073
2074    playback->state.media.entry.atom = NULL;
2075
2076    if (playback->state.state != eStopped) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_state;}
2077    if (playback->params.playpump == NULL)  { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_state;}
2078    if (file->file.data == NULL) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_state;}
2079
2080    BDBG_MSG(("NEXUS_Playback_Start: %#lx", playback));
2081    BKNI_Memset(&playback->state, 0, sizeof(playback->state)); /* wipe-out all temporary state */
2082    playback->state.validPts = false;
2083    playback->state.drain_mode = false;
2084    playback->state.frame_advance = NEXUS_Playback_P_FrameAdvance_Forward;
2085
2086    switch(pSettings->indexFileIo.mode) {
2087    case NEXUS_PlaybackIndexFileIo_eCallback:
2088        /* do nothing */
2089        break;
2090    case NEXUS_PlaybackIndexFileIo_eModule:
2091        if(playback->playpump_event == NULL) {
2092            rc = BKNI_CreateEvent(&playback->playpump_event);
2093            if(rc!=BERR_SUCCESS) { rc = BERR_TRACE(rc); goto error_index_mode;}
2094            playback->playpump_event_callback = NEXUS_RegisterEvent(playback->playpump_event, b_play_read_callback, playback);
2095            if(playback->playpump_event_callback==NULL) {
2096                rc = BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY);
2097                BKNI_DestroyEvent(playback->playpump_event);
2098                playback->playpump_event=NULL;
2099                goto error_index_mode;
2100            }
2101        }
2102        break;
2103    case NEXUS_PlaybackIndexFileIo_eThread:
2104        if(playback->playpump_thread_event == NULL) {
2105            rc = BKNI_CreateEvent(&playback->playpump_thread_event);
2106            if(rc!=BERR_SUCCESS) {rc=BERR_TRACE(rc);goto error_index_mode;}
2107        }
2108        if(playback->playpump_thread) {
2109            if(BKNI_Memcmp(&playback->playpump_thread_settings, &pSettings->indexFileIo.threadSettings, sizeof(pSettings->indexFileIo.threadSettings))==0) {
2110                break; /* thread configuration is the same, keep on using old thread */
2111            }
2112            NEXUS_Playback_P_KillPlaypumpThread(playback);
2113        }
2114        {
2115            char thread_name[32];
2116            BKNI_Snprintf(thread_name, sizeof(thread_name), "nx_playback%u", (unsigned)playback);
2117            playback->playpump_thread = NEXUS_Thread_Create(thread_name, NEXUS_Playback_P_PlaypumpThread, playback, &pSettings->indexFileIo.threadSettings);
2118            if(!playback->playpump_thread) { rc = BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY); goto error_index_mode; }
2119        }
2120        break;
2121    default:
2122        rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);
2123        goto error_index_mode;
2124    }
2125    playback->index_file_mode = pSettings->indexFileIo.mode;
2126
2127    b_play_trick_init(playback);
2128    /* trick must know about stcTrick now in case of pause */
2129    playback->trick.settings.stc_trick = playback->params.stcTrick;
2130    /* reset any currently attached decoders */
2131    rc = b_play_trick_set(playback, NULL);
2132    if (rc) {rc = BERR_TRACE(rc); goto error_state;}
2133
2134    playback->state.state = eStopped;
2135    playback->file = file;
2136    NEXUS_Time_Get(&playback->state.fifoLast); /* get init value */
2137
2138    rc = NEXUS_Playpump_GetStatus(playback->params.playpump, &playpump_status);
2139    if (rc) {rc = BERR_TRACE(rc); goto error_state;}
2140
2141    playback->buf_limit = (uint8_t*)playpump_status.bufferBase + playpump_status.fifoSize - B_IO_BLOCK_LIMIT;
2142    BDBG_MSG(("playback buffer %#lx(%d)",
2143        (unsigned long)playpump_status.bufferBase, playpump_status.fifoSize));
2144
2145    bio_read_attach_priority(playback->file->file.data, NEXUS_P_Playback_GetPriority, playback);
2146
2147    playback->state.mode = NEXUS_PlaybackState_ePlaying;
2148    playback->state.encrypted = false ; /* mpeg->encryption.type != bencryption_type_none; */
2149
2150    playback->state.state = eTransition;
2151    playback->media_player = NULL;
2152    /* seek to the beginning of the file */
2153    playback->file->file.data->seek(playback->file->file.data, 0, SEEK_SET);
2154
2155    NEXUS_Playpump_GetSettings(playback->params.playpump, &pumpCfg);
2156    pumpCfg.mode = NEXUS_PlaypumpMode_eFifo;
2157    rc = NEXUS_Playpump_SetSettings(playback->params.playpump, &pumpCfg);
2158    if(rc!=NEXUS_SUCCESS) { rc = BERR_TRACE(rc); goto error_playpump_mode; }
2159
2160    rc = b_play_start_media(playback, file, &playpump_status, pSettings);
2161    if(rc!=NEXUS_SUCCESS) { rc = BERR_TRACE(rc); goto error_player;}
2162
2163    NEXUS_StartCallbacks(playback->params.playpump);
2164    /* assume that playback and decode will start in normal play */
2165    NEXUS_Playback_GetDefaultTrickModeSettings(&playback->state.trickmode_params);
2166
2167    playback->state.state = eWaitingPlayback;
2168    playback->state.start_paused = playback->params.startPaused;
2169    rc = NEXUS_Playpump_Start(playback->params.playpump);
2170    if (rc!=NEXUS_SUCCESS) { rc = BERR_TRACE(rc); goto error_playpump; }
2171
2172    return BERR_SUCCESS;
2173
2174error_playpump:
2175    b_play_stop_media(playback);
2176error_playpump_mode:
2177error_player:
2178error_state:
2179error_index_mode:
2180    playback->state.state = eStopped;
2181    /* On any Start failure, we must end in a clean eStopped state that can be restarted. */
2182    return rc;
2183}
2184
2185void
2186NEXUS_Playback_GetSettings(NEXUS_PlaybackHandle playback, NEXUS_PlaybackSettings *settings)
2187{
2188    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2189    BDBG_ASSERT(settings);
2190    *settings = playback->params;
2191    return;
2192}
2193
2194static NEXUS_Error
2195b_play_playpump_read_callback_guard(void *context)
2196{
2197    NEXUS_PlaybackHandle playback = (NEXUS_PlaybackHandle)context;
2198
2199    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2200    /* no need to acquire lock here, since this mode is only changing when starting playback */
2201    if(playback->state.state == eStopped) {
2202        return NEXUS_CALLBACK_DEFLECTED;
2203    }
2204    switch(playback->index_file_mode) {
2205    case NEXUS_PlaybackIndexFileIo_eCallback:
2206        return NEXUS_SUCCESS;
2207        break;
2208    case NEXUS_PlaybackIndexFileIo_eModule:
2209        BKNI_SetEvent(playback->playpump_event); /* set event and do work inside module's context */
2210        break;
2211    case NEXUS_PlaybackIndexFileIo_eThread:
2212        BKNI_SetEvent(playback->playpump_thread_event); /* set event and do work inside thread */
2213        break;
2214    default:
2215        BDBG_ASSERT(0);
2216        break;
2217    }
2218    return NEXUS_CALLBACK_DEFLECTED;
2219}
2220
2221static void
2222b_play_playpump_read_callback_locked(void *context)
2223{
2224    b_play_read_callback(context);
2225}
2226
2227NEXUS_Error
2228NEXUS_Playback_SetSettings(NEXUS_PlaybackHandle playback, const NEXUS_PlaybackSettings *settings)
2229{
2230    NEXUS_Error  rc;
2231
2232    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2233    BDBG_ASSERT(settings);
2234
2235    if(settings->endOfStreamAction >= NEXUS_PlaybackLoopMode_eMax) {
2236        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2237    }
2238    if(settings->beginningOfStreamAction >= NEXUS_PlaybackLoopMode_eMax) {
2239        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2240    }
2241    if(settings->playErrorHandling>=NEXUS_PlaybackErrorHandlingMode_eMax) {
2242        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2243    }
2244    if(settings->seekErrorHandling>=NEXUS_PlaybackErrorHandlingMode_eMax) {
2245        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2246    }
2247    if(settings->trickErrorHandling>=NEXUS_PlaybackErrorHandlingMode_eMax) {
2248        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2249    }
2250    if(settings->playErrorHandling!=NEXUS_PlaybackErrorHandlingMode_eEndOfStream
2251        && settings->playErrorHandling!=NEXUS_PlaybackErrorHandlingMode_eAbort) {
2252        return BERR_TRACE(NEXUS_NOT_SUPPORTED);
2253    }
2254
2255    if (settings->stcTrick && !settings->stcChannel) {
2256        BDBG_ERR(("NEXUS_PlaybackSettings.stcTrick requires NEXUS_PlaybackSettings.stcChannel"));
2257        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2258    }
2259
2260    if (settings->stcTrick != playback->params.stcTrick && playback->state.state != eStopped) {
2261        BDBG_ERR(("You must set NEXUS_PlaybackSettings.stcTrick before calling NEXUS_Playback_Start"));
2262        return BERR_TRACE(NEXUS_INVALID_PARAMETER);
2263    }
2264
2265    if(playback->state.state == eStopped && BLST_S_FIRST(&playback->pid_list)==NULL) {
2266        if(settings->playpump) {
2267           NEXUS_PlaypumpSettings pumpCfg = settings->playpumpSettings;
2268            /* replace dataCallback, and don't leak it outside */
2269           NEXUS_CallbackHandler_PrepareCallback(playback->dataCallbackHandler, pumpCfg.dataCallback);
2270           pumpCfg.originalTransportType = NEXUS_TransportType_eUnknown;
2271           switch(pumpCfg.transportType) {
2272#if 0
2273           case NEXUS_TransportType_eMp4:
2274           case NEXUS_TransportType_eMkv:
2275               pumpCfg.originalTransportType = pumpCfg.transportType;
2276               pumpCfg.transportType = NEXUS_TransportType_eMpeg2Pes;
2277               break;
2278#endif
2279           case NEXUS_TransportType_eEs:
2280                if(settings->enableStreamProcessing) {
2281                    pumpCfg.originalTransportType = pumpCfg.transportType;
2282                    pumpCfg.transportType = NEXUS_TransportType_eMpeg2Pes;
2283                }
2284               break;
2285           default:
2286               break;
2287           }
2288
2289           playback->actualTransportType = pumpCfg.transportType;
2290           rc = NEXUS_Playpump_SetSettings(settings->playpump, &pumpCfg);
2291           if(rc!=NEXUS_SUCCESS) { rc = BERR_TRACE(rc); goto err_playpump; }
2292        }
2293    } else {
2294        if(settings->playpump != playback->params.playpump) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);goto err_settings;}
2295        if(BKNI_Memcmp(&settings->playpumpSettings, &playback->params.playpumpSettings, sizeof(settings->playpumpSettings))!=0) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);goto err_settings;}
2296    }
2297
2298    if (playback->state.state != eStopped && (playback->state.mode == NEXUS_PlaybackState_eTrickMode || playback->state.trickmode_params.rate != NEXUS_NORMAL_PLAY_SPEED)) {
2299        if(settings->stcChannel != playback->params.stcChannel) {
2300            BDBG_ERR(("Cannot change Playback's stcChannel during a trick mode."));
2301            rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);
2302            goto err_settings;
2303        }
2304    }
2305
2306    /* cannot call b_play_trick_set here. it is init'd in Start */
2307
2308    NEXUS_TaskCallback_Set(playback->errorCallback, &settings->errorCallback);
2309    NEXUS_TaskCallback_Set(playback->endOfStreamCallback, &settings->endOfStreamCallback);
2310    NEXUS_TaskCallback_Set(playback->beginningOfStreamCallback, &settings->beginningOfStreamCallback);
2311    NEXUS_TaskCallback_Set(playback->parsingErrorCallback, &settings->parsingErrorCallback);
2312
2313    if(playback->state.state != eStopped && (!settings->timeshifting && playback->params.timeshifting)) {
2314        playback->params.timeshifting = false;  /* clear timeshifting flag and simulate notification from record */
2315        NEXUS_Playback_RecordProgress_priv(playback);
2316    }
2317
2318    playback->params = *settings;
2319    return NEXUS_SUCCESS;
2320err_playpump:
2321err_settings:
2322    return rc;
2323}
2324
2325void
2326NEXUS_Playback_Stop(NEXUS_PlaybackHandle playback)
2327{
2328    BERR_Code rc;
2329
2330    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
2331    BDBG_MSG(("NEXUS_Playback_Stop: %#lx", playback));
2332    if(playback->state.state == eStopped) {
2333        BDBG_WRN(("NEXUS_Playback_Stop: %#lx playback already stoped", (unsigned long)playback));
2334        goto done;
2335    }
2336
2337    /* if aborted, we can try to stop. */
2338    if(playback->state.state != eAborted) {
2339        rc = NEXUS_Playback_P_CheckWaitingIo(playback, true);
2340        if (rc) {
2341            BDBG_ERR(("Playback unable to stop. System is now unstable."));
2342            rc = BERR_TRACE(rc);
2343        }
2344    }
2345
2346    /* stop timer before unlocking module so that no additional calls to Playpump are made after NEXUS_Playpump_Stop */
2347    if( playback->state.timer ) {
2348        BDBG_MSG(("Timer still active, cancelling %#x", playback->state.timer ));
2349        NEXUS_CancelTimer(playback->state.timer);
2350        playback->state.timer = NULL;
2351    }
2352
2353    BDBG_ASSERT(playback->params.playpump);
2354    NEXUS_Playpump_Stop(playback->params.playpump);
2355
2356
2357    b_play_stop_media(playback);
2358    b_play_trick_shutdown(playback);
2359    b_play_capture_close(playback);
2360    playback->state.inAccurateSeek = false;
2361    playback->state.state = eStopped;
2362
2363    /* We must unlock because Playback takes the dataReady callback from playpump and acquires
2364    the Playback module lock inside that callback. If we don't unlock Playback here, we deadlock. */
2365    NEXUS_UnlockModule();
2366    NEXUS_StopCallbacks(playback->params.playpump);
2367    NEXUS_LockModule();
2368
2369
2370done:
2371    return ;
2372}
2373
Note: See TracBrowser for help on using the repository browser.