source: svn/trunk/newcon3bcm2_21bu/nexus/modules/playback/src/nexus_playback_media.c

Last change on this file was 2, checked in by jglee, 11 years ago

first commit

  • Property svn:executable set to *
File size: 54.0 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_media.c $
39 * $brcm_Revision: 86 $
40 * $brcm_Date: 10/25/11 11:36a $
41 *
42 * Module Description:
43 *
44 * Revision History:
45 *
46 * $brcm_Log: /nexus/modules/playback/src/nexus_playback_media.c $
47 *
48 * 86   10/25/11 11:36a vsilyaev
49 * SWDTV-8388: Added NEXUS_TransportType_eOgg type
50 *
51 * 85   8/31/11 2:44p vsilyaev
52 * SW7420-1978: Add selection of index type for playback of MPEG-2 TS
53 *  files
54 *
55 * 84   8/12/11 9:40a erickson
56 * SW7358-83: add NEXUS_PlaybackTrickModeSettings.brcmTrickMode
57 *
58 * 83   6/17/11 4:55p vsilyaev
59 * SWDTV-7046: Properly set PES ID for audio only MP4/MKV streams
60 *
61 * 82   5/26/11 7:32p vsilyaev
62 * SW7425-646: Added configurable 'gap' for timeshifting
63 *
64 * 81   5/12/11 3:43p jtna
65 * SW7550-739: replace all instances of 'NEXUS_HAS_DMA &&
66 *  NEXUS_HAS_SECURITY' with 'NEXUS_ENCRYPTED_DVR_WITH_M2M'. replace some
67 *  instances of 'NEXUS_HAS_DMA' with 'NEXUS_NUM_DMA_CHANNELS'
68 *
69 * 80   4/22/11 12:08p vsilyaev
70 * SW7422-14: Fixed detection of the 'master' video
71 *
72 * 79   4/19/11 5:28p vsilyaev
73 * SW7422-14: Differentiate extra audio/video and 'other' tracks
74 *
75 * 78   3/23/11 3:00p vsilyaev
76 * SW7422-14: Added facility to associate  stream id(pid) with a chunk of
77 *  payload
78 *
79 * 77   2/28/11 9:48a erickson
80 * SW7125-818: add #include to fix warning
81 *
82 * 76   2/25/11 12:47p erickson
83 * SW7125-818: use BNAV_GetEntrySize
84 *
85 * 75   2/24/11 1:36p vsilyaev
86 * SWDTV-5485: Differentiates ES and ES->PES types
87 *
88 * 74   2/22/11 7:53p vsilyaev
89 * SW7422-107: Added FLV mapping
90 *
91 * 73   2/15/11 6:25p dlwin
92 * SW7405-5140: Fixed ES stream playback issue with Index file. Playback
93 *  of ES stream did not work with Index file.
94 *
95 * 72   10/22/10 3:21p vsilyaev
96 * SW35230-1600: Don't dereference non-existing structure members
97 *
98 * 71   10/22/10 11:48a vsilyaev
99 * SW35230-1600: Fixed typo
100 *
101 * 70   10/22/10 11:46a vsilyaev
102 * SW35230-1600: When decrypting payload clear file buffer on transitions.
103 *
104 * 69   9/24/10 1:55p vsilyaev
105 * SWGERRARD-557: Properly handle case when MP3 playback supplied with
106 *  self-indexing scheme
107 *
108 * 68   9/20/10 11:37a gmohile
109 * SW7325-806 : Increase nest timer timeout to 10ms
110 *
111 * 62   7/23/10 2:13p vsilyaev
112 * SW7405-4654: Fixed resource cleaning on error during
113 *  NEXUS_PlaybackStart
114 *
115 * 61   7/20/10 5:24p gmohile
116 * SW3548-2994 : Add MKV DRM support
117 *
118 * 60   7/19/10 4:24p gmohile
119 * SW3548-2994 : Add support for encrypted mkv tracks
120 *
121 * 59   5/6/10 3:45p vsilyaev
122 * SW7405-3773: Added support for demuxing fragments from fragmented MP4
123 *  container
124 *
125 * 58   4/14/10 5:48p jtna
126 * SW7125-318: Coverity Defect ID:20563 USE_AFTER_FREE
127 *
128 * 57   3/26/10 1:59p vsilyaev
129 * SW7550-313: Flush video decoder on loop, this needed to prevent using
130 *  stale PTS when crossing through file boundary in frame reverse mode
131 *
132 * 56   3/11/10 5:26p vsilyaev
133 * SW3556-913: Always use header file to declare external functions
134 *
135 * 55   3/5/10 7:13p jtna
136 * SW3556-913: support for ErrorHandlingMode_eAbort
137 *
138 * 54   3/4/10 11:44a vsilyaev
139 * SW3556-913: Demoted EOS debug message
140 *
141 * 53   2/18/10 12:05p vsilyaev
142 * SW3556-913: Differentiate between index error and end of stream
143 *
144 * 52   2/11/10 2:54p erickson
145 * SW7550-252: fix dma/security tests
146 *
147 * 51   8/20/09 10:15a gmohile
148 * PR 55991 : Add PES/VOB trickmode
149 *
150 * 50   8/19/09 3:29p erickson
151 * PR57840: start timer during eWaitingRecord state so that endOfStream
152 *  callback can be fired
153 *
154 * 49   7/31/09 4:24p erickson
155 * PR57235: cleanup comments
156 *
157 * 48   7/29/09 2:05p mward
158 * PR57192: Reduce stack use in b_play_mpeg2ts_probe_index.
159 *
160 * 47   7/15/09 7:24p vsilyaev
161 * PR 55653: Added WAV format
162 *
163 * 46   7/9/09 11:34a vsilyaev
164 * PR 55989: Added autodetection between MPEG2-TS stream and NAV index
165 *
166 * 45   7/8/09 3:25p vsilyaev
167 * PR 55989: Used size of the video decoder CDB to control size of the
168 *  data chunk
169 *
170 * 44   7/8/09 11:59a vsilyaev
171 * PR 55989: Added support for OTF trickmodes
172 *
173 * 43   6/22/09 4:09p vsilyaev
174 * PR 55554: PR 55417: Fixed handling of bmedia_player_entry_type_error
175 *
176 * 42   6/8/09 7:09p vsilyaev
177 * PR 55554: Fixed handling of huge frames in MP4 stream
178 *
179 * 41   6/4/09 5:11p erickson
180 * PR54129: remove SEEK_CUR which results in loss of packet alignment when
181 *  a file fifo is trimmed. consolidate logic in
182 *  NEXUS_Playback_P_EndOfDataFile.
183 *
184 * 40   6/2/09 7:38p vsilyaev
185 * PR 55417: Added support for read method returning no data or completing
186 *  with a partial read
187 *
188 * 39   5/19/09 3:02p vsilyaev
189 * PR 55299: Fixed PES PID mapping for the case of multiple audio tracks
190 *
191 * 36   3/17/09 5:41p vsilyaev
192 * PR 46190: Adding mappings to the PCM audio codec
193 *
194 * 35   3/10/09 4:44p erickson
195 * PR52946: move fix to the right spot
196 *
197 * 34   3/10/09 2:57p vsilyaev
198 * PR 52946: Pass proper packet size for the transport streams
199 *
200 * 33   3/9/09 12:28p mphillip
201 * PR52786: Add b_play_media_send_eos to send
202 *  NEXUS_Playpump_ReadComplete(0,0) to signal end of data
203 *
204 * 32   3/6/09 10:27a erickson
205 * PR52857: add missing breaks
206 *
207 * 31   3/6/09 9:44a erickson
208 * PR52854: fix warnings
209 *
210 * 30   3/5/09 5:46p vsilyaev
211 * PR 52579: Updated circular FIFO/timeshifting code
212 *
213 * 29   3/2/09 5:09p vsilyaev
214 * PR 52434: Added handling of bmedia_player_entry_type_noop
215 *
216 * 28   2/4/09 1:10p erickson
217 * PR51841: NEXUS_Playback_Start should fail for MKV or MP4 files without
218 *  an index
219 *
220 * 27   1/26/09 11:38a vsilyaev
221 * PR 51579: Added ES to PES packetization and support for capturing of
222 *  streams produced by the playback module
223 *
224 * 26   1/15/09 2:28p erickson
225 * PR50763: remove non-error ERR
226 *
227 * 25   1/12/09 5:44p vsilyaev
228 * PR 50763: Improved seek to position 0 after rewind reached begining of
229 *  the file
230 *
231 * 24   12/21/08 8:31p nickh
232 * PR50605: Fix compile errors when running without Security
233 *
234 * 23   12/19/08 5:59p vsilyaev
235 * PR 50214: Added callbacks and counters for parsing index files
236 *
237 * 22   12/10/08 2:57p vsilyaev
238 * PR 48760: Removed duplicated information
239 *
240 * 21   12/10/08 12:49p vsilyaev
241 * PR 49718: Fixed error reporting/handling from MKV player
242 *
243 * 20   12/10/08 2:06a vsilyaev
244 * PR 48760: Using ES player for accurate progress monitor of MP3 streams
245 *
246 * 19   12/8/08 11:20a erickson
247 * PR49930: implement NEXUS_PlaybackSettings.accurateSeek
248 *
249 * 18   12/2/08 2:23p jgarrett
250 * PR 47993: Adding VOB format
251 *
252 * 17   9/12/08 1:16p vsilyaev
253 * PR 46832: Verify data that comes from the media_player
254 *
255 * 16   9/3/08 4:55p vsilyaev
256 * PR 45454: Don't use auto-index playback in default configuration
257 *
258 * 15   8/29/08 5:48p vsilyaev
259 * PR 33812: Added support for navigation in the MPEG-2 TS file without an
260 *  index
261 *
262 * 14   7/24/08 3:43p vishk
263 * PR 45134: Playback of a recorded stream loops continuously at the end
264 *  of the stream.
265 *
266 * 13   7/17/08 11:24a vsilyaev
267 * PR 44857: strenghtened test for bcmplayer generated packets
268 *
269 * 12   7/3/08 1:07p vsilyaev
270 * PR 44381: Added code to limi nestendess of recursive calls
271 *
272 * 11   6/25/08 7:10p vsilyaev
273 * PR 41869: Added markings of BTP packet
274 *
275 * 10   6/24/08 11:50a vsilyaev
276 * PR 38347: Use separate timestamps for audio and video streams
277 *
278 * 9   6/12/08 3:02p erickson
279 * PR43606: rename NEXUS_PlaybackStreamSettings to
280 *  NEXUS_PlaybackStartSettings
281 *
282 * 8   6/4/08 10:18a vsilyaev
283 * PR 43184: Don't detect EOS in the timeshifting mode
284 *
285 * 7   5/16/08 11:50a erickson
286 * PR40832: avoid bcmplayer warning for no video pid
287 *
288 * 6   4/10/08 5:07p erickson
289 * PR40832: add WRN if timeshifting not true in certain context
290 *
291 * 5   4/3/08 2:02p vsilyaev
292 * PR 39818: Optimized MKV handling
293 *
294 * 4   3/8/08 7:45a erickson
295 * PR40103: convert to NEXUS_TaskCallback
296 *
297 * 3   3/3/08 3:36p vsilyaev
298 * PR 39818: Support of MKV format
299 *
300 * 2   2/26/08 10:04a erickson
301 * PR38810: fix LoopMode ePlay
302 *
303 * 1   1/18/08 2:36p jgarrett
304 * PR 38808: Merging to main branch
305 *
306 * Nexus_Devel/15   1/10/08 2:49p erickson
307 * PR36068: 3563 has no brcm trick mode support
308 *
309 * Nexus_Devel/14   1/8/08 2:23p vsilyaev
310 * PR 35824: Fixed resync on transition between modes
311 *
312 * Nexus_Devel/13   1/7/08 5:09p erickson
313 * PR35824: add new continuous record support
314 *
315 * Nexus_Devel/12   12/21/07 5:55p vsilyaev
316 * PR 38073: Fixed fa -> fb -> play transitions
317 *
318 * Nexus_Devel/11   12/20/07 5:24p vsilyaev
319 * PR 38073: Improved handling of trickmodes
320 *
321 * Nexus_Devel/10   12/20/07 1:24p vsilyaev
322 * PR 38073: Fixed handling of loop
323 *
324 * Nexus_Devel/PR38073/1   12/19/07 10:39a vsilyaev
325 * PR 38073: Modifed code for exlusive use of media player
326 *
327 * Nexus_Devel/8   12/18/07 6:45p vsilyaev
328 * PR 38073: Added handling of no-index streams
329 *
330 * Nexus_Devel/7   12/18/07 4:57p vsilyaev
331 * PR 38073: Updated playback module to work exclusively with media player
332 *
333 * Nexus_Devel/6   12/7/07 11:55a vsilyaev
334 * PR 37574: Improved handling media formats
335 *
336 * Nexus_Devel/5   12/6/07 5:24p vsilyaev
337 * PR 37574: Added handling of segmented feed
338 *
339 * Nexus_Devel/4   11/1/07 9:41a erickson
340 * PR36633: base enum changes
341 *
342 * Nexus_Devel/3   10/30/07 5:05p vsilyaev
343 * PR 36404: Added audio tracking for slow motion modes
344 *
345 * Nexus_Devel/2   10/16/07 4:59p vsilyaev
346 * PR 35824: Rearranged code
347 *
348 * $copied_brcm_Log: /BSEAV/api/src/pvr/bsettop_playback.c $
349 * $copied_brcm_Revision: 172 $
350 * $copied_brcm_Date: 10/1/07 11:13a $
351 **************************************************************************/
352#include "nexus_playback_module.h"
353#include "nexus_playback_impl.h"
354#include "biobits.h"
355
356#if B_HAS_DIVX_DRM
357#include "bdrm_decrypt.h"
358#endif
359
360BDBG_MODULE(nexus_playback_media);
361
362#define BDBG_MSG_FLOW(X) /* BDBG_MSG(X) */
363
364static void
365b_play_media_send_eos(NEXUS_PlaybackHandle p);
366
367static void
368b_play_media_nest_timer(void *playback)
369{
370    NEXUS_PlaybackHandle p = playback;
371    BDBG_ASSERT(p->state.media.nest_count==0);
372    p->state.media.nest_timer = NULL;
373    if (b_play_control(p, eControlDataIO)) {
374        p->state.io_size = B_MEDIA_NEST_MAGIC;
375        BDBG_MSG(("b_play_media_nest_timer: %#lx interrupted",(unsigned long)p));
376        return;
377    }
378    BDBG_MSG(("b_play_media_nest_timer: %#lx continue",(unsigned long)p));
379    b_play_next_frame(p);
380    return;
381}
382
383static void
384b_play_handle_player_error(NEXUS_PlaybackHandle p, bool endofstream /* true of endofstream, false if file error */)
385{
386
387    BDBG_MSG(("b_play_handle_player_error: %#lx %s", (unsigned long)p, endofstream?"End Of Stream":"File Error"));
388    if(!endofstream) {
389        BDBG_MSG(("b_play_handle_player_error: %#lx forcing %s", (unsigned long)p, (p->params.playErrorHandling==NEXUS_PlaybackErrorHandlingMode_eAbort)?"abort":"EOF"));
390
391        p->state.index_error_cnt++;
392        NEXUS_TaskCallback_Fire(p->errorCallback);
393
394        if (p->params.playErrorHandling == NEXUS_PlaybackErrorHandlingMode_eAbort) {
395            NEXUS_Playback_P_AbortPlayback(p);
396            return;
397        }
398    }
399    /* If we are doing normal play speed or slower, then we should wait.
400    This code won't work for a decode skip mode + slow motion which is slower than normal play, but that's unlikely. */
401    if (p->params.timeshifting && (p->state.trickmode_params.rate > 0 && p->state.trickmode_params.rate <= NEXUS_NORMAL_PLAY_SPEED)) {
402        bmedia_player_pos pos;
403        bmedia_player_status status;
404
405        b_play_update_location(p);
406        bmedia_player_tell(p->media_player, &pos);
407        bmedia_player_get_status(p->media_player, &status);
408
409        /* only go into eWaitingRecord state if we are certain about the current decoder postion and we know
410        we can reliably start waiting. */
411        if (p->state.validPts && pos >= status.bounds.first) {
412            BDBG_MSG(("index->wait_for_record"));
413            p->state.read_size = 0; /* this is used as a flag that we are waiting for index */
414            p->state.state = eWaitingRecord;
415            if (!p->state.timer) {
416                p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME * 5, b_play_timer, p);
417            }
418            return;
419        }
420    }
421    BDBG_MSG(("b_play_handle_player_error-> wait_for_drain"));
422    b_play_media_send_eos(p);
423    return;
424}
425
426static void
427b_play_media_next_frame(NEXUS_PlaybackHandle p)
428{
429    int rc;
430    off_t offset;
431
432    BDBG_ASSERT(p->media_player);
433    if (b_play_control(p, eControlFrame)) {
434        return ;
435    }
436    if(p->state.media.nest_count>4) {
437        if(p->state.media.nest_timer==NULL) {
438            /* schedule a call-out and unwind a stack */
439            p->state.media.nest_timer = NEXUS_ScheduleTimer(10, b_play_media_nest_timer, p);
440            BDBG_MSG(("b_play_media_next_frame: %#lx nest:%u schedule timer %#lx",(unsigned long)p, p->state.media.nest_count, (unsigned long)p->state.media.nest_timer));
441            if(p->state.media.nest_timer) {
442                p->state.state = eWaitingIo; /* Reuse eWaitingIo state */
443                return;
444            }
445        } else {
446            BDBG_WRN(("b_play_media_next_frame: %#lx detected fork in control flow",(unsigned long)p));
447        }
448    }
449    p->state.media.nest_count++;
450    rc = bmedia_player_next(p->media_player, &p->state.media.entry);
451    if (rc!=0) {
452        b_play_handle_player_error(p, false);
453        goto done;
454    }
455    if(p->state.media.entry.type == bmedia_player_entry_type_async) {
456        p->state.state = eWaitingIo;
457        goto done;
458    }
459
460    if(p->state.media.entry.type==bmedia_player_entry_type_file) {
461       off_t seek_result;
462       offset = p->state.media.entry.start;
463       p->state.frame.offset = offset;
464#ifdef DIRECT_IO_SUPPORT
465        p->state.io.file_behind = offset - B_IO_ALIGN_TRUNC(offset);
466        p->state.io.next_data = 0;
467        BDBG_MSG_FLOW((">>>>>> seek to %lld (aligned=%lld + pre=%d)",
468            (uint64_t)offset,
469            (uint64_t)B_IO_ALIGN_TRUNC(offset), p->state.io.file_behind));
470        offset = B_IO_ALIGN_TRUNC(offset);
471#endif
472        p->state.io.last_file_offset = offset;
473        seek_result = p->file->file.data->seek(p->file->file.data, offset, SEEK_SET);
474        if (offset != seek_result) {
475            BDBG_ERR(("seek wasn't able to find mpeg data"));
476            BDBG_MSG(("b_play_next_media_frame -> wait_for_drain"));
477            b_play_media_send_eos(p);
478            goto done;
479        }
480    }
481
482    p->state.frame.cur = 0;
483    p->state.frame.size = p->state.media.entry.length;
484    BDBG_ASSERT((int)p->state.frame.size  >=0);
485    b_play_media_send_meta(p);
486done:
487    p->state.media.nest_count--;
488    return;
489}
490
491void
492bplay_p_clear_buffer(NEXUS_PlaybackHandle p)
493{
494    BSTD_UNUSED(p);
495#if NEXUS_HAS_SECURITY
496    if(p->params.playpumpSettings.securityContext &&  p->state.media.buffer) {
497        /* if there is decryption [in place] and file buffer used, it should be cleared on transition to prevent double decryption */
498        bfile_buffer_clear(p->state.media.buffer);
499    }
500#endif
501    return;
502}
503
504
505NEXUS_Error
506b_play_media_handle_loop_condition(NEXUS_PlaybackHandle p, bool is_beginning, NEXUS_PlaybackLoopMode loopmode, const bmedia_player_bounds *bounds)
507{
508    int rc;
509    int pos;
510
511    BDBG_ASSERT(p->media_player);
512    switch(loopmode) {
513    case NEXUS_PlaybackLoopMode_eLoop:
514        if (is_beginning) {
515            BDBG_MSG(("loop to end (index %ld)", bounds->last));
516            b_play_flush(p);
517            rc = bmedia_player_seek(p->media_player, bounds->last);
518            if (rc!=0) {
519                BDBG_WRN(("b_play_media_handle_loop_condition: bmedia_player_seek failed %d", rc));
520                rc = bmedia_player_seek(p->media_player, bounds->first/2 + bounds->last/2);
521            }
522        } else {
523            BDBG_MSG(("loop to beginning"));
524            b_play_flush(p);
525            rc = bmedia_player_seek(p->media_player, bounds->first);
526            if (rc!=0) {
527                BDBG_WRN(("b_play_media_handle_loop_condition: bmedia_player_seek failed %d", rc));
528                rc = bmedia_player_seek(p->media_player, bounds->first/2 + bounds->last/2);
529            }
530        }
531        bplay_p_clear_buffer(p);
532        break;
533    case NEXUS_PlaybackLoopMode_ePlay:
534        if (is_beginning) {
535            /* if the beginning has been trimmed, we should gap so that we avoid getting chopped again by bfile_fifo. */
536            pos = (bounds->first == 0) ? bounds->first : bounds->first + p->params.timeshiftingSettings.beginningOfStreamGap;
537        } else {
538            /* play at EOF requires a gap for record, otherwise, there's really nothing we can do. */
539            if (!p->params.timeshifting) {
540                BDBG_WRN(("endOfStreamAction == NEXUS_PlaybackLoopMode_ePlay without timeshifting has been converted to ePause"));
541                bplay_p_pause(p);
542                break;
543            } else {
544                /* The recommended endOfStream action for timeshifting is to switch to live decode. This can only be done
545                by the application. This code provides a gap to reduce stuttering. */
546                if (bounds->last >= p->params.timeshiftingSettings.endOfStreamGap) {
547                    pos = bounds->last - p->params.timeshiftingSettings.endOfStreamGap;
548                }
549                else {
550                    pos = 0;
551                }
552            }
553        }
554        BDBG_MSG(("seek pos %u in %u...%u", pos, bounds->first, bounds->last));
555
556        bplay_p_clear_buffer(p);
557        bplay_p_play_from(p, pos);
558        break;
559    default:
560    case NEXUS_PlaybackLoopMode_ePause:
561        bplay_p_pause(p);
562        break;
563    }
564    return NEXUS_SUCCESS;
565}
566
567/* this function called when it's time to feed new frame into the playback buffer,
568it should be called without pending I/O */
569void
570b_play_next_frame(NEXUS_PlaybackHandle p)
571{
572    p->state.decoder_flushed = false; /* clear flushed state */
573    b_play_media_next_frame(p);
574    return;
575}
576
577static void
578b_play_media_pts_timer(void *playback)
579{
580    NEXUS_PlaybackHandle p = playback;
581    p->state.media.pts_timer = NEXUS_ScheduleTimer(BMEDIA_UPDATE_POSITION_INTERVAL, b_play_media_pts_timer, p);
582    b_play_update_location(p);
583    return;
584}
585
586void
587b_play_media_time_test(void)
588{
589#if 0
590    uint32_t time, pts;
591    bmedia_dword delta;
592
593    delta = bmedia_pts2time((uint32_t)(BMEDIA_PTS_MODULO-1), BMEDIA_TIME_SCALE_BASE);
594    pts = bmedia_time2pts(delta, BMEDIA_TIME_SCALE_BASE);
595    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
596    BDBG_MSG(("modulo_test %u %u %u", delta, pts, time));
597    BDBG_ASSERT(time == delta);
598
599    pts = bmedia_time2pts(delta+100, BMEDIA_TIME_SCALE_BASE);
600    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
601    BDBG_MSG(("modulo_test %u %u %u", delta, pts, time));
602    BDBG_ASSERT(time == 99);
603
604    pts = bmedia_time2pts(delta, -BMEDIA_TIME_SCALE_BASE);
605    time = bmedia_pts2time(pts, -BMEDIA_TIME_SCALE_BASE);
606    BDBG_MSG(("modulo_test %u %u %u", delta, pts, time));
607    BDBG_ASSERT(time == delta);
608
609    pts = bmedia_time2pts(delta+100, -BMEDIA_TIME_SCALE_BASE);
610    time = bmedia_pts2time(pts, -BMEDIA_TIME_SCALE_BASE);
611    BDBG_MSG(("modulo_test %u %u %u", delta, pts, time));
612    BDBG_ASSERT(time == 99);
613
614    pts = bmedia_time2pts(1000, BMEDIA_TIME_SCALE_BASE);
615    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
616    BDBG_MSG(("time_test: %u %u", (unsigned)pts, (unsigned)time));
617    BDBG_ASSERT(time == 1000);
618    pts = bmedia_time2pts(1000, -BMEDIA_TIME_SCALE_BASE);
619    time = bmedia_pts2time(pts, -BMEDIA_TIME_SCALE_BASE);
620    BDBG_MSG(("time_test: %u %u", (unsigned)pts, (unsigned)time));
621    BDBG_ASSERT(time == 1000);
622
623    pts = bmedia_time2pts(337023, BMEDIA_TIME_SCALE_BASE);
624    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
625    BDBG_MSG(("time_test: %u %u", (unsigned)pts, (unsigned)time));
626    BDBG_ASSERT(time == 337023);
627
628    pts = bmedia_time2pts(337023, -BMEDIA_TIME_SCALE_BASE);
629    time = bmedia_pts2time(pts, -BMEDIA_TIME_SCALE_BASE);
630    BDBG_MSG(("time_test: %u %u", (unsigned)pts, (unsigned)time));
631    BDBG_ASSERT(time == 337023);
632
633    pts = bmedia_time2pts(2*delta+337023, BMEDIA_TIME_SCALE_BASE);
634    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
635    BDBG_MSG(("time_test: %u %u %u", (unsigned)pts, (unsigned)time, 337023+2*delta));
636    BDBG_ASSERT(337023-time<10);
637
638    pts = bmedia_time2pts(2*delta+337023, BMEDIA_TIME_SCALE_BASE);
639    time = bmedia_pts2time(pts, BMEDIA_TIME_SCALE_BASE);
640    BDBG_MSG(("time_test: %u %u %u", (unsigned)pts, (unsigned)time, 337023+2*delta));
641    BDBG_ASSERT(337023-time<10);
642
643
644    return;
645#endif
646}
647
648static void
649b_play_media_async_atom_ready(void *cntx, bmedia_player_entry *entry)
650{
651    NEXUS_PlaybackHandle p = cntx;
652
653    p->state.frame.cur = 0;
654    p->state.media.entry = *entry;
655    p->state.frame.size = p->state.media.entry.length;
656    BDBG_ASSERT((int)p->state.frame.size  >=0);
657    if (b_play_control(p, eControlDataIO)) {
658        p->state.io_size = B_MEDIA_ATOM_MAGIC;
659        goto done;
660    }
661
662    if (p->state.state != eWaitingIo) {
663        BDBG_WRN(("b_play_media_async_atom_ready got unexpected data"));
664        goto done;
665    }
666
667    b_play_media_send_meta(p);
668
669done:
670    return;
671}
672
673/* this function is called when we want to send generated atom (scatteg/gather packet) */
674static void
675b_play_media_send_atom(NEXUS_PlaybackHandle p)
676{
677    NEXUS_Error rc;
678    size_t size;
679    void *vaddr;
680
681    BDBG_ASSERT(p->state.media.entry.atom);
682
683    BDBG_ASSERT(p->params.playpump);
684
685    for(;;) {
686        if (NEXUS_Playpump_GetBuffer(p->params.playpump, &vaddr, &size)!=NEXUS_SUCCESS || size == 0) {
687            goto keep_waiting;
688        }
689        size = batom_cursor_copy(&p->state.media.cursor, vaddr, size);
690        BDBG_MSG_FLOW(("sending %d out of %d bytes left in packet",
691            size, p->state.packet.size-p->state.packet.cur));
692
693        /* Can't be in eWaitingPlayback or we risk a deadlock from playpump callback. */
694        p->state.state = eTransition;
695        b_play_capture_write(p, vaddr, size);
696        rc = NEXUS_Playpump_ReadComplete(p->params.playpump, 0, size);
697        if (rc!=NEXUS_SUCCESS) {
698            rc = BERR_TRACE(rc);
699            BDBG_ERR(("b_play_media_send_atom NEXUS_Playpump_ReadComplete error %#x, playback aborted", (unsigned)rc));
700            p->state.state = eAborted;
701            NEXUS_TaskCallback_Fire(p->errorCallback);
702            return;
703        }
704
705        p->state.packet.cur+=size;
706        if (p->state.packet.cur >= p->state.packet.size) {
707            batom_release(p->state.media.entry.atom);
708            p->state.media.entry.atom = NULL;
709            /* we're done sending the packet, so go get the next frame */
710            b_play_next_frame(p);
711            return;
712        }
713        /* keep writing data */
714    }
715    return;
716keep_waiting:
717    p->state.state = eWaitingPlayback;
718    p->state.data_source = b_play_media_send_atom; /* schedule to call us when space is avaliable */
719    return;
720}
721
722
723
724void
725b_play_media_send_meta(NEXUS_PlaybackHandle p)
726{
727    NEXUS_Error rc;
728    size_t size;
729    void *vaddr;
730    NEXUS_PlaypumpSegment segment;
731
732    BDBG_ASSERT(p->media_player);
733    BDBG_ASSERT(p->params.playpump);
734
735    /* handle no data cases prior to sending segment */
736    switch(p->state.media.entry.type) {
737    case bmedia_player_entry_type_atom:
738    case bmedia_player_entry_type_embedded:
739    case bmedia_player_entry_type_file:
740        if(p->state.media.entry.length!=0) {
741            break;
742        }
743        /* keep going */
744    case bmedia_player_entry_type_noop:
745    case bmedia_player_entry_type_no_data:
746        b_play_media_next_frame(p); /* keep on reading  */
747        return;
748    case bmedia_player_entry_type_end_of_stream:
749        b_play_handle_player_error(p, true);
750        return;
751    case bmedia_player_entry_type_error:
752        b_play_handle_player_error(p, false);
753        return;
754    case bmedia_player_entry_type_async:
755        BDBG_ASSERT(0); /* keep going */
756    }
757
758    if(p->state.media.segmented) {
759        for(;;) {
760            if (NEXUS_Playpump_GetBuffer(p->params.playpump, &vaddr, &size)!=NEXUS_SUCCESS || size == 0) {
761                goto keep_waiting;
762            }
763            if (vaddr > (void *)p->state.media.last_segment_hdr) {
764                /* skip buffer */
765                BDBG_MSG(("b_play_media_send_meta: skip %#lx %lu", (unsigned long)vaddr, (unsigned long)size));
766                rc = NEXUS_Playpump_ReadComplete(p->params.playpump, size, 0);
767                if (rc!=NEXUS_SUCCESS) {
768                    BDBG_ERR(("b_play_media_send_meta: NEXUS_Playpump_ReadComplete error %#x, playback aborted", (unsigned)rc));
769                    p->state.state = eAborted;
770                    NEXUS_TaskCallback_Fire(p->errorCallback);
771                    return;
772                }
773                continue;
774            }
775            if(size<sizeof(segment)) {
776                goto keep_waiting;
777            }
778            BKNI_Memset(&segment, 0, sizeof(segment));
779            segment.length = p->state.media.entry.length + sizeof(segment);
780            segment.offset = p->state.media.entry.start;
781            segment.timestamp = p->state.media.entry.timestamp;
782            segment.signature = NEXUS_PLAYPUMP_SEGMENT_SIGNATURE;
783            if(p->state.media.entry.entry){
784                unsigned i;
785                /* Video timestamp is always on slot 0*/
786                segment.timestamp = p->state.media.entry.entry->resync[0].timestamp;
787                segment.timestamp_delta[0].stream_id = p->state.media.entry.entry->resync[0].stream_id;
788                /* extra  slots used to pass timestamp */
789                for(i=1; i<sizeof(segment.timestamp_delta)/sizeof(segment.timestamp_delta[0]); i++){
790                    segment.timestamp_delta[i].stream_id = p->state.media.entry.entry->resync[i].stream_id;
791                    segment.timestamp_delta[i].timestamp_delta = p->state.media.entry.entry->resync[i].timestamp - segment.timestamp;
792                }
793            }
794
795            BKNI_Memcpy(vaddr, &segment, sizeof(segment)); /* vaddr might be not alligned, therefore use temporary buffer */
796            BDBG_MSG_FLOW(("segment at %#lx %#lx %lu(%lu)", (unsigned long)vaddr, (unsigned long)segment.signature, (unsigned long)segment.offset, (unsigned long)segment.length));
797            /* Can't be in eWaitingPlayback or we risk a deadlock from playpump callback. */
798            p->state.state = eTransition;
799            rc = NEXUS_Playpump_ReadComplete(p->params.playpump, 0, sizeof(segment));
800            if (rc!=NEXUS_SUCCESS) {
801                BDBG_ERR(("b_play_media_send_meta: NEXUS_Playpump_ReadComplete error %#x, playback aborted", (unsigned)rc));
802                p->state.state = eAborted;
803                NEXUS_TaskCallback_Fire(p->errorCallback);
804                return;
805            }
806            break;
807        }
808    }
809    switch(p->state.media.entry.type) {
810    case bmedia_player_entry_type_embedded:
811        BDBG_ASSERT(p->state.media.entry.embedded);
812        p->state.packet.size = p->state.media.entry.length;
813        p->state.packet.cur = 0;
814        p->state.packet.buf = p->state.media.entry.embedded;
815#if NEXUS_ENCRYPTED_DVR_WITH_M2M
816        if( p->params.playpumpSettings.securityContext && p->params.playpumpSettings.transportType==NEXUS_TransportType_eTs && (p->state.packet.size==188 || p->state.packet.size == 192) ) {
817            /* if playing back encrypted data, we need to mark inserted packets as encrypted and then playpump would see it,
818             * bypass decryption, and then clear encryption marking. Life is weird. */
819            uint8_t timestampoffset = p->state.packet.size==188?0:4;
820            ((uint8_t *)p->state.packet.buf)[3 + timestampoffset] = B_GET_BITS(((uint8_t *)p->state.packet.buf)[3 + timestampoffset],5,0) | B_SET_BITS( transport_scrambling_control, 0x03, 7, 6); /* set adaptation field */
821        }
822#endif
823        b_play_send_packet(p);
824        break;
825    case bmedia_player_entry_type_atom:
826        BDBG_ASSERT(p->state.media.entry.atom);
827        batom_cursor_from_atom(&p->state.media.cursor, p->state.media.entry.atom);
828        p->state.packet.size = p->state.media.entry.length;
829        p->state.packet.cur = 0;
830        b_play_media_send_atom(p);
831        break;
832    case bmedia_player_entry_type_file:
833        b_play_send_frame(p);
834        break;
835
836    /* should have handled previously */
837    case bmedia_player_entry_type_end_of_stream:
838    case bmedia_player_entry_type_no_data:
839    case bmedia_player_entry_type_error:
840    case bmedia_player_entry_type_async:
841    case bmedia_player_entry_type_noop:
842        BDBG_ASSERT(0);
843        break;
844    }
845    return;
846keep_waiting:
847    p->state.state = eWaitingPlayback;
848    p->state.data_source = b_play_media_send_meta; /* schedule to call us when space is avaliable */
849    return;
850}
851
852static void
853b_play_media_send_eos(NEXUS_PlaybackHandle p)
854{
855    NEXUS_Error rc;
856    size_t size;
857    void *vaddr;
858
859    BDBG_ASSERT(p->media_player);
860    BDBG_ASSERT(p->params.playpump);
861
862    if(!p->state.media.segmented) {
863        if (NEXUS_Playpump_GetBuffer(p->params.playpump, &vaddr, &size)!=NEXUS_SUCCESS || size == 0) {
864            goto keep_waiting;
865        }
866        BDBG_MSG(("b_play_media_send_eos: skip %#lx %lu", (unsigned long)vaddr, (unsigned long)size));
867        rc = NEXUS_Playpump_ReadComplete(p->params.playpump, 0, 0);
868        if (rc!=NEXUS_SUCCESS) {
869            BDBG_ERR(("b_play_media_send_eos: NEXUS_Playpump_ReadComplete error %#x, playback aborted", (unsigned)rc));
870            p->state.state = eAborted;
871            NEXUS_TaskCallback_Fire(p->errorCallback);
872            return;
873        }
874    } else {
875        BDBG_MSG(("b_play_media_send_eos: not supported"));
876    }
877    b_play_start_drain(p);
878    return;
879keep_waiting:
880    p->state.state = eWaitingPlayback;
881    p->state.data_source = b_play_media_send_eos; /* schedule to call us when space is avaliable */
882    return;
883}
884
885static void
886b_play_media_io_read_complete(void *playback_, ssize_t size)
887{
888    NEXUS_PlaybackHandle playback = playback_;
889    void (*read_cont)(void *cont, ssize_t size);
890    void *cntx;
891    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
892    /* acquire mutex and call continuation */
893    BDBG_ASSERT(playback->state.media.async_read.read_cont);
894    read_cont = playback->state.media.async_read.read_cont;
895    cntx = playback->state.media.async_read.cntx;
896    playback->state.media.async_read.read_cont=NULL;
897    playback->state.media.async_read.cntx=NULL;
898    read_cont(cntx, size);
899}
900
901static void
902b_play_media_io_async_read(void *sync_cnxt, bfile_io_read_t fd, void *buf, size_t length, void (*read_cont)(void *cont, ssize_t size), void *cntx)
903{
904    NEXUS_PlaybackHandle playback = sync_cnxt;
905    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
906    BDBG_ASSERT(playback->state.media.async_read.read_cont==NULL);
907    BDBG_ASSERT(playback->state.media.async_read.cntx==NULL);
908    /* save context and call into async I/O */
909    playback->state.media.async_read.read_cont=read_cont;
910    playback->state.media.async_read.cntx=cntx;
911    NEXUS_File_AsyncRead(fd, buf, length, NEXUS_MODULE_SELF, b_play_media_io_read_complete, playback);
912    return;
913}
914
915void
916b_play_stop_media(NEXUS_PlaybackHandle playback)
917{
918    if(playback->state.media.entry.atom) {
919        batom_release(playback->state.media.entry.atom);
920        playback->state.media.entry.atom = NULL;
921    }
922    if(playback->state.media.pts_timer) {
923        NEXUS_CancelTimer(playback->state.media.pts_timer);
924        playback->state.media.pts_timer = NULL;
925    }
926    if(playback->state.media.nest_timer) {
927        NEXUS_CancelTimer(playback->state.media.nest_timer);
928        playback->state.media.nest_timer = NULL;
929    }
930    bmedia_player_destroy(playback->media_player);
931    playback->media_player = NULL;
932    if(playback->state.media.buffer) {
933        BDBG_ASSERT(playback->state.media.factory);
934        bfile_buffer_destroy(playback->state.media.buffer);
935        batom_factory_destroy(playback->state.media.factory);
936        BDBG_ASSERT(playback->state.media.buf);
937        BKNI_Free(playback->state.media.buf);
938        playback->state.media.factory = NULL;
939        playback->state.media.buffer = NULL;
940        playback->state.media.buf = NULL;
941    }
942    return;
943}
944
945static void
946b_play_media_player_error(void *cntx)
947{
948    NEXUS_PlaybackHandle playback = cntx;
949    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
950    BDBG_MSG(("b_play_media_player_error: %#lx", (unsigned long)playback));
951    NEXUS_TaskCallback_Fire(playback->errorCallback);
952    return;
953}
954
955void
956b_play_update_media_player_config(NEXUS_PlaybackHandle p, bmedia_player_decoder_config *config)
957{
958    const NEXUS_Playback_P_PidChannel *pid;
959
960    for (pid = BLST_S_FIRST(&p->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
961        NEXUS_VideoDecoderStatus videoStatus;
962        if(pid->cfg.pidSettings.pidType==NEXUS_PidType_eVideo && pid->cfg.pidTypeSettings.video.decoder) {
963            NEXUS_Error rc = NEXUS_VideoDecoder_GetStatus(pid->cfg.pidTypeSettings.video.decoder, &videoStatus);
964            if(rc==BERR_SUCCESS) {
965                config->video_buffer_size = videoStatus.fifoSize;
966            }
967        }
968    }
969    config->fragmented = true;
970#if NEXUS_OTFPVR
971    config->otf = true;
972#endif
973    return;
974}
975
976
977typedef enum b_play_mpeg2ts_index_type {
978    b_play_mpeg2ts_index_type_unknown = NEXUS_PlaybackMpeg2TsIndexType_eAutoDetect,
979    b_play_mpeg2ts_index_type_nav = NEXUS_PlaybackMpeg2TsIndexType_eNav,
980    b_play_mpeg2ts_index_type_mpeg = NEXUS_PlaybackMpeg2TsIndexType_eSelf
981} b_play_mpeg2ts_index_type;
982
983#include "bcmindexer.h"
984#include "bcmplayer.h"
985#include "../src/bcmindexerpriv.h"
986
987static b_play_mpeg2ts_index_type
988b_play_mpeg2ts_probe_index(bfile_io_read_t fd, NEXUS_PlaybackHandle playback)
989{
990    NEXUS_TransportTimestampType timestampType = playback->params.playpumpSettings.timestamp.type;
991    int rc;
992    const BNAV_Entry *entry;
993    size_t nav_size;
994    int nav_version;
995    unsigned nav_count;
996    unsigned nav_total_count;
997    unsigned off;
998    unsigned ts_header;
999    unsigned ts_count;
1000    unsigned ts_total_count;
1001
1002    fd->seek(fd, 0, SEEK_SET);
1003    rc = fd->read(fd, playback->state.media.probe_buf, sizeof(playback->state.media.probe_buf));
1004    fd->seek(fd, 0, SEEK_SET);
1005    if(rc!=sizeof(playback->state.media.probe_buf)) {
1006        return b_play_mpeg2ts_index_type_unknown;
1007    }
1008    entry = (void *)playback->state.media.probe_buf;
1009    nav_version = BNAV_get_version( entry );
1010    nav_size = BNAV_GetEntrySize(nav_version);
1011    for(nav_total_count=0,nav_count=0;(uint8_t *)entry+nav_size<playback->state.media.probe_buf+sizeof(playback->state.media.probe_buf);
1012         nav_total_count++,entry = (void *)((uint8_t *)entry + nav_size)) {
1013        if(nav_version == BNAV_get_version(entry)) {
1014            nav_count++;
1015        }
1016    }
1017    ts_header = timestampType==NEXUS_TransportTimestampType_eNone?0:4;
1018    for(ts_total_count=0,ts_count=0,off=ts_header;off<sizeof(playback->state.media.probe_buf)-1;off+=ts_header+188,ts_total_count++) {
1019        if(playback->state.media.probe_buf[off]==0x47) {
1020            ts_count++;
1021        }
1022    }
1023    BDBG_MSG(("b_play_mpeg2ts_probe_index: ts_count:%u/%u nav_count:%u/%u", ts_count,ts_total_count, nav_count, nav_total_count));
1024    if(nav_count==nav_total_count) {
1025        return b_play_mpeg2ts_index_type_nav;
1026    } else if(ts_count==ts_total_count) {
1027        return b_play_mpeg2ts_index_type_mpeg;
1028    } else {
1029        return b_play_mpeg2ts_index_type_unknown;
1030    }
1031}
1032
1033static const struct {
1034    NEXUS_VideoCodec nexusVideoCodec;
1035    bvideo_codec media_video_codec;
1036} NEXUS_Playback_P_VideoCodecMap[]= {
1037    {NEXUS_VideoCodec_eMpeg1, bvideo_codec_mpeg1},
1038    {NEXUS_VideoCodec_eMpeg2, bvideo_codec_mpeg2},
1039    {NEXUS_VideoCodec_eMpeg4Part2, bvideo_codec_mpeg4_part2},
1040    {NEXUS_VideoCodec_eH263, bvideo_codec_h263},
1041    {NEXUS_VideoCodec_eH264, bvideo_codec_h264},
1042    {NEXUS_VideoCodec_eH264_Svc, bvideo_codec_h264_svc},
1043    {NEXUS_VideoCodec_eH264_Mvc, bvideo_codec_h264_mvc},
1044    {NEXUS_VideoCodec_eVc1, bvideo_codec_vc1},
1045    {NEXUS_VideoCodec_eVc1SimpleMain, bvideo_codec_vc1_sm},
1046    {NEXUS_VideoCodec_eDivx311, bvideo_codec_divx_311},
1047    {NEXUS_VideoCodec_eAvs, bvideo_codec_avs}
1048};
1049
1050static bvideo_codec
1051NEXUS_Playback_P_VideoCodecMap_ToMedia(NEXUS_VideoCodec videoCodec)
1052{
1053    unsigned i;
1054    for(i=0;i<sizeof(NEXUS_Playback_P_VideoCodecMap)/sizeof(*NEXUS_Playback_P_VideoCodecMap);i++) {
1055        if(NEXUS_Playback_P_VideoCodecMap[i].nexusVideoCodec == videoCodec) {
1056            return NEXUS_Playback_P_VideoCodecMap[i].media_video_codec;
1057        }
1058    }
1059    return bvideo_codec_unknown;
1060}
1061
1062static const struct {
1063    NEXUS_AudioCodec nexusAudioCodec;
1064    baudio_format media_audio_codec;
1065} NEXUS_Playback_P_AudioCodecMap[]= {
1066    {NEXUS_AudioCodec_eMpeg, baudio_format_mpeg},
1067    {NEXUS_AudioCodec_eMp3, baudio_format_mp3},
1068    {NEXUS_AudioCodec_eAac, baudio_format_aac},
1069    {NEXUS_AudioCodec_eAacLoas, baudio_format_aac},
1070    {NEXUS_AudioCodec_eAacPlus, baudio_format_aac_plus},
1071    {NEXUS_AudioCodec_eAacPlusAdts, baudio_format_aac_plus_adts},
1072    {NEXUS_AudioCodec_eAc3,  baudio_format_ac3},
1073    {NEXUS_AudioCodec_eAc3Plus,  baudio_format_ac3_plus},
1074    {NEXUS_AudioCodec_eDts,  baudio_format_dts},
1075    {NEXUS_AudioCodec_eLpcmDvd,  baudio_format_lpcm_dvd},
1076    {NEXUS_AudioCodec_eLpcmHdDvd,  baudio_format_lpcm_hddvd},
1077    {NEXUS_AudioCodec_eLpcmBluRay, baudio_format_lpcm_bluray},
1078    {NEXUS_AudioCodec_eDtsHd,  baudio_format_dts_hd},
1079    {NEXUS_AudioCodec_eWmaStd, baudio_format_wma_std},
1080    {NEXUS_AudioCodec_eWmaPro, baudio_format_wma_pro},
1081    {NEXUS_AudioCodec_eAvs,  baudio_format_avs},
1082    {NEXUS_AudioCodec_ePcm,  baudio_format_pcm}
1083};
1084
1085static baudio_format
1086NEXUS_Playback_P_AudioCodecMap_ToMedia(NEXUS_AudioCodec audioCodec)
1087{
1088    unsigned i;
1089    for(i=0;i<sizeof(NEXUS_Playback_P_AudioCodecMap)/sizeof(*NEXUS_Playback_P_AudioCodecMap);i++) {
1090        if(NEXUS_Playback_P_AudioCodecMap[i].nexusAudioCodec == audioCodec) {
1091            return NEXUS_Playback_P_AudioCodecMap[i].media_audio_codec;
1092        }
1093    }
1094    return baudio_format_unknown;
1095}
1096
1097
1098NEXUS_Error
1099b_play_start_media(NEXUS_PlaybackHandle playback, NEXUS_FilePlayHandle file, const NEXUS_PlaypumpStatus *playpump_status,const NEXUS_PlaybackStartSettings *params)
1100{
1101    bmedia_player_stream stream;
1102    bmedia_player_config player_config;
1103    unsigned i;
1104    const NEXUS_Playback_P_PidChannel *pid;
1105    const NEXUS_Playback_P_PidChannel *master_pid;
1106    NEXUS_Error rc=NEXUS_SUCCESS;
1107    NEXUS_PlaypumpSettings pumpCfg;
1108    bmedia_player_decoder_mode mode;
1109    b_play_mpeg2ts_index_type mpeg2ts_index_type = b_play_mpeg2ts_index_type_unknown;
1110
1111    playback->media_player = NULL;
1112    playback->state.media.last_segment_hdr = (uint8_t*)playpump_status->bufferBase + playpump_status->fifoSize - sizeof(NEXUS_PlaypumpSegment);
1113    playback->state.media.time_scale = BMEDIA_TIME_SCALE_BASE;
1114    playback->state.media.buffer = NULL;
1115    playback->state.media.buf = NULL;
1116    playback->state.media.factory = NULL;
1117    playback->state.media.pts_timer = NEXUS_ScheduleTimer(BMEDIA_UPDATE_POSITION_INTERVAL, b_play_media_pts_timer, playback);
1118
1119    b_play_media_time_test();
1120    bmedia_player_init_stream(&stream);
1121    switch(playback->params.playpumpSettings.transportType) {
1122    case NEXUS_TransportType_eAsf:
1123        stream.format = bstream_mpeg_type_asf;
1124        playback->state.media.segmented = true;
1125        break;
1126    case NEXUS_TransportType_eAvi:
1127        stream.format = bstream_mpeg_type_avi;
1128        playback->state.media.segmented = true;
1129        break;
1130    case NEXUS_TransportType_eMp4:
1131        stream.format = bstream_mpeg_type_mp4;
1132        playback->state.media.segmented = true;
1133        break;
1134    case NEXUS_TransportType_eMkv:
1135        stream.format = bstream_mpeg_type_mkv;
1136        playback->state.media.segmented = true;
1137        break;
1138    case NEXUS_TransportType_eTs:
1139        stream.format = bstream_mpeg_type_ts;
1140        playback->state.media.segmented = false;
1141        break;
1142    case NEXUS_TransportType_eDssPes:
1143    case NEXUS_TransportType_eDssEs:
1144        stream.format = bstream_mpeg_type_dss_es;
1145        playback->state.media.segmented = false;
1146        break;
1147    case NEXUS_TransportType_eMpeg2Pes:
1148        stream.format = bstream_mpeg_type_pes;
1149        playback->state.media.segmented = false;
1150        break;
1151    case NEXUS_TransportType_eMpeg1Ps:
1152        stream.format = bstream_mpeg_type_ts;
1153        playback->state.media.segmented = false;
1154        break;
1155    case NEXUS_TransportType_eVob:
1156        stream.format = bstream_mpeg_type_vob;
1157        playback->state.media.segmented = false;
1158        break;
1159    case NEXUS_TransportType_eWav:
1160        stream.format = bstream_mpeg_type_wav;
1161        playback->state.media.segmented = true;
1162        break;
1163    case NEXUS_TransportType_eMp4Fragment:
1164        stream.format = bstream_mpeg_type_mp4_fragment;
1165        playback->state.media.segmented = true;
1166        break;
1167    case NEXUS_TransportType_eRmff:
1168        stream.format = bstream_mpeg_type_rmff;
1169        playback->state.media.segmented = true;
1170        break;
1171    case NEXUS_TransportType_eFlv:
1172        stream.format = bstream_mpeg_type_flv;
1173        playback->state.media.segmented = true;
1174        break;
1175    case NEXUS_TransportType_eOgg:
1176        stream.format = bstream_mpeg_type_ogg;
1177        playback->state.media.segmented = false;
1178        break;
1179    default:
1180        BDBG_WRN(("Unknown transport type %u, defaulting to ES", playback->params.playpumpSettings.transportType));
1181        /* fallthrough */
1182    case NEXUS_TransportType_eEs:
1183        stream.format = bstream_mpeg_type_es;
1184        playback->state.media.segmented = false;
1185        break;
1186    }
1187
1188    master_pid = NULL;
1189
1190    /* choose a master track */
1191    for(pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
1192        switch(pid->cfg.pidSettings.pidType) {
1193        case NEXUS_PidType_eVideo:
1194            if(pid->cfg.pidTypeSettings.video.codec==NEXUS_VideoCodec_eH264) {
1195                /* H264 (base layer) overwrite master pid */
1196                master_pid = pid;
1197            } else {
1198                if(master_pid==NULL || master_pid->cfg.pidSettings.pidType!=NEXUS_PidType_eVideo) { /* Video overwrites audio */
1199                    master_pid = pid;
1200                }
1201            }
1202            break;
1203        case NEXUS_PidType_eAudio:
1204            if(master_pid==NULL) {
1205                master_pid = pid;
1206            }
1207            break;
1208        default: break;
1209        }
1210    }
1211    if(master_pid) {
1212        stream.master = master_pid->pid;
1213    } else {
1214        /* if there's no masgter pid, then we can still do playback-only work. setting to the NULL
1215        pid tells media framework plugs that we have not forgotten a param in their API. */
1216        stream.master = 0x1fff;
1217    }
1218
1219    for(i=0,pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
1220        bool statusValid;
1221        NEXUS_PidChannelStatus status;
1222        baudio_format audio_codec = baudio_format_unknown; 
1223        bvideo_codec video_codec = bvideo_codec_unknown;
1224
1225        switch(pid->cfg.pidSettings.pidType) {
1226        case NEXUS_PidType_eVideo:
1227            video_codec = NEXUS_Playback_P_VideoCodecMap_ToMedia(pid->cfg.pidTypeSettings.video.codec);
1228            break;
1229        case NEXUS_PidType_eAudio:
1230            audio_codec = NEXUS_Playback_P_AudioCodecMap_ToMedia(pid->cfg.pidSettings.pidTypeSettings.audio.codec);
1231            break;
1232        default: break;
1233        }
1234        statusValid = NEXUS_PidChannel_GetStatus(pid->pidChn, &status)==NEXUS_SUCCESS;
1235        if(pid==master_pid) {
1236            stream.stream.es.video_codec = video_codec;
1237            stream.stream.es.audio_codec = audio_codec;
1238            if(statusValid) {
1239                BDBG_MSG(("%s:%lx mapping track %u(master) -> %#x:%#x", "b_play_start_media", (unsigned)playback, (unsigned)pid->pid, (unsigned)status.remappedPid, (unsigned)status.pid));
1240                stream.stream.id.master = status.remappedPid;
1241            }
1242        } else {
1243            if(i<BMEDIA_PLAYER_MAX_TRACKS) {
1244                stream.stream.es.other_video[i] = video_codec;
1245                stream.stream.es.other_audio[i] = audio_codec;
1246                if(statusValid) {
1247                    BDBG_MSG(("%s:%lx mapping track %u(%u) -> %#x:%#x", "b_play_start_media", (unsigned)playback, (unsigned)pid->pid, (unsigned)i, (unsigned)status.remappedPid, (unsigned)status.pid));
1248                    stream.stream.id.other[i] = status.remappedPid;
1249                }
1250                stream.other[i] = pid->pid; 
1251            } else {
1252                BDBG_WRN(("%s:%#lx track:%u exceeded max number of tracks %u>=%u", "b_play_start_media", (unsigned)playback, (unsigned)pid->pid, (unsigned)i, BMEDIA_PLAYER_MAX_TRACKS));
1253            }
1254            i++;
1255        } 
1256    }
1257
1258    if( (playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMp4 ||
1259         playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMkv) &&
1260        file->file.index==NULL
1261       )
1262    {
1263        BDBG_ERR(("NEXUS_FilePlayHandle must be opened with an index for this transport type"));
1264        rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);
1265        goto error_file_index;
1266    }
1267
1268    if(file->file.index && params->mode == NEXUS_PlaybackMode_eIndexed && playback->params.playpumpSettings.transportType==NEXUS_TransportType_eTs) {
1269        switch(params->mpeg2TsIndexType) {
1270        case NEXUS_PlaybackMpeg2TsIndexType_eAutoDetect:
1271            mpeg2ts_index_type = b_play_mpeg2ts_probe_index(file->file.index, playback);
1272            break;
1273        case NEXUS_PlaybackMpeg2TsIndexType_eNav:
1274        case NEXUS_PlaybackMpeg2TsIndexType_eSelf:
1275            mpeg2ts_index_type = (b_play_mpeg2ts_index_type)params->mpeg2TsIndexType;
1276            break;
1277        default:
1278            rc = BERR_TRACE(NEXUS_INVALID_PARAMETER);
1279            goto error_file_index;
1280        }
1281    }
1282    if( playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMp4 ||
1283        playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMkv ||
1284        (playback->params.playpumpSettings.transportType==NEXUS_TransportType_eEs && (file->file.index || params->mode == NEXUS_PlaybackMode_eAutoBitrate || playback->params.enableStreamProcessing)) ||
1285        ((params->mode != NEXUS_PlaybackMode_eIndexed || file->file.index==NULL) && playback->params.playpumpSettings.transportType==NEXUS_TransportType_eTs) ||
1286        (playback->params.playpumpSettings.transportType==NEXUS_TransportType_eTs && mpeg2ts_index_type==b_play_mpeg2ts_index_type_mpeg) ||
1287    ((playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMpeg2Pes || playback->params.playpumpSettings.transportType==NEXUS_TransportType_eVob) && params->mode==NEXUS_PlaybackMode_eIndexed && file->file.index)
1288    )
1289      {
1290          bfile_buffer_cfg buffer_cfg;
1291          playback->state.media.factory = batom_factory_create(bkni_alloc, 16);
1292          if(!playback->state.media.factory) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_factory; }
1293          bfile_buffer_default_cfg(&buffer_cfg);
1294          playback->state.media.buf = BKNI_Malloc(buffer_cfg.buf_len+BIO_BLOCK_SIZE);
1295          if(!playback->state.media.buf) {
1296              BDBG_ERR(("b_play_start_media: %#lx can't allocate %u bytes for buffer", (unsigned long)playback, (unsigned)buffer_cfg.buf_len+BIO_BLOCK_SIZE));
1297              rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);
1298              goto error_media;
1299          }
1300          buffer_cfg.buf = (void*)B_IO_ALIGN_ROUND((unsigned long)playback->state.media.buf);
1301          buffer_cfg.fd = playback->file->file.data;
1302          buffer_cfg.async = true;
1303          buffer_cfg.sync_cnxt = playback;
1304          /* since async I/O is not synchronized with playback, we need to pass async I/O over custom layer that would acquire lock before calling back into the bfile_buffer */
1305          buffer_cfg.async_read = b_play_media_io_async_read;
1306          playback->state.media.buffer = bfile_buffer_create(playback->state.media.factory, &buffer_cfg);
1307          if(!playback->state.media.buffer) {
1308              rc = BERR_TRACE(NEXUS_NOT_SUPPORTED);
1309              goto error_buffer;
1310          }
1311      }
1312
1313#if B_HAS_DIVX_DRM
1314    if(playback->params.playpumpSettings.securityContext && playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMkv){
1315        stream.cntx = playback->params.playpumpSettings.securityContext;
1316        stream.decrypt_callback = bdrm_decrypt_mkv_payload;
1317    }
1318#endif
1319
1320    bmedia_player_init_config(&player_config);
1321    player_config.buffer = playback->state.media.buffer;
1322    player_config.factory = playback->state.media.factory;
1323    player_config.cntx = playback;
1324    player_config.atom_ready = b_play_media_async_atom_ready;
1325    player_config.error_detected = b_play_media_player_error;
1326    player_config.timeshifting = playback->params.timeshifting;
1327    b_play_update_media_player_config(playback, &player_config.decoder_features);
1328
1329    if(playback->params.playpumpSettings.transportType==NEXUS_TransportType_eTs) {
1330        stream.stream.mpeg2ts.packet_size = playback->params.playpumpSettings.timestamp.type == NEXUS_TransportTimestampType_eNone?188:192;
1331    }
1332
1333    if(params->mode == NEXUS_PlaybackMode_eIndexed && file->file.index) {
1334        if(mpeg2ts_index_type==b_play_mpeg2ts_index_type_mpeg || playback->params.playpumpSettings.transportType==NEXUS_TransportType_eMpeg2Pes || playback->params.playpumpSettings.transportType==NEXUS_TransportType_eVob || playback->params.playpumpSettings.transportType==NEXUS_TransportType_eEs)  {
1335            playback->state.media.segmented = false;
1336            stream.without_index = true;
1337            stream.stream.noindex.auto_rate = true;
1338            stream.stream.noindex.bitrate = params->bitrate;
1339            if(playback->params.enableStreamProcessing) {
1340                stream.stream.noindex.auto_rate = true;
1341                stream.stream.id.master = 0xC0;
1342           }
1343           else if( playback->params.playpumpSettings.transportType==NEXUS_TransportType_eEs)
1344           {
1345                stream.stream.id.master = 0x0;
1346           }
1347        }
1348        if (!player_config.timeshifting) {
1349            off_t first, last;
1350            if (!file->file.data->bounds(file->file.data, &first, &last)) {
1351                player_config.data_file_size = last;
1352            }
1353        }
1354        playback->media_player = bmedia_player_create(file->file.index, &player_config, &stream);
1355        if (!playback->media_player) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_player; }
1356    } else {
1357        playback->state.media.segmented = false;
1358        stream.without_index = true;
1359        stream.stream.noindex.auto_rate = params->mode == NEXUS_PlaybackMode_eAutoBitrate;
1360        if(playback->params.playpumpSettings.transportType==NEXUS_TransportType_eEs) {
1361            if(playback->params.enableStreamProcessing) {
1362                stream.stream.noindex.auto_rate = true;
1363                stream.stream.id.master = 0xC0;
1364            } else {
1365                if(params->mode == NEXUS_PlaybackMode_eAutoBitrate) {
1366                    stream.stream.id.master = 0;
1367                }
1368            }
1369        }
1370        stream.stream.noindex.bitrate = params->bitrate;
1371        playback->media_player = bmedia_player_create(file->file.data, &player_config, &stream);
1372        if (!playback->media_player) { rc = BERR_TRACE(NEXUS_NOT_SUPPORTED); goto error_player; }
1373    }
1374    bmedia_player_set_direction(playback->media_player, 0, BMEDIA_TIME_SCALE_BASE, &mode); /* normal decode */
1375    NEXUS_Playpump_GetSettings(playback->params.playpump, &pumpCfg);
1376    playback->actualTransportType = pumpCfg.transportType;
1377    pumpCfg.mode = playback->state.media.segmented ? NEXUS_PlaypumpMode_eSegment : NEXUS_PlaypumpMode_eFifo;
1378    rc = NEXUS_Playpump_SetSettings(playback->params.playpump, &pumpCfg);
1379    if(rc!=NEXUS_SUCCESS) { rc = BERR_TRACE(rc); goto error_playpump; }
1380    playback->state.direction = 1;
1381    playback->state.data_source = b_play_next_frame;
1382    return NEXUS_SUCCESS;
1383
1384error_playpump:
1385    bmedia_player_destroy(playback->media_player);
1386    playback->media_player = NULL;
1387error_player:
1388    if(playback->state.media.buffer) {
1389        BDBG_ASSERT(playback->state.media.factory);
1390        bfile_buffer_destroy(playback->state.media.buffer);
1391        batom_factory_destroy(playback->state.media.factory);
1392        playback->state.media.factory = NULL;
1393        playback->state.media.buffer = NULL;
1394    }
1395error_buffer:
1396    if(playback->state.media.buf) {
1397        BKNI_Free(playback->state.media.buf);
1398        playback->state.media.buf = NULL;
1399    }
1400error_media:
1401    if(playback->state.media.factory) {
1402        batom_factory_destroy(playback->state.media.factory);
1403        playback->state.media.factory = NULL;
1404    }
1405error_file_index:
1406error_factory:
1407    if(playback->state.media.pts_timer) {
1408        NEXUS_CancelTimer(playback->state.media.pts_timer);
1409        playback->state.media.pts_timer = NULL;
1410    }
1411    return rc;
1412}
1413
Note: See TracBrowser for help on using the repository browser.