source: svn/trunk/newcon3bcm2_21bu/BSEAV/lib/bcmplayer/src/tsplayer.c @ 45

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

first commit

  • Property svn:executable set to *
File size: 35.4 KB
Line 
1/***************************************************************************
2 *     Copyright (c) 2002, Broadcom Corporation
3 *     All Rights Reserved
4 *     Confidential Property of Broadcom Corporation
5 *
6 *  THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE
7 *  AGREEMENT  BETWEEN THE USER AND BROADCOM.  YOU HAVE NO RIGHT TO USE OR
8 *  EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT.
9 *
10 * $brcm_Workfile: tsplayer.c $
11 * $brcm_Revision: Irvine_BSEAVSW_Devel/59 $
12 * $brcm_Date: 6/24/03 10:42a $
13 *
14 * Module Description: Transport Stream Index Player
15 * Created: 02/08/2001 by Marcus Kellerman
16 *
17 * Revision History:
18 *
19 * $brcm_Log: /vobs/SetTop/bcmplayer/tsplayer.c $
20 *
21 * Irvine_BSEAVSW_Devel/59   6/24/03 10:42a erickson
22 * fixed compilation error related to dbg macros
23 *
24 * Irvine_BSEAVSW_Devel/58   3/11/03 3:59p erickson
25 * fixed debug interface usage
26 *
27 * Irvine_BSEAVSW_Devel/57   2/21/03 9:50a marcusk
28 * Fixed mode when playing back PES streams in trick modes.
29 *
30 * Irvine_BSEAVSW_Devel/56   2/20/03 4:28p marcusk
31 * Fixed problem when searching for PES start codes.
32 *
33 * Irvine_BSEAVSW_Devel/55   1/27/03 4:34p vsilyaev
34 * Fixed issue, when tsplayer returned chunk size equal to zero.
35 *
36 * Irvine_BSEAVSW_Devel/54   11/15/02 3:44p vsilyaev
37 * Added support for new 'stat' callback, this callback uset to get begin
38 * and end file offsets.
39 *
40 * Irvine_HDDemo_Devel\53   5/17/02 9:47a marcusk
41 * Added "" around BRCM_DBG_MODULE to support new debug routines.
42 *
43 * Irvine_HDDemo_Devel\52   4/17/02 9:55a marcusk
44 * Removed printf's and fixed compiler warning.
45 *
46 * Irvine_HDDemo_Devel\51   4/16/02 2:0p marcusk
47 * Fixed eBpPlayFromPes.
48 *
49 * Irvine_HDDemo_Devel\50   4/16/02 11:55a marcusk
50 * Changed parameter values to be singed.
51 *
52 * Irvine_HDDemo_Devel\49   4/16/02 10:33a marcusk
53 * Updated to use unified interface with bcmplayer.
54 *
55 * Irvine_HDDemo_Devel\1   4/16/02 12:56p erickson
56 * Moved from SetTop/dvrtable
57 *
58 * \main\Irvine_HDDemo_Devel\48   3/15/02 6:42p erickson
59 * Moved some documentation to .h and made sTsPlayer private
60 *
61 * \main\Irvine_HDDemo_Devel\47   3/8/02 5:16p erickson
62 * Added \n to BRCM_DBG_ERR/WRN/MSG macros, like kernelinterface does.
63 *
64 * \main\Irvine_HDDemo_Devel\46   3/5/02 3:49p erickson
65 * Fixed unsigned long compile warning
66 *
67 * \main\Irvine_HDDemo_Devel\45   3/5/02 3:36p erickson
68 * Moved tsplayer_returnPictureCode and tsplayer_returnStartCode to
69 * tsindexer, made them public, and changed change to tsindex_XXX. They
70 * need to be used by external programs, and this is the right spot for
71 * them.
72 *
73 * \main\Irvine_HDDemo_Devel\44   2/27/02 12:41p erickson
74 * Added PRINT_PACKETTYPES_SENT debugging.
75 *
76 ***************************************************************************/
77#ifdef CMDLINE_TEST
78#include <stdlib.h>
79#include <memory.h>
80#define bcmKNIMalloc    malloc
81#define bcmKNIFree              free
82#define BRCM_DBG_ERR(a) do {printf a; printf("\n");} while(0)
83#define BRCM_DBG_WRN(a) /*do {printf a; printf("\n");} while(0)*/
84#define BRCM_DBG_MSG(a) /*do {printf a; printf("\n");} while(0)*/ /* Don't print messages */
85#else
86#include "bcmkernel.h"
87#define BRCM_DBG_MODULE_NAME "tsplayer.c"
88#include "brcm_dbg.h"
89#endif
90
91#define VERSION 101 /* see playertypes.h for definition of version */
92
93#include <stdio.h>
94#include "tsplayer.h"
95#include "mpeg2types.h"
96
97/* #define PRINT_PACKETTYPES_SENT */
98
99#define NUM_SCT_ENTRIES_TO_OVERLAP_IN_CACHE             (NUM_SCT_ENTRIES_TO_CACHE/4)
100
101struct sTsPlayer {
102#define NUM_SCT_ENTRIES_TO_CACHE        1000
103        eBpPlayModeParam                        playMode;
104        eBpDirectionParam                       playDir;
105        eBpDataStartModeParam           dataStartMode;
106        eBpLoopModeParam                        loopMode;
107        BP_READ_CB                      readCb;
108        BP_TELL_CB                      tellCb;
109        BP_SEEK_CB                      seekCb;
110        BP_STAT_CB                      statCb;         /* optional file stat callback */
111        void                            *sctFp;
112        void                            *streamFp;
113        int                                     streamIsLE;
114        sIndexEntry                     sctCache[NUM_SCT_ENTRIES_TO_CACHE];
115        long                            sctCacheIndexStart;
116        long                            sctCacheIndexEnd;
117        long                            curSctIndex;
118        long                            numFramesToSkip;
119        long                            curNumSkippedBFrames;
120        int                                     currentVersion;
121};
122
123/*---------------------------------------------------------------
124- PRIVATE FUNCTIONS
125---------------------------------------------------------------*/
126static sIndexEntry *tsplayer_readSct( sTsPlayer * const p_tsp, long index );
127static char tsplayer_getNextFrameEntry( sTsPlayer * const p_tsp, sTpEntry * const p_tpEntry, eBpPlayModeParam mode );
128static long tsplayer_findSctIndexFromOffset( sTsPlayer * const p_tsp, unsigned long streamByteOffset );
129static long tsplayer_findStartCodeIndex( sTsPlayer * const p_tsp, long startIndex, unsigned char startCode, eBpDirectionParam dir );
130static long tsplayer_findPictureIndex( sTsPlayer * const p_tsp, long startIndex, unsigned char picCode, eBpDirectionParam dir );
131static long tsplayer_findSctIndexFromPts( sTsPlayer * const p_tsp, unsigned long streamPts );
132static unsigned long tsplayer_returnPts( sIndexEntry *p_sct );
133static char tsplayer_verifyIndexEntry( sTsPlayer * const p_tsp, sIndexEntry *p_sct );
134static int tsplayer_getIndexLimits(sTsPlayer * const p_tsp, long *pFirstIndex, long *pLastIndex);
135
136#ifdef PRINT_PACKETTYPES_SENT
137static char *pictureCodeStr[] = {
138        "I",
139        "P",
140        "B"
141};
142#endif
143
144/*! Function Definitions */
145
146/******************************************************************************
147* INPUTS:       readCb = pointer to a function that reads table entries (same format as fread)
148*                       seekCb = pointer to a function that seeks inside SCT file (same format as fseek)
149*                       sctFp = file pointer to SCT file (used in above callbacks)
150* OUTPUTS:      None.
151* RETURNS:      pointer to sTsPlayer structure
152* FUNCTION: This function allocates and initializes an player structure.
153******************************************************************************/
154sTsPlayer *tsplayer_allocate( BP_READ_CB readCb, BP_TELL_CB tellCb, BP_SEEK_CB seekCb, void *sctFp, int version )
155{
156        sTsPlayer *p_tsp;
157        p_tsp = bcmKNIMalloc( sizeof( sTsPlayer ) );
158        if (tsplayer_reset(p_tsp, readCb, tellCb, seekCb, sctFp, version)) {
159                bcmKNIFree(p_tsp);
160                p_tsp = NULL;
161        }
162        return p_tsp;
163}
164
165int tsplayer_reset( sTsPlayer *p_tsp, BP_READ_CB readCb, BP_TELL_CB tellCb, BP_SEEK_CB seekCb, void *sctFp, int version )
166{
167        int result;
168
169        p_tsp->readCb   = readCb;
170        p_tsp->seekCb   = seekCb;
171        p_tsp->tellCb   = tellCb;
172        p_tsp->sctFp    = sctFp;
173
174        p_tsp->playMode                         = eBpPlayNormal;
175        p_tsp->playDir                          = eBpForward;
176        p_tsp->dataStartMode            = eBpPlayFromTrans;
177        p_tsp->loopMode                         = eBpLoopForever;
178        p_tsp->sctCacheIndexStart       = -1;
179        p_tsp->sctCacheIndexEnd         = -1;
180        p_tsp->curSctIndex                      = 0;
181        p_tsp->numFramesToSkip          = 0;
182        p_tsp->curNumSkippedBFrames = 0;
183        p_tsp->streamFp                         = 0;
184        p_tsp->statCb                           = NULL;
185        p_tsp->currentVersion           = VERSION;
186
187        if (version == -1)
188                version = VERSION;
189        switch (version) {
190        case VERSION:
191                p_tsp->currentVersion = version;
192                result = 0;
193                break;
194        default:
195                result = -1;
196                break;
197        }
198
199        return result;
200}
201
202/******************************************************************************
203* INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
204* OUTPUTS:      None.
205* RETURNS:      None.
206* FUNCTION: This function frees any memory used by a player structure.
207******************************************************************************/
208void tsplayer_free( sTsPlayer *p_tsp )
209{
210        bcmKNIFree( p_tsp );
211}
212
213/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
214+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
215+                       index = index number of SCT entry you would like to read
216+ OUTPUTS:      None.
217+ RETURNS:      pointer to SCT entry
218+ FUNCTION: This function returns a pointer to an SCT entry.  This entry is
219+                       stored in an internal cache, and may become invalid if another
220+                       tsplayer_readSct() function is called.  Therefore, any processing
221+                       that needs to be perfomed with the SCT entry must be done before
222+                       reading another one.
223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
224sIndexEntry *tsplayer_readSct( sTsPlayer * const p_tsp, long index )
225{
226        long firstIndex;
227        long lastIndex;
228
229        tsplayer_getIndexLimits(p_tsp, &firstIndex, &lastIndex);
230        if (firstIndex == -1 || lastIndex == -1 || index < firstIndex || index >= lastIndex)
231                return 0;
232
233        /* Check if this sct is in our cache */
234        if( (p_tsp->sctCacheIndexStart == -1)
235                || !((index >= p_tsp->sctCacheIndexStart) && (index < p_tsp->sctCacheIndexEnd)) )
236        {
237                unsigned long saveStart = p_tsp->sctCacheIndexStart;
238                unsigned long saveEnd = p_tsp->sctCacheIndexEnd;
239                unsigned long pos;
240                long t;
241
242                if( index < p_tsp->sctCacheIndexStart )
243                {
244                        /* We are reading indexes backwards so cache backwards */
245                        p_tsp->sctCacheIndexStart = index - NUM_SCT_ENTRIES_TO_CACHE + NUM_SCT_ENTRIES_TO_OVERLAP_IN_CACHE + 1;
246                }
247                else
248                {
249                        p_tsp->sctCacheIndexStart = index - NUM_SCT_ENTRIES_TO_OVERLAP_IN_CACHE;
250                }
251
252                if( p_tsp->sctCacheIndexStart < firstIndex)
253                {
254                        p_tsp->sctCacheIndexStart = firstIndex;
255                }
256
257                pos = p_tsp->sctCacheIndexStart*sizeof(sIndexEntry);
258                if ((*p_tsp->seekCb)( p_tsp->sctFp, pos, SEEK_SET ))
259                        t = -1;
260                else {
261                        t = (*p_tsp->tellCb)( p_tsp->sctFp );
262                        if (t != -1)
263                                t /= sizeof(sIndexEntry);
264                }
265                /* We may not get exactly what we asked for, but it may work. */
266                if (t != -1 && t <= index && t < index + NUM_SCT_ENTRIES_TO_CACHE)
267                        p_tsp->sctCacheIndexStart = t;
268                else {
269                        BRCM_DBG_MSG(("TSPLAYER seek was invalidated: %ld\n", t));
270                        p_tsp->sctCacheIndexStart = saveStart;
271                        p_tsp->sctCacheIndexEnd = saveEnd;
272                        return 0;
273                }
274
275                p_tsp->sctCacheIndexEnd = p_tsp->sctCacheIndexStart;
276
277                /* The end is equal to the start plus whatever we are able to read */
278                p_tsp->sctCacheIndexEnd += ((*p_tsp->readCb)( p_tsp->sctCache, 1, sizeof(sIndexEntry) * NUM_SCT_ENTRIES_TO_CACHE, p_tsp->sctFp ) / sizeof(sIndexEntry));
279
280                if( index >= p_tsp->sctCacheIndexEnd )
281                        return 0;
282        }
283
284        return &(p_tsp->sctCache[index-p_tsp->sctCacheIndexStart]);
285}
286
287/******************************************************************************
288* INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
289*                       param = parameter that is to be set
290*                       paramEnum = enum value of the parameter
291*                       value = value that you wish to program into the paramEnum
292* OUTPUTS:      None.
293* RETURNS:      0 (success), -1 (fail)
294* FUNCTION: This function sets a parameter of the playback
295******************************************************************************/
296int tsplayer_setParam( sTsPlayer * const p_tsp, eBpParam param, int paramEnum, long value )
297{
298        int status = 0;
299        long index;
300
301        BRCM_DBG_MSG(("tsplayer_setParam %d, %d, %ld\n", param, paramEnum, value));
302        switch( param )
303        {
304        case eBpPlayMode:
305                if( p_tsp->playMode != paramEnum )
306                {
307                        p_tsp->playMode = paramEnum;
308
309                        /* When switching modes, always start from an I-frame (if we can find one) */
310                        index = tsplayer_findPictureIndex( p_tsp, p_tsp->curSctIndex, PC_I_FRAME, eBpForward );
311                        if( index != -1 )
312                        {
313                                p_tsp->curSctIndex = index;
314
315                                /* If possible we want to find the previous sequence header for the decoder */
316                                index = tsplayer_findStartCodeIndex( p_tsp, p_tsp->curSctIndex, SC_SEQUENCE, eBpReverse );
317                                if( index != -1 )
318                                {
319                                        p_tsp->curSctIndex = index;
320                                }
321                        }
322                }
323                if( p_tsp->playMode == eBpPlaySkipB || p_tsp->playMode == eBpPlayI )
324                {
325                        if( value < 0 )
326                        {
327                                value *= -1;
328                                p_tsp->playDir = eBpReverse;
329                        }
330                        p_tsp->numFramesToSkip = value;
331                        p_tsp->curNumSkippedBFrames = 0;
332                }
333                break;
334
335        case eBpDirection:
336                p_tsp->playDir = paramEnum;
337                break;
338
339        case eBpDataStartMode:
340                p_tsp->dataStartMode = paramEnum;
341                break;
342
343        case eBpLoopMode:
344                p_tsp->loopMode = paramEnum;
345                break;
346
347        case eBpCurrentStreamOffset:
348                index = tsplayer_findSctIndexFromOffset( p_tsp, value );
349                if( index != -1 )
350                        p_tsp->curSctIndex = index;
351                else
352                        status = -1;
353                break;
354
355        case eBpCurrentStreamPts:
356                index = tsplayer_findSctIndexFromPts( p_tsp, value );
357                if( index != -1 )
358                        p_tsp->curSctIndex = index;
359                else
360                        status = -1;
361                break;
362
363        case eBpCurrentSctIndex:
364                p_tsp->curSctIndex = value;
365                break;
366
367        case eBpFindIFrame:
368                value = p_tsp->curSctIndex;
369                if (paramEnum < 0)
370                        value++;
371                index = tsplayer_findPictureIndex( p_tsp, value, PC_I_FRAME, paramEnum);
372                if( index == -1 )
373                {
374                        BRCM_DBG_WRN(("Unable to find i-frame on set(currentSctIndex)\n"));
375                        status = -1;
376                }
377                else {
378                        BRCM_DBG_WRN(("findiframe hopped %ld\n", index-p_tsp->curSctIndex));
379                        p_tsp->curSctIndex = index;
380
381                        /* If possible we want to find the previous sequence header for the decoder */
382                        index = tsplayer_findStartCodeIndex( p_tsp, p_tsp->curSctIndex, SC_SEQUENCE, eBpReverse );
383                        if( index != -1 )
384                                p_tsp->curSctIndex = index;
385                }
386                break;
387
388        case eBpStreamEnd:
389                /* Jump the end of the stream */
390                tsplayer_getIndexLimits(p_tsp, NULL, &index);
391
392                /* Search back the desired number of frames */
393                while( value )
394                {
395                        /* Start at the previous start code */
396                        index--;
397
398                        index = tsplayer_findStartCodeIndex( p_tsp, index, SC_PICTURE, eBpReverse );
399                        if( index == -1 )
400                        {
401                                break;
402                        }
403
404                        value--;
405                }
406
407                if( index != -1 )
408                        p_tsp->curSctIndex = index;
409                else
410                        status = -1;
411                break;
412
413        default:
414                status = -1;
415                break;
416        }
417
418        return status;
419}
420
421/******************************************************************************
422* INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
423*                       param = parameter that is to be returned
424* OUTPUTS:      p_paramEnum = enum value of the parameter
425*                       p_value = value that is currently used by paramEnum
426* RETURNS:      0 (success), -1 (fail)
427* FUNCTION: This function gets a parameter of the playback
428******************************************************************************/
429int tsplayer_getParam( sTsPlayer * const p_tsp, eBpParam param, int * const p_paramEnum, long * const p_value )
430{
431        sIndexEntry *p_sct;
432        int status = 0;
433        long index;
434
435        switch( param )
436        {
437        case eBpPlayMode:
438                *p_paramEnum = p_tsp->playMode;
439                if( p_tsp->playMode == eBpPlaySkipB || p_tsp->playMode == eBpPlayI )
440                {
441                        *p_value = p_tsp->numFramesToSkip;
442                }
443                break;
444
445        case eBpDirection:
446                *p_paramEnum = p_tsp->playDir;
447                break;
448
449        case eBpDataStartMode:
450                *p_paramEnum = p_tsp->dataStartMode;
451                break;
452
453        case eBpCurrentStreamOffset:
454                p_sct = tsplayer_readSct( p_tsp, tsplayer_findStartCodeIndex( p_tsp, p_tsp->curSctIndex, SC_ANY_NON_PTS, eBpForward ) );
455                if (p_sct)
456                        *p_value = p_sct->recordByteCount;
457                else
458                        status = -1;
459                break;
460
461        case eBpLoopMode:
462                *p_paramEnum = p_tsp->loopMode;
463                break;
464
465        case eBpCurrentStreamPts:
466                index = tsplayer_findStartCodeIndex( p_tsp, p_tsp->curSctIndex, SC_PTS, eBpForward );
467                p_sct = tsplayer_readSct( p_tsp, index );
468                if (p_sct)
469                        *p_value = tsplayer_returnPts( p_sct );
470                else
471                        status = -1;
472                break;
473
474        case eBpCurrentSctIndex:
475                *p_value = p_tsp->curSctIndex;
476                break;
477
478        case eBpLastSctIndex:
479                tsplayer_getIndexLimits(p_tsp, NULL, p_value);
480                break;
481
482        case eBpIndexFromPts:
483                *p_value = tsplayer_findSctIndexFromPts( p_tsp, *p_value );
484                break;
485
486        case eBpIndexFromOffset:
487                *p_value = tsplayer_findSctIndexFromOffset( p_tsp, *p_value );
488                break;
489
490        case eBpVersion:
491                *p_value = p_tsp->currentVersion;
492                break;
493
494        default:
495                status = -1;
496                break;
497        }
498
499        return status;
500}
501
502/******************************************************************************
503* INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
504* OUTPUTS:      p_tpEntry = filled with information regarding next playback unit
505* RETURNS:      0 (success), -1 (fail)
506* FUNCTION: This function returns the next playback unit.  The playback unit
507*                       is based upon the current direction and mode set by the functions
508*                       provided.
509******************************************************************************/
510char tsplayer_getNextPlayEntry( sTsPlayer * const p_tsp, sTpEntry * const p_tpEntry )
511{
512        memset( p_tpEntry, 0, sizeof( sTpEntry ) );
513
514        switch( p_tsp->playMode )
515        {
516        case eBpPlayNormal:
517                /* Fallthrough */
518        case eBpPlayI:
519                /* Falthrough */
520        case eBpPlayIP:
521                /* Falthrough */
522        case eBpPlaySkipB:
523                if( tsplayer_getNextFrameEntry( p_tsp, p_tpEntry, p_tsp->playMode ) != 0 )
524            return -1;
525        /* Make sure we don't return a byteCount of 0 when possible */
526        if( p_tpEntry->byteCount == 0 )
527                    return tsplayer_getNextFrameEntry( p_tsp, p_tpEntry, p_tsp->playMode );
528        return 0;
529        default:
530                return -1;
531        }
532}
533
534void tsplayer_dumpDebugInfo( sTsPlayer * const p_tsp )
535{
536        unsigned long i;
537
538        BRCM_DBG_ERR(("TSPLAYER DEBUG INFO DUMP"));
539        BRCM_DBG_ERR(("---------------------------------------"));
540
541        BRCM_DBG_ERR((" playMode = %d", p_tsp->playMode));
542        BRCM_DBG_ERR((" playDir = %d", p_tsp->playDir));
543        BRCM_DBG_ERR((" dataStartMode = %d", p_tsp->dataStartMode));
544        BRCM_DBG_ERR((" loopMode = %d", p_tsp->loopMode));
545        BRCM_DBG_ERR((" curSctIndex = %ld", p_tsp->curSctIndex));
546        BRCM_DBG_ERR((" sctCacheIndexStart = %ld", p_tsp->sctCacheIndexStart));
547        BRCM_DBG_ERR((" sctCacheIndexEnd = %ld", p_tsp->sctCacheIndexEnd));
548
549        BRCM_DBG_ERR((" SCT CACHE:"));
550        for( i = 0; i < NUM_SCT_ENTRIES_TO_CACHE; i++ )
551        {
552                BRCM_DBG_ERR(("  %05ld: 0x%08lX 0x%08lX 0x%08lX 0x%08lX", i+p_tsp->sctCacheIndexStart, p_tsp->sctCache[i].startCodeBytes, p_tsp->sctCache[i].recordByteCount, p_tsp->sctCache[i].recordByteCountHi, p_tsp->sctCache[i].flags));
553        }
554}
555
556/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
557+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
558+                       mode = Normal, I frame or IP frame mode
559+ OUTPUTS:      p_tpEntry = filled with information regarding next playback unit
560+ RETURNS:      0 (success), -1 (fail)
561+ FUNCTION: This function returns a filled in playback unit entry.  This
562+                       function is used for I or IP playback.  Depending on the data mode
563+                       it will return these frames aligned to transport or PES transport
564+                       packets.
565+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
566char tsplayer_getNextFrameEntry( sTsPlayer * const p_tsp, sTpEntry * const p_tpEntry, eBpPlayModeParam mode )
567{
568        sIndexEntry *p_startSct, *p_endSct, *p_sct;
569        long            startIndex, endIndex;
570        char            entryFound = 0;
571        char            endFound = 0;
572        long            loopCount = 0;
573        long            numSkippedIFrames;
574        long            firstIndex;
575
576        startIndex = p_tsp->curSctIndex;
577
578        tsplayer_getIndexLimits(p_tsp, &firstIndex, NULL);
579
580        while( !entryFound )
581        {
582                if( mode == eBpPlayNormal )
583                {
584                        if( p_tsp->playDir == eBpReverse )
585                        {
586                                BRCM_DBG_ERR(("Normal playback does not support reverse play!"));
587                                return -1;
588                        }
589
590                        /* DME: if we get caught behind the circular buffer, we must jump into it */
591                        if (startIndex < firstIndex) {
592                                firstIndex = tsplayer_findPictureIndex( p_tsp, firstIndex, PC_I_FRAME, eBpForward );
593                                if (firstIndex  == -1)
594                                        return -1;
595                                startIndex = p_tsp->curSctIndex = firstIndex;
596                                BRCM_DBG_WRN(("tsplayer: Assigned startIndex to firstIndex %ld\n", startIndex));
597                        }
598
599                        startIndex = tsplayer_findStartCodeIndex( p_tsp, startIndex, SC_ANY_NON_PTS, eBpForward );
600                }
601                else if( mode == eBpPlayI )
602                {
603                        for( numSkippedIFrames = 0; numSkippedIFrames <= p_tsp->numFramesToSkip; numSkippedIFrames++ )
604                        {
605                                startIndex = tsplayer_findPictureIndex( p_tsp, startIndex+p_tsp->playDir, PC_I_FRAME, p_tsp->playDir );
606
607                                if( startIndex == -1 )
608                                {
609                                        /* Exit this for loop because we are past the stream boundaries */
610                                        break;
611                                }
612                        }
613                }
614                else
615                {
616                        char destFrameFound = 0;
617
618                        if( p_tsp->playDir == eBpReverse )
619                        {
620                                BRCM_DBG_ERR(("IP/SkipB playback not supported in reverse play!"));
621                                return -1;
622                        }
623
624                        while( !destFrameFound )
625                        {
626                                startIndex = tsplayer_findPictureIndex( p_tsp, startIndex, PC_ANY_FRAME, p_tsp->playDir );
627                                if( startIndex == -1 )
628                                {
629                                        /* Exit this loop because we are past the stream boundaries */
630                                        destFrameFound = 1;
631                                        continue;
632                                }
633
634                                p_startSct = tsplayer_readSct( p_tsp, startIndex );
635                                if (!p_startSct)
636                                        return -1;
637
638                                /* Check if this start code contains an P frame (we need to include this picture) */
639                                if( returnPictureCode(p_startSct->startCodeBytes) == PC_B_FRAME )
640                                {
641                                        if( p_tsp->playMode == eBpPlaySkipB && p_tsp->curNumSkippedBFrames >= p_tsp->numFramesToSkip )
642                                        {
643                                                /* We have skipped the correct number of B frames */
644                                                destFrameFound = 1;
645                                                p_tsp->curNumSkippedBFrames = 0;
646                                        }
647                                        else
648                                        {
649                                                /* Keep searching past any B frames */
650                                                p_tsp->curNumSkippedBFrames += 1;
651                                                startIndex++;
652                                        }
653                                }
654                                else
655                                {
656                                        destFrameFound = 1;
657                                }
658                        }
659                }
660
661                if (startIndex != -1)
662                        endIndex = tsplayer_findPictureIndex( p_tsp, startIndex + 1, PC_ANY_FRAME, eBpForward );
663                else
664                        endIndex = -1;
665
666                if( startIndex == -1 || endIndex == -1 )
667                {
668                        loopCount++;
669
670                        if( p_tsp->loopMode == eBpSinglePlay || loopCount > 1 )
671                        {
672                                BRCM_DBG_WRN(("Frame start code not found. End of file."));
673                                return -1;
674                        }
675                        else
676                        {
677                                long index;
678
679                                BRCM_DBG_WRN(("Frame start code not found. Looping file."));
680                                if( p_tsp->playDir == eBpForward )
681                                {
682                                        startIndex = 0;
683                                }
684                                else
685                                {
686                                        tsplayer_getIndexLimits(p_tsp, NULL, &startIndex);
687                                }
688
689                                /* Find the previous/next I-frame to start from (if one is found) */
690                                index = tsplayer_findPictureIndex( p_tsp, startIndex, PC_I_FRAME, p_tsp->playDir );
691                                if( index != -1 )
692                                {
693                                        startIndex = index;
694                                }
695                                endFound = 0;
696                                continue;
697                        }
698                }
699
700                if( mode != eBpPlayNormal )
701                {
702                        /* If possible we want to find the previous sequence header for the decoder */
703                        long prevIndex;
704
705                        prevIndex = tsplayer_findStartCodeIndex( p_tsp, startIndex, SC_SEQUENCE, eBpReverse );
706                        if( prevIndex != -1 && prevIndex == (startIndex - 1) )
707                        {
708                                startIndex = prevIndex;
709                        }
710
711                        /* The following checks are only used for transport mode (do not use them when in PES mode) */
712                        if( p_tsp->dataStartMode != eBpPlayFromPes )
713                        {
714                                /* We also want to start from the previous PES header if it was immediately before the current start code */
715                                if( (p_sct = tsplayer_readSct( p_tsp, startIndex-1 )) )
716                                {
717                                        if( returnStartCode(p_sct->startCodeBytes) >= SC_PES_START
718                                                && returnStartCode(p_sct->startCodeBytes) <= SC_PES_END )
719                                        {
720                                                startIndex -= 1;
721                                        }
722                                }
723
724                                /* We do not want to include the end PES header if it exists */
725                                if( (p_sct = tsplayer_readSct( p_tsp, endIndex-1 )) )
726                                {
727                                        if( returnStartCode(p_sct->startCodeBytes) >= SC_PES_START
728                                                && returnStartCode(p_sct->startCodeBytes) <= SC_PES_END )
729                                        {
730                                                endIndex -= 1;
731                                        }
732                                }
733                        }
734                }
735
736                entryFound = 1;
737        }
738
739        if (firstIndex > startIndex) {
740                BRCM_DBG_WRN(("bad start index, cache was bad\n"));
741                return -1;
742        }
743
744        /* We must store the data for the start before we read the end sct, due to cache coherency */
745        p_startSct = tsplayer_readSct( p_tsp, startIndex );
746        if( tsplayer_verifyIndexEntry( p_tsp, p_startSct ) )
747        {
748                BRCM_DBG_ERR(("tsplayer_getNextFrameEntry(): Error with index entry: %ld!", startIndex));
749                return -1;
750        }
751
752        p_tpEntry->startOffset = p_startSct->recordByteCount;
753        p_tpEntry->startOffsetHi = (p_startSct->recordByteCountHi >> 24);
754
755
756        p_endSct = tsplayer_readSct( p_tsp, endIndex );
757        if( tsplayer_verifyIndexEntry( p_tsp, p_endSct ) )
758        {
759                BRCM_DBG_ERR(("tsplayer_getNextFrameEntry(): Error with index entry: %ld!", endIndex));
760                return -1;
761        }
762
763        /* Check if we have wrapped 32 bits of byte count */
764        if( p_endSct->recordByteCount < p_tpEntry->startOffset )
765        {
766                p_tpEntry->byteCount = 0xFFFFFFFF - p_tpEntry->startOffset;
767                p_tpEntry->byteCount += p_endSct->recordByteCount;
768        }
769        else
770        {
771                p_tpEntry->byteCount = p_endSct->recordByteCount - p_tpEntry->startOffset;
772        }
773
774#ifdef PRINT_PACKETTYPES_SENT
775        {
776        int pc = returnPictureCode(p_startSct->startCodeBytes);
777        if (pc >= 1 && pc <= 3)
778                BRCM_DBG_ERR(("sent %s (%ld %ld pts 0x%lx)\n",
779                        pictureCodeStr[pc-1],
780                        p_startSct->recordByteCount,
781                        p_tpEntry->byteCount,
782                        tsplayer_returnPts(p_startSct)));
783        else
784                BRCM_DBG_ERR(("sent unknown %d\n", pc));
785        }
786#endif
787
788        if( p_tsp->playDir == eBpForward )
789        {
790                p_tsp->curSctIndex = endIndex;
791        }
792        else
793        {
794                p_tsp->curSctIndex = startIndex;
795        }
796
797        /* Don't allow byte counts larger than 10 mbytes */
798        if( p_tpEntry->byteCount > 1024 * 1024 * 10 )
799        {
800                BRCM_DBG_ERR(("tsplayer_getNextFrameEntry(): Invalid byte count of: %ld found!", p_tpEntry->byteCount));
801                p_tpEntry->byteCount = 0;
802                return -1;
803        }
804
805        return 0;
806}
807
808/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
809+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
810+                       streamByteOffset = offset in stream search for
811+ OUTPUTS:      None.
812+ RETURNS:      SCT index covering this stream offset
813+ FUNCTION: This function searches the SCT table for an entry that occupies
814+                       this portion of the stream.
815+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
816long tsplayer_findSctIndexFromOffset( sTsPlayer * const p_tsp, unsigned long streamByteOffset )
817{
818        sIndexEntry             *p_sct;
819        char                    sctFound = 0;
820        unsigned long   prevRecOffset = 0;
821        long                    index, tempIndex;
822
823        /* need to search in either forward or reverse direction for startcode index */
824        eBpDirectionParam dir;
825
826        /* Start from our current index */
827        index = p_tsp->curSctIndex;
828
829        if( index == -1 )
830        {
831                index = 0;
832                prevRecOffset = 0;
833        }
834        else
835        {
836                index = tsplayer_findStartCodeIndex( p_tsp, index, SC_ANY_NON_PTS, eBpForward );
837
838                /* Initialize our prevRecOffset to our current offset */
839                if( !(p_sct = tsplayer_readSct( p_tsp, index )) )
840                {
841                        BRCM_DBG_WRN(("Unable to find SCT entry %ld (1)", index));
842                        return -1;
843                }
844                prevRecOffset = p_sct->recordByteCount;
845        }
846
847        dir = eBpForward;
848        while( !sctFound )
849        {
850                index = tsplayer_findStartCodeIndex( p_tsp, index, SC_ANY_NON_PTS, dir);
851                if (index <= 0)
852                {
853                        index = 0;
854                        break;
855                }
856
857                if( !(p_sct = tsplayer_readSct( p_tsp, index )) )
858                {
859                        BRCM_DBG_WRN(("Unable to find SCT entry %ld (2)", index));
860                        return -1;
861                }
862
863                if( streamByteOffset >= prevRecOffset && streamByteOffset <= p_sct->recordByteCount )
864                {
865                        sctFound = 1;
866                }
867                else if( p_sct->recordByteCount < streamByteOffset )
868                {
869                        dir = eBpForward;
870                        index += 1;
871                }
872                else
873                {
874                        dir = eBpReverse;
875                        index -= 1;
876                        if( index == 0 )
877                        {
878                                sctFound = 1;
879                        }
880                }
881
882                prevRecOffset = p_sct->recordByteCount;
883        }
884
885        /*
886         * Since there may be several start codes for the same offset we need to start
887         * at the first one of them
888         */
889        tempIndex = index;
890        while( sctFound )
891        {
892                tempIndex = tsplayer_findStartCodeIndex( p_tsp, tempIndex-1, SC_ANY_NON_PTS, eBpReverse );
893
894                p_sct = tsplayer_readSct( p_tsp, tempIndex );
895                if( !p_sct )
896                {
897                        break;
898                }
899
900                if( p_sct->recordByteCount == prevRecOffset )
901                {
902                        /* This is still an index that is valid so make sure to store it */
903                        index = tempIndex;
904                }
905                else
906                {
907                        /*
908                         * We went on index beyond the one we are looking for... The real index
909                         * is stored in index so we can now quit searching.
910                         */
911                        sctFound = 0;
912                }
913        }
914
915        return index;
916}
917
918/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
919+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
920+                       streamPts = PTS in stream search for
921+ OUTPUTS:      None.
922+ RETURNS:      SCT index covering this PTS
923+ FUNCTION: This function searches the SCT table for an entry that occupies
924+                       this portion of the stream.
925+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
926long tsplayer_findSctIndexFromPts( sTsPlayer * const p_tsp, unsigned long streamPts )
927{
928        sIndexEntry             *p_sct;
929        char                    sctFound = 0;
930        long                    index;
931        unsigned long   entryPts;
932        eBpDirectionParam               deltaIndex;
933
934        /* Start from our current index */
935        index = p_tsp->curSctIndex;
936
937        if( index == -1 )
938        {
939                index = 0;
940                deltaIndex = eBpForward;
941        }
942        else
943        {
944                deltaIndex = eBpReverse;
945        }
946
947        /*  This loop works as follows :
948            1) start by going backwards
949                2) once we've gone too far, turn around
950                3) go forward until we pass the desired pts by one then return */
951        while( (!sctFound) && (index != 0) )
952        {
953                index = tsplayer_findStartCodeIndex( p_tsp, index, SC_PTS, deltaIndex );
954                if( index == -1 )
955                {
956                        BRCM_DBG_WRN(("Unable to find SCT entry %ld (3)", index));
957                        return -1;
958                }
959
960                p_sct = tsplayer_readSct( p_tsp, index );
961                if (!p_sct)
962                        return -1;
963                entryPts = tsplayer_returnPts( p_sct );
964
965                if( deltaIndex == eBpForward && streamPts <= entryPts )
966                {
967                        sctFound = 1;
968                        continue;
969                }
970                else if( entryPts < streamPts )
971                {
972                        deltaIndex = eBpForward;
973                }
974                else
975                {
976                        deltaIndex = eBpReverse;
977                }
978
979                index += deltaIndex;
980        }
981
982        return index;
983}
984
985/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
986+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
987+                       startIndex = index in stream to begin search
988+                       startCode = start code to search for
989+                       dir = direction to search
990+ OUTPUTS:      None.
991+ RETURNS:      SCT index matching this start code
992+ FUNCTION: This function searches the SCT table for a matching start code.
993+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
994long tsplayer_findStartCodeIndex( sTsPlayer * const p_tsp, long startIndex, unsigned char startCode, eBpDirectionParam dir )
995{
996        sIndexEntry             *p_sct;
997        char                    startCodeFound = 0;
998
999        while( !startCodeFound )
1000        {
1001                if( !(p_sct = tsplayer_readSct( p_tsp, startIndex )) )
1002                {
1003                        BRCM_DBG_WRN(("Unable to find SCT entry %ld (4)", p_tsp->curSctIndex));
1004                        return -1;
1005                }
1006
1007                if( startCode == SC_ANY_NON_PTS && returnStartCode(p_sct->startCodeBytes) != SC_PTS )
1008                {
1009                        startCodeFound = 1;
1010                }
1011                else if( startCode == SC_PES )
1012                {
1013                        if( returnStartCode(p_sct->startCodeBytes) >= SC_PES_START
1014                                && returnStartCode(p_sct->startCodeBytes) <= SC_PES_END )
1015                        {
1016                                startCodeFound = 1;
1017                        }
1018                        else
1019                        {
1020                                startIndex += dir;
1021                        }
1022                }
1023                else if( returnStartCode(p_sct->startCodeBytes) == startCode )
1024                {
1025                        startCodeFound = 1;
1026                }
1027                else
1028                {
1029                        startIndex += dir;
1030                }
1031        }
1032
1033        return startIndex;
1034}
1035
1036/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1037+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
1038+                       startIndex = index in stream to begin search
1039+                       picCode = picture code to search for
1040+                       dir = direction to search
1041+ OUTPUTS:      None.
1042+ RETURNS:      SCT index matching this picture code
1043+ FUNCTION: This function searches the SCT table for a matching picture code.
1044+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
1045long tsplayer_findPictureIndex( sTsPlayer * const p_tsp, long startIndex, unsigned char picCode, eBpDirectionParam dir )
1046{
1047        sIndexEntry *p_sct = 0;
1048        char            pictureFound = 0;
1049        unsigned char   foundPictureCode;
1050
1051        /* Always start at the previous index when searching backwards */
1052        if( dir == eBpReverse )
1053        {
1054                startIndex -= 1;
1055        }
1056
1057        while( !pictureFound )
1058        {
1059                startIndex = tsplayer_findStartCodeIndex( p_tsp, startIndex, SC_PICTURE, dir );
1060                if( startIndex == -1 )
1061                {
1062                        BRCM_DBG_WRN(("Unable to find Picture!"));
1063                        return -1;
1064                }
1065
1066                p_sct = tsplayer_readSct( p_tsp, startIndex );
1067                if (!p_sct)
1068                        return -1;
1069
1070                foundPictureCode = returnPictureCode(p_sct->startCodeBytes);
1071
1072                /* Make sure this is a valid picture code type! */
1073                if( (foundPictureCode != PC_I_FRAME)
1074                        && (foundPictureCode != PC_B_FRAME)
1075                        && (foundPictureCode != PC_P_FRAME) )
1076                {
1077                        BRCM_DBG_WRN(("Found invalid picture code: 0x%0X in SC Num: %ld", foundPictureCode, startIndex));
1078                }
1079                else if( picCode == PC_ANY_FRAME || foundPictureCode == picCode )
1080                {
1081                        pictureFound = 1;
1082                        continue;
1083                }
1084                else
1085                {
1086                        BRCM_DBG_MSG(("Looking for picCode %d.  Found frame type %d.", picCode, foundPictureCode ));
1087                }
1088                startIndex += dir;
1089        }
1090        return startIndex;
1091}
1092
1093/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1094+ INPUTS:       p_sct = pointer to sct entry
1095+ OUTPUTS:      None.
1096+ RETURNS:      pts value (bits [32:1]
1097+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
1098unsigned long tsplayer_returnPts( sIndexEntry *p_sct )
1099{
1100        unsigned long pts;
1101       
1102        if( p_sct )
1103        {
1104                /* Calculate PTS[32:1] */
1105                pts = p_sct->startCodeBytes & 0x1;
1106                pts = (pts << 31) + (p_sct->recordByteCount >> 1);
1107        }
1108        else
1109        {
1110                pts = 0;
1111        }
1112
1113        return pts;
1114}
1115
1116/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1117+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
1118+ OUTPUTS:      *pFirstIndex - first index
1119*                       *pLastIndex - last index
1120+ RETURNS:      0 - no errors detected
1121*                       -1 - error detected
1122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
1123int
1124tsplayer_getIndexLimits(sTsPlayer * const p_tsp, long *pFirstIndex, long *pLastIndex)
1125{
1126        long firstByteOffset, lastByteOffset;
1127        int rc;
1128
1129        if (p_tsp->statCb) { /* if stat cb defined operation is straight forward */
1130                rc = p_tsp->statCb(p_tsp->sctFp, &firstByteOffset, &lastByteOffset);
1131                if (rc<0) {
1132                        goto error;
1133                }
1134        } else {
1135                /* load only required offsets */
1136                if (pFirstIndex) {
1137                        rc = p_tsp->seekCb( p_tsp->sctFp, 0, SEEK_SET );
1138                        if (rc) {
1139                                goto error;
1140                        }
1141                        firstByteOffset = p_tsp->tellCb( p_tsp->sctFp );
1142                        if  (firstByteOffset<0) {
1143                                goto error;
1144                        }
1145                }
1146                if (pLastIndex) {
1147                        /* Jump to end of the file */
1148                        rc = p_tsp->seekCb( p_tsp->sctFp, 0, SEEK_END );
1149                        if (rc) {
1150                                goto error;
1151                        }
1152                        lastByteOffset = p_tsp->tellCb( p_tsp->sctFp );
1153                        lastByteOffset++;
1154                }
1155        }
1156        if (pFirstIndex) {
1157                *pFirstIndex = (firstByteOffset + sizeof(sIndexEntry)-1)/sizeof(sIndexEntry);
1158        }
1159        if (pLastIndex) {
1160                *pLastIndex = ((lastByteOffset)/sizeof(sIndexEntry)) - 1;
1161        }
1162        return 0;
1163error:
1164        if (pFirstIndex) {
1165                *pFirstIndex = -1;
1166        }
1167        if (pLastIndex) {
1168                *pLastIndex = -1;
1169        }
1170        return -1;
1171}
1172
1173
1174void tsplayer_verifyIndexWithStream( sTsPlayer * const p_tsp, void *streamFp, int streamIsLE )
1175{
1176        p_tsp->streamFp = streamFp;
1177        p_tsp->streamIsLE = streamIsLE;
1178
1179        if( p_tsp->streamIsLE )
1180        {
1181                BRCM_DBG_ERR(("tsp_verifyIndexWithStream(): This stream will not be checked due to LE format!"));
1182        }
1183}
1184
1185/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1186+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
1187+                       p_sct = index entry to verify
1188+ OUTPUTS:      None.
1189+ RETURNS:      0 (no error), -1 (error)
1190+ FUNCTION: This function verifies that the start code
1191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
1192char tsplayer_verifyIndexEntry( sTsPlayer * const p_tsp, sIndexEntry *p_sct )
1193{
1194        if (!p_sct || !p_tsp)
1195                return -1;
1196
1197        /* Check stream to make sure the start code matches the stream */
1198        if( p_tsp->streamFp && p_sct->recordByteCountHi == 0 )
1199        {
1200                unsigned char data[4];
1201                unsigned long startCode;
1202
1203                if( p_tsp->streamIsLE )
1204                {
1205                        /* Verifing a LE stream is a pain, as lots of byte swapping and alignment checking is needed.
1206                         * Since only old streams will be LE, don't bother to check streams of this format...
1207                         */
1208                        return 0;
1209                }
1210
1211                if( (p_sct->startCodeBytes&0xFF) > 184 )
1212                {
1213                        BRCM_DBG_WRN(("tsp_verifyIndexEntry(): Start code spans transport packet! Unable to verify."));
1214                        return 0;
1215                }
1216
1217                if ((*p_tsp->seekCb)( p_tsp->streamFp, p_sct->recordByteCount+(p_sct->startCodeBytes&0xFF), SEEK_SET ))
1218                        return 0;
1219                if( (*p_tsp->readCb)( data, 1, 4, p_tsp->streamFp ) != 4 )
1220                {
1221                        BRCM_DBG_ERR(("tsp_verifyIndexEntry(): Unable to read at stream offset: %ld!", p_sct->recordByteCount+(p_sct->startCodeBytes&0xFF)));
1222                        return 0;
1223                }
1224
1225                /* Do this in an endian neutral fashion */
1226                startCode = data[0];
1227                startCode <<= 8;
1228                startCode |= data[1];
1229                startCode <<= 8;
1230                startCode |= data[2];
1231                startCode <<= 8;
1232                startCode |= data[3];
1233
1234                if( startCode != (0x00000100 | ((p_sct->startCodeBytes >> 24) & 0xFF)) )
1235                {
1236                        BRCM_DBG_ERR(("tsp_verifyIndexEntry(): Start code mismatch at stream offset: %ld!", p_sct->recordByteCount+(p_sct->startCodeBytes&0xFF)));
1237                        BRCM_DBG_ERR(("tsp_verifyIndexEntry(): Expecting 0x%08lX, Got 0x%08lX", (0x00000100 | ((p_sct->startCodeBytes >> 24) & 0xFF)), startCode));
1238                        return -1;
1239                }
1240        }
1241        return 0;
1242}
1243
1244
1245/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1246+ INPUTS:       p_tsp = pointer to a previously allocated sTsPlayer structure
1247+                       statCb = pointer to stat function
1248+ OUTPUTS:      None.
1249+ RETURNS:      0 (no error), -1 (error)
1250+ FUNCTION: This function verifies that the start code
1251+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
1252int 
1253tsplayer_set_statcb( sTsPlayer *p_tsp, BP_STAT_CB statCb)
1254{
1255        p_tsp->statCb = statCb;
1256        return 0;
1257}
1258
Note: See TracBrowser for help on using the repository browser.