Coverage Report

Created: 2026-09-03 07:09

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