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