Coverage Report

Created: 2023-06-07 07:26

/src/vlc/modules/demux/ty.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * ty.c - TiVo ty stream video demuxer for VLC
3
 *****************************************************************************
4
 * Copyright (C) 2005 VLC authors and VideoLAN
5
 * Copyright (C) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005
6
 * based on code by Christopher Wingert for tivo-mplayer
7
 * tivo(at)wingert.org, February 2003
8
 *
9
 *
10
 * This program is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation; either version 2.1 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this program; if not, write to the Free Software Foundation,
22
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *
24
 * CODE CHANGES:
25
 * v1.0.0 - 24-Feb-2005 - Initial release - Series 1 support ONLY!
26
 * v1.0.1 - 25-Feb-2005 - Added fix for bad GOP headers - Neal
27
 * v1.0.2 - 26-Feb-2005 - No longer require "seekable" input stream - Neal
28
 * v2.0.0 - 21-Mar-2005 - Series 2 support!  No AC-3 on S2 DTivo yet.
29
 * v2.1.0 - 22-Mar-2005 - Support for AC-3 on S2 DTivo (long ac3 packets)
30
 * v3.0.0 - 14-Jul-2005 - Support for skipping fwd/back via VLC hotkeys
31
 *****************************************************************************/
32
33
/*****************************************************************************
34
 * Preamble
35
 *****************************************************************************/
36
37
#ifdef HAVE_CONFIG_H
38
# include "config.h"
39
#endif
40
41
#include <limits.h>
42
43
#include <vlc_common.h>
44
#include <vlc_plugin.h>
45
#include <vlc_demux.h>
46
#include <vlc_codec.h>
47
#include <vlc_meta.h>
48
#include <vlc_input_item.h>
49
#include "../codec/cc.h"
50
51
#include "mpeg/pes.h"
52
53
#include <assert.h>
54
55
/*****************************************************************************
56
 * Module descriptor
57
 *****************************************************************************/
58
static int  Open ( vlc_object_t * );
59
static void Close( vlc_object_t * );
60
61
4
vlc_module_begin ()
62
2
    set_shortname( N_("TY") )
63
2
    set_description(N_("TY Stream audio/video demux"))
64
2
    set_subcategory( SUBCAT_INPUT_DEMUX )
65
2
    set_capability("demux", 6)
66
    /* FIXME: there seems to be a segfault when using PVR access
67
     * and TY demux has a bigger priority than PS
68
     * Something must be wrong.
69
     */
70
4
    set_callbacks( Open, Close )
71
2
    add_shortcut("ty", "tivo")
72
2
    add_file_extension("ty")
73
2
    add_file_extension("ty+")
74
2
vlc_module_end ()
75
76
/*****************************************************************************
77
 * Local prototypes
78
 *****************************************************************************/
79
static int Demux  ( demux_t * );
80
static int Control( demux_t *, int, va_list );
81
82
16
#define SERIES1_PES_LENGTH  (11)    /* length of audio PES hdr on S1 */
83
15
#define SERIES2_PES_LENGTH  (16)    /* length of audio PES hdr on S2 */
84
26
#define AC3_PES_LENGTH      (14)    /* length of audio PES hdr for AC3 */
85
0
#define VIDEO_PES_LENGTH    (16)    /* length of video PES header */
86
0
#define DTIVO_PTS_OFFSET    (6)     /* offs into PES for MPEG PTS on DTivo */
87
0
#define SA_PTS_OFFSET       (9)     /* offset into PES for MPEG PTS on SA */
88
26
#define AC3_PTS_OFFSET      (9)     /* offset into PES for AC3 PTS on DTivo */
89
0
#define VIDEO_PTS_OFFSET    (9)     /* offset into PES for video PTS on all */
90
0
#define AC3_PKT_LENGTH      (1536)  /* size of TiVo AC3 pkts (w/o PES hdr) */
91
static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
92
static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
93
static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
94
95
137
#define CHUNK_PEEK_COUNT    (3)         /* number of chunks to probe */
96
97
/* packet types for reference:
98
 2/c0: audio data continued
99
 3/c0: audio packet header (PES header)
100
 4/c0: audio data (S/A only?)
101
 9/c0: audio packet header, AC-3 audio
102
 2/e0: video data continued
103
 6/e0: video packet header (PES header)
104
 7/e0: video sequence header start
105
 8/e0: video I-frame header start
106
 a/e0: video P-frame header start
107
 b/e0: video B-frame header start
108
 c/e0: video GOP header start
109
 e/01: closed-caption data
110
 e/02: Extended data services data
111
 e/03: ipreview data ("thumbs up to record" signal)
112
 e/05: UK Teletext
113
*/
114
115
252
#define TIVO_PES_FILEID   ( 0xf5467abd )
116
0
#define TIVO_PART_LENGTH  ( 0x20000000 )    /* 536,870,912 bytes */
117
180
#define CHUNK_SIZE        ( 128 * 1024 )
118
119
typedef struct
120
{
121
  long l_rec_size;
122
  uint8_t ex[2];
123
  uint8_t rec_type;
124
  uint8_t subrec_type;
125
  bool b_ext;
126
  uint64_t l_ty_pts;            /* TY PTS in the record header */
127
} ty_rec_hdr_t;
128
129
typedef struct
130
{
131
    uint64_t l_timestamp;
132
    uint8_t chunk_bitmask[8];
133
} ty_seq_table_t;
134
135
typedef enum
136
{
137
    TIVO_TYPE_UNKNOWN,
138
    TIVO_TYPE_SA,
139
    TIVO_TYPE_DTIVO
140
} tivo_type_t;
141
142
typedef enum
143
{
144
    TIVO_SERIES_UNKNOWN,
145
    TIVO_SERIES1,
146
    TIVO_SERIES2
147
} tivo_series_t;
148
149
typedef enum
150
{
151
    TIVO_AUDIO_UNKNOWN,
152
    TIVO_AUDIO_AC3,
153
    TIVO_AUDIO_MPEG
154
} tivo_audio_t;
155
156
0
#define XDS_MAX_DATA_SIZE (32)
157
typedef enum
158
{
159
    XDS_CLASS_CURRENT        = 0,
160
    XDS_CLASS_FUTURE         = 1,
161
    XDS_CLASS_CHANNEL        = 2,
162
    XDS_CLASS_MISCELLANEOUS  = 3,
163
    XDS_CLASS_PUBLIC_SERVICE = 4,
164
    XDS_CLASS_RESERVED       = 5,
165
    XDS_CLASS_UNDEFINED      = 6,
166
    XDS_CLASS_OTHER          = 7,
167
168
    XDS_MAX_CLASS_COUNT
169
} xds_class_t;
170
typedef struct
171
{
172
    bool b_started;
173
    size_t     i_data;
174
    uint8_t    p_data[XDS_MAX_DATA_SIZE];
175
    int        i_sum;
176
} xds_packet_t;
177
typedef enum
178
{
179
    XDS_META_PROGRAM_RATING_NONE,
180
    XDS_META_PROGRAM_RATING_MPAA,
181
    XDS_META_PROGRAM_RATING_TPG,
182
    /* TODO add CA/CE rating */
183
} xds_meta_program_rating_t;
184
typedef struct
185
{
186
    char *psz_name;
187
    xds_meta_program_rating_t rating;
188
    char *psz_rating;
189
    /* Add the other fields once I have the samples */
190
} xds_meta_program_t;
191
typedef struct
192
{
193
    char *psz_channel_name;
194
    char *psz_channel_call_letter;
195
    char *psz_channel_number;
196
197
    xds_meta_program_t  current;
198
    xds_meta_program_t  future;
199
} xds_meta_t;
200
typedef struct
201
{
202
    /* Are we in XDS mode */
203
    bool b_xds;
204
205
    /* Current class type */
206
    xds_class_t i_class;
207
    int         i_type;
208
    bool  b_future;
209
210
    /* */
211
    xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
212
213
    /* */
214
    bool  b_meta_changed;
215
    xds_meta_t  meta;
216
217
} xds_t;
218
219
typedef struct
220
{
221
  es_out_id_t *p_video;               /* ptr to video codec */
222
  es_out_id_t *p_audio;               /* holds either ac3 or mpeg codec ptr */
223
224
  cc_data_t   cc;
225
  es_out_id_t *p_cc[4];
226
227
  xds_t       xds;
228
229
  int             i_cur_chunk;
230
  int             i_stuff_cnt;
231
  size_t          i_stream_size;      /* size of input stream (if known) */
232
  //uint64_t        l_program_len;      /* length of this stream in msec */
233
  bool      b_seekable;         /* is this stream seekable? */
234
  bool      b_have_master;      /* are master chunks present? */
235
  tivo_type_t     tivo_type;          /* tivo type (SA / DTiVo) */
236
  tivo_series_t   tivo_series;        /* Series1 or Series2 */
237
  tivo_audio_t    audio_type;         /* AC3 or MPEG */
238
  int             i_Pes_Length;       /* Length of Audio PES header */
239
  int             i_Pts_Offset;       /* offset into audio PES of PTS */
240
  uint8_t         pes_buffer[20];     /* holds incomplete pes headers */
241
  int             i_pes_buf_cnt;      /* how many bytes in our buffer */
242
  size_t          l_ac3_pkt_size;     /* len of ac3 pkt we've seen so far */
243
  uint64_t        l_last_ty_pts;      /* last TY timestamp we've seen */
244
  //vlc_tick_t      l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
245
  uint64_t        l_first_ty_pts;     /* first TY PTS in this master chunk */
246
  uint64_t        l_final_ty_pts;     /* final TY PTS in this master chunk */
247
  unsigned        i_seq_table_size;   /* number of entries in SEQ table */
248
  unsigned        i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
249
250
  vlc_tick_t      lastAudioPTS;
251
  vlc_tick_t      lastVideoPTS;
252
253
  ty_rec_hdr_t    *rec_hdrs;          /* record headers array */
254
  int             i_cur_rec;          /* current record in this chunk */
255
  int             i_num_recs;         /* number of recs in this chunk */
256
  int             i_seq_rec;          /* record number where seq start is */
257
  ty_seq_table_t  *seq_table;         /* table of SEQ entries from mstr chk */
258
  bool      eof;
259
  bool      b_first_chunk;
260
} demux_sys_t;
261
262
static int get_chunk_header(demux_t *);
263
static vlc_tick_t get_pts( const uint8_t *buf );
264
static int find_es_header( const uint8_t *header,
265
                           const uint8_t *buffer, int i_search_len );
266
static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
267
static int ty_stream_seek_time(demux_t *, uint64_t);
268
269
static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
270
                                          int i_num_recs, int *pi_payload_size);
271
static int probe_stream(demux_t *p_demux);
272
static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
273
static int  parse_master(demux_t *p_demux);
274
275
static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
276
static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
277
static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
278
279
static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
280
281
static void XdsInit( xds_t * );
282
static void XdsExit( xds_t * );
283
284
50
#define TY_ES_GROUP (1)
285
286
/*
287
 * Open: check file and initialize demux structures
288
 *
289
 * here's what we do:
290
 * 1. peek at the first 12 bytes of the stream for the
291
 *    magic TiVo PART header & stream type & chunk size
292
 * 2. if it's not there, error with VLC_EGENERIC
293
 * 3. set up video (mpgv) codec
294
 * 4. return VLC_SUCCESS
295
 */
296
static int Open(vlc_object_t *p_this)
297
39
{
298
39
    demux_t *p_demux = (demux_t *)p_this;
299
39
    demux_sys_t *p_sys;
300
39
    es_format_t fmt;
301
39
    const uint8_t *p_peek;
302
39
    int i;
303
304
    /* peek at the first 12 bytes. */
305
    /* for TY streams, they're always the same */
306
39
    if( vlc_stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
307
0
        return VLC_EGENERIC;
308
309
39
    if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
310
39
         U32_AT(&p_peek[4]) != 0x02 ||
311
39
         U32_AT(&p_peek[8]) != CHUNK_SIZE )
312
11
    {
313
11
        if( !p_demux->obj.force )
314
11
            return VLC_EGENERIC;
315
0
        msg_Warn( p_demux, "this does not look like a TY file, "
316
0
                           "continuing anyway..." );
317
0
    }
318
319
    /* at this point, we assume we have a valid TY stream */
320
28
    msg_Dbg( p_demux, "valid TY stream detected" );
321
322
28
    p_sys = malloc(sizeof(demux_sys_t));
323
28
    if( unlikely(p_sys == NULL) )
324
0
        return VLC_ENOMEM;
325
326
    /* Set exported functions */
327
28
    p_demux->pf_demux = Demux;
328
28
    p_demux->pf_control = Control;
329
330
    /* create our structure that will hold all data */
331
28
    p_demux->p_sys = p_sys;
332
28
    memset(p_sys, 0, sizeof(demux_sys_t));
333
334
    /* set up our struct (most were zero'd out with the memset above) */
335
28
    p_sys->b_first_chunk = true;
336
28
    p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
337
28
    p_sys->lastAudioPTS  = VLC_TICK_INVALID;
338
28
    p_sys->lastVideoPTS  = VLC_TICK_INVALID;
339
28
    p_sys->i_stream_size = stream_Size(p_demux->s);
340
28
    p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
341
28
    p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
342
28
    p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
343
28
    p_sys->i_Pes_Length = 0;
344
28
    p_sys->i_Pts_Offset = 0;
345
28
    p_sys->l_ac3_pkt_size = 0;
346
347
    /* see if this stream is seekable */
348
28
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
349
350
28
    if (probe_stream(p_demux) != VLC_SUCCESS) {
351
        //TyClose(p_demux);
352
3
        return VLC_EGENERIC;
353
3
    }
354
355
25
    if (!p_sys->b_have_master)
356
25
      msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
357
358
    /* register the proper audio codec */
359
25
    if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
360
0
        es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA );
361
25
    } else {
362
25
        es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_A52 );
363
25
    }
364
25
    fmt.i_group = TY_ES_GROUP;
365
25
    p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
366
367
    /* register the video stream */
368
25
    es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV );
369
25
    fmt.i_group = TY_ES_GROUP;
370
25
    p_sys->p_video = es_out_Add( p_demux->out, &fmt );
371
372
    /* */
373
125
    for( i = 0; i < 4; i++ )
374
100
        p_sys->p_cc[i] = NULL;
375
25
    cc_Init( &p_sys->cc );
376
377
25
    XdsInit( &p_sys->xds );
378
379
25
    return VLC_SUCCESS;
380
28
}
381
382
/* =========================================================================== */
383
/* Demux: Read & Demux one record from the chunk
384
 *
385
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
386
 *
387
 * NOTE: I think we can return the number of packets sent instead of just 1.
388
 * that means we can demux an entire chunk and shoot it back (may be more efficient)
389
 * -- should try that some day :) --
390
 */
391
static int Demux( demux_t *p_demux )
392
1.32k
{
393
1.32k
    demux_sys_t  *p_sys = p_demux->p_sys;
394
1.32k
    ty_rec_hdr_t *p_rec;
395
1.32k
    block_t      *p_block_in = NULL;
396
397
    /*msg_Dbg(p_demux, "ty demux processing" );*/
398
399
    /* did we hit EOF earlier? */
400
1.32k
    if( p_sys->eof )
401
0
        return VLC_DEMUXER_EOF;
402
403
    /*
404
     * what we do (1 record now.. maybe more later):
405
    * - use vlc_stream_Read() to read the chunk header & record headers
406
    * - discard entire chunk if it is a PART header chunk
407
    * - parse all the headers into record header array
408
    * - keep a pointer of which record we're on
409
    * - use vlc_stream_Block() to fetch each record
410
    * - parse out PTS from PES headers
411
    * - set PTS for data packets
412
    * - pass the data on to the proper codec via es_out_Send()
413
414
    * if this is the first time or
415
    * if we're at the end of this chunk, start a new one
416
    */
417
    /* parse the next chunk's record headers */
418
1.32k
    if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
419
53
    {
420
53
        if( get_chunk_header(p_demux) == 0 || p_sys->i_num_recs == 0 )
421
14
            return VLC_DEMUXER_EOF;
422
53
    }
423
424
    /*======================================================================
425
     * parse & send one record of the chunk
426
     *====================================================================== */
427
1.31k
    p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
428
429
1.31k
    if( !p_rec->b_ext )
430
1.21k
    {
431
1.21k
        const long l_rec_size = p_rec->l_rec_size;
432
        /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
433
                    subrec_type, p_rec->rec_type, l_rec_size );*/
434
435
        /* some normal records are 0 length, so check for that... */
436
1.21k
        if( l_rec_size <= 0 )
437
1.13k
        {
438
            /* no data in payload; we're done */
439
1.13k
            p_sys->i_cur_rec++;
440
1.13k
            return VLC_DEMUXER_SUCCESS;
441
1.13k
        }
442
443
        /* read in this record's payload */
444
77
        if( !( p_block_in = vlc_stream_Block( p_demux->s, l_rec_size ) ) )
445
11
            return VLC_DEMUXER_EOF;
446
447
        /* set these as 'unknown' for now */
448
66
        p_block_in->i_pts =
449
66
        p_block_in->i_dts = VLC_TICK_INVALID;
450
66
    }
451
    /*else
452
    {
453
        -- don't read any data from the stream, data was in the record header --
454
        msg_Dbg(p_demux,
455
               "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
456
                p_rec->rec_type, p_rec->ex1, p_rec->ex2);
457
    }*/
458
459
165
    switch( p_rec->rec_type )
460
165
    {
461
14
        case 0xe0: /* video */
462
14
            DemuxRecVideo( p_demux, p_rec, p_block_in );
463
14
            break;
464
465
6
        case 0xc0: /* audio */
466
6
            DemuxRecAudio( p_demux, p_rec, p_block_in );
467
6
            break;
468
469
4
        case 0x01:
470
8
        case 0x02:
471
            /* closed captions/XDS */
472
8
            DemuxRecCc( p_demux, p_rec, p_block_in );
473
8
            break;
474
475
136
        default:
476
136
            msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
477
            /* fall-through */
478
479
137
        case 0x03: /* tivo data services */
480
137
        case 0x05: /* unknown, but seen regularly */
481
137
            if( p_block_in )
482
49
                block_Release( p_block_in );
483
165
    }
484
485
    /* */
486
165
    p_sys->i_cur_rec++;
487
165
    return VLC_DEMUXER_SUCCESS;
488
165
}
489
490
/* Control */
491
static int Control(demux_t *p_demux, int i_query, va_list args)
492
0
{
493
0
    demux_sys_t *p_sys = p_demux->p_sys;
494
0
    double f, *pf;
495
0
    int64_t i64;
496
497
    /*msg_Info(p_demux, "control cmd %d", i_query);*/
498
0
    switch( i_query )
499
0
    {
500
0
    case DEMUX_CAN_SEEK:
501
0
        *va_arg( args, bool * ) = p_sys->b_seekable;
502
0
        return VLC_SUCCESS;
503
504
0
    case DEMUX_GET_POSITION:
505
        /* arg is 0.0 - 1.0 percent of overall file position */
506
0
        if( ( i64 = p_sys->i_stream_size ) > 0 )
507
0
        {
508
0
            pf = va_arg( args, double* );
509
0
            *pf = ((double)1.0) * vlc_stream_Tell( p_demux->s ) / (double) i64;
510
0
            return VLC_SUCCESS;
511
0
        }
512
0
        return VLC_EGENERIC;
513
514
0
    case DEMUX_SET_POSITION:
515
        /* arg is 0.0 - 1.0 percent of overall file position */
516
0
        f = va_arg( args, double );
517
        /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
518
0
        if ((i64 = p_sys->i_stream_size) > 0)
519
0
            return ty_stream_seek_pct(p_demux, f);
520
0
        return VLC_EGENERIC;
521
0
    case DEMUX_GET_TIME:
522
        /* return TiVo timestamp */
523
        //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
524
        //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
525
        //    p_sys->l_last_ty_pts_sync);
526
0
        *va_arg(args, vlc_tick_t *) = VLC_TICK_FROM_NS(p_sys->l_last_ty_pts);
527
0
        return VLC_SUCCESS;
528
0
    case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
529
        /* size / bitrate */
530
0
        *va_arg(args, vlc_tick_t *) = 0;
531
0
        return VLC_SUCCESS;
532
0
    case DEMUX_SET_TIME:      /* arg is time in microsecs */
533
0
        return ty_stream_seek_time(p_demux,
534
0
                                   NS_FROM_VLC_TICK(va_arg( args, vlc_tick_t )));
535
0
    case DEMUX_CAN_PAUSE:
536
0
    case DEMUX_SET_PAUSE_STATE:
537
0
    case DEMUX_CAN_CONTROL_PACE:
538
0
    case DEMUX_GET_PTS_DELAY:
539
0
        return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
540
0
    case DEMUX_GET_FPS:
541
0
    default:
542
0
        return VLC_EGENERIC;
543
0
    }
544
0
}
545
546
/* Close */
547
static void Close( vlc_object_t *p_this )
548
25
{
549
25
    demux_t *p_demux = (demux_t*)p_this;
550
25
    demux_sys_t *p_sys = p_demux->p_sys;
551
552
25
    XdsExit( &p_sys->xds );
553
25
    cc_Exit( &p_sys->cc );
554
25
    free( p_sys->rec_hdrs );
555
25
    free( p_sys->seq_table );
556
25
    free(p_sys);
557
25
}
558
559
560
/* =========================================================================== */
561
/* Compute Presentation Time Stamp (PTS)
562
 * Assume buf points to beginning of PTS */
563
static vlc_tick_t get_pts( const uint8_t *buf )
564
5
{
565
5
    stime_t i_pts = GetPESTimestamp( buf );
566
5
    return FROM_SCALE_NZ(i_pts); /* convert PTS (90Khz clock) to microseconds */
567
5
}
568
569
570
/* =========================================================================== */
571
static int find_es_header( const uint8_t *header,
572
                           const uint8_t *buffer, int i_search_len )
573
11
{
574
11
    int count;
575
576
51
    for( count = 0; count < i_search_len; count++ )
577
45
    {
578
45
        if( !memcmp( &buffer[count], header, 4 ) )
579
5
            return count;
580
45
    }
581
6
    return -1;
582
11
}
583
584
585
/* =========================================================================== */
586
/* check if we have a full PES header, if not, then save what we have.
587
 * this is called when audio-start packets are encountered.
588
 * Returns:
589
 *     1 partial PES hdr found, some audio data found (buffer adjusted),
590
 *    -1 partial PES hdr found, no audio data found
591
 *     0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
592
/* TODO: HD support -- nothing known about those streams */
593
static int check_sync_pes( demux_t *p_demux, block_t *p_block,
594
                           int32_t offset, int32_t rec_len )
595
6
{
596
6
    demux_sys_t *p_sys = p_demux->p_sys;
597
598
6
    if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
599
1
    {
600
        /* entire PES header not present */
601
1
        msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
602
1
                 offset );
603
        /* save the partial pes header */
604
1
        if( offset < 0 )
605
1
        {
606
            /* no header found, fake some 00's (this works, believe me) */
607
1
            memset( p_sys->pes_buffer, 0, 4 );
608
1
            p_sys->i_pes_buf_cnt = 4;
609
1
            if( rec_len > 4 )
610
1
                msg_Err( p_demux, "PES header not found in record of %d bytes!",
611
1
                         rec_len );
612
1
            return -1;
613
1
        }
614
        /* copy the partial pes header we found */
615
0
        memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
616
0
                rec_len - offset );
617
0
        p_sys->i_pes_buf_cnt = rec_len - offset;
618
619
0
        if( offset > 0 )
620
0
        {
621
            /* PES Header was found, but not complete, so trim the end of this record */
622
0
            p_block->i_buffer -= rec_len - offset;
623
0
            return 1;
624
0
        }
625
0
        return -1;    /* partial PES, no audio data */
626
0
    }
627
    /* full PES header present, extract PTS */
628
5
    p_sys->lastAudioPTS = VLC_TICK_0 + get_pts( &p_block->p_buffer[ offset +
629
5
                                   p_sys->i_Pts_Offset ] );
630
5
    p_block->i_pts = p_sys->lastAudioPTS;
631
    /*msg_Dbg(p_demux, "Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
632
    /* adjust audio record to remove PES header */
633
5
    memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
634
5
            p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
635
5
    p_block->i_buffer -= p_sys->i_Pes_Length;
636
#if 0
637
    msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
638
             "%02x %02x %02x %02x %02x %02x %02x %02x "
639
             "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
640
             p_block->p_buffer[0], p_block->p_buffer[1],
641
             p_block->p_buffer[2], p_block->p_buffer[3],
642
             p_block->p_buffer[4], p_block->p_buffer[5],
643
             p_block->p_buffer[6], p_block->p_buffer[7],
644
             p_block->p_buffer[8], p_block->p_buffer[9],
645
             p_block->p_buffer[10], p_block->p_buffer[11],
646
             p_block->p_buffer[12], p_block->p_buffer[13],
647
             p_block->p_buffer[14], p_block->p_buffer[15]);
648
#endif
649
5
    return 0;
650
6
}
651
652
static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
653
14
{
654
14
    demux_sys_t *p_sys = p_demux->p_sys;
655
14
    const int subrec_type = rec_hdr->subrec_type;
656
14
    const long l_rec_size = rec_hdr->l_rec_size;    // p_block_in->i_buffer might be better
657
14
    int esOffset1;
658
14
    int i;
659
660
14
    assert( rec_hdr->rec_type == 0xe0 );
661
14
    if( !p_block_in )
662
11
        return -1;
663
664
#if 0
665
    msg_Dbg(p_demux, "packet buffer has "
666
            "%02x %02x %02x %02x %02x %02x %02x %02x "
667
            "%02x %02x %02x %02x %02x %02x %02x %02x",
668
            p_block_in->p_buffer[0], p_block_in->p_buffer[1],
669
            p_block_in->p_buffer[2], p_block_in->p_buffer[3],
670
            p_block_in->p_buffer[4], p_block_in->p_buffer[5],
671
            p_block_in->p_buffer[6], p_block_in->p_buffer[7],
672
            p_block_in->p_buffer[8], p_block_in->p_buffer[9],
673
            p_block_in->p_buffer[10], p_block_in->p_buffer[11],
674
            p_block_in->p_buffer[12], p_block_in->p_buffer[13],
675
            p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
676
#endif
677
    //if( subrec_type == 0x06 || subrec_type == 0x07 )
678
3
    if( subrec_type != 0x02 && subrec_type != 0x0c &&
679
3
        subrec_type != 0x08 && l_rec_size > 4 )
680
3
    {
681
        /* get the PTS from this packet if it has one.
682
         * on S1, only 0x06 has PES.  On S2, however, most all do.
683
         * Do NOT Pass the PES Header to the MPEG2 codec */
684
3
        esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
685
3
        if( esOffset1 != -1 )
686
0
        {
687
            //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
688
                //subrec_type, esOffset1);
689
0
            p_sys->lastVideoPTS = VLC_TICK_0 + get_pts(
690
0
                    &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
691
            /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
692
                        p_sys->lastVideoPTS );*/
693
0
            if (subrec_type != 0x06) {
694
                /* if we found a PES, and it's not type 6, then we're S2 */
695
                /* The packet will have video data (& other headers) so we
696
                 * chop out the PES header and send the rest */
697
0
                if (l_rec_size >= VIDEO_PES_LENGTH) {
698
0
                    p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
699
0
                    p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
700
0
                } else {
701
0
                    msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
702
0
                        " (%ld bytes)", subrec_type, l_rec_size);
703
                    /* nuke this block; it's too short, but has PES marker */
704
0
                    p_block_in->i_buffer = 0;
705
0
                }
706
0
            }
707
0
        }/* else
708
            msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
709
                subrec_type); */
710
3
    }
711
712
3
    if(subrec_type == 0x06 )
713
0
    {
714
        /* type 6 (S1 DTivo) has no data, so we're done */
715
0
        block_Release(p_block_in);
716
0
        return 0;
717
0
    }
718
719
    /* if it's not a continue blk, then set PTS */
720
3
    if( subrec_type != 0x02 )
721
3
    {
722
        /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
723
                   subrec_type);*/
724
        /* if it's a GOP header, make sure it's legal
725
         * (if we have enough data) */
726
        /* Some ty files don't have this bit set
727
         * and it causes problems */
728
3
        if (subrec_type == 0x0c && l_rec_size >= 6)
729
0
            p_block_in->p_buffer[5] |= 0x08;
730
        /* store the TY PTS if there is one */
731
3
        if (subrec_type == 0x07) {
732
0
            p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
733
            /* should we use audio or video PTS? */
734
            //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
735
3
        } else {
736
            /* yes I know this is a cheap hack.  It's the timestamp
737
               used for display and skipping fwd/back, so it
738
               doesn't have to be accurate to the millisecond.
739
               I adjust it here by roughly one 1/30 sec.  Yes it
740
               will be slightly off for UK streams, but it's OK.
741
             */
742
3
            p_sys->l_last_ty_pts += 35000000;
743
            //p_sys->l_last_ty_pts += 33366667;
744
3
        }
745
        /* set PTS for this block before we send */
746
3
        if (p_sys->lastVideoPTS != VLC_TICK_INVALID)
747
0
        {
748
0
            p_block_in->i_pts = p_sys->lastVideoPTS;
749
            /* PTS gets used ONCE.
750
             * Any subsequent frames we get BEFORE next PES
751
             * header will have their PTS computed in the codec */
752
0
            p_sys->lastVideoPTS = VLC_TICK_INVALID;
753
0
        }
754
3
    }
755
756
    /* Register the CC decoders when needed */
757
3
    uint64_t i_chans = p_sys->cc.i_608channels;
758
3
    for( i = 0; i_chans > 0; i++, i_chans >>= 1 )
759
0
    {
760
0
        if( (i_chans & 1) == 0 || p_sys->p_cc[i] )
761
0
            continue;
762
763
0
        static const char *ppsz_description[4] = {
764
0
            N_("Closed captions 1"),
765
0
            N_("Closed captions 2"),
766
0
            N_("Closed captions 3"),
767
0
            N_("Closed captions 4"),
768
0
        };
769
770
0
        es_format_t fmt;
771
772
773
0
        es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
774
0
        fmt.subs.cc.i_channel = i;
775
0
        fmt.psz_description = strdup( vlc_gettext(ppsz_description[i]) );
776
0
        fmt.i_group = TY_ES_GROUP;
777
0
        p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
778
0
        es_format_Clean( &fmt );
779
780
0
    }
781
    /* Send the CC data */
782
3
    if( p_block_in->i_pts != VLC_TICK_INVALID && p_sys->cc.i_data > 0 )
783
0
    {
784
0
        for( i = 0; i < 4; i++ )
785
0
        {
786
0
            if( p_sys->p_cc[i] )
787
0
            {
788
0
                block_t *p_cc = block_Alloc( p_sys->cc.i_data );
789
0
                p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
790
0
                p_cc->i_pts = p_block_in->i_pts;
791
0
                memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
792
793
0
                es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
794
0
            }
795
0
        }
796
0
        cc_Flush( &p_sys->cc );
797
0
    }
798
799
    //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
800
            //p_sys->i_cur_rec, subrec_type);
801
3
    es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
802
3
    return 0;
803
3
}
804
static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
805
6
{
806
6
    demux_sys_t *p_sys = p_demux->p_sys;
807
6
    const int subrec_type = rec_hdr->subrec_type;
808
6
    const long l_rec_size = rec_hdr->l_rec_size;
809
6
    int esOffset1;
810
811
6
    assert( rec_hdr->rec_type == 0xc0 );
812
6
    if( !p_block_in )
813
0
        return -1;
814
#if 0
815
        int i;
816
        fprintf( stderr, "Audio Packet Header " );
817
        for( i = 0 ; i < 24 ; i++ )
818
            fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] );
819
        fprintf( stderr, "\n" );
820
#endif
821
822
6
    if( subrec_type == 2 )
823
0
    {
824
        /* SA or DTiVo Audio Data, no PES (continued block)
825
         * ================================================
826
         */
827
828
        /* continue PES if previous was incomplete */
829
0
        if (p_sys->i_pes_buf_cnt > 0)
830
0
        {
831
0
            const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
832
833
0
            msg_Dbg(p_demux, "continuing PES header");
834
            /* do we have enough data to complete? */
835
0
            if (i_need >= l_rec_size)
836
0
            {
837
                /* don't have complete PES hdr; save what we have and return */
838
0
                memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
839
0
                        p_block_in->p_buffer, l_rec_size);
840
0
                p_sys->i_pes_buf_cnt += l_rec_size;
841
                /* */
842
0
                block_Release(p_block_in);
843
0
                return 0;
844
0
            }
845
846
            /* we have enough; reconstruct this p_frame with the new hdr */
847
0
            memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
848
0
                   p_block_in->p_buffer, i_need);
849
            /* advance the block past the PES header (don't want to send it) */
850
0
            p_block_in->p_buffer += i_need;
851
0
            p_block_in->i_buffer -= i_need;
852
            /* get the PTS out of this PES header (MPEG or AC3) */
853
0
            if (p_sys->audio_type == TIVO_AUDIO_MPEG)
854
0
                esOffset1 = find_es_header(ty_MPEGAudioPacket,
855
0
                        p_sys->pes_buffer, 5);
856
0
            else
857
0
                esOffset1 = find_es_header(ty_AC3AudioPacket,
858
0
                        p_sys->pes_buffer, 5);
859
0
            if (esOffset1 < 0)
860
0
            {
861
                /* god help us; something's really wrong */
862
0
                msg_Err(p_demux, "can't find audio PES header in packet");
863
0
            }
864
0
            else
865
0
            {
866
0
                p_sys->lastAudioPTS = VLC_TICK_0 + get_pts(
867
0
                    &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
868
0
                p_block_in->i_pts = p_sys->lastAudioPTS;
869
0
            }
870
0
            p_sys->i_pes_buf_cnt = 0;
871
0
        }
872
        /* S2 DTivo has AC3 packets with 2 padding bytes at end.  This is
873
         * not allowed in the AC3 spec and will cause problems.  So here
874
         * we try to trim things. */
875
        /* Also, S1 DTivo has alternating short / long AC3 packets.  That
876
         * is, one packet is short (incomplete) and the next packet has
877
         * the first one's missing data, plus all of its own.  Strange. */
878
0
        if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
879
0
                p_sys->tivo_series == TIVO_SERIES2) {
880
0
            if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
881
0
                    AC3_PKT_LENGTH) {
882
0
                p_block_in->i_buffer -= 2;
883
0
                p_sys->l_ac3_pkt_size = 0;
884
0
            } else {
885
0
                p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
886
0
            }
887
0
        }
888
0
    }
889
6
    else if( subrec_type == 0x03 )
890
1
    {
891
        /* MPEG Audio with PES Header, either SA or DTiVo   */
892
        /* ================================================ */
893
1
        esOffset1 = find_es_header( ty_MPEGAudioPacket,
894
1
                p_block_in->p_buffer, 5 );
895
896
        /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
897
           p_block_in->p_buffer[0], p_block_in->p_buffer[1],
898
           p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
899
        msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
900
901
        /* SA PES Header, No Audio Data                     */
902
        /* ================================================ */
903
1
        if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
904
0
        {
905
0
            p_sys->lastAudioPTS = VLC_TICK_0 + get_pts( &p_block_in->p_buffer[
906
0
                        SA_PTS_OFFSET ] );
907
908
0
            block_Release(p_block_in);
909
0
            return 0;
910
            /*msg_Dbg(p_demux, "SA Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
911
0
        }
912
        /* DTiVo Audio with PES Header                      */
913
        /* ================================================ */
914
915
        /* Check for complete PES */
916
1
        if (check_sync_pes(p_demux, p_block_in, esOffset1,
917
1
                            l_rec_size) == -1)
918
1
        {
919
            /* partial PES header found, nothing else.
920
             * we're done. */
921
1
            block_Release(p_block_in);
922
1
            return 0;
923
1
        }
924
#if 0
925
        msg_Dbg(p_demux, "packet buffer has "
926
                 "%02x %02x %02x %02x %02x %02x %02x %02x "
927
                 "%02x %02x %02x %02x %02x %02x %02x %02x",
928
                 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
929
                 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
930
                 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
931
                 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
932
                 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
933
                 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
934
                 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
935
                 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
936
#endif
937
1
    }
938
5
    else if( subrec_type == 0x04 )
939
0
    {
940
        /* SA Audio with no PES Header                      */
941
        /* ================================================ */
942
        /*msg_Dbg(p_demux,
943
                "Adding SA Audio Packet Size %ld", l_rec_size ); */
944
945
0
        if (p_sys->lastAudioPTS != VLC_TICK_INVALID )
946
0
            p_block_in->i_pts = p_sys->lastAudioPTS;
947
0
    }
948
5
    else if( subrec_type == 0x09 )
949
5
    {
950
        /* DTiVo AC3 Audio Data with PES Header             */
951
        /* ================================================ */
952
5
        esOffset1 = find_es_header( ty_AC3AudioPacket,
953
5
                p_block_in->p_buffer, 5 );
954
955
#if 0
956
        msg_Dbg(p_demux, "buffer has "
957
                 "%02x %02x %02x %02x %02x %02x %02x %02x "
958
                 "%02x %02x %02x %02x %02x %02x %02x %02x",
959
                 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
960
                 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
961
                 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
962
                 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
963
                 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
964
                 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
965
                 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
966
                 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
967
        msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
968
#endif
969
970
        /* Check for complete PES */
971
5
        if (check_sync_pes(p_demux, p_block_in, esOffset1,
972
5
                            l_rec_size) == -1)
973
0
        {
974
            /* partial PES header found, nothing else.  we're done. */
975
0
            block_Release(p_block_in);
976
0
            return 0;
977
0
        }
978
        /* S2 DTivo has invalid long AC3 packets */
979
5
        if (p_sys->tivo_series == TIVO_SERIES2) {
980
0
            if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
981
0
                p_block_in->i_buffer -= 2;
982
0
                p_sys->l_ac3_pkt_size = 0;
983
0
            } else {
984
0
                p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
985
0
            }
986
0
        }
987
5
    }
988
0
    else
989
0
    {
990
        /* Unsupported/Unknown */
991
0
        block_Release(p_block_in);
992
0
        return 0;
993
0
    }
994
995
    /* set PCR before we send (if PTS found) */
996
5
    if( p_block_in->i_pts != VLC_TICK_INVALID )
997
5
        es_out_SetPCR( p_demux->out, p_block_in->i_pts );
998
999
    /* Send data */
1000
5
    es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1001
5
    return 0;
1002
6
}
1003
1004
static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1005
8
{
1006
8
    demux_sys_t *p_sys = p_demux->p_sys;
1007
8
    int i_field;
1008
1009
8
    if( p_block_in )
1010
8
        block_Release(p_block_in);
1011
1012
8
    if( rec_hdr->rec_type == 0x01 )
1013
4
        i_field = 0;
1014
4
    else if( rec_hdr->rec_type == 0x02 )
1015
4
        i_field = 1;
1016
0
    else
1017
0
        return 0;
1018
1019
    /* XDS data (extract programs infos) transmitted on field 2 only */
1020
8
    if( i_field == 1 )
1021
4
        DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] );
1022
1023
8
    if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1024
0
        return 0;
1025
1026
8
    cc_AppendData( &p_sys->cc, CC_PKT_BYTE0(i_field), rec_hdr->ex );
1027
8
    return 0;
1028
8
}
1029
1030
/* seek to a position within the stream, if possible */
1031
static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1032
0
{
1033
0
    demux_sys_t *p_sys = p_demux->p_sys;
1034
0
    int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1035
0
    uint64_t l_skip_amt;
1036
0
    unsigned i_cur_part;
1037
1038
    /* if we're not seekable, there's nothing to do */
1039
0
    if (!p_sys->b_seekable)
1040
0
        return VLC_EGENERIC;
1041
1042
    /* figure out which part & chunk we want & go there */
1043
0
    i_cur_part = seek_pos / TIVO_PART_LENGTH;
1044
0
    p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1045
1046
    /* try to read the part header (master chunk) if it's there */
1047
0
    if (vlc_stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ) ||
1048
0
        parse_master(p_demux) != VLC_SUCCESS)
1049
0
    {
1050
        /* can't seek stream */
1051
0
        return VLC_EGENERIC;
1052
0
    }
1053
1054
    /* now for the actual chunk */
1055
0
    if ( vlc_stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1056
0
    {
1057
        /* can't seek stream */
1058
0
        return VLC_EGENERIC;
1059
0
    }
1060
    /* load the chunk */
1061
0
    p_sys->i_stuff_cnt = 0;
1062
0
    get_chunk_header(p_demux);
1063
1064
    /* seek within the chunk to get roughly to where we want */
1065
0
    p_sys->i_cur_rec = (int)
1066
0
      ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1067
0
    msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1068
0
    msg_Dbg(p_demux, " (chunk %d, record %d)",
1069
0
             p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1070
1071
    /* seek to the start of this record's data.
1072
     * to do that, we have to skip past all prior records */
1073
0
    l_skip_amt = 0;
1074
0
    for ( int i=0; i<p_sys->i_cur_rec; i++)
1075
0
        l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1076
0
    if( vlc_stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1077
0
                        (p_sys->i_num_recs * 16) + l_skip_amt + 4) != VLC_SUCCESS )
1078
0
        return VLC_EGENERIC;
1079
1080
    /* to hell with syncing any audio or video, just start reading records... :) */
1081
    /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TICK_INVALID;*/
1082
0
    return VLC_SUCCESS;
1083
0
}
1084
1085
/* XDS decoder */
1086
//#define TY_XDS_DEBUG
1087
static void XdsInit( xds_t *h )
1088
25
{
1089
25
    h->b_xds = false;
1090
25
    h->i_class = XDS_MAX_CLASS_COUNT;
1091
25
    h->i_type = 0;
1092
25
    h->b_future = false;
1093
225
    for( int i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1094
200
    {
1095
25.8k
        for( int j = 0; j < 128; j++ )
1096
25.6k
            h->pkt[i][j].b_started = false;
1097
200
    }
1098
25
    h->b_meta_changed = false;
1099
25
    memset( &h->meta, 0, sizeof(h->meta) );
1100
25
}
1101
static void XdsExit( xds_t *h )
1102
25
{
1103
    /* */
1104
25
    free( h->meta.psz_channel_name );
1105
25
    free( h->meta.psz_channel_call_letter );
1106
25
    free( h->meta.psz_channel_number );
1107
1108
    /* */
1109
25
    free( h->meta.current.psz_name );
1110
25
    free( h->meta.current.psz_rating );
1111
    /* */
1112
25
    free( h->meta.future.psz_name );
1113
25
    free( h->meta.future.psz_rating );
1114
25
}
1115
static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, size_t i_src )
1116
0
{
1117
0
    size_t i_dst = 0;
1118
0
    for( size_t i = 0; i < i_src; i++ )
1119
0
    {
1120
0
        switch( p_src[i] )
1121
0
        {
1122
0
#define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1123
0
        E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1124
0
        E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1125
0
        E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1126
0
        E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1127
0
        E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1128
0
        E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1129
0
        E2( 0x7c, 0xc3,0xb7); // division symbol
1130
0
        E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1131
0
        E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1132
0
#undef E2
1133
0
        default:
1134
0
            dst[i_dst++] = p_src[i];
1135
0
            break;
1136
0
        }
1137
0
    }
1138
0
    dst[i_dst++] = '\0';
1139
0
}
1140
static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1141
0
{
1142
0
    if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1143
0
        return false;
1144
0
    if( *ppsz_dst == NULL && psz_new == NULL )
1145
0
        return false;
1146
1147
0
    free( *ppsz_dst );
1148
0
    if( psz_new )
1149
0
        *ppsz_dst = strdup( psz_new );
1150
0
    else
1151
0
        *ppsz_dst = NULL;
1152
1153
0
    h->b_meta_changed = true;
1154
0
    return true;
1155
0
}
1156
1157
static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1158
0
{
1159
0
    xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1160
0
    char name[2*32+1];
1161
0
    int i_rating;
1162
1163
0
    switch( h->i_type )
1164
0
    {
1165
0
    case 0x03:
1166
0
        XdsStringUtf8( name, pk->p_data, pk->i_data );
1167
0
        if( XdsChangeString( h, &p_prg->psz_name, name ) )
1168
0
        {
1169
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1170
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1171
0
        }
1172
0
        break;
1173
0
    case 0x05:
1174
0
        i_rating = (pk->p_data[0] & 0x18);
1175
0
        if( i_rating == 0x08 )
1176
0
        {
1177
            /* TPG */
1178
0
            static const char *pppsz_ratings[8][2] = {
1179
0
                { "None",   "No rating (no content advisory)" },
1180
0
                { "TV-Y",   "All Children (no content advisory)" },
1181
0
                { "TV-Y7",  "Directed to Older Children (V = Fantasy Violence)" },
1182
0
                { "TV-G",   "General Audience (no content advisory)" },
1183
0
                { "TV-PG",  "Parental Guidance Suggested" },
1184
0
                { "TV-14",  "Parents Strongly Cautioned" },
1185
0
                { "TV-MA",  "Mature Audience Only" },
1186
0
                { "None",   "No rating (no content advisory)" }
1187
0
            };
1188
0
            p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1189
0
            if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1190
0
            {
1191
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1192
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1193
                //         pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1194
0
            }
1195
0
        }
1196
0
        else if( i_rating == 0x00 || i_rating == 0x10 )
1197
0
        {
1198
            /* MPAA */
1199
0
            static const char *pppsz_ratings[8][2] = {
1200
0
                { "N/A",    "N/A" },
1201
0
                { "G",      "General Audiences" },
1202
0
                { "PG",     "Parental Guidance Suggested" },
1203
0
                { "PG-13",  "Parents Strongly Cautioned" },
1204
0
                { "R",      "Restricted" },
1205
0
                { "NC-17",  "No one 17 and under admitted" },
1206
0
                { "X",      "No one under 17 admitted" },
1207
0
                { "NR",     "Not Rated" },
1208
0
            };
1209
0
            p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1210
0
            if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1211
0
            {
1212
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1213
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1214
                //         pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1215
0
            }
1216
0
        }
1217
0
        else
1218
0
        {
1219
            /* Non US Rating TODO */
1220
0
            assert( i_rating == 0x18 ); // only left value possible */
1221
0
            p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1222
0
            if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1223
0
            {
1224
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1225
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1226
0
            }
1227
0
        }
1228
0
        break;
1229
1230
0
    default:
1231
#ifdef TY_XDS_DEBUG
1232
        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1233
#endif
1234
0
        break;
1235
0
    }
1236
0
}
1237
1238
static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1239
0
{
1240
0
    char name[2*32+1];
1241
0
    char chan[2*32+1];
1242
1243
0
    switch( h->i_type )
1244
0
    {
1245
0
    case 0x01:
1246
0
        if( pk->i_data < 2 )
1247
0
            return;
1248
0
        XdsStringUtf8( name, pk->p_data, pk->i_data );
1249
0
        if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1250
0
        {
1251
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1252
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1253
0
        }
1254
0
        break;
1255
1256
0
    case 0x02:
1257
0
        if( pk->i_data < 4 )
1258
0
            return;
1259
1260
0
        XdsStringUtf8( name, pk->p_data, 4 );
1261
0
        if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1262
0
        {
1263
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1264
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1265
0
        }
1266
0
        if( pk->i_data >= 6 )
1267
0
        {
1268
0
            XdsStringUtf8( chan, &pk->p_data[4], 2 );
1269
0
            if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1270
0
            {
1271
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1272
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1273
0
            }
1274
0
        }
1275
0
        else
1276
0
        {
1277
0
            if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1278
0
            {
1279
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1280
                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1281
0
            }
1282
0
        }
1283
0
        break;
1284
0
    case 0x03:
1285
        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1286
0
        break;
1287
0
    case 0x04:
1288
        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1289
0
        break;
1290
0
    default:
1291
#ifdef TY_XDS_DEBUG
1292
        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1293
#endif
1294
0
        break;
1295
0
    }
1296
0
}
1297
1298
static void XdsDecode( xds_t *h, xds_packet_t *pk )
1299
0
{
1300
0
    switch( h->i_class )
1301
0
    {
1302
0
    case XDS_CLASS_CURRENT:
1303
0
    case XDS_CLASS_FUTURE:
1304
0
        XdsDecodeCurrentFuture( h, pk );
1305
0
        break;
1306
0
    case XDS_CLASS_CHANNEL:
1307
0
        XdsDecodeChannel( h, pk );
1308
0
        break;
1309
0
    case XDS_CLASS_MISCELLANEOUS:
1310
#ifdef TY_XDS_DEBUG
1311
        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1312
#endif
1313
0
        break;
1314
0
    case XDS_CLASS_PUBLIC_SERVICE:
1315
#ifdef TY_XDS_DEBUG
1316
        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1317
#endif
1318
0
        break;
1319
0
    default:
1320
        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1321
0
        break;
1322
0
    }
1323
0
}
1324
1325
static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1326
4
{
1327
    /* TODO check parity */
1328
4
    d1 &= 0x7f;
1329
4
    d2 &= 0x7f;
1330
1331
    /* */
1332
4
    if( d1 >= 0x01 && d1 <= 0x0e )
1333
0
    {
1334
0
        const xds_class_t i_class = ( d1 - 1 ) >> 1;
1335
0
        const int i_type = d2;
1336
0
        const bool b_start = d1 & 0x01;
1337
0
        xds_packet_t *pk = &h->pkt[i_class][i_type];
1338
1339
0
        if( !b_start && !pk->b_started )
1340
0
        {
1341
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1342
0
            h->b_xds = false;
1343
0
            return;
1344
0
        }
1345
1346
0
        h->b_xds = true;
1347
0
        h->i_class = i_class;
1348
0
        h->i_type  = i_type;
1349
0
        h->b_future = !b_start;
1350
0
        pk->b_started = true;
1351
0
        if( b_start )
1352
0
        {
1353
0
            pk->i_data = 0;
1354
0
            pk->i_sum = d1 + d2;
1355
0
        }
1356
0
    }
1357
4
    else if( d1 == 0x0f && h->b_xds )
1358
0
    {
1359
0
        xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1360
1361
        /* TODO checksum and decode */
1362
0
        pk->i_sum += d1 + d2;
1363
0
        if( pk->i_sum & 0x7f )
1364
0
        {
1365
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1366
0
            pk->b_started = false;
1367
0
            return;
1368
0
        }
1369
0
        if( pk->i_data <= 0 )
1370
0
        {
1371
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1372
0
            pk->b_started = false;
1373
0
            return;
1374
0
        }
1375
1376
        //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1377
        //    pk->i_data--;
1378
0
        XdsDecode( h, pk );
1379
1380
        /* Reset it */
1381
0
        pk->b_started = false;
1382
0
    }
1383
4
    else if( d1 >= 0x20 && h->b_xds )
1384
0
    {
1385
0
        xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1386
1387
0
        if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1388
0
        {
1389
            /* Broken -> reinit */
1390
            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1391
0
            h->b_xds = false;
1392
0
            pk->b_started = false;
1393
0
            return;
1394
0
        }
1395
        /* TODO check parity bit */
1396
0
        pk->p_data[pk->i_data++] = d1 & 0x7f;
1397
0
        pk->p_data[pk->i_data++] = d2 & 0x7f;
1398
0
        pk->i_sum += d1+d2;
1399
0
    }
1400
4
    else
1401
4
    {
1402
4
        h->b_xds = false;
1403
4
    }
1404
4
}
1405
1406
static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1407
4
{
1408
4
    demux_sys_t *p_sys = p_demux->p_sys;
1409
1410
4
    XdsParse( &p_sys->xds, d1, d2 );
1411
4
    if( p_sys->xds.b_meta_changed )
1412
0
    {
1413
0
        xds_meta_t *m = &p_sys->xds.meta;
1414
0
        vlc_meta_t *p_meta;
1415
1416
        /* Channel meta data */
1417
0
        p_meta = vlc_meta_New();
1418
0
        if( m->psz_channel_name )
1419
0
            vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1420
0
        if( m->psz_channel_call_letter )
1421
0
            vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1422
0
        if( m->psz_channel_number )
1423
0
            vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1424
0
        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1425
0
        vlc_meta_Delete( p_meta );
1426
1427
        /* Event meta data (current/future) */
1428
0
        if( m->current.psz_name )
1429
0
        {
1430
0
            vlc_epg_t *p_epg = vlc_epg_New( TY_ES_GROUP, TY_ES_GROUP );
1431
0
            if ( p_epg )
1432
0
            {
1433
0
                vlc_epg_event_t *p_evt = vlc_epg_event_New( 0, 0, 0 );
1434
0
                if ( p_evt )
1435
0
                {
1436
0
                    if( m->current.psz_name )
1437
0
                        p_evt->psz_name = strdup( m->current.psz_name );
1438
0
                    if( !vlc_epg_AddEvent( p_epg, p_evt ) )
1439
0
                        vlc_epg_event_Delete( p_evt );
1440
0
                }
1441
                //if( m->current.psz_rating )
1442
                //  TODO but VLC cannot yet handle rating per epg event
1443
0
                vlc_epg_SetCurrent( p_epg, 0 );
1444
1445
0
                if( m->future.psz_name )
1446
0
                {
1447
0
                }
1448
0
                if( p_epg->i_event > 0 )
1449
0
                    es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
1450
0
                                    TY_ES_GROUP, p_epg );
1451
0
                vlc_epg_Delete( p_epg );
1452
0
            }
1453
0
        }
1454
0
    }
1455
4
    p_sys->xds.b_meta_changed = false;
1456
4
}
1457
1458
/* seek to an exact time position within the stream, if possible.
1459
 * l_seek_time is in nanoseconds, the TIVO time standard.
1460
 */
1461
static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1462
0
{
1463
0
    demux_sys_t *p_sys = p_demux->p_sys;
1464
0
    unsigned i_seq_entry = 0;
1465
0
    unsigned i;
1466
0
    int i_skip_cnt;
1467
0
    int64_t l_cur_pos = vlc_stream_Tell(p_demux->s);
1468
0
    unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1469
0
    uint64_t l_seek_secs = l_seek_time / 1000000000;
1470
0
    uint64_t l_fwd_stamp = 1;
1471
1472
    /* if we're not seekable, there's nothing to do */
1473
0
    if (!p_sys->b_seekable || !p_sys->b_have_master)
1474
0
        return VLC_EGENERIC;
1475
1476
0
    msg_Dbg(p_demux, "Skipping to time %02"PRIu64":%02"PRIu64":%02"PRIu64,
1477
0
            l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1478
1479
    /* seek to the proper segment if necessary */
1480
    /* first see if we need to go back */
1481
0
    while (l_seek_time < p_sys->l_first_ty_pts) {
1482
0
        msg_Dbg(p_demux, "skipping to prior segment.");
1483
        /* load previous part */
1484
0
        if (i_cur_part == 0) {
1485
0
            p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1486
0
            msg_Err(p_demux, "Attempt to seek past BOF");
1487
0
            return VLC_EGENERIC;
1488
0
        }
1489
0
        if(vlc_stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1490
0
            return VLC_EGENERIC;
1491
0
        i_cur_part--;
1492
0
        if(parse_master(p_demux) != VLC_SUCCESS)
1493
0
            return VLC_EGENERIC;
1494
0
    }
1495
    /* maybe we need to go forward */
1496
0
    while (l_seek_time > p_sys->l_final_ty_pts) {
1497
0
        msg_Dbg(p_demux, "skipping to next segment.");
1498
        /* load next part */
1499
0
        if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1500
            /* error; restore previous file position */
1501
0
            p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1502
0
            msg_Err(p_demux, "seek error");
1503
0
            return VLC_EGENERIC;
1504
0
        }
1505
0
        if(vlc_stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1506
0
            return VLC_EGENERIC;
1507
0
        i_cur_part++;
1508
0
        if(parse_master(p_demux) != VLC_SUCCESS)
1509
0
            return VLC_EGENERIC;
1510
0
    }
1511
1512
    /* our target is somewhere within this part;
1513
       find the proper chunk using seq_table */
1514
0
    for (i=1; i<p_sys->i_seq_table_size; i++) {
1515
0
        if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1516
            /* i-1 is the section we want; remember the next timestamp in case
1517
               we have to use it (this section may not have a proper SEQ hdr
1518
               for the time we're seeking) */
1519
0
            msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1520
0
            l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1521
0
            i_seq_entry = i-1;
1522
0
            break;
1523
0
        }
1524
0
    }
1525
1526
    /* if we went through the entire last loop and didn't find our target,
1527
       then we skip to the next part.  What has happened is that the actual
1528
       time we're seeking is within this part, but there isn't a SEQ hdr
1529
       for it here.  So we skip to the next part */
1530
0
    if (i == p_sys->i_seq_table_size) {
1531
0
        if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1532
            /* error; restore previous file position */
1533
0
            p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1534
0
            msg_Err(p_demux, "seek error");
1535
0
            return VLC_EGENERIC;
1536
0
        }
1537
0
        if(vlc_stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1538
0
            return VLC_EGENERIC;
1539
0
        i_cur_part++;
1540
0
        if(parse_master(p_demux) != VLC_SUCCESS)
1541
0
            return VLC_EGENERIC;
1542
0
        i_seq_entry = 0;
1543
0
    }
1544
1545
    /* determine which chunk has our seek_time */
1546
0
    for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1547
0
        uint64_t l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1548
0
        uint64_t l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1549
0
        msg_Dbg(p_demux, "testing part %d chunk %"PRIu64" mask 0x%02X bit %d",
1550
0
            i_cur_part, l_chunk_nr,
1551
0
            p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1552
0
        if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1553
            /* check this chunk's SEQ header timestamp */
1554
0
            msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%"PRIu64,
1555
0
                (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1556
0
            if(vlc_stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1557
0
                l_chunk_offset) != VLC_SUCCESS)
1558
0
                return VLC_EGENERIC;
1559
            // TODO: we don't have to parse the full header set;
1560
            // just test the seq_rec entry for its timestamp
1561
0
            p_sys->i_stuff_cnt = 0;
1562
0
            get_chunk_header(p_demux);
1563
            // check ty PTS for the SEQ entry in this chunk
1564
0
            if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1565
0
                msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1566
                /* Seek to beginning of original chunk & reload it */
1567
0
                if(vlc_stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE) != VLC_SUCCESS)
1568
0
                    p_sys->eof = true;
1569
0
                p_sys->i_stuff_cnt = 0;
1570
0
                get_chunk_header(p_demux);
1571
0
                return VLC_EGENERIC;
1572
0
            }
1573
0
            l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1574
0
                1000000000;
1575
0
            msg_Dbg(p_demux, "found SEQ hdr for timestamp %02"PRIu64":%02"PRIu64":%02"PRIu64,
1576
0
                l_seek_secs / 3600,
1577
0
                (l_seek_secs / 60) % 60, l_seek_secs % 60);
1578
0
            if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1579
                // keep this one?  go back?
1580
                /* for now, we take this one.  it's the first SEQ hdr AFTER
1581
                   the time we were searching for. */
1582
0
                msg_Dbg(p_demux, "seek target found.");
1583
0
                break;
1584
0
            }
1585
0
            msg_Dbg(p_demux, "timestamp too early. still scanning.");
1586
0
        }
1587
0
    }
1588
    /* if we made it through this entire loop without finding our target,
1589
       then we skip to the next section.  What has happened is that the actual
1590
       time we're seeking is within this section, but there isn't a SEQ hdr
1591
       for it here.  So we skip to the next closest one (l_fwd_stamp) */
1592
0
    if (i == p_sys->i_bits_per_seq_entry)
1593
0
        return ty_stream_seek_time(p_demux, l_fwd_stamp);
1594
1595
    /* current stream ptr is at beginning of data for this chunk,
1596
       so we need to skip past any stream data prior to the seq_rec
1597
       in this chunk */
1598
0
    i_skip_cnt = 0;
1599
0
    for (int j=0; j<p_sys->i_seq_rec; j++)
1600
0
        i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size;
1601
0
    if(vlc_stream_Read(p_demux->s, NULL, i_skip_cnt) != i_skip_cnt)
1602
0
        return VLC_EGENERIC;
1603
0
    p_sys->i_cur_rec = p_sys->i_seq_rec;
1604
    //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1605
    //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1606
1607
0
    return VLC_SUCCESS;
1608
0
}
1609
1610
1611
/* parse a master chunk, filling the SEQ table and other variables.
1612
 * We assume the stream is currently pointing to it.
1613
 */
1614
static int parse_master(demux_t *p_demux)
1615
25
{
1616
25
    demux_sys_t *p_sys = p_demux->p_sys;
1617
25
    uint8_t mst_buf[32];
1618
25
    int64_t i_save_pos = vlc_stream_Tell(p_demux->s);
1619
25
    int64_t i_pts_secs;
1620
1621
    /* Note that the entries in the SEQ table in the stream may have
1622
       different sizes depending on the bits per entry.  We store them
1623
       all in the same size structure, so we have to parse them out one
1624
       by one.  If we had a dynamic structure, we could simply read the
1625
       entire table directly from the stream into memory in place. */
1626
1627
    /* clear the SEQ table */
1628
25
    free(p_sys->seq_table);
1629
1630
    /* parse header info */
1631
25
    if( vlc_stream_Read(p_demux->s, mst_buf, 32) != 32 )
1632
0
        return VLC_EGENERIC;
1633
1634
25
    uint32_t i_map_size = U32_AT(&mst_buf[20]);  /* size of bitmask, in bytes */
1635
25
    uint32_t i = U32_AT(&mst_buf[28]);   /* size of SEQ table, in bytes */
1636
1637
25
    p_sys->i_bits_per_seq_entry = i_map_size * 8;
1638
25
    p_sys->i_seq_table_size = i / (8 + i_map_size);
1639
1640
25
    if(p_sys->i_seq_table_size == 0)
1641
15
    {
1642
15
        p_sys->seq_table = NULL;
1643
15
        return VLC_SUCCESS;
1644
15
    }
1645
1646
#if (UINT32_MAX > SSIZE_MAX)
1647
    if (i_map_size > SSIZE_MAX)
1648
        return VLC_EGENERIC;
1649
#endif
1650
1651
    /* parse all the entries */
1652
10
    p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t));
1653
10
    if (p_sys->seq_table == NULL)
1654
0
    {
1655
0
        p_sys->i_seq_table_size = 0;
1656
0
        return VLC_SUCCESS;
1657
0
    }
1658
100k
    for (unsigned j=0; j<p_sys->i_seq_table_size; j++) {
1659
100k
        if(vlc_stream_Read(p_demux->s, mst_buf, 8) != 8)
1660
2
            return VLC_EGENERIC;
1661
100k
        p_sys->seq_table[j].l_timestamp = U64_AT(&mst_buf[0]);
1662
100k
        if (i_map_size > 8) {
1663
37
            msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1664
37
            if (vlc_stream_Read(p_demux->s, NULL, i_map_size) != i_map_size )
1665
5
                return VLC_EGENERIC;
1666
100k
        } else {
1667
100k
            if (vlc_stream_Read(p_demux->s, mst_buf + 8, i_map_size)
1668
100k
                                              < (ssize_t)i_map_size)
1669
0
                return VLC_EGENERIC;
1670
100k
            memcpy(p_sys->seq_table[j].chunk_bitmask, &mst_buf[8], i_map_size);
1671
100k
        }
1672
100k
    }
1673
1674
    /* set up a few of our variables */
1675
3
    p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1676
3
    p_sys->l_final_ty_pts =
1677
3
        p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1678
3
    p_sys->b_have_master = true;
1679
1680
3
    i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1681
3
    msg_Dbg( p_demux,
1682
3
             "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1683
3
             i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1684
3
    i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1685
3
    msg_Dbg( p_demux,
1686
3
             "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1687
3
             i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1688
1689
    /* seek past this chunk */
1690
3
    return vlc_stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1691
10
}
1692
1693
1694
/* ======================================================================== */
1695
/* "Peek" at some chunks.  Skip over the Part header if we find it.
1696
 * We parse the peeked data and determine audio type,
1697
 * SA vs. DTivo, & Tivo Series.
1698
 * Set global vars i_Pes_Length, i_Pts_Offset,
1699
 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1700
static int probe_stream(demux_t *p_demux)
1701
28
{
1702
28
    demux_sys_t *p_sys = p_demux->p_sys;
1703
28
    const uint8_t *p_buf;
1704
28
    int i;
1705
28
    bool b_probe_error = false;
1706
1707
    /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1708
28
    if (vlc_stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1709
28
            CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1710
0
        msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1711
        /* TODO: if seekable, then loop reading chunks into a temp buffer */
1712
0
        return VLC_EGENERIC;
1713
0
    }
1714
1715
    /* the real work: analyze this chunk */
1716
81
    for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1717
78
        analyze_chunk(p_demux, p_buf);
1718
78
        if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1719
78
            p_sys->audio_type  != TIVO_AUDIO_UNKNOWN &&
1720
78
            p_sys->tivo_type   != TIVO_TYPE_UNKNOWN)
1721
25
            break;
1722
53
        p_buf += CHUNK_SIZE;
1723
53
    }
1724
1725
    /* the final tally */
1726
28
    if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1727
2
        msg_Err(p_demux, "Can't determine Tivo Series.");
1728
2
        b_probe_error = true;
1729
2
    }
1730
28
    if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1731
2
        msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1732
2
        b_probe_error = true;
1733
2
    }
1734
28
    if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1735
3
        msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1736
3
        b_probe_error = true;
1737
3
    }
1738
28
    return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1739
28
}
1740
1741
1742
/* ======================================================================== */
1743
/* gather statistics for this chunk & set our tivo-type vars accordingly */
1744
static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1745
78
{
1746
78
    demux_sys_t *p_sys = p_demux->p_sys;
1747
78
    int i_num_recs, i;
1748
78
    ty_rec_hdr_t *p_hdrs;
1749
78
    int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1750
78
    int i_payload_size;
1751
1752
    /* skip if it's a Part header */
1753
78
    if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1754
28
        return;
1755
1756
    /* number of records in chunk (we ignore high order byte;
1757
     * rarely are there > 256 chunks & we don't need that many anyway) */
1758
50
    i_num_recs = p_chunk[0];
1759
50
    if (i_num_recs < 5) {
1760
        /* try again with the next chunk.  Sometimes there are dead ones */
1761
5
        return;
1762
5
    }
1763
1764
45
    p_chunk += 4;       /* skip past rec count & SEQ bytes */
1765
    //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1766
45
    p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
1767
    /* scan headers.
1768
     * 1. check video packets.  Presence of 0x6e0 means S1.
1769
     *    No 6e0 but have be0 means S2.
1770
     * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1771
     *    If AC-3, then we have DTivo.
1772
     *    If MPEG, search for PTS offset.  This will determine SA vs. DTivo.
1773
     */
1774
45
    i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1775
7.82k
    for (i=0; i<i_num_recs; i++) {
1776
        //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1777
            //p_hdrs[i].rec_type,
1778
            //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1779
7.77k
        switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1780
36
            case 0x6e0:
1781
36
                i_num_6e0++;
1782
36
                break;
1783
37
            case 0xbe0:
1784
37
                i_num_be0++;
1785
37
                break;
1786
2
            case 0x3c0:
1787
2
                i_num_3c0++;
1788
2
                break;
1789
57
            case 0x9c0:
1790
57
                i_num_9c0++;
1791
57
                break;
1792
7.77k
        }
1793
7.77k
    }
1794
45
    msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1795
45
        i_num_6e0, i_num_be0);
1796
1797
    /* set up our variables */
1798
45
    if (i_num_6e0 > 0) {
1799
16
        msg_Dbg(p_demux, "detected Series 1 Tivo");
1800
16
        p_sys->tivo_series = TIVO_SERIES1;
1801
16
        p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1802
29
    } else if (i_num_be0 > 0) {
1803
15
        msg_Dbg(p_demux, "detected Series 2 Tivo");
1804
15
        p_sys->tivo_series = TIVO_SERIES2;
1805
15
        p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1806
15
    }
1807
45
    if (i_num_9c0 > 0) {
1808
26
        msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1809
26
        p_sys->audio_type = TIVO_AUDIO_AC3;
1810
26
        p_sys->tivo_type = TIVO_TYPE_DTIVO;
1811
26
        p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1812
26
        p_sys->i_Pes_Length = AC3_PES_LENGTH;
1813
26
    } else if (i_num_3c0 > 0) {
1814
1
        p_sys->audio_type = TIVO_AUDIO_MPEG;
1815
1
        msg_Dbg(p_demux, "detected MPEG Audio" );
1816
1
    }
1817
1818
    /* if tivo_type still unknown, we can check PTS location
1819
     * in MPEG packets to determine tivo_type */
1820
45
    if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1821
19
        uint32_t i_data_offset = (16 * i_num_recs);
1822
3.19k
        for (i=0; i<i_num_recs; i++) {
1823
3.17k
            if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1824
3.17k
                    p_hdrs[i].l_rec_size > 15) {
1825
                /* first make sure we're aligned */
1826
2
                int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1827
2
                        &p_chunk[i_data_offset], 5);
1828
2
                if (i_pes_offset >= 0) {
1829
                    /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1830
                    //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1831
                            //i, i_pes_offset);
1832
0
                    if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1833
                        /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1834
0
                        if (p_sys->tivo_series == TIVO_SERIES1)
1835
0
                            msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1836
0
                        p_sys->tivo_type = TIVO_TYPE_SA;
1837
0
                        p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1838
0
                    } else {
1839
0
                        if (p_sys->tivo_series == TIVO_SERIES1)
1840
0
                            msg_Dbg(p_demux, "detected DirecTV Tivo" );
1841
0
                        p_sys->tivo_type = TIVO_TYPE_DTIVO;
1842
0
                        p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1843
0
                    }
1844
0
                    break;
1845
0
                }
1846
2
            }
1847
3.17k
            i_data_offset += p_hdrs[i].l_rec_size;
1848
3.17k
        }
1849
19
    }
1850
45
    free(p_hdrs);
1851
45
}
1852
1853
1854
/* =========================================================================== */
1855
static int get_chunk_header(demux_t *p_demux)
1856
71
{
1857
71
    int i_readSize, i_num_recs;
1858
71
    uint8_t *p_hdr_buf;
1859
71
    const uint8_t *p_peek;
1860
71
    demux_sys_t *p_sys = p_demux->p_sys;
1861
71
    int i_payload_size;             /* sum of all records' sizes */
1862
1863
71
    msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1864
1865
    /* if we have left-over filler space from the last chunk, get that */
1866
71
    if (p_sys->i_stuff_cnt > 0) {
1867
18
        if(vlc_stream_Read(p_demux->s, NULL, p_sys->i_stuff_cnt) != p_sys->i_stuff_cnt)
1868
1
            return 0;
1869
17
        p_sys->i_stuff_cnt = 0;
1870
17
    }
1871
1872
    /* read the TY packet header */
1873
70
    i_readSize = vlc_stream_Peek( p_demux->s, &p_peek, 4 );
1874
70
    p_sys->i_cur_chunk++;
1875
1876
70
    if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1877
2
    {
1878
        /* EOF */
1879
2
        p_sys->eof = 1;
1880
2
        return 0;
1881
2
    }
1882
1883
    /* check if it's a PART Header */
1884
68
    if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1885
25
    {
1886
        /* parse master chunk */
1887
25
        if(parse_master(p_demux) != VLC_SUCCESS)
1888
7
            return 0;
1889
18
        return get_chunk_header(p_demux);
1890
25
    }
1891
1892
    /* number of records in chunk (8- or 16-bit number) */
1893
43
    if (p_peek[3] & 0x80)
1894
8
    {
1895
        /* 16 bit rec cnt */
1896
8
        p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1897
8
        p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1898
8
        if (p_sys->i_seq_rec != 0xffff)
1899
8
        {
1900
8
            p_sys->i_seq_rec &= ~0x8000;
1901
8
        }
1902
8
    }
1903
35
    else
1904
35
    {
1905
        /* 8 bit reclen - tivo 1.3 format */
1906
35
        p_sys->i_num_recs = i_num_recs = p_peek[0];
1907
35
        p_sys->i_seq_rec = p_peek[1];
1908
35
    }
1909
43
    p_sys->i_cur_rec = 0;
1910
43
    p_sys->b_first_chunk = false;
1911
1912
    /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1913
1914
43
    free(p_sys->rec_hdrs);
1915
43
    p_sys->rec_hdrs = NULL;
1916
1917
    /* skip past the 4 bytes we "peeked" earlier */
1918
43
    if(vlc_stream_Read(p_demux->s, NULL, 4) != 4)
1919
0
        return 0;
1920
1921
    /* read the record headers into a temp buffer */
1922
43
    p_hdr_buf = xmalloc(i_num_recs * 16);
1923
43
    if (vlc_stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1924
3
        free( p_hdr_buf );
1925
3
        p_sys->eof = true;
1926
3
        return 0;
1927
3
    }
1928
    /* parse them */
1929
40
    p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs,
1930
40
            &i_payload_size);
1931
40
    free(p_hdr_buf);
1932
1933
40
    p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1934
40
        (p_sys->i_num_recs * 16) - i_payload_size;
1935
40
    if (p_sys->i_stuff_cnt > 0)
1936
40
        msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1937
40
                 p_sys->i_stuff_cnt );
1938
40
    return 1;
1939
43
}
1940
1941
1942
static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
1943
                                          int i_num_recs, int *pi_payload_size)
1944
85
{
1945
85
    int i;
1946
85
    ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1947
1948
85
    *pi_payload_size = 0;
1949
85
    p_hdrs = xmalloc(i_num_recs * sizeof(ty_rec_hdr_t));
1950
1951
11.1k
    for (i = 0; i < i_num_recs; i++)
1952
11.1k
    {
1953
11.1k
        const uint8_t *record_header = p_buf + (i * 16);
1954
11.1k
        p_rec_hdr = &p_hdrs[i];     /* for brevity */
1955
11.1k
        p_rec_hdr->rec_type = record_header[3];
1956
11.1k
        p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1957
11.1k
        if ((record_header[ 0 ] & 0x80) == 0x80)
1958
5.28k
        {
1959
5.28k
            uint8_t b1, b2;
1960
            /* marker bit 2 set, so read extended data */
1961
5.28k
            b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1962
5.28k
                   ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1963
5.28k
            b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1964
5.28k
                   ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1965
1966
5.28k
            p_rec_hdr->ex[0] = b1;
1967
5.28k
            p_rec_hdr->ex[1] = b2;
1968
5.28k
            p_rec_hdr->l_rec_size = 0;
1969
5.28k
            p_rec_hdr->l_ty_pts = 0;
1970
5.28k
            p_rec_hdr->b_ext = true;
1971
5.28k
        }
1972
5.83k
        else
1973
5.83k
        {
1974
5.83k
            p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1975
5.83k
                record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1976
5.83k
            *pi_payload_size += p_rec_hdr->l_rec_size;
1977
5.83k
            p_rec_hdr->b_ext = false;
1978
5.83k
            p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1979
5.83k
        }
1980
        //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1981
11.1k
    } /* end of record-header loop */
1982
85
    return p_hdrs;
1983
85
}