demux: ty: fix all warnings
[vlc.git] / modules / demux / ty.c
bloba305ab03befcba5ff80fbc109fcedef4c5b498e7
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 "mpeg/pes.h"
52 #include <assert.h>
54 /*****************************************************************************
55 * Module descriptor
56 *****************************************************************************/
57 static int Open ( vlc_object_t * );
58 static void Close( vlc_object_t * );
60 vlc_module_begin ()
61 set_shortname( N_("TY") )
62 set_description(N_("TY Stream audio/video demux"))
63 set_category( CAT_INPUT )
64 set_subcategory( SUBCAT_INPUT_DEMUX )
65 set_capability("demux", 6)
66 /* FIXME: there seems to be a segfault when using PVR access
67 * and TY demux has a bigger priority than PS
68 * Something must be wrong.
70 set_callbacks( Open, Close )
71 add_shortcut("ty", "tivo")
72 vlc_module_end ()
74 /*****************************************************************************
75 * Local prototypes
76 *****************************************************************************/
77 static int Demux ( demux_t * );
78 static int Control( demux_t *, int, va_list );
80 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
81 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
82 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
83 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
84 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
85 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
86 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
87 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
88 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
89 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
90 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
91 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
93 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
95 /* packet types for reference:
96 2/c0: audio data continued
97 3/c0: audio packet header (PES header)
98 4/c0: audio data (S/A only?)
99 9/c0: audio packet header, AC-3 audio
100 2/e0: video data continued
101 6/e0: video packet header (PES header)
102 7/e0: video sequence header start
103 8/e0: video I-frame header start
104 a/e0: video P-frame header start
105 b/e0: video B-frame header start
106 c/e0: video GOP header start
107 e/01: closed-caption data
108 e/02: Extended data services data
109 e/03: ipreview data ("thumbs up to record" signal)
110 e/05: UK Teletext
113 #define TIVO_PES_FILEID ( 0xf5467abd )
114 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
115 #define CHUNK_SIZE ( 128 * 1024 )
117 typedef struct
119 long l_rec_size;
120 uint8_t ex[2];
121 uint8_t rec_type;
122 uint8_t subrec_type;
123 bool b_ext;
124 uint64_t l_ty_pts; /* TY PTS in the record header */
125 } ty_rec_hdr_t;
127 typedef struct
129 uint64_t l_timestamp;
130 uint8_t chunk_bitmask[8];
131 } ty_seq_table_t;
133 typedef enum
135 TIVO_TYPE_UNKNOWN,
136 TIVO_TYPE_SA,
137 TIVO_TYPE_DTIVO
138 } tivo_type_t;
140 typedef enum
142 TIVO_SERIES_UNKNOWN,
143 TIVO_SERIES1,
144 TIVO_SERIES2
145 } tivo_series_t;
147 typedef enum
149 TIVO_AUDIO_UNKNOWN,
150 TIVO_AUDIO_AC3,
151 TIVO_AUDIO_MPEG
152 } tivo_audio_t;
154 #define XDS_MAX_DATA_SIZE (32)
155 typedef enum
157 XDS_CLASS_CURRENT = 0,
158 XDS_CLASS_FUTURE = 1,
159 XDS_CLASS_CHANNEL = 2,
160 XDS_CLASS_MISCELLANEOUS = 3,
161 XDS_CLASS_PUBLIC_SERVICE = 4,
162 XDS_CLASS_RESERVED = 5,
163 XDS_CLASS_UNDEFINED = 6,
164 XDS_CLASS_OTHER = 7,
166 XDS_MAX_CLASS_COUNT
167 } xds_class_t;
168 typedef struct
170 bool b_started;
171 size_t i_data;
172 uint8_t p_data[XDS_MAX_DATA_SIZE];
173 int i_sum;
174 } xds_packet_t;
175 typedef enum
177 XDS_META_PROGRAM_RATING_NONE,
178 XDS_META_PROGRAM_RATING_MPAA,
179 XDS_META_PROGRAM_RATING_TPG,
180 /* TODO add CA/CE rating */
181 } xds_meta_program_rating_t;
182 typedef struct
184 char *psz_name;
185 xds_meta_program_rating_t rating;
186 char *psz_rating;
187 /* Add the other fields once I have the samples */
188 } xds_meta_program_t;
189 typedef struct
191 char *psz_channel_name;
192 char *psz_channel_call_letter;
193 char *psz_channel_number;
195 xds_meta_program_t current;
196 xds_meta_program_t future;
197 } xds_meta_t;
198 typedef struct
200 /* Are we in XDS mode */
201 bool b_xds;
203 /* Current class type */
204 xds_class_t i_class;
205 int i_type;
206 bool b_future;
208 /* */
209 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
211 /* */
212 bool b_meta_changed;
213 xds_meta_t meta;
215 } xds_t;
217 typedef struct
219 es_out_id_t *p_video; /* ptr to video codec */
220 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
222 cc_data_t cc;
223 es_out_id_t *p_cc[4];
225 xds_t xds;
227 int i_cur_chunk;
228 int i_stuff_cnt;
229 size_t i_stream_size; /* size of input stream (if known) */
230 //uint64_t l_program_len; /* length of this stream in msec */
231 bool b_seekable; /* is this stream seekable? */
232 bool b_have_master; /* are master chunks present? */
233 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
234 tivo_series_t tivo_series; /* Series1 or Series2 */
235 tivo_audio_t audio_type; /* AC3 or MPEG */
236 int i_Pes_Length; /* Length of Audio PES header */
237 int i_Pts_Offset; /* offset into audio PES of PTS */
238 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
239 int i_pes_buf_cnt; /* how many bytes in our buffer */
240 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
241 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
242 //vlc_tick_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
243 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
244 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
245 unsigned i_seq_table_size; /* number of entries in SEQ table */
246 unsigned i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
248 vlc_tick_t lastAudioPTS;
249 vlc_tick_t lastVideoPTS;
251 ty_rec_hdr_t *rec_hdrs; /* record headers array */
252 int i_cur_rec; /* current record in this chunk */
253 int i_num_recs; /* number of recs in this chunk */
254 int i_seq_rec; /* record number where seq start is */
255 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
256 bool eof;
257 bool b_first_chunk;
258 } demux_sys_t;
260 static int get_chunk_header(demux_t *);
261 static vlc_tick_t get_pts( const uint8_t *buf );
262 static int find_es_header( const uint8_t *header,
263 const uint8_t *buffer, int i_search_len );
264 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
265 static int ty_stream_seek_time(demux_t *, uint64_t);
267 static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
268 int i_num_recs, int *pi_payload_size);
269 static int probe_stream(demux_t *p_demux);
270 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
271 static int parse_master(demux_t *p_demux);
273 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
274 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
275 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
277 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
279 static void XdsInit( xds_t * );
280 static void XdsExit( xds_t * );
282 #define TY_ES_GROUP (1)
285 * Open: check file and initialize demux structures
287 * here's what we do:
288 * 1. peek at the first 12 bytes of the stream for the
289 * magic TiVo PART header & stream type & chunk size
290 * 2. if it's not there, error with VLC_EGENERIC
291 * 3. set up video (mpgv) codec
292 * 4. return VLC_SUCCESS
294 static int Open(vlc_object_t *p_this)
296 demux_t *p_demux = (demux_t *)p_this;
297 demux_sys_t *p_sys;
298 es_format_t fmt;
299 const uint8_t *p_peek;
300 int i;
302 /* peek at the first 12 bytes. */
303 /* for TY streams, they're always the same */
304 if( vlc_stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
305 return VLC_EGENERIC;
307 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
308 U32_AT(&p_peek[4]) != 0x02 ||
309 U32_AT(&p_peek[8]) != CHUNK_SIZE )
311 if( !p_demux->obj.force &&
312 !demux_IsPathExtension( p_demux, ".ty" ) &&
313 !demux_IsPathExtension( p_demux, ".ty+" ) )
314 return VLC_EGENERIC;
315 msg_Warn( p_demux, "this does not look like a TY file, "
316 "continuing anyway..." );
319 /* at this point, we assume we have a valid TY stream */
320 msg_Dbg( p_demux, "valid TY stream detected" );
322 p_sys = malloc(sizeof(demux_sys_t));
323 if( unlikely(p_sys == NULL) )
324 return VLC_ENOMEM;
326 /* Set exported functions */
327 p_demux->pf_demux = Demux;
328 p_demux->pf_control = Control;
330 /* create our structure that will hold all data */
331 p_demux->p_sys = p_sys;
332 memset(p_sys, 0, sizeof(demux_sys_t));
334 /* set up our struct (most were zero'd out with the memset above) */
335 p_sys->b_first_chunk = true;
336 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
337 p_sys->lastAudioPTS = VLC_TICK_INVALID;
338 p_sys->lastVideoPTS = VLC_TICK_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 VLC_DEMUXER_EOF;
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 VLC_DEMUXER_EOF;
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 VLC_DEMUXER_SUCCESS;
443 /* read in this record's payload */
444 if( !( p_block_in = vlc_stream_Block( p_demux->s, l_rec_size ) ) )
445 return VLC_DEMUXER_EOF;
447 /* set these as 'unknown' for now */
448 p_block_in->i_pts =
449 p_block_in->i_dts = VLC_TICK_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 );
477 /* fall-through */
479 case 0x03: /* tivo data services */
480 case 0x05: /* unknown, but seen regularly */
481 if( p_block_in )
482 block_Release( p_block_in );
485 /* */
486 p_sys->i_cur_rec++;
487 return VLC_DEMUXER_SUCCESS;
490 /* Control */
491 static int Control(demux_t *p_demux, int i_query, va_list args)
493 demux_sys_t *p_sys = p_demux->p_sys;
494 double f, *pf;
495 int64_t i64;
497 /*msg_Info(p_demux, "control cmd %d", i_query);*/
498 switch( i_query )
500 case DEMUX_CAN_SEEK:
501 *va_arg( args, bool * ) = p_sys->b_seekable;
502 return VLC_SUCCESS;
504 case DEMUX_GET_POSITION:
505 /* arg is 0.0 - 1.0 percent of overall file position */
506 if( ( i64 = p_sys->i_stream_size ) > 0 )
508 pf = va_arg( args, double* );
509 *pf = ((double)1.0) * vlc_stream_Tell( p_demux->s ) / (double) i64;
510 return VLC_SUCCESS;
512 return VLC_EGENERIC;
514 case DEMUX_SET_POSITION:
515 /* arg is 0.0 - 1.0 percent of overall file position */
516 f = va_arg( args, double );
517 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
518 if ((i64 = p_sys->i_stream_size) > 0)
519 return ty_stream_seek_pct(p_demux, f);
520 return VLC_EGENERIC;
521 case DEMUX_GET_TIME:
522 /* return TiVo timestamp */
523 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
524 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
525 // p_sys->l_last_ty_pts_sync);
526 *va_arg(args, vlc_tick_t *) = VLC_TICK_FROM_NS(p_sys->l_last_ty_pts);
527 return VLC_SUCCESS;
528 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
529 /* size / bitrate */
530 *va_arg(args, vlc_tick_t *) = 0;
531 return VLC_SUCCESS;
532 case DEMUX_SET_TIME: /* arg is time in microsecs */
533 return ty_stream_seek_time(p_demux,
534 NS_FROM_VLC_TICK(va_arg( args, vlc_tick_t )));
535 case DEMUX_CAN_PAUSE:
536 case DEMUX_SET_PAUSE_STATE:
537 case DEMUX_CAN_CONTROL_PACE:
538 case DEMUX_GET_PTS_DELAY:
539 return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
540 case DEMUX_GET_FPS:
541 default:
542 return VLC_EGENERIC;
546 /* Close */
547 static void Close( vlc_object_t *p_this )
549 demux_t *p_demux = (demux_t*)p_this;
550 demux_sys_t *p_sys = p_demux->p_sys;
552 XdsExit( &p_sys->xds );
553 cc_Exit( &p_sys->cc );
554 free( p_sys->rec_hdrs );
555 free( p_sys->seq_table );
556 free(p_sys);
560 /* =========================================================================== */
561 /* Compute Presentation Time Stamp (PTS)
562 * Assume buf points to beginning of PTS */
563 static vlc_tick_t get_pts( const uint8_t *buf )
565 stime_t i_pts = GetPESTimestamp( buf );
566 return FROM_SCALE_NZ(i_pts); /* convert PTS (90Khz clock) to microseconds */
570 /* =========================================================================== */
571 static int find_es_header( const uint8_t *header,
572 const uint8_t *buffer, int i_search_len )
574 int count;
576 for( count = 0; count < i_search_len; count++ )
578 if( !memcmp( &buffer[count], header, 4 ) )
579 return count;
581 return -1;
585 /* =========================================================================== */
586 /* check if we have a full PES header, if not, then save what we have.
587 * this is called when audio-start packets are encountered.
588 * Returns:
589 * 1 partial PES hdr found, some audio data found (buffer adjusted),
590 * -1 partial PES hdr found, no audio data found
591 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
592 /* TODO: HD support -- nothing known about those streams */
593 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
594 int32_t offset, int32_t rec_len )
596 demux_sys_t *p_sys = p_demux->p_sys;
598 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
600 /* entire PES header not present */
601 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
602 offset );
603 /* save the partial pes header */
604 if( offset < 0 )
606 /* no header found, fake some 00's (this works, believe me) */
607 memset( p_sys->pes_buffer, 0, 4 );
608 p_sys->i_pes_buf_cnt = 4;
609 if( rec_len > 4 )
610 msg_Err( p_demux, "PES header not found in record of %d bytes!",
611 rec_len );
612 return -1;
614 /* copy the partial pes header we found */
615 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
616 rec_len - offset );
617 p_sys->i_pes_buf_cnt = rec_len - offset;
619 if( offset > 0 )
621 /* PES Header was found, but not complete, so trim the end of this record */
622 p_block->i_buffer -= rec_len - offset;
623 return 1;
625 return -1; /* partial PES, no audio data */
627 /* full PES header present, extract PTS */
628 p_sys->lastAudioPTS = VLC_TICK_0 + get_pts( &p_block->p_buffer[ offset +
629 p_sys->i_Pts_Offset ] );
630 p_block->i_pts = p_sys->lastAudioPTS;
631 /*msg_Dbg(p_demux, "Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
632 /* adjust audio record to remove PES header */
633 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
634 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
635 p_block->i_buffer -= p_sys->i_Pes_Length;
636 #if 0
637 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
638 "%02x %02x %02x %02x %02x %02x %02x %02x "
639 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
640 p_block->p_buffer[0], p_block->p_buffer[1],
641 p_block->p_buffer[2], p_block->p_buffer[3],
642 p_block->p_buffer[4], p_block->p_buffer[5],
643 p_block->p_buffer[6], p_block->p_buffer[7],
644 p_block->p_buffer[8], p_block->p_buffer[9],
645 p_block->p_buffer[10], p_block->p_buffer[11],
646 p_block->p_buffer[12], p_block->p_buffer[13],
647 p_block->p_buffer[14], p_block->p_buffer[15]);
648 #endif
649 return 0;
652 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
654 demux_sys_t *p_sys = p_demux->p_sys;
655 const int subrec_type = rec_hdr->subrec_type;
656 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
657 int esOffset1;
658 int i;
660 assert( rec_hdr->rec_type == 0xe0 );
661 if( !p_block_in )
662 return -1;
664 #if 0
665 msg_Dbg(p_demux, "packet buffer has "
666 "%02x %02x %02x %02x %02x %02x %02x %02x "
667 "%02x %02x %02x %02x %02x %02x %02x %02x",
668 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
669 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
670 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
671 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
672 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
673 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
674 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
675 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
676 #endif
677 //if( subrec_type == 0x06 || subrec_type == 0x07 )
678 if( subrec_type != 0x02 && subrec_type != 0x0c &&
679 subrec_type != 0x08 && l_rec_size > 4 )
681 /* get the PTS from this packet if it has one.
682 * on S1, only 0x06 has PES. On S2, however, most all do.
683 * Do NOT Pass the PES Header to the MPEG2 codec */
684 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
685 if( esOffset1 != -1 )
687 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
688 //subrec_type, esOffset1);
689 p_sys->lastVideoPTS = VLC_TICK_0 + get_pts(
690 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
691 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
692 p_sys->lastVideoPTS );*/
693 if (subrec_type != 0x06) {
694 /* if we found a PES, and it's not type 6, then we're S2 */
695 /* The packet will have video data (& other headers) so we
696 * chop out the PES header and send the rest */
697 if (l_rec_size >= VIDEO_PES_LENGTH) {
698 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
699 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
700 } else {
701 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
702 " (%ld bytes)", subrec_type, l_rec_size);
703 /* nuke this block; it's too short, but has PES marker */
704 p_block_in->i_buffer = 0;
707 }/* else
708 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
709 subrec_type); */
712 if(subrec_type == 0x06 )
714 /* type 6 (S1 DTivo) has no data, so we're done */
715 block_Release(p_block_in);
716 return 0;
719 /* if it's not a continue blk, then set PTS */
720 if( subrec_type != 0x02 )
722 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
723 subrec_type);*/
724 /* if it's a GOP header, make sure it's legal
725 * (if we have enough data) */
726 /* Some ty files don't have this bit set
727 * and it causes problems */
728 if (subrec_type == 0x0c && l_rec_size >= 6)
729 p_block_in->p_buffer[5] |= 0x08;
730 /* store the TY PTS if there is one */
731 if (subrec_type == 0x07) {
732 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
733 /* should we use audio or video PTS? */
734 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
735 } else {
736 /* yes I know this is a cheap hack. It's the timestamp
737 used for display and skipping fwd/back, so it
738 doesn't have to be accurate to the millisecond.
739 I adjust it here by roughly one 1/30 sec. Yes it
740 will be slightly off for UK streams, but it's OK.
742 p_sys->l_last_ty_pts += 35000000;
743 //p_sys->l_last_ty_pts += 33366667;
745 /* set PTS for this block before we send */
746 if (p_sys->lastVideoPTS != VLC_TICK_INVALID)
748 p_block_in->i_pts = p_sys->lastVideoPTS;
749 /* PTS gets used ONCE.
750 * Any subsequent frames we get BEFORE next PES
751 * header will have their PTS computed in the codec */
752 p_sys->lastVideoPTS = VLC_TICK_INVALID;
756 /* Register the CC decoders when needed */
757 uint64_t i_chans = p_sys->cc.i_608channels;
758 for( i = 0; i_chans > 0; i++, i_chans >>= 1 )
760 if( (i_chans & 1) == 0 || p_sys->p_cc[i] )
761 continue;
763 static const char *ppsz_description[4] = {
764 N_("Closed captions 1"),
765 N_("Closed captions 2"),
766 N_("Closed captions 3"),
767 N_("Closed captions 4"),
770 es_format_t fmt;
773 es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
774 fmt.subs.cc.i_channel = i;
775 fmt.psz_description = strdup( vlc_gettext(ppsz_description[i]) );
776 fmt.i_group = TY_ES_GROUP;
777 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
778 es_format_Clean( &fmt );
781 /* Send the CC data */
782 if( p_block_in->i_pts != VLC_TICK_INVALID && p_sys->cc.i_data > 0 )
784 for( i = 0; i < 4; i++ )
786 if( p_sys->p_cc[i] )
788 block_t *p_cc = block_Alloc( p_sys->cc.i_data );
789 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
790 p_cc->i_pts = p_block_in->i_pts;
791 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
793 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
796 cc_Flush( &p_sys->cc );
799 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
800 //p_sys->i_cur_rec, subrec_type);
801 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
802 return 0;
804 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
806 demux_sys_t *p_sys = p_demux->p_sys;
807 const int subrec_type = rec_hdr->subrec_type;
808 const long l_rec_size = rec_hdr->l_rec_size;
809 int esOffset1;
811 assert( rec_hdr->rec_type == 0xc0 );
812 if( !p_block_in )
813 return -1;
814 #if 0
815 int i;
816 fprintf( stderr, "Audio Packet Header " );
817 for( i = 0 ; i < 24 ; i++ )
818 fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] );
819 fprintf( stderr, "\n" );
820 #endif
822 if( subrec_type == 2 )
824 /* SA or DTiVo Audio Data, no PES (continued block)
825 * ================================================
828 /* continue PES if previous was incomplete */
829 if (p_sys->i_pes_buf_cnt > 0)
831 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
833 msg_Dbg(p_demux, "continuing PES header");
834 /* do we have enough data to complete? */
835 if (i_need >= l_rec_size)
837 /* don't have complete PES hdr; save what we have and return */
838 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
839 p_block_in->p_buffer, l_rec_size);
840 p_sys->i_pes_buf_cnt += l_rec_size;
841 /* */
842 block_Release(p_block_in);
843 return 0;
846 /* we have enough; reconstruct this p_frame with the new hdr */
847 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
848 p_block_in->p_buffer, i_need);
849 /* advance the block past the PES header (don't want to send it) */
850 p_block_in->p_buffer += i_need;
851 p_block_in->i_buffer -= i_need;
852 /* get the PTS out of this PES header (MPEG or AC3) */
853 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
854 esOffset1 = find_es_header(ty_MPEGAudioPacket,
855 p_sys->pes_buffer, 5);
856 else
857 esOffset1 = find_es_header(ty_AC3AudioPacket,
858 p_sys->pes_buffer, 5);
859 if (esOffset1 < 0)
861 /* god help us; something's really wrong */
862 msg_Err(p_demux, "can't find audio PES header in packet");
864 else
866 p_sys->lastAudioPTS = VLC_TICK_0 + get_pts(
867 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
868 p_block_in->i_pts = p_sys->lastAudioPTS;
870 p_sys->i_pes_buf_cnt = 0;
872 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
873 * not allowed in the AC3 spec and will cause problems. So here
874 * we try to trim things. */
875 /* Also, S1 DTivo has alternating short / long AC3 packets. That
876 * is, one packet is short (incomplete) and the next packet has
877 * the first one's missing data, plus all of its own. Strange. */
878 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
879 p_sys->tivo_series == TIVO_SERIES2) {
880 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
881 AC3_PKT_LENGTH) {
882 p_block_in->i_buffer -= 2;
883 p_sys->l_ac3_pkt_size = 0;
884 } else {
885 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
889 else if( subrec_type == 0x03 )
891 /* MPEG Audio with PES Header, either SA or DTiVo */
892 /* ================================================ */
893 esOffset1 = find_es_header( ty_MPEGAudioPacket,
894 p_block_in->p_buffer, 5 );
896 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
897 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
898 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
899 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
901 /* SA PES Header, No Audio Data */
902 /* ================================================ */
903 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
905 p_sys->lastAudioPTS = VLC_TICK_0 + get_pts( &p_block_in->p_buffer[
906 SA_PTS_OFFSET ] );
908 block_Release(p_block_in);
909 return 0;
910 /*msg_Dbg(p_demux, "SA Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
912 /* DTiVo Audio with PES Header */
913 /* ================================================ */
915 /* Check for complete PES */
916 if (check_sync_pes(p_demux, p_block_in, esOffset1,
917 l_rec_size) == -1)
919 /* partial PES header found, nothing else.
920 * we're done. */
921 block_Release(p_block_in);
922 return 0;
924 #if 0
925 msg_Dbg(p_demux, "packet buffer has "
926 "%02x %02x %02x %02x %02x %02x %02x %02x "
927 "%02x %02x %02x %02x %02x %02x %02x %02x",
928 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
929 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
930 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
931 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
932 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
933 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
934 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
935 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
936 #endif
938 else if( subrec_type == 0x04 )
940 /* SA Audio with no PES Header */
941 /* ================================================ */
942 /*msg_Dbg(p_demux,
943 "Adding SA Audio Packet Size %ld", l_rec_size ); */
945 if (p_sys->lastAudioPTS != VLC_TICK_INVALID )
946 p_block_in->i_pts = p_sys->lastAudioPTS;
948 else if( subrec_type == 0x09 )
950 /* DTiVo AC3 Audio Data with PES Header */
951 /* ================================================ */
952 esOffset1 = find_es_header( ty_AC3AudioPacket,
953 p_block_in->p_buffer, 5 );
955 #if 0
956 msg_Dbg(p_demux, "buffer has "
957 "%02x %02x %02x %02x %02x %02x %02x %02x "
958 "%02x %02x %02x %02x %02x %02x %02x %02x",
959 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
960 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
961 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
962 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
963 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
964 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
965 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
966 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
967 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
968 #endif
970 /* Check for complete PES */
971 if (check_sync_pes(p_demux, p_block_in, esOffset1,
972 l_rec_size) == -1)
974 /* partial PES header found, nothing else. we're done. */
975 block_Release(p_block_in);
976 return 0;
978 /* S2 DTivo has invalid long AC3 packets */
979 if (p_sys->tivo_series == TIVO_SERIES2) {
980 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
981 p_block_in->i_buffer -= 2;
982 p_sys->l_ac3_pkt_size = 0;
983 } else {
984 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
988 else
990 /* Unsupported/Unknown */
991 block_Release(p_block_in);
992 return 0;
995 /* set PCR before we send (if PTS found) */
996 if( p_block_in->i_pts != VLC_TICK_INVALID )
997 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
998 p_block_in->i_pts );
999 /* Send data */
1000 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1001 return 0;
1004 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1006 demux_sys_t *p_sys = p_demux->p_sys;
1007 int i_field;
1009 if( p_block_in )
1010 block_Release(p_block_in);
1012 if( rec_hdr->rec_type == 0x01 )
1013 i_field = 0;
1014 else if( rec_hdr->rec_type == 0x02 )
1015 i_field = 1;
1016 else
1017 return 0;
1019 /* XDS data (extract programs infos) transmitted on field 2 only */
1020 if( i_field == 1 )
1021 DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] );
1023 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1024 return 0;
1026 cc_AppendData( &p_sys->cc, CC_PKT_BYTE0(i_field), rec_hdr->ex );
1027 return 0;
1030 /* seek to a position within the stream, if possible */
1031 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1033 demux_sys_t *p_sys = p_demux->p_sys;
1034 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1035 uint64_t l_skip_amt;
1036 unsigned i_cur_part;
1038 /* if we're not seekable, there's nothing to do */
1039 if (!p_sys->b_seekable)
1040 return VLC_EGENERIC;
1042 /* figure out which part & chunk we want & go there */
1043 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1044 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1046 /* try to read the part header (master chunk) if it's there */
1047 if (vlc_stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ) ||
1048 parse_master(p_demux) != VLC_SUCCESS)
1050 /* can't seek stream */
1051 return VLC_EGENERIC;
1054 /* now for the actual chunk */
1055 if ( vlc_stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1057 /* can't seek stream */
1058 return VLC_EGENERIC;
1060 /* load the chunk */
1061 p_sys->i_stuff_cnt = 0;
1062 get_chunk_header(p_demux);
1064 /* seek within the chunk to get roughly to where we want */
1065 p_sys->i_cur_rec = (int)
1066 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1067 msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1068 msg_Dbg(p_demux, " (chunk %d, record %d)",
1069 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1071 /* seek to the start of this record's data.
1072 * to do that, we have to skip past all prior records */
1073 l_skip_amt = 0;
1074 for ( int i=0; i<p_sys->i_cur_rec; i++)
1075 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1076 if( vlc_stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1077 (p_sys->i_num_recs * 16) + l_skip_amt + 4) != VLC_SUCCESS )
1078 return VLC_EGENERIC;
1080 /* to hell with syncing any audio or video, just start reading records... :) */
1081 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TICK_INVALID;*/
1082 return VLC_SUCCESS;
1085 /* XDS decoder */
1086 //#define TY_XDS_DEBUG
1087 static void XdsInit( xds_t *h )
1089 h->b_xds = false;
1090 h->i_class = XDS_MAX_CLASS_COUNT;
1091 h->i_type = 0;
1092 h->b_future = false;
1093 for( int i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1095 for( int j = 0; j < 128; j++ )
1096 h->pkt[i][j].b_started = false;
1098 h->b_meta_changed = false;
1099 memset( &h->meta, 0, sizeof(h->meta) );
1101 static void XdsExit( xds_t *h )
1103 /* */
1104 free( h->meta.psz_channel_name );
1105 free( h->meta.psz_channel_call_letter );
1106 free( h->meta.psz_channel_number );
1108 /* */
1109 free( h->meta.current.psz_name );
1110 free( h->meta.current.psz_rating );
1111 /* */
1112 free( h->meta.future.psz_name );
1113 free( h->meta.future.psz_rating );
1115 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, size_t i_src )
1117 size_t i_dst = 0;
1118 for( size_t i = 0; i < i_src; i++ )
1120 switch( p_src[i] )
1122 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1123 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1124 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1125 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1126 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1127 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1128 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1129 E2( 0x7c, 0xc3,0xb7); // division symbol
1130 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1131 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1132 #undef E2
1133 default:
1134 dst[i_dst++] = p_src[i];
1135 break;
1138 dst[i_dst++] = '\0';
1140 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1142 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1143 return false;
1144 if( *ppsz_dst == NULL && psz_new == NULL )
1145 return false;
1147 free( *ppsz_dst );
1148 if( psz_new )
1149 *ppsz_dst = strdup( psz_new );
1150 else
1151 *ppsz_dst = NULL;
1153 h->b_meta_changed = true;
1154 return true;
1157 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1159 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1160 char name[2*32+1];
1161 int i_rating;
1163 switch( h->i_type )
1165 case 0x03:
1166 XdsStringUtf8( name, pk->p_data, pk->i_data );
1167 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1169 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1170 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1172 break;
1173 case 0x05:
1174 i_rating = (pk->p_data[0] & 0x18);
1175 if( i_rating == 0x08 )
1177 /* TPG */
1178 static const char *pppsz_ratings[8][2] = {
1179 { "None", "No rating (no content advisory)" },
1180 { "TV-Y", "All Children (no content advisory)" },
1181 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1182 { "TV-G", "General Audience (no content advisory)" },
1183 { "TV-PG", "Parental Guidance Suggested" },
1184 { "TV-14", "Parents Strongly Cautioned" },
1185 { "TV-MA", "Mature Audience Only" },
1186 { "None", "No rating (no content advisory)" }
1188 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1189 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1191 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1192 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1193 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1196 else if( i_rating == 0x00 || i_rating == 0x10 )
1198 /* MPAA */
1199 static const char *pppsz_ratings[8][2] = {
1200 { "N/A", "N/A" },
1201 { "G", "General Audiences" },
1202 { "PG", "Parental Guidance Suggested" },
1203 { "PG-13", "Parents Strongly Cautioned" },
1204 { "R", "Restricted" },
1205 { "NC-17", "No one 17 and under admitted" },
1206 { "X", "No one under 17 admitted" },
1207 { "NR", "Not Rated" },
1209 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1210 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1212 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1213 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1214 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1217 else
1219 /* Non US Rating TODO */
1220 assert( i_rating == 0x18 ); // only left value possible */
1221 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1222 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1224 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1225 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1228 break;
1230 default:
1231 #ifdef TY_XDS_DEBUG
1232 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1233 #endif
1234 break;
1238 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1240 char name[2*32+1];
1241 char chan[2*32+1];
1243 switch( h->i_type )
1245 case 0x01:
1246 if( pk->i_data < 2 )
1247 return;
1248 XdsStringUtf8( name, pk->p_data, pk->i_data );
1249 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1251 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1252 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1254 break;
1256 case 0x02:
1257 if( pk->i_data < 4 )
1258 return;
1260 XdsStringUtf8( name, pk->p_data, 4 );
1261 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1263 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1264 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1266 if( pk->i_data >= 6 )
1268 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1269 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1271 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1272 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1275 else
1277 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1279 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1280 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1283 break;
1284 case 0x03:
1285 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1286 break;
1287 case 0x04:
1288 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1289 break;
1290 default:
1291 #ifdef TY_XDS_DEBUG
1292 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1293 #endif
1294 break;
1298 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1300 switch( h->i_class )
1302 case XDS_CLASS_CURRENT:
1303 case XDS_CLASS_FUTURE:
1304 XdsDecodeCurrentFuture( h, pk );
1305 break;
1306 case XDS_CLASS_CHANNEL:
1307 XdsDecodeChannel( h, pk );
1308 break;
1309 case XDS_CLASS_MISCELLANEOUS:
1310 #ifdef TY_XDS_DEBUG
1311 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1312 #endif
1313 break;
1314 case XDS_CLASS_PUBLIC_SERVICE:
1315 #ifdef TY_XDS_DEBUG
1316 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1317 #endif
1318 break;
1319 default:
1320 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1321 break;
1325 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1327 /* TODO check parity */
1328 d1 &= 0x7f;
1329 d2 &= 0x7f;
1331 /* */
1332 if( d1 >= 0x01 && d1 <= 0x0e )
1334 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1335 const int i_type = d2;
1336 const bool b_start = d1 & 0x01;
1337 xds_packet_t *pk = &h->pkt[i_class][i_type];
1339 if( !b_start && !pk->b_started )
1341 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1342 h->b_xds = false;
1343 return;
1346 h->b_xds = true;
1347 h->i_class = i_class;
1348 h->i_type = i_type;
1349 h->b_future = !b_start;
1350 pk->b_started = true;
1351 if( b_start )
1353 pk->i_data = 0;
1354 pk->i_sum = d1 + d2;
1357 else if( d1 == 0x0f && h->b_xds )
1359 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1361 /* TODO checksum and decode */
1362 pk->i_sum += d1 + d2;
1363 if( pk->i_sum & 0x7f )
1365 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1366 pk->b_started = false;
1367 return;
1369 if( pk->i_data <= 0 )
1371 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1372 pk->b_started = false;
1373 return;
1376 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1377 // pk->i_data--;
1378 XdsDecode( h, pk );
1380 /* Reset it */
1381 pk->b_started = false;
1383 else if( d1 >= 0x20 && h->b_xds )
1385 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1387 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1389 /* Broken -> reinit */
1390 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1391 h->b_xds = false;
1392 pk->b_started = false;
1393 return;
1395 /* TODO check parity bit */
1396 pk->p_data[pk->i_data++] = d1 & 0x7f;
1397 pk->p_data[pk->i_data++] = d2 & 0x7f;
1398 pk->i_sum += d1+d2;
1400 else
1402 h->b_xds = false;
1406 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1408 demux_sys_t *p_sys = p_demux->p_sys;
1410 XdsParse( &p_sys->xds, d1, d2 );
1411 if( p_sys->xds.b_meta_changed )
1413 xds_meta_t *m = &p_sys->xds.meta;
1414 vlc_meta_t *p_meta;
1416 /* Channel meta data */
1417 p_meta = vlc_meta_New();
1418 if( m->psz_channel_name )
1419 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1420 if( m->psz_channel_call_letter )
1421 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1422 if( m->psz_channel_number )
1423 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1424 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1425 vlc_meta_Delete( p_meta );
1427 /* Event meta data (current/future) */
1428 if( m->current.psz_name )
1430 vlc_epg_t *p_epg = vlc_epg_New( TY_ES_GROUP, TY_ES_GROUP );
1431 if ( p_epg )
1433 vlc_epg_event_t *p_evt = vlc_epg_event_New( 0, 0, 0 );
1434 if ( p_evt )
1436 if( m->current.psz_name )
1437 p_evt->psz_name = strdup( m->current.psz_name );
1438 if( !vlc_epg_AddEvent( p_epg, p_evt ) )
1439 vlc_epg_event_Delete( p_evt );
1441 //if( m->current.psz_rating )
1442 // TODO but VLC cannot yet handle rating per epg event
1443 vlc_epg_SetCurrent( p_epg, 0 );
1445 if( m->future.psz_name )
1448 if( p_epg->i_event > 0 )
1449 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
1450 TY_ES_GROUP, p_epg );
1451 vlc_epg_Delete( p_epg );
1455 p_sys->xds.b_meta_changed = false;
1458 /* seek to an exact time position within the stream, if possible.
1459 * l_seek_time is in nanoseconds, the TIVO time standard.
1461 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1463 demux_sys_t *p_sys = p_demux->p_sys;
1464 unsigned i_seq_entry = 0;
1465 unsigned i;
1466 int i_skip_cnt;
1467 int64_t l_cur_pos = vlc_stream_Tell(p_demux->s);
1468 unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1469 uint64_t l_seek_secs = l_seek_time / 1000000000;
1470 uint64_t l_fwd_stamp = 1;
1472 /* if we're not seekable, there's nothing to do */
1473 if (!p_sys->b_seekable || !p_sys->b_have_master)
1474 return VLC_EGENERIC;
1476 msg_Dbg(p_demux, "Skipping to time %02"PRIu64":%02"PRIu64":%02"PRIu64,
1477 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1479 /* seek to the proper segment if necessary */
1480 /* first see if we need to go back */
1481 while (l_seek_time < p_sys->l_first_ty_pts) {
1482 msg_Dbg(p_demux, "skipping to prior segment.");
1483 /* load previous part */
1484 if (i_cur_part == 0) {
1485 p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1486 msg_Err(p_demux, "Attempt to seek past BOF");
1487 return VLC_EGENERIC;
1489 if(vlc_stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1490 return VLC_EGENERIC;
1491 i_cur_part--;
1492 if(parse_master(p_demux) != VLC_SUCCESS)
1493 return VLC_EGENERIC;
1495 /* maybe we need to go forward */
1496 while (l_seek_time > p_sys->l_final_ty_pts) {
1497 msg_Dbg(p_demux, "skipping to next segment.");
1498 /* load next part */
1499 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1500 /* error; restore previous file position */
1501 p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1502 msg_Err(p_demux, "seek error");
1503 return VLC_EGENERIC;
1505 if(vlc_stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1506 return VLC_EGENERIC;
1507 i_cur_part++;
1508 if(parse_master(p_demux) != VLC_SUCCESS)
1509 return VLC_EGENERIC;
1512 /* our target is somewhere within this part;
1513 find the proper chunk using seq_table */
1514 for (i=1; i<p_sys->i_seq_table_size; i++) {
1515 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1516 /* i-1 is the section we want; remember the next timestamp in case
1517 we have to use it (this section may not have a proper SEQ hdr
1518 for the time we're seeking) */
1519 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1520 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1521 i_seq_entry = i-1;
1522 break;
1526 /* if we went through the entire last loop and didn't find our target,
1527 then we skip to the next part. What has happened is that the actual
1528 time we're seeking is within this part, but there isn't a SEQ hdr
1529 for it here. So we skip to the next part */
1530 if (i == p_sys->i_seq_table_size) {
1531 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1532 /* error; restore previous file position */
1533 p_sys->eof = (vlc_stream_Seek(p_demux->s, l_cur_pos) != VLC_SUCCESS);
1534 msg_Err(p_demux, "seek error");
1535 return VLC_EGENERIC;
1537 if(vlc_stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH) != VLC_SUCCESS)
1538 return VLC_EGENERIC;
1539 i_cur_part++;
1540 if(parse_master(p_demux) != VLC_SUCCESS)
1541 return VLC_EGENERIC;
1542 i_seq_entry = 0;
1545 /* determine which chunk has our seek_time */
1546 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1547 uint64_t l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1548 uint64_t l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1549 msg_Dbg(p_demux, "testing part %d chunk %"PRIu64" mask 0x%02X bit %d",
1550 i_cur_part, l_chunk_nr,
1551 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1552 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1553 /* check this chunk's SEQ header timestamp */
1554 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%"PRIu64,
1555 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1556 if(vlc_stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1557 l_chunk_offset) != VLC_SUCCESS)
1558 return VLC_EGENERIC;
1559 // TODO: we don't have to parse the full header set;
1560 // just test the seq_rec entry for its timestamp
1561 p_sys->i_stuff_cnt = 0;
1562 get_chunk_header(p_demux);
1563 // check ty PTS for the SEQ entry in this chunk
1564 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1565 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1566 /* Seek to beginning of original chunk & reload it */
1567 if(vlc_stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE) != VLC_SUCCESS)
1568 p_sys->eof = true;
1569 p_sys->i_stuff_cnt = 0;
1570 get_chunk_header(p_demux);
1571 return VLC_EGENERIC;
1573 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1574 1000000000;
1575 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02"PRIu64":%02"PRIu64":%02"PRIu64,
1576 l_seek_secs / 3600,
1577 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1578 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1579 // keep this one? go back?
1580 /* for now, we take this one. it's the first SEQ hdr AFTER
1581 the time we were searching for. */
1582 msg_Dbg(p_demux, "seek target found.");
1583 break;
1585 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1588 /* if we made it through this entire loop without finding our target,
1589 then we skip to the next section. What has happened is that the actual
1590 time we're seeking is within this section, but there isn't a SEQ hdr
1591 for it here. So we skip to the next closest one (l_fwd_stamp) */
1592 if (i == p_sys->i_bits_per_seq_entry)
1593 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1595 /* current stream ptr is at beginning of data for this chunk,
1596 so we need to skip past any stream data prior to the seq_rec
1597 in this chunk */
1598 i_skip_cnt = 0;
1599 for (int j=0; j<p_sys->i_seq_rec; j++)
1600 i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size;
1601 if(vlc_stream_Read(p_demux->s, NULL, i_skip_cnt) != i_skip_cnt)
1602 return VLC_EGENERIC;
1603 p_sys->i_cur_rec = p_sys->i_seq_rec;
1604 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1605 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1607 return VLC_SUCCESS;
1611 /* parse a master chunk, filling the SEQ table and other variables.
1612 * We assume the stream is currently pointing to it.
1614 static int parse_master(demux_t *p_demux)
1616 demux_sys_t *p_sys = p_demux->p_sys;
1617 uint8_t mst_buf[32];
1618 int64_t i_save_pos = vlc_stream_Tell(p_demux->s);
1619 int64_t i_pts_secs;
1621 /* Note that the entries in the SEQ table in the stream may have
1622 different sizes depending on the bits per entry. We store them
1623 all in the same size structure, so we have to parse them out one
1624 by one. If we had a dynamic structure, we could simply read the
1625 entire table directly from the stream into memory in place. */
1627 /* clear the SEQ table */
1628 free(p_sys->seq_table);
1630 /* parse header info */
1631 if( vlc_stream_Read(p_demux->s, mst_buf, 32) != 32 )
1632 return VLC_EGENERIC;
1634 uint32_t i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1635 uint32_t i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1637 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1638 p_sys->i_seq_table_size = i / (8 + i_map_size);
1640 if(p_sys->i_seq_table_size == 0)
1642 p_sys->seq_table = NULL;
1643 return VLC_SUCCESS;
1646 /* parse all the entries */
1647 p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t));
1648 if (p_sys->seq_table == NULL)
1650 p_sys->i_seq_table_size = 0;
1651 return VLC_SUCCESS;
1653 for (unsigned j=0; j<p_sys->i_seq_table_size; j++) {
1654 if(vlc_stream_Read(p_demux->s, mst_buf, 8) != 8)
1655 return VLC_EGENERIC;
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 if(vlc_stream_Read(p_demux->s, NULL, i_map_size) != i_map_size)
1660 return VLC_EGENERIC;
1661 } else {
1662 if(vlc_stream_Read(p_demux->s, mst_buf + 8, i_map_size) != i_map_size)
1663 return VLC_EGENERIC;
1664 memcpy(p_sys->seq_table[j].chunk_bitmask, &mst_buf[8], i_map_size);
1668 /* set up a few of our variables */
1669 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1670 p_sys->l_final_ty_pts =
1671 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1672 p_sys->b_have_master = true;
1674 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1675 msg_Dbg( p_demux,
1676 "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1677 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1678 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1679 msg_Dbg( p_demux,
1680 "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1681 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1683 /* seek past this chunk */
1684 return vlc_stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1688 /* ======================================================================== */
1689 /* "Peek" at some chunks. Skip over the Part header if we find it.
1690 * We parse the peeked data and determine audio type,
1691 * SA vs. DTivo, & Tivo Series.
1692 * Set global vars i_Pes_Length, i_Pts_Offset,
1693 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1694 static int probe_stream(demux_t *p_demux)
1696 demux_sys_t *p_sys = p_demux->p_sys;
1697 const uint8_t *p_buf;
1698 int i;
1699 bool b_probe_error = false;
1701 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1702 if (vlc_stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1703 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1704 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1705 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1706 return VLC_EGENERIC;
1709 /* the real work: analyze this chunk */
1710 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1711 analyze_chunk(p_demux, p_buf);
1712 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1713 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1714 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1715 break;
1716 p_buf += CHUNK_SIZE;
1719 /* the final tally */
1720 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1721 msg_Err(p_demux, "Can't determine Tivo Series.");
1722 b_probe_error = true;
1724 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1725 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1726 b_probe_error = true;
1728 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1729 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1730 b_probe_error = true;
1732 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1736 /* ======================================================================== */
1737 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1738 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1740 demux_sys_t *p_sys = p_demux->p_sys;
1741 int i_num_recs, i;
1742 ty_rec_hdr_t *p_hdrs;
1743 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1744 int i_payload_size;
1746 /* skip if it's a Part header */
1747 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1748 return;
1750 /* number of records in chunk (we ignore high order byte;
1751 * rarely are there > 256 chunks & we don't need that many anyway) */
1752 i_num_recs = p_chunk[0];
1753 if (i_num_recs < 5) {
1754 /* try again with the next chunk. Sometimes there are dead ones */
1755 return;
1758 p_chunk += 4; /* skip past rec count & SEQ bytes */
1759 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1760 p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
1761 /* scan headers.
1762 * 1. check video packets. Presence of 0x6e0 means S1.
1763 * No 6e0 but have be0 means S2.
1764 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1765 * If AC-3, then we have DTivo.
1766 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1768 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1769 for (i=0; i<i_num_recs; i++) {
1770 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1771 //p_hdrs[i].rec_type,
1772 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1773 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1774 case 0x6e0:
1775 i_num_6e0++;
1776 break;
1777 case 0xbe0:
1778 i_num_be0++;
1779 break;
1780 case 0x3c0:
1781 i_num_3c0++;
1782 break;
1783 case 0x9c0:
1784 i_num_9c0++;
1785 break;
1788 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1789 i_num_6e0, i_num_be0);
1791 /* set up our variables */
1792 if (i_num_6e0 > 0) {
1793 msg_Dbg(p_demux, "detected Series 1 Tivo");
1794 p_sys->tivo_series = TIVO_SERIES1;
1795 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1796 } else if (i_num_be0 > 0) {
1797 msg_Dbg(p_demux, "detected Series 2 Tivo");
1798 p_sys->tivo_series = TIVO_SERIES2;
1799 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1801 if (i_num_9c0 > 0) {
1802 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1803 p_sys->audio_type = TIVO_AUDIO_AC3;
1804 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1805 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1806 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1807 } else if (i_num_3c0 > 0) {
1808 p_sys->audio_type = TIVO_AUDIO_MPEG;
1809 msg_Dbg(p_demux, "detected MPEG Audio" );
1812 /* if tivo_type still unknown, we can check PTS location
1813 * in MPEG packets to determine tivo_type */
1814 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1815 uint32_t i_data_offset = (16 * i_num_recs);
1816 for (i=0; i<i_num_recs; i++) {
1817 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1818 p_hdrs[i].l_rec_size > 15) {
1819 /* first make sure we're aligned */
1820 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1821 &p_chunk[i_data_offset], 5);
1822 if (i_pes_offset >= 0) {
1823 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1824 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1825 //i, i_pes_offset);
1826 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1827 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1828 if (p_sys->tivo_series == TIVO_SERIES1)
1829 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1830 p_sys->tivo_type = TIVO_TYPE_SA;
1831 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1832 } else {
1833 if (p_sys->tivo_series == TIVO_SERIES1)
1834 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1835 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1836 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1838 break;
1841 i_data_offset += p_hdrs[i].l_rec_size;
1844 free(p_hdrs);
1848 /* =========================================================================== */
1849 static int get_chunk_header(demux_t *p_demux)
1851 int i_readSize, i_num_recs;
1852 uint8_t *p_hdr_buf;
1853 const uint8_t *p_peek;
1854 demux_sys_t *p_sys = p_demux->p_sys;
1855 int i_payload_size; /* sum of all records' sizes */
1857 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1859 /* if we have left-over filler space from the last chunk, get that */
1860 if (p_sys->i_stuff_cnt > 0) {
1861 if(vlc_stream_Read(p_demux->s, NULL, p_sys->i_stuff_cnt) != p_sys->i_stuff_cnt)
1862 return 0;
1863 p_sys->i_stuff_cnt = 0;
1866 /* read the TY packet header */
1867 i_readSize = vlc_stream_Peek( p_demux->s, &p_peek, 4 );
1868 p_sys->i_cur_chunk++;
1870 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1872 /* EOF */
1873 p_sys->eof = 1;
1874 return 0;
1877 /* check if it's a PART Header */
1878 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1880 /* parse master chunk */
1881 if(parse_master(p_demux) != VLC_SUCCESS)
1882 return 0;
1883 return get_chunk_header(p_demux);
1886 /* number of records in chunk (8- or 16-bit number) */
1887 if (p_peek[3] & 0x80)
1889 /* 16 bit rec cnt */
1890 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1891 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1892 if (p_sys->i_seq_rec != 0xffff)
1894 p_sys->i_seq_rec &= ~0x8000;
1897 else
1899 /* 8 bit reclen - tivo 1.3 format */
1900 p_sys->i_num_recs = i_num_recs = p_peek[0];
1901 p_sys->i_seq_rec = p_peek[1];
1903 p_sys->i_cur_rec = 0;
1904 p_sys->b_first_chunk = false;
1906 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1908 free(p_sys->rec_hdrs);
1909 p_sys->rec_hdrs = NULL;
1911 /* skip past the 4 bytes we "peeked" earlier */
1912 if(vlc_stream_Read(p_demux->s, NULL, 4) != 4)
1913 return 0;
1915 /* read the record headers into a temp buffer */
1916 p_hdr_buf = xmalloc(i_num_recs * 16);
1917 if (vlc_stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1918 free( p_hdr_buf );
1919 p_sys->eof = true;
1920 return 0;
1922 /* parse them */
1923 p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs,
1924 &i_payload_size);
1925 free(p_hdr_buf);
1927 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1928 (p_sys->i_num_recs * 16) - i_payload_size;
1929 if (p_sys->i_stuff_cnt > 0)
1930 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1931 p_sys->i_stuff_cnt );
1932 return 1;
1936 static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
1937 int i_num_recs, int *pi_payload_size)
1939 int i;
1940 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1942 *pi_payload_size = 0;
1943 p_hdrs = xmalloc(i_num_recs * sizeof(ty_rec_hdr_t));
1945 for (i = 0; i < i_num_recs; i++)
1947 const uint8_t *record_header = p_buf + (i * 16);
1948 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1949 p_rec_hdr->rec_type = record_header[3];
1950 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1951 if ((record_header[ 0 ] & 0x80) == 0x80)
1953 uint8_t b1, b2;
1954 /* marker bit 2 set, so read extended data */
1955 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1956 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1957 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1958 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1960 p_rec_hdr->ex[0] = b1;
1961 p_rec_hdr->ex[1] = b2;
1962 p_rec_hdr->l_rec_size = 0;
1963 p_rec_hdr->l_ty_pts = 0;
1964 p_rec_hdr->b_ext = true;
1966 else
1968 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1969 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1970 *pi_payload_size += p_rec_hdr->l_rec_size;
1971 p_rec_hdr->b_ext = false;
1972 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1974 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1975 } /* end of record-header loop */
1976 return p_hdrs;