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

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

first commit

  • Property svn:executable set to *
File size: 41.5 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_flow.c $
39 * $brcm_Revision: 32 $
40 * $brcm_Date: 12/6/11 8:38a $
41 *
42 * Module Description:
43 *
44 * Revision History:
45 *
46 * $brcm_Log: /nexus/modules/playback/src/nexus_playback_flow.c $
47 *
48 * 32   12/6/11 8:38a erickson
49 * SW7405-5559: remove uninitialized p->record and replace with p-
50 *  >params.timeshifting test
51 *
52 * 31   9/6/11 10:51a erickson
53 * SW7405-5477: ensure timer is running before entering eWaitingRecord
54 *  state
55 *
56 * 30   3/23/11 3:00p vsilyaev
57 * SW7422-14: Added facility to associate  stream id(pid) with a chunk of
58 *  payload
59 *
60 * 29   8/10/10 3:34p erickson
61 * SW7420-934: rename NEXUS_Playpump_ReadComplete to
62 *  NEXUS_Playpump_WriteComplete
63 *
64 * 28   3/11/10 5:26p vsilyaev
65 * SW3556-913: Always use header file to declare external functions
66 *
67 * 27   3/5/10 7:12p jtna
68 * SW3556-913: support for ErrorHandlingMode_eAbort
69 *
70 * 26   2/23/10 4:52p erickson
71 * SW7400-2684: add state variable so we only fire beginningOfStreamAction
72 *  or endOfStreamAction once when paused
73 *
74 * 25   12/17/09 5:52p vsilyaev
75 * SW3548-2677: Added configuration that allows user to choose what
76 *  context should be used for synchronous I/O
77 *
78 * 24   8/20/09 4:25p erickson
79 * PR57840: fix timer creation logic
80 *
81 * 23   8/19/09 3:29p erickson
82 * PR57840: start timer during eWaitingRecord state so that endOfStream
83 *  callback can be fired
84 *
85 * 22   6/4/09 5:11p erickson
86 * PR54129: remove SEEK_CUR which results in loss of packet alignment when
87 *  a file fifo is trimmed. consolidate logic in
88 *  NEXUS_Playback_P_EndOfDataFile.
89 *
90 * 21   6/2/09 7:38p vsilyaev
91 * PR 55417: Added support for read method returning no data or completing
92 *  with a partial read
93 *
94 * 20   4/16/09 1:29p erickson
95 * PR54129: improve DBG
96 *
97 * 19   2/26/09 6:11p mphillip
98 * PR52074: When data ends before index, wait for end of index not data
99 *
100 * 18   2/9/09 2:37p gmohile
101 * PR 51813 : Truncate large entries due to corrupted index
102 *
103 * 17   1/26/09 11:38a vsilyaev
104 * PR 51579: Added ES to PES packetization and support for capturing of
105 *  streams produced by the playback module
106 *
107 * 16   12/18/08 10:50a erickson
108 * PR50078: rework b_play_timer to allow drain logic, yet still guarantee
109 *  that timer will always be rescheduled if p->state.mode ==
110 *  NEXUS_PlaybackState_ePaused
111 *
112 * 15   10/21/08 10:06a vsilyaev
113 * PR 47225: Streamlined logic to detect EndOfStream condition
114 *
115 * 14   6/17/08 12:14p katrep
116 * PR43437: Fixed the crash introduced by previous checkin
117 *
118 * 13   6/16/08 7:13p katrep
119 * PR43437: Add workaround to prevent loop around during the AVI
120 *  trickmodes.
121 *
122 * 12   6/13/08 5:37p katrep
123 * PR43437: Add logic the detect the eof if the CDB is not getting
124 *  consumed by the decoders.
125 *
126 * 11   5/30/08 2:21p erickson
127 * PR43156: audioStatus.queuedFrames wait for end test should be > 8
128 *
129 * 10   5/22/08 2:27p erickson
130 * PR42926: fix test for beginning of file. on transitions, video decoder
131 *  may return no PTS, so pos == 0 should not be compared against
132 *  bounds.first
133 *
134 * 9   5/16/08 12:13p vsilyaev
135 * PR 42365: Added more debug output
136 *
137 * 8   5/16/08 11:37a erickson
138 * PR42751: bplay_get_decode_mark must factor in audio and video decoder
139 *  CDB depth as well
140 *
141 * 7   5/15/08 4:18p erickson
142 * PR42221: use audioStatus.queuedFrames in flow algo
143 *
144 * 6   5/6/08 11:35a erickson
145 * PR42365: don't NEXUS_ScheduleTimer over another timer. add
146 *  BDBG_OBJECT_ASSERT's for better debug.
147 *
148 * 5   4/1/08 4:46p erickson
149 * PR41154: check rc
150 *
151 * 4   3/8/08 7:45a erickson
152 * PR40103: convert to NEXUS_TaskCallback
153 *
154 * 3   3/3/08 3:10p erickson
155 * PR40194: at EOF, do a NEXUS_Playpump_WriteComplete(0,0) to flush media
156 *  framework
157 *
158 * 2   1/30/08 6:21p vsilyaev
159 * PR 38682: Don't use try_lock for callbacks from playpump, we could miss
160 *  the only callback and then playback would never kickstart
161 *
162 * 1   1/18/08 2:36p jgarrett
163 * PR 38808: Merging to main branch
164 *
165 * Nexus_Devel/14   1/7/08 5:09p erickson
166 * PR35824: add new continuous record support
167 *
168 * Nexus_Devel/13   12/21/07 5:55p vsilyaev
169 * PR 38073: Fixed fa -> fb -> play transitions
170 *
171 * Nexus_Devel/12   12/20/07 5:24p vsilyaev
172 * PR 38073: Improved handling of trickmodes
173 *
174 * Nexus_Devel/11   12/20/07 3:41p vsilyaev
175 * PR 38073: Improving use of bmedia_player
176 *
177 * Nexus_Devel/10   12/20/07 1:24p vsilyaev
178 * PR 38073: Fixed handling of loop
179 *
180 * Nexus_Devel/PR38073/1   12/19/07 10:39a vsilyaev
181 * PR 38073: Modifed code for exlusive use of media player
182 *
183 * Nexus_Devel/8   12/18/07 4:57p vsilyaev
184 * PR 38073: Updated playback module to work exclusively with media player
185 *
186 * Nexus_Devel/7   12/4/07 2:38p erickson
187 * PR36068: use timestampType and pacing instead of timestampEnabled
188 *
189 * Nexus_Devel/6   11/1/07 9:41a erickson
190 * PR36633: base enum changes
191 *
192 * Nexus_Devel/5   10/30/07 5:05p vsilyaev
193 * PR 36404: Added audio tracking for slow motion modes
194 *
195 * Nexus_Devel/4   10/19/07 11:21a vsilyaev
196 * PR 35824: Fixed data corruption on trick modes
197 *
198 * Nexus_Devel/3   10/17/07 5:38p vsilyaev
199 * PR 35824: Added trickmode test
200 *
201 * Nexus_Devel/2   10/16/07 4:59p vsilyaev
202 * PR 35824: Rearranged code
203 *
204 * Nexus_Devel/1   10/16/07 12:56p vsilyaev
205 * PR 35824: Splitting playback into managable piexies
206 *
207 * $copied_brcm_Log: /BSEAV/api/src/pvr/bsettop_playback.c $
208 * $copied_brcm_Revision: 172 $
209 * $copied_brcm_Date: 10/1/07 11:13a $
210 **************************************************************************/
211#include "nexus_playback_module.h"
212#include "nexus_playback_impl.h"
213#include "nexus_audio_decoder_trick.h"
214
215BDBG_MODULE(nexus_playback_flow);
216#define BDBG_MSG_FLOW(X)  /* BDBG_MSG(X) */
217#define BDBG_MSG_TRACE(X)  /* BDBG_MSG(X) */
218
219static void bplay_p_pre_read_complete(NEXUS_PlaybackHandle p, size_t skip, size_t amount_used);
220static bool b_play_wait_for_end(NEXUS_PlaybackHandle p);
221
222#if B_PLAYBACK_CAPTURE
223void
224b_play_capture_open(NEXUS_PlaybackHandle p)
225{
226    char filename[64];
227    BKNI_Snprintf(filename, sizeof(filename),"playback_%u.mpg", p->capture.no);
228    p->capture.fout = fopen(filename, "w");
229    p->capture.has_data = false;
230    p->capture.no++;
231    return;
232}
233
234void b_play_capture_close(NEXUS_PlaybackHandle p)
235{
236    if(p->capture.fout) {
237        fclose(p->capture.fout);
238        p->capture.fout = NULL;
239        p->capture.has_data = false;
240    }
241    return;
242}
243
244void b_play_capture_write(NEXUS_PlaybackHandle p, const void *buf, size_t len)
245{
246    if(p->capture.fout) {
247        p->capture.has_data = true;
248        fwrite(buf, 1, len, p->capture.fout);
249    }
250    return;
251}
252#endif /* B_PLAYBACK_CAPTURE */
253
254#ifdef DIRECT_IO_SUPPORT
255static int
256b_test_buffer(NEXUS_PlaybackHandle p, uint8_t **addr, size_t *size)
257{
258    /* The last portion at the end of the playback buffer may be unusable for
259    O_DIRECT because of alignment requirements, so skip it.
260    This test shall be first test, otherwise deadlock is possible. */
261    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
262    BDBG_ASSERT(p->params.playpump);
263    if (*size > 0 && (void *)(*addr) > p->buf_limit) {
264        NEXUS_Error rc;
265        BDBG_MSG(("Throwing away useless buffer %#x(%u), recycling", (unsigned)(*addr), *size));
266        rc = NEXUS_Playpump_WriteComplete(p->params.playpump, *size, 0);
267        BDBG_ASSERT(rc == NEXUS_SUCCESS);
268        if (NEXUS_Playpump_GetBuffer(p->params.playpump, (void**)addr, size)!=NEXUS_SUCCESS || (*size) == 0) {
269          return -1;
270        } else {
271          return 0;
272        }
273    }
274    /* Make sure the block is large enough to satisfy any possible alignment
275    requirement. */
276    if (*size < B_IO_BLOCK_LIMIT) {
277        /* minimum size needed for buffer alignment */
278        BDBG_MSG_FLOW(("available space is too small (%#lx). wait for more.", (unsigned long)*size));
279        return -1;
280    }
281    return 0;
282}
283#endif
284
285/**
286Process data before going to bplaypump_read_complete.
287This allows the scrambling bits to be set prior to being fed to bplaypump_read_complete
288(and any other pre-processing which may be required).  At the moment, this only affects
289188 bytes of valid data in encrypted playback due to a limitation in M2M decryption.
290**/
291static void
292bplay_p_pre_read_complete(NEXUS_PlaybackHandle p, size_t skip, size_t amount_used )
293{
294    BSTD_UNUSED(skip);
295    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
296    if (p->state.encrypted && amount_used == 188) {
297        uint8_t *buf = (uint8_t *)p->state.buf + p->state.io.file_behind;
298        buf[3] |= 0xC0;
299    }
300}
301
302/* this function shall be called from API functions *BEFORE* releasing lock */
303void
304b_play_check_buffer(NEXUS_PlaybackHandle p)
305{
306    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
307    if (p->state.state == eWaitingPlayback) {
308        BDBG_MSG_FLOW(("trying to get a buffer"));
309        p->state.data_source(p);
310    }
311}
312
313
314
315/**
316This is the callback that is called either playpump, from module scheduler or from dedicated thread.
317Translate the context and route it to the appropriate function.
318**/
319void
320b_play_read_callback(void *context)
321{
322    NEXUS_PlaybackHandle playback = (NEXUS_PlaybackHandle)context;
323
324    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
325    /* We have to lock before testing state so that we don't change state
326    between the check and the lock. */
327    if (!playback->state.start_paused && playback->state.state == eWaitingPlayback) {
328            playback->state.data_source(playback);
329    }
330}
331
332void
333b_play_handle_read_error(NEXUS_PlaybackHandle p, ssize_t size)
334{
335    BSTD_UNUSED(size);
336    BDBG_MSG(("read returned size %d", (int)size));
337
338    p->state.data_error_cnt++;
339    NEXUS_TaskCallback_Fire(p->errorCallback);
340   
341    if (p->params.playErrorHandling == NEXUS_PlaybackErrorHandlingMode_eAbort) {
342        NEXUS_Playback_P_AbortPlayback(p);
343        return;
344    }
345    else {
346        /* read has failed, so we shall drain a decoder buffer and then handle loop */
347        b_play_start_drain(p);
348        return;
349    }
350}
351
352/* when data file read returns less than expected, we have to decide if we start the end-of-file drain or wait for record */
353static void NEXUS_Playback_P_EndOfDataFile(NEXUS_PlaybackHandle p, ssize_t size, bool allowTimeshiftingWait)
354{
355    /* we have not read any useful data */
356    if (p->params.timeshifting && allowTimeshiftingWait) { /* wait for record and try latter */
357#ifdef DIRECT_IO_SUPPORT
358        if (size) { /* revert partial read */
359            off_t seek_result, seek_off;
360            seek_off = p->state.io.last_file_offset - size;
361            seek_result = p->file->file.data->seek(p->file->file.data, seek_off, SEEK_SET);
362            if (seek_result != seek_off) {
363                BDBG_ERR(("Unable to recover position after partial read"));
364                p->state.state = eTransition;
365                b_play_start_drain(p);
366                return;
367            }
368        }
369#else
370        BSTD_UNUSED(size);
371#endif
372        BDBG_MSG(("Waiting for record to pump more data"));
373        p->state.state = eWaitingRecord;
374        if (!p->state.timer) {
375            p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME * 5, b_play_timer, p);
376        }
377        return;
378    }
379
380    p->state.state = eTransition; /* Can't be in the eWaitingPlayback or we risk a deadlock from the playpump callback. */
381    BDBG_MSG(("NEXUS_Playback_P_EndOfDataFile -> wait_for_drain"));
382    if (p->params.playpumpSettings.transportType == NEXUS_TransportType_eTs && p->file->file.index) {
383        /* Data could end before index ends, ignore end of data,
384         * wait for end of index */
385        b_play_next_frame(p);
386    } else {
387        b_play_start_drain(p);
388
389        /* do a special ReadComplete which notifies media framework to flush its remaining data */
390        {
391            void *buffer;
392            size_t size;
393            if (!NEXUS_Playpump_GetBuffer(p->params.playpump, &buffer, &size)) {
394                NEXUS_Error rc;
395                rc = NEXUS_Playpump_WriteComplete(p->params.playpump, 0, 0);
396                if (rc) BERR_TRACE(rc); /* keep going */
397            }
398        }
399    }
400}
401
402static void bplay_p_filter_frame(NEXUS_PlaybackHandle p, unsigned filteredPid, uint8_t *buf, size_t block_size, unsigned frame_offset)
403{
404    unsigned hdr_offset;
405    unsigned packet_offset;
406    unsigned pkt_size = p->params.playpumpSettings.timestamp.type == NEXUS_TransportTimestampType_eNone?188:192;
407    unsigned packets, filtered_packets;
408
409
410    packet_offset = frame_offset % pkt_size;
411    /* start peeking at the packet boundary */
412    if(packet_offset==0) {
413        packet_offset = 0;
414    } else {
415        packet_offset = pkt_size - packet_offset;
416    }
417    hdr_offset = p->state.packet.size == 188 ? 0 : 4;
418    for(filtered_packets=packets=0;packet_offset + hdr_offset + 4 < block_size;packet_offset += pkt_size, packets++) {
419        unsigned pid;
420        uint8_t *ts_hdr = buf+hdr_offset+packet_offset;
421
422        if(ts_hdr[0] != 0x47) {
423            BDBG_WRN(("%u", block_size));
424            BDBG_WRN(("%u", packet_offset));
425            BDBG_WRN(("bplay_p_filter_frame:%#x out of sync at %u %#x(%u,%u)", (unsigned)p, packet_offset, ts_hdr[0], block_size, frame_offset));
426            break;
427        }
428        pid = ((unsigned)(ts_hdr[1]&0x1F))<<8 | ts_hdr[2];
429        if(pid!=filteredPid) {
430            BDBG_MSG_TRACE(("bplay_p_filter_frame:%#x filtering out pid %#x at %u", (unsigned)p, pid, packet_offset));
431            ts_hdr[1] = 0x1F;
432            ts_hdr[2] = 0xFF;
433            filtered_packets++;
434        }
435    }
436    BDBG_MSG(("bplay_p_filter_frame:%#x pid %#x filtered %u/%u packets frame:%u/%u", (unsigned)p, filteredPid, packets, filtered_packets, block_size, frame_offset));
437    return;
438}
439
440/* Callback from async io (already synced) or from sync_playback when reviving io */
441void
442b_play_frame_data(void *playback_, ssize_t size)
443{
444    NEXUS_Error rc;
445    NEXUS_PlaybackHandle p = playback_;
446    int last;
447    size_t read_skip, read_size;
448
449    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
450    BDBG_ASSERT(p->params.playpump);
451
452    last = p->state.frame.cur + (p->state.read_size - (p->state.io.file_behind) -
453        p->state.io.next_data); /* last byte what we've just read */
454    BDBG_MSG_FLOW(("finished read %d out of %d (frame %d:%d:%d, pre=%d, post=%d)",
455        (int)size, (int)p->state.read_size,
456        (int)p->state.frame.cur, (int)last, p->state.frame.size,
457        p->state.io.file_behind, p->state.io.next_data));
458    BDBG_ASSERT(size <= (int)p->state.read_size);
459
460    if (b_play_control(p,eControlDataIO)) {
461        p->state.io_size = size;
462        return;
463    }
464
465    if (size<0) {
466        if(size == BFILE_ERROR_NO_DATA) { /* if got no data then start I/O again, however we have gotten chance to go through the b_play_control logic */
467            NEXUS_File_AsyncRead(p->file->file.data, p->state.buf, p->state.read_size, NEXUS_MODULE_SELF, b_play_frame_data, p);
468        } else {
469            b_play_handle_read_error(p, size);
470        }
471        return;
472    }
473
474#ifdef DIRECT_IO_SUPPORT
475    if (size<(ssize_t)p->state.read_size) { /* we read less than expected */
476        if (B_IO_ALIGN_REST(size)) {
477            off_t seek_result, seek_off;
478
479            /* On linux 2.6, O_DIRECT will read the last byte, but leave the file pointer unaligned to 4K pages. This causes an O_DIRECT
480            read failure. On linux 2.4, linux simply doesn't read the last bit of data. On files we record with the refsw, this is a moot
481            point because we always record in 4K aligned pages. But for imported streams, especially stills, where getting every byte is important,
482            this can be a problem. This technique gives us consistency. We may need to address non-4K aligned files with additional Settop API
483            configurability. */
484            BDBG_WRN(("Guarantee that we stay block aligned in O_DIRECT mode. Seek back %d, truncating read to %d", B_IO_ALIGN_REST(size), B_IO_ALIGN_TRUNC(size)));
485            size = B_IO_ALIGN_TRUNC(size);
486            seek_off = p->state.io.last_file_offset+size;
487            seek_result = p->file->file.data->seek(p->file->file.data, seek_off, SEEK_SET);
488            if (seek_result != seek_off) {
489                NEXUS_Playback_P_EndOfDataFile(p, 0, false);
490                return;
491            }
492            }
493        p->state.io.last_file_offset += size;
494
495        if (size<=(ssize_t)p->state.io.file_behind) {
496            NEXUS_Playback_P_EndOfDataFile(p, size, true);
497            return;
498        }
499        /* there is still some data what we could use */
500        if (p->state.read_size - size < p->state.io.next_data) {
501            p->state.io.next_data -= p->state.read_size - size; /* adjust number of prefetched bytes and don't adjust 'last' */
502        } else {
503            /* clear number of prefetched bytes and adjust last */
504            last  = last - ((p->state.read_size - size) - p->state.io.next_data);
505            p->state.io.next_data = 0;
506        }
507        BDBG_MSG(("new prefetch size is %d", (int)p->state.io.next_data));
508        BDBG_ASSERT(last >= (int)p->state.frame.cur); /* at this point last shall point ahead of frame_cur */
509    }
510    BDBG_ASSERT(last <= (int)p->state.frame.size);
511
512    read_skip = p->state.io.junk_data;
513    read_size = size - p->state.io.next_data - p->state.io.file_behind;
514
515    BDBG_MSG_FLOW(("bplay_frame_data read_complete skip=%u, size=%u frame:last %u size %u", read_skip, read_size, last, p->state.frame.size));
516    if (read_size > BMEDIA_PLAYER_MAX_BLOCK) {
517        BDBG_ERR(("%d, %d, %d, %d", size, p->state.io.next_data,
518            p->state.io.junk_data,p->state.io.file_behind));
519        BDBG_ASSERT(false);
520    }
521#else
522    BDBG_ASSERT(size % 188 == 0);
523    if (size<(ssize_t)p->state.read_size) { /* we read less than expected */
524        if (size == 0) {
525            NEXUS_Playback_P_EndOfDataFile(p, size, true);
526            return;
527        }
528    }
529    read_skip = 0;
530    read_size = size;
531#endif
532
533    p->state.state = eTransition; /* Can't be in the eWaitingPlayback or we risk a deadlock from the playpump callback. */
534    bplay_p_pre_read_complete(p, read_skip, read_size);
535    if(p->state.media.entry.filteredPid!=0) {
536        bplay_p_filter_frame(p, p->state.media.entry.filteredPid, (uint8_t *)p->state.buf + p->state.io.file_behind, read_size, p->state.frame.cur);
537    }
538
539    b_play_capture_write(p, (uint8_t *)p->state.buf + p->state.io.file_behind, read_size);
540    rc = NEXUS_Playpump_WriteComplete(p->params.playpump, read_skip, read_size);
541    if (rc!=NEXUS_SUCCESS) {
542        BDBG_ERR(("bplay_frame_data bplaypump_read_complete error %d, playback aborted", (unsigned)rc));
543        p->state.state = eAborted;
544        NEXUS_TaskCallback_Fire(p->errorCallback);
545        return;
546    }
547
548    /* Now that we've processed the file read, we're ready for the next read,
549    so clear file_behind. If there's more to read without seeking, we'll use next_data
550    to reset file_behind. If we need to seek, then the value of file_behind and
551    next_data will be reset. */
552    p->state.io.file_behind = 0;
553
554    p->state.frame.cur = (unsigned)last;
555    if (p->state.frame.cur==p->state.frame.size) {
556        /* got all data, read next frame */
557        p->state.state = eTransition;
558        b_play_next_frame(p);
559    } else {
560        b_play_send_frame(p); /* continue reading data */
561    }
562}
563
564
565/* this function is called when we want to send generated packet */
566void
567b_play_send_packet(NEXUS_PlaybackHandle p)
568{
569    NEXUS_Error rc;
570    size_t size;
571    void *vaddr;
572
573    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
574    BDBG_ASSERT(p->params.playpump);
575
576    for(;;) {
577        if (NEXUS_Playpump_GetBuffer(p->params.playpump, &vaddr, &size)!=NEXUS_SUCCESS || size == 0) {
578            goto keep_waiting;
579        }
580
581        size = b_imin(size, p->state.packet.size - p->state.packet.cur);
582        BDBG_MSG_FLOW(("sending %d out of %d bytes left in packet",
583            size, p->state.packet.size-p->state.packet.cur));
584        BKNI_Memcpy(vaddr, p->state.packet.buf+p->state.packet.cur, size);
585
586        /* Can't be in eWaitingPlayback or we risk a deadlock from playpump callback. */
587        p->state.state = eTransition;
588        b_play_capture_write(p, vaddr, size);
589        rc = NEXUS_Playpump_WriteComplete(p->params.playpump, 0, size);
590        if (rc!=NEXUS_SUCCESS) {
591            BDBG_ERR(("bplay_send_packet bplaypump_read_complete error %#x, playback aborted", (unsigned)rc));
592            p->state.state = eAborted;
593            NEXUS_TaskCallback_Fire(p->errorCallback);
594            return;
595        }
596
597        p->state.packet.cur+=size;
598        if (p->state.packet.cur >= p->state.packet.size) {
599            /* we're done sending the packet, so go get the next frame */
600            b_play_next_frame(p);
601            return;
602        }
603        /* keep writing data */
604    }
605    return;
606keep_waiting:
607    p->state.state = eWaitingPlayback;
608    p->state.data_source = b_play_send_packet; /* schedule to call us when space is avaliable */
609    return;
610}
611
612void
613b_play_send_frame(NEXUS_PlaybackHandle p)
614{
615    size_t skip;
616    uint8_t *addr;
617    void *vd_addr;
618    size_t size;
619    size_t left;
620
621    BDBG_ASSERT(p->params.playpump);
622
623    if (NEXUS_Playpump_GetBuffer(p->params.playpump, (void**)&vd_addr, &size) || size == 0) {
624        goto keep_waiting;
625    }
626    addr = (uint8_t *) vd_addr;
627    BDBG_MSG_FLOW(("get_buffer %x %d", addr, size));
628#ifdef DIRECT_IO_SUPPORT
629    if (b_test_buffer(p, &addr, &size)) {
630        goto keep_waiting;
631    }
632
633    skip = B_IO_ALIGN_REST(addr);
634    if (skip>0) { /* if playback buffer address is not aligned, compensate it */
635        skip = B_IO_BLOCK_SIZE - skip;
636        addr += skip;
637        size -= skip;
638    }
639    size = B_IO_ALIGN_TRUNC(size);
640    if (p->state.io.next_data) {
641        off_t seek_result, seek_off;
642        seek_off = p->state.io.last_file_offset-B_IO_BLOCK_SIZE;
643        seek_result = p->file->file.data->seek(p->file->file.data, seek_off, SEEK_SET);
644        if (seek_result != seek_off) {
645            NEXUS_Playback_P_EndOfDataFile(p, 0, false);
646            return;
647        }
648        p->state.io.last_file_offset = seek_off;
649        p->state.io.file_behind = B_IO_BLOCK_SIZE-p->state.io.next_data;
650        p->state.io.next_data = 0; /* clear flag out */
651        BDBG_MSG_FLOW(("seeking back to %d bytes now behind %d bytes", B_IO_BLOCK_SIZE, p->state.io.file_behind));
652    }
653    left = p->state.io.file_behind + p->state.frame.size - p->state.frame.cur;
654    if (size>left) {
655        p->state.io.next_data = B_IO_ALIGN_ROUND(left) - left;
656        size = B_IO_ALIGN_ROUND(left);
657    } else {
658        p->state.io.next_data = 0;
659    }
660    if (size == 0) {
661        BDBG_MSG(("After alignment, nothing left. Waiting for more space."));
662        goto keep_waiting;
663    }
664#else
665    skip = 0;
666    left = p->state.frame.size - p->state.frame.cur;
667    size = b_imin(size, left);
668    p->state.io.next_data = 0;
669#endif
670
671    p->state.io.junk_data = skip + p->state.io.file_behind;
672    p->state.buf = addr;
673    BDBG_MSG_FLOW(("reading into pb (%#lx), read=%d (skip=%d+%d, overrun=%d)",
674        (unsigned long)p->state.buf, size, skip, p->state.io.file_behind, p->state.io.next_data));
675    p->state.state = eWaitingIo;
676    p->state.read_size = size;
677    NEXUS_File_AsyncRead(p->file->file.data, p->state.buf, size, NEXUS_MODULE_SELF, b_play_frame_data, p);
678    return;
679
680keep_waiting:
681    p->state.state = eWaitingPlayback;
682    p->state.data_source = b_play_send_frame; /* call us when space is avaliable */
683    return;
684}
685
686/* Retrieve some mark from the decoder to show current location.
687This uses a combination to CDB depth, PTS change and DM picture queue depth.
688*/
689/* B_MIN_CDB_DEPTH should be greater than max picture. If there's no video_picture_queue info, and if PTS's aren't changing,
690we will wait for playback+CDB to go below this. */
691#define B_MIN_CDB_DEPTH (1024*1024) /* With 1080p source, we need to set this at 1 MB */
692
693
694void
695bplay_get_decode_mark(NEXUS_PlaybackHandle playback, uint32_t *pFifoMarker, unsigned *pCdbDepth)
696{
697    const NEXUS_Playback_P_PidChannel *pid;
698    NEXUS_Error rc;
699    NEXUS_PlaypumpStatus playpumpStatus;
700    unsigned cdbDepth=0;
701    uint32_t fifoMarker=0;
702
703    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
704    BDBG_ASSERT(playback->params.playpump);
705    rc = NEXUS_Playpump_GetStatus(playback->params.playpump, &playpumpStatus);
706    if(rc==BERR_SUCCESS) {
707        BDBG_MSG_FLOW(("%s:%lx playpump %u",  "bplay_get_decode_mark", (unsigned long)playback, (unsigned)playpumpStatus.fifoDepth));
708        if(playpumpStatus.fifoDepth>0) {
709            /* if there's even 1 byte in the playback fifo, we must still wait */
710            cdbDepth += B_MIN_CDB_DEPTH;
711        }
712    } else { rc=BERR_TRACE(rc);}
713
714
715    for(pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link)) {
716        NEXUS_AudioDecoderStatus audioStatus;
717
718        switch(pid->cfg.pidSettings.pidType) {
719        default: break;
720        case NEXUS_PidType_eVideo:
721            if(pid->cfg.pidTypeSettings.video.decoder) {
722                NEXUS_VideoDecoderStatus videoStatus;
723
724                rc = NEXUS_VideoDecoder_GetStatus(pid->cfg.pidTypeSettings.video.decoder, &videoStatus);
725                if(rc!=BERR_SUCCESS) { rc = BERR_TRACE(rc);}
726                else if(videoStatus.started) {
727                    BDBG_MSG_FLOW(("%s:%lx video %u:%u:%u",  "bplay_get_decode_mark", (unsigned long)playback, (unsigned)videoStatus.fifoDepth, (unsigned)videoStatus.queueDepth, (unsigned)videoStatus.pts));
728                    cdbDepth += videoStatus.fifoDepth;
729                    if(videoStatus.queueDepth>0) {
730                        cdbDepth += B_MIN_CDB_DEPTH;
731                    }
732                    fifoMarker += videoStatus.pts;
733                    fifoMarker += videoStatus.queueDepth;
734                }
735            }
736            break;
737        case NEXUS_PidType_eAudio:
738            if(pid->cfg.pidTypeSettings.audio.primary) {
739                rc = NEXUS_AudioDecoder_GetStatus(pid->cfg.pidTypeSettings.audio.primary, &audioStatus);
740                if(rc!=BERR_SUCCESS) { rc = BERR_TRACE(rc);}
741                else if(audioStatus.started) {
742                    BDBG_MSG_FLOW(("%s:%lx audio[PRI] %u:%u:%u",  "bplay_get_decode_mark", (unsigned long)playback, (unsigned)audioStatus.fifoDepth, (unsigned)audioStatus.queuedFrames, (unsigned)audioStatus.pts));
743                    cdbDepth += audioStatus.fifoDepth;
744                    if(audioStatus.queuedFrames>8) {
745                        cdbDepth += B_MIN_CDB_DEPTH;
746                    }
747                    fifoMarker += audioStatus.pts;
748                    fifoMarker += audioStatus.queuedFrames;
749                }
750            }
751            if(pid->cfg.pidTypeSettings.audio.secondary) {
752                rc = NEXUS_AudioDecoder_GetStatus(pid->cfg.pidTypeSettings.audio.secondary, &audioStatus);
753                if(rc!=BERR_SUCCESS) { rc = BERR_TRACE(rc);}
754                else if(audioStatus.started) {
755                    BDBG_MSG_FLOW(("%s:%lx audio[SEC] %u:%u:%u",  "bplay_get_decode_mark", (unsigned long)playback, (unsigned)audioStatus.fifoDepth, (unsigned)audioStatus.queuedFrames, (unsigned)audioStatus.pts));
756                    if(audioStatus.queuedFrames>8) {
757                        cdbDepth += B_MIN_CDB_DEPTH;
758                    }
759                    fifoMarker += audioStatus.pts;
760                    fifoMarker += audioStatus.queuedFrames;
761                }
762            }
763            break;
764        }
765    }
766    *pCdbDepth = cdbDepth;
767    *pFifoMarker = fifoMarker+cdbDepth;
768    BDBG_MSG_FLOW(("%s:%lx total %u:%u",  "bplay_get_decode_mark", (unsigned long)playback, (unsigned)cdbDepth, (unsigned)fifoMarker));
769    if(cdbDepth==0 && fifoMarker==0) {
770        BDBG_WRN(("unable to get decode mark"));
771    }
772    return;
773}
774
775
776struct b_play_active_decoders {
777    bool video;
778    bool audio[2];
779};
780
781static void
782bplay_p_get_active_decodes(NEXUS_PlaybackHandle playback, struct b_play_active_decoders *active)
783{
784    const NEXUS_Playback_P_PidChannel *pid;
785    NEXUS_Error rc;
786
787    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
788    BDBG_ASSERT(playback->params.playpump);
789    active->video = active->audio[0] = active->audio[1] = false;
790
791    for (pid = BLST_S_FIRST(&playback->pid_list); pid ; pid = BLST_S_NEXT(pid, link))
792    {
793        NEXUS_AudioDecoderStatus audioStatus;
794        NEXUS_AudioDecoderTrickState state;
795
796        switch (pid->cfg.pidSettings.pidType)
797        {
798        default: break;
799        case NEXUS_PidType_eVideo:
800            if (pid->cfg.pidTypeSettings.video.decoder)
801            {
802                NEXUS_VideoDecoderStatus videoStatus;
803
804                rc = NEXUS_VideoDecoder_GetStatus(pid->cfg.pidTypeSettings.video.decoder, &videoStatus);
805                if (rc!=BERR_SUCCESS)
806                {
807                    rc = BERR_TRACE(rc);
808                }
809                else if (videoStatus.started)
810                {
811                    active->video = true;
812                }
813            }
814            break;
815        case NEXUS_PidType_eAudio:
816            if (pid->cfg.pidTypeSettings.audio.primary)
817            {
818                rc = NEXUS_AudioDecoder_GetStatus(pid->cfg.pidTypeSettings.audio.primary, &audioStatus);
819                if (rc!=BERR_SUCCESS)
820                {
821                    rc = BERR_TRACE(rc);
822                }
823                else if (audioStatus.started)
824                {
825                    NEXUS_AudioDecoder_GetTrickState(pid->cfg.pidTypeSettings.audio.primary, &state);
826                    active->audio[0] = !state.forceStopped;
827                }
828            }
829            if (pid->cfg.pidTypeSettings.audio.secondary)
830            {
831                rc = NEXUS_AudioDecoder_GetStatus(pid->cfg.pidTypeSettings.audio.secondary, &audioStatus);
832                if (rc!=BERR_SUCCESS)
833                {
834                    rc = BERR_TRACE(rc);
835                }
836                else if (audioStatus.started)
837                {
838                    NEXUS_AudioDecoder_GetTrickState(pid->cfg.pidTypeSettings.audio.secondary, &state);
839                    active->audio[1] = !state.forceStopped;
840                }
841            }
842            break;
843        }
844    }
845    BDBG_MSG_FLOW(("video %s started,primary audio %s started ,secondary audio %s started",
846                   active->video?"":"not",active->audio[0]?"":"not",active->audio[1]?"":"not"));
847    return;
848}
849
850
851/* Wait for the decoder to see if we've decoded to the end.
852Returns true if end of stream was reached
853*/
854static bool
855b_play_wait_for_end(NEXUS_PlaybackHandle p)
856{
857    uint32_t fifoMarker;
858    unsigned cdbDepth;
859    NEXUS_Time now;
860    long diff;
861    struct b_play_active_decoders active;
862
863    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
864    /* in the case of forward frame advance, we have to use different logic */
865    if (p->state.mode == NEXUS_PlaybackState_ePaused) {
866        if (!p->state.fifoMarkerCounter) {
867            return false;
868        }
869        p->state.fifoMarkerCounter--;
870        if (p->state.fifoMarkerCounter) {
871            return false;
872        }
873        /* when p->state.fifoMarkerCounter hits 0, then it will fall through and perform the test once */
874    }
875
876    bplay_get_decode_mark(p, &fifoMarker, &cdbDepth);
877    if (fifoMarker != p->state.fifoMarker) {
878        p->state.fifoMarker = fifoMarker;
879        NEXUS_Time_Get(&p->state.fifoLast);
880        return false;
881    }
882    NEXUS_Time_Get(&now);
883    diff = NEXUS_Time_Diff(&now, &p->state.fifoLast);
884
885    bplay_p_get_active_decodes(p, &active);
886
887    BDBG_MSG_FLOW(("b_play_wait_for_end:%#lx %s %s %s %u %u", (unsigned long)p, active.video?"video":"", active.audio[0]?"audio1":"", active.audio[1]?"audio2":"", diff, cdbDepth));
888
889
890    if(active.audio[0] || active.audio[1]) {
891        /* Audio decoder may not consume all data, so limit waiting time to 1000ms (1second) */
892        if(diff >= 1000 ){ goto eof_detected; }
893    }
894    if (cdbDepth >= B_MIN_CDB_DEPTH || diff < B_FRAME_DISPLAY_TIME * 6) {
895        /* need to wait longer */
896        return false;
897    }
898eof_detected:
899    /* else fall through and jump right now.
900    reinit depth and timer for the next time. */
901    p->state.fifoMarker = 0;
902    /* reset */
903    NEXUS_Time_Get(&p->state.fifoLast);
904    return true;
905}
906
907/**
908* If ts/BNAV_Player_getNextEntry fails, we must assume we've come to the beginning or end.
909*
910* Returns true if playback should continue without reading more data
911*    because we're waiting for decode to finish
912* Returns false if the player was reset and another readindex should be performed
913*/
914static NEXUS_Error
915b_play_handle_loop_condition(NEXUS_PlaybackHandle p)
916{
917    NEXUS_PlaybackLoopMode loopmode;
918    bool is_beginning;
919    bmedia_player_pos pos;
920    bmedia_player_status status;
921    NEXUS_Error rc;
922
923    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
924    BDBG_ASSERT(p->media_player);
925
926    if (p->state.mode == NEXUS_PlaybackState_ePaused) {
927        if (p->state.loopedDuringPause) {
928            return 0;
929        }
930    }
931    else {
932        p->state.loopedDuringPause = false;
933    }
934
935    b_play_update_location(p);
936    bmedia_player_tell(p->media_player, &pos);
937    bmedia_player_get_status(p->media_player, &status);
938
939    /* Now we have a verified end of stream, so notity the app and take action. */
940    if (p->state.direction < 0 || (pos && (pos < status.bounds.first))) {
941        /* BOF can happen when going backwards or if you fall off the beginning during continuous record.
942        We careful that pos can be zero if the decoder has no PTS on a transition. */
943        is_beginning = true;
944        NEXUS_TaskCallback_Fire(p->beginningOfStreamCallback);
945        loopmode = p->params.beginningOfStreamAction;
946        BDBG_MSG(("Beginning of stream reached (pos %d in %d...%d), loopmode %d", pos, status.bounds.first, status.bounds.last, loopmode));
947    } else {
948        is_beginning = false;
949        NEXUS_TaskCallback_Fire(p->endOfStreamCallback);
950        loopmode = p->params.endOfStreamAction;
951        BDBG_MSG(("End of stream reached (pos %d in %d...%d), loopmode %d", pos, status.bounds.first, status.bounds.last, loopmode));
952    }
953    rc = b_play_media_handle_loop_condition(p, is_beginning, loopmode, &status.bounds);
954
955    if (!rc && p->state.mode == NEXUS_PlaybackState_ePaused) {
956        /* we have looped (i.e. fired an endOfStream or beginningOfStream action) while paused, so don't fire it again. */
957        p->state.loopedDuringPause = true;
958    }
959
960    return rc;
961}
962
963/* this function called from the timer context */
964void
965b_play_timer(void *playback)
966{
967    NEXUS_PlaybackHandle p = playback;
968
969    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
970    p->state.timer = NULL;
971
972    /* In timeshifting mode, we may have hit the BOF. This should never invoke the drain which monitors EOF.
973    Check for this special condition and invoke loop_condition directly if so.
974    If we're waiting for an IO request to complete we can't reset our state. We'll soon be in eWaitingPlayback, and then it's safe to make a change. */
975    if (p->params.timeshifting && p->state.state != eWaitingIo) {
976        bmedia_player_pos pos;
977        bmedia_player_status status;
978        b_play_update_location(p);
979        bmedia_player_tell(p->media_player, &pos);
980        bmedia_player_get_status(p->media_player, &status);
981        if (pos < status.bounds.first) {
982            b_play_handle_loop_condition(p);
983            p->state.drain_mode = false;
984            b_play_next_frame(playback);
985            goto done;
986        }
987
988        if (p->state.state == eWaitingRecord) {
989            /* if we're at the end of the file, we should test if we've come to the end of decode.
990            if so, the application needs an endOfStream callback so it can switch to live decode. */
991            if (b_play_wait_for_end(playback)) {
992                b_play_handle_loop_condition(p);
993            }
994            if (!p->state.timer) { /* timer could have been started by b_play_handle_loop_condition */
995                p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME*5, b_play_timer, p);
996            }
997            goto done;
998        }
999    }
1000
1001    if (p->state.state == eTimer) {
1002        if (p->state.drain_mode) {
1003            if (b_play_control(p, eControlFrame)) { goto done; }
1004            if (b_play_wait_for_end(playback)) {
1005                /* all data is decoded now, ask user what to do next */
1006                b_play_handle_loop_condition(p);
1007                p->state.drain_mode = false;
1008                /* schedule timer and it will restart playback */
1009                BDBG_MSG(("Turn off drain. Rescheduling timer event."));
1010                if (!p->state.timer) { /* timer could have been started by b_play_handle_loop_condition */
1011                    p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME, b_play_timer, p);  /* schedule another call into the same function after while */
1012                }
1013            } else {
1014                /* decoder FIFO not drained yet, schedule another timer */
1015                BDBG_MSG(("FIFO not drained. Rescheduling timer event."));
1016                BDBG_ASSERT(!p->state.timer);
1017                p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME, b_play_timer, p);  /* schedule another call into the same function after while */
1018            }
1019        } else {
1020            BDBG_MSG(("timer feed"));
1021            b_play_next_frame(playback);
1022        }
1023    } else {
1024        BDBG_MSG(("Timer: next frame was cancelled %d", p->state.state));
1025    }
1026
1027    if( p->state.mode == NEXUS_PlaybackState_ePaused && !p->state.timer) {
1028        /* If we're in ePaused state, we cannot exit this function without the timer being rescheduled.
1029        If the drain logic has already rescheduled it, that's fine. But if not, we must schedule it here. */
1030        BDBG_MSG(("Paused. Rescheduling timer event."));
1031        p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME, b_play_timer, p);  /* schedule another call into the same function after while */
1032    }
1033
1034done:
1035    b_play_check_buffer(p);
1036    return;
1037}
1038
1039
1040/* This function is used to initiate drain in a decoder buffer, once drain is completed, b_play_handle_loop_condition will be called */
1041NEXUS_Error
1042b_play_start_drain(NEXUS_PlaybackHandle p)
1043{
1044    BDBG_OBJECT_ASSERT(p, NEXUS_Playback);
1045    if (p->state.state != eSleeping || p->state.mode != NEXUS_PlaybackState_ePaused) { /* if we are in pause mode, just exit from the data pump loop, API would restart it when required */
1046        if (b_play_control(p, eControlFrame)) { return true; }
1047        /* we are waiting for some external event */
1048        p->state.state = eTimer;
1049        p->state.drain_mode = true;
1050        BDBG_MSG(("b_play_start_drain starting timer"));
1051        if (!p->state.timer) {
1052            p->state.timer = NEXUS_ScheduleTimer(B_FRAME_DISPLAY_TIME, b_play_timer, p);  /* schedule another call into the same function after while */
1053        }
1054    } else {
1055        BDBG_WRN(("b_play_start_drain while in pause"));
1056    }
1057    return NEXUS_SUCCESS;
1058}
1059
1060/*
1061 * this function monitors any user requests, and handles them as necessary,
1062 * it returns true, if normal execution shall be aborted and false otherwise
1063 */
1064bool
1065b_play_control(NEXUS_PlaybackHandle playback, enum b_control_origin origin)
1066{
1067    BDBG_OBJECT_ASSERT(playback, NEXUS_Playback);
1068    if (origin != eControlDataIO &&
1069        (playback->state.state == eStopping || playback->state.state == eCancelIo))
1070    {
1071        BDBG_MSG(("Alive, but waiting for I/O to finish"));
1072        return false; /* keep going */
1073    }
1074
1075    if (playback->state.state == eStopping) {
1076        BDBG_MSG(("Stop request received, sending ack"));
1077        playback->state.state = eStopped;
1078        BKNI_SetEvent(playback->ack_event);
1079        return true; /* stop execution */
1080    }
1081    if (playback->state.state == eCancelIo) {
1082        BDBG_MSG(("CancelIo request received, sending ack"));
1083        playback->state.state = eIoCanceled;
1084        BKNI_SetEvent(playback->ack_event);
1085        return true; /* stop execution */
1086    }
1087
1088    if(playback->state.state == eStopped || playback->state.state == eAborted) {
1089        BDBG_MSG(("Playback stopped"));
1090        return true; /* stop execution */
1091    }
1092
1093    return false; /* keep going */
1094}
1095
Note: See TracBrowser for help on using the repository browser.