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
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.
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 /*****************************************************************************
36 *****************************************************************************/
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_demux.h>
45 #include <vlc_codec.h>
47 #include <vlc_input.h>
48 #include "../codec/cc.h"
52 /*****************************************************************************
54 *****************************************************************************/
55 static int Open ( vlc_object_t
* );
56 static void Close( vlc_object_t
* );
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")
72 /*****************************************************************************
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)
111 #define TIVO_PES_FILEID ( 0xf5467abd )
112 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
113 #define CHUNK_SIZE ( 128 * 1024 )
122 uint64_t l_ty_pts
; /* TY PTS in the record header */
127 uint64_t l_timestamp
;
128 uint8_t chunk_bitmask
[8];
152 #define XDS_MAX_DATA_SIZE (32)
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,
170 uint8_t p_data
[XDS_MAX_DATA_SIZE
];
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
;
183 xds_meta_program_rating_t rating
;
185 /* Add the other fields once I have the samples */
186 } xds_meta_program_t
;
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
;
198 /* Are we in XDS mode */
201 /* Current class type */
207 xds_packet_t pkt
[XDS_MAX_CLASS_COUNT
][128]; /* XXX it is way too much, but simpler */
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 */
221 es_out_id_t
*p_cc
[4];
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 */
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
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
;
298 const uint8_t *p_peek
;
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 )
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+" ) )
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
) )
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
) {
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
);
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
);
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
);
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
;
395 block_t
*p_block_in
= NULL
;
397 /*msg_Dbg(p_demux, "ty demux processing" );*/
399 /* did we hit EOF earlier? */
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 )
424 /*======================================================================
425 * parse & send one record of the chunk
426 *====================================================================== */
427 p_rec
= &p_sys
->rec_hdrs
[p_sys
->i_cur_rec
];
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 */
443 /* read in this record's payload */
444 if( !( p_block_in
= vlc_stream_Block( p_demux
->s
, l_rec_size
) ) )
447 /* set these as 'unknown' for now */
449 p_block_in
->i_dts
= VLC_TS_INVALID
;
453 -- don't read any data from the stream, data was in the record header --
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
);
465 case 0xc0: /* audio */
466 DemuxRecAudio( p_demux
, p_rec
, p_block_in
);
471 /* closed captions/XDS */
472 DemuxRecCc( p_demux
, p_rec
, p_block_in
);
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 */
481 block_Release( p_block_in
);
490 static int Control(demux_t
*p_demux
, int i_query
, va_list args
)
492 demux_sys_t
*p_sys
= p_demux
->p_sys
;
496 /*msg_Info(p_demux, "control cmd %d", i_query);*/
500 *va_arg( args
, bool * ) = p_sys
->b_seekable
;
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
;
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
);
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);
528 case DEMUX_GET_LENGTH
: /* length of program in microseconds, 0 if unk */
530 p_i64
= va_arg(args
, int64_t *);
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);
543 static void Close( vlc_object_t
*p_this
)
545 demux_t
*p_demux
= (demux_t
*)p_this
;
546 demux_sys_t
*p_sys
= p_demux
->p_sys
;
548 XdsExit( &p_sys
->xds
);
549 cc_Exit( &p_sys
->cc
);
550 free( p_sys
->rec_hdrs
);
551 free( p_sys
->seq_table
);
556 /* =========================================================================== */
557 /* Compute Presentation Time Stamp (PTS)
558 * Assume buf points to beginning of PTS */
559 static mtime_t
get_pts( const uint8_t *buf
)
563 i_pts
= ((mtime_t
)(buf
[0]&0x0e ) << 29)|
564 (mtime_t
)(buf
[1] << 22)|
565 ((mtime_t
)(buf
[2]&0xfe) << 14)|
566 (mtime_t
)(buf
[3] << 7)|
567 (mtime_t
)(buf
[4] >> 1);
568 i_pts
*= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
573 /* =========================================================================== */
574 static int find_es_header( const uint8_t *header
,
575 const uint8_t *buffer
, int i_search_len
)
579 for( count
= 0; count
< i_search_len
; count
++ )
581 if( !memcmp( &buffer
[count
], header
, 4 ) )
588 /* =========================================================================== */
589 /* check if we have a full PES header, if not, then save what we have.
590 * this is called when audio-start packets are encountered.
592 * 1 partial PES hdr found, some audio data found (buffer adjusted),
593 * -1 partial PES hdr found, no audio data found
594 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
595 /* TODO: HD support -- nothing known about those streams */
596 static int check_sync_pes( demux_t
*p_demux
, block_t
*p_block
,
597 int32_t offset
, int32_t rec_len
)
599 demux_sys_t
*p_sys
= p_demux
->p_sys
;
601 if ( offset
< 0 || offset
+ p_sys
->i_Pes_Length
> rec_len
)
603 /* entire PES header not present */
604 msg_Dbg( p_demux
, "PES header at %d not complete in record. storing.",
606 /* save the partial pes header */
609 /* no header found, fake some 00's (this works, believe me) */
610 memset( p_sys
->pes_buffer
, 0, 4 );
611 p_sys
->i_pes_buf_cnt
= 4;
613 msg_Err( p_demux
, "PES header not found in record of %d bytes!",
617 /* copy the partial pes header we found */
618 memcpy( p_sys
->pes_buffer
, p_block
->p_buffer
+ offset
,
620 p_sys
->i_pes_buf_cnt
= rec_len
- offset
;
624 /* PES Header was found, but not complete, so trim the end of this record */
625 p_block
->i_buffer
-= rec_len
- offset
;
628 return -1; /* partial PES, no audio data */
630 /* full PES header present, extract PTS */
631 p_sys
->lastAudioPTS
= VLC_TS_0
+ get_pts( &p_block
->p_buffer
[ offset
+
632 p_sys
->i_Pts_Offset
] );
633 if (p_sys
->firstAudioPTS
< 0)
634 p_sys
->firstAudioPTS
= p_sys
->lastAudioPTS
;
635 p_block
->i_pts
= p_sys
->lastAudioPTS
;
636 /*msg_Dbg(p_demux, "Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
637 /* adjust audio record to remove PES header */
638 memmove(p_block
->p_buffer
+ offset
, p_block
->p_buffer
+ offset
+
639 p_sys
->i_Pes_Length
, rec_len
- p_sys
->i_Pes_Length
);
640 p_block
->i_buffer
-= p_sys
->i_Pes_Length
;
642 msg_Dbg(p_demux
, "pes hdr removed; buffer len=%d and has "
643 "%02x %02x %02x %02x %02x %02x %02x %02x "
644 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block
->i_buffer
,
645 p_block
->p_buffer
[0], p_block
->p_buffer
[1],
646 p_block
->p_buffer
[2], p_block
->p_buffer
[3],
647 p_block
->p_buffer
[4], p_block
->p_buffer
[5],
648 p_block
->p_buffer
[6], p_block
->p_buffer
[7],
649 p_block
->p_buffer
[8], p_block
->p_buffer
[9],
650 p_block
->p_buffer
[10], p_block
->p_buffer
[11],
651 p_block
->p_buffer
[12], p_block
->p_buffer
[13],
652 p_block
->p_buffer
[14], p_block
->p_buffer
[15]);
657 static int DemuxRecVideo( demux_t
*p_demux
, ty_rec_hdr_t
*rec_hdr
, block_t
*p_block_in
)
659 demux_sys_t
*p_sys
= p_demux
->p_sys
;
660 const int subrec_type
= rec_hdr
->subrec_type
;
661 const long l_rec_size
= rec_hdr
->l_rec_size
; // p_block_in->i_buffer might be better
665 assert( rec_hdr
->rec_type
== 0xe0 );
670 msg_Dbg(p_demux
, "packet buffer has "
671 "%02x %02x %02x %02x %02x %02x %02x %02x "
672 "%02x %02x %02x %02x %02x %02x %02x %02x",
673 p_block_in
->p_buffer
[0], p_block_in
->p_buffer
[1],
674 p_block_in
->p_buffer
[2], p_block_in
->p_buffer
[3],
675 p_block_in
->p_buffer
[4], p_block_in
->p_buffer
[5],
676 p_block_in
->p_buffer
[6], p_block_in
->p_buffer
[7],
677 p_block_in
->p_buffer
[8], p_block_in
->p_buffer
[9],
678 p_block_in
->p_buffer
[10], p_block_in
->p_buffer
[11],
679 p_block_in
->p_buffer
[12], p_block_in
->p_buffer
[13],
680 p_block_in
->p_buffer
[14], p_block_in
->p_buffer
[15]);
682 //if( subrec_type == 0x06 || subrec_type == 0x07 )
683 if( subrec_type
!= 0x02 && subrec_type
!= 0x0c &&
684 subrec_type
!= 0x08 && l_rec_size
> 4 )
686 /* get the PTS from this packet if it has one.
687 * on S1, only 0x06 has PES. On S2, however, most all do.
688 * Do NOT Pass the PES Header to the MPEG2 codec */
689 esOffset1
= find_es_header( ty_VideoPacket
, p_block_in
->p_buffer
, 5 );
690 if( esOffset1
!= -1 )
692 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
693 //subrec_type, esOffset1);
694 p_sys
->lastVideoPTS
= VLC_TS_0
+ get_pts(
695 &p_block_in
->p_buffer
[ esOffset1
+ VIDEO_PTS_OFFSET
] );
696 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
697 p_sys->lastVideoPTS );*/
698 if (subrec_type
!= 0x06) {
699 /* if we found a PES, and it's not type 6, then we're S2 */
700 /* The packet will have video data (& other headers) so we
701 * chop out the PES header and send the rest */
702 if (l_rec_size
>= VIDEO_PES_LENGTH
) {
703 p_block_in
->p_buffer
+= VIDEO_PES_LENGTH
+ esOffset1
;
704 p_block_in
->i_buffer
-= VIDEO_PES_LENGTH
+ esOffset1
;
706 msg_Dbg(p_demux
, "video rec type 0x%02x has short PES"
707 " (%ld bytes)", subrec_type
, l_rec_size
);
708 /* nuke this block; it's too short, but has PES marker */
709 p_block_in
->i_buffer
= 0;
713 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
717 if(subrec_type
== 0x06 )
719 /* type 6 (S1 DTivo) has no data, so we're done */
720 block_Release(p_block_in
);
724 /* if it's not a continue blk, then set PTS */
725 if( subrec_type
!= 0x02 )
727 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
729 /* if it's a GOP header, make sure it's legal
730 * (if we have enough data) */
731 /* Some ty files don't have this bit set
732 * and it causes problems */
733 if (subrec_type
== 0x0c && l_rec_size
>= 6)
734 p_block_in
->p_buffer
[5] |= 0x08;
735 /* store the TY PTS if there is one */
736 if (subrec_type
== 0x07) {
737 p_sys
->l_last_ty_pts
= rec_hdr
->l_ty_pts
;
738 /* should we use audio or video PTS? */
739 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
741 /* yes I know this is a cheap hack. It's the timestamp
742 used for display and skipping fwd/back, so it
743 doesn't have to be accurate to the millisecond.
744 I adjust it here by roughly one 1/30 sec. Yes it
745 will be slightly off for UK streams, but it's OK.
747 p_sys
->l_last_ty_pts
+= 35000000;
748 //p_sys->l_last_ty_pts += 33366667;
750 /* set PTS for this block before we send */
751 if (p_sys
->lastVideoPTS
> VLC_TS_INVALID
)
753 p_block_in
->i_pts
= p_sys
->lastVideoPTS
;
754 /* PTS gets used ONCE.
755 * Any subsequent frames we get BEFORE next PES
756 * header will have their PTS computed in the codec */
757 p_sys
->lastVideoPTS
= VLC_TS_INVALID
;
761 /* Register the CC decoders when needed */
762 uint64_t i_chans
= p_sys
->cc
.i_608channels
;
763 for( i
= 0; i_chans
> 0; i
++, i_chans
>>= 1 )
765 if( (i_chans
& 1) == 0 || p_sys
->p_cc
[i
] )
768 static const char *ppsz_description
[4] = {
769 N_("Closed captions 1"),
770 N_("Closed captions 2"),
771 N_("Closed captions 3"),
772 N_("Closed captions 4"),
778 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_CEA608
);
779 fmt
.subs
.cc
.i_channel
= i
;
780 fmt
.psz_description
= strdup( vlc_gettext(ppsz_description
[i
]) );
781 fmt
.i_group
= TY_ES_GROUP
;
782 p_sys
->p_cc
[i
] = es_out_Add( p_demux
->out
, &fmt
);
783 es_format_Clean( &fmt
);
786 /* Send the CC data */
787 if( p_block_in
->i_pts
> VLC_TS_INVALID
&& p_sys
->cc
.i_data
> 0 )
789 for( i
= 0; i
< 4; i
++ )
793 block_t
*p_cc
= block_Alloc( p_sys
->cc
.i_data
);
794 p_cc
->i_flags
|= BLOCK_FLAG_TYPE_I
;
795 p_cc
->i_pts
= p_block_in
->i_pts
;
796 memcpy( p_cc
->p_buffer
, p_sys
->cc
.p_data
, p_sys
->cc
.i_data
);
798 es_out_Send( p_demux
->out
, p_sys
->p_cc
[i
], p_cc
);
801 cc_Flush( &p_sys
->cc
);
804 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
805 //p_sys->i_cur_rec, subrec_type);
806 es_out_Send(p_demux
->out
, p_sys
->p_video
, p_block_in
);
809 static int DemuxRecAudio( demux_t
*p_demux
, ty_rec_hdr_t
*rec_hdr
, block_t
*p_block_in
)
811 demux_sys_t
*p_sys
= p_demux
->p_sys
;
812 const int subrec_type
= rec_hdr
->subrec_type
;
813 const long l_rec_size
= rec_hdr
->l_rec_size
;
816 assert( rec_hdr
->rec_type
== 0xc0 );
821 fprintf( stderr
, "Audio Packet Header " );
822 for( i
= 0 ; i
< 24 ; i
++ )
823 fprintf( stderr
, "%2.2x ", p_block_in
->p_buffer
[i
] );
824 fprintf( stderr
, "\n" );
827 if( subrec_type
== 2 )
829 /* SA or DTiVo Audio Data, no PES (continued block)
830 * ================================================
833 /* continue PES if previous was incomplete */
834 if (p_sys
->i_pes_buf_cnt
> 0)
836 const int i_need
= p_sys
->i_Pes_Length
- p_sys
->i_pes_buf_cnt
;
838 msg_Dbg(p_demux
, "continuing PES header");
839 /* do we have enough data to complete? */
840 if (i_need
>= l_rec_size
)
842 /* don't have complete PES hdr; save what we have and return */
843 memcpy(&p_sys
->pes_buffer
[p_sys
->i_pes_buf_cnt
],
844 p_block_in
->p_buffer
, l_rec_size
);
845 p_sys
->i_pes_buf_cnt
+= l_rec_size
;
847 block_Release(p_block_in
);
851 /* we have enough; reconstruct this p_frame with the new hdr */
852 memcpy(&p_sys
->pes_buffer
[p_sys
->i_pes_buf_cnt
],
853 p_block_in
->p_buffer
, i_need
);
854 /* advance the block past the PES header (don't want to send it) */
855 p_block_in
->p_buffer
+= i_need
;
856 p_block_in
->i_buffer
-= i_need
;
857 /* get the PTS out of this PES header (MPEG or AC3) */
858 if (p_sys
->audio_type
== TIVO_AUDIO_MPEG
)
859 esOffset1
= find_es_header(ty_MPEGAudioPacket
,
860 p_sys
->pes_buffer
, 5);
862 esOffset1
= find_es_header(ty_AC3AudioPacket
,
863 p_sys
->pes_buffer
, 5);
866 /* god help us; something's really wrong */
867 msg_Err(p_demux
, "can't find audio PES header in packet");
871 p_sys
->lastAudioPTS
= VLC_TS_0
+ get_pts(
872 &p_sys
->pes_buffer
[ esOffset1
+ p_sys
->i_Pts_Offset
] );
873 p_block_in
->i_pts
= p_sys
->lastAudioPTS
;
875 p_sys
->i_pes_buf_cnt
= 0;
877 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
878 * not allowed in the AC3 spec and will cause problems. So here
879 * we try to trim things. */
880 /* Also, S1 DTivo has alternating short / long AC3 packets. That
881 * is, one packet is short (incomplete) and the next packet has
882 * the first one's missing data, plus all of its own. Strange. */
883 if (p_sys
->audio_type
== TIVO_AUDIO_AC3
&&
884 p_sys
->tivo_series
== TIVO_SERIES2
) {
885 if (p_sys
->l_ac3_pkt_size
+ p_block_in
->i_buffer
>
887 p_block_in
->i_buffer
-= 2;
888 p_sys
->l_ac3_pkt_size
= 0;
890 p_sys
->l_ac3_pkt_size
+= p_block_in
->i_buffer
;
894 else if( subrec_type
== 0x03 )
896 /* MPEG Audio with PES Header, either SA or DTiVo */
897 /* ================================================ */
898 esOffset1
= find_es_header( ty_MPEGAudioPacket
,
899 p_block_in
->p_buffer
, 5 );
901 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
902 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
903 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
904 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
906 /* SA PES Header, No Audio Data */
907 /* ================================================ */
908 if ( ( esOffset1
== 0 ) && ( l_rec_size
== 16 ) )
910 p_sys
->lastAudioPTS
= VLC_TS_0
+ get_pts( &p_block_in
->p_buffer
[
912 if (p_sys
->firstAudioPTS
< 0)
913 p_sys
->firstAudioPTS
= p_sys
->lastAudioPTS
;
915 block_Release(p_block_in
);
917 /*msg_Dbg(p_demux, "SA Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
919 /* DTiVo Audio with PES Header */
920 /* ================================================ */
922 /* Check for complete PES */
923 if (check_sync_pes(p_demux
, p_block_in
, esOffset1
,
926 /* partial PES header found, nothing else.
928 block_Release(p_block_in
);
932 msg_Dbg(p_demux
, "packet buffer has "
933 "%02x %02x %02x %02x %02x %02x %02x %02x "
934 "%02x %02x %02x %02x %02x %02x %02x %02x",
935 p_block_in
->p_buffer
[0], p_block_in
->p_buffer
[1],
936 p_block_in
->p_buffer
[2], p_block_in
->p_buffer
[3],
937 p_block_in
->p_buffer
[4], p_block_in
->p_buffer
[5],
938 p_block_in
->p_buffer
[6], p_block_in
->p_buffer
[7],
939 p_block_in
->p_buffer
[8], p_block_in
->p_buffer
[9],
940 p_block_in
->p_buffer
[10], p_block_in
->p_buffer
[11],
941 p_block_in
->p_buffer
[12], p_block_in
->p_buffer
[13],
942 p_block_in
->p_buffer
[14], p_block_in
->p_buffer
[15]);
945 else if( subrec_type
== 0x04 )
947 /* SA Audio with no PES Header */
948 /* ================================================ */
950 "Adding SA Audio Packet Size %ld", l_rec_size ); */
952 if (p_sys
->lastAudioPTS
> VLC_TS_INVALID
)
953 p_block_in
->i_pts
= p_sys
->lastAudioPTS
;
955 else if( subrec_type
== 0x09 )
957 /* DTiVo AC3 Audio Data with PES Header */
958 /* ================================================ */
959 esOffset1
= find_es_header( ty_AC3AudioPacket
,
960 p_block_in
->p_buffer
, 5 );
963 msg_Dbg(p_demux
, "buffer has "
964 "%02x %02x %02x %02x %02x %02x %02x %02x "
965 "%02x %02x %02x %02x %02x %02x %02x %02x",
966 p_block_in
->p_buffer
[0], p_block_in
->p_buffer
[1],
967 p_block_in
->p_buffer
[2], p_block_in
->p_buffer
[3],
968 p_block_in
->p_buffer
[4], p_block_in
->p_buffer
[5],
969 p_block_in
->p_buffer
[6], p_block_in
->p_buffer
[7],
970 p_block_in
->p_buffer
[8], p_block_in
->p_buffer
[9],
971 p_block_in
->p_buffer
[10], p_block_in
->p_buffer
[11],
972 p_block_in
->p_buffer
[12], p_block_in
->p_buffer
[13],
973 p_block_in
->p_buffer
[14], p_block_in
->p_buffer
[15]);
974 msg_Dbg(p_demux
, "audio ES AC3 hdr at offset %d", esOffset1
);
977 /* Check for complete PES */
978 if (check_sync_pes(p_demux
, p_block_in
, esOffset1
,
981 /* partial PES header found, nothing else. we're done. */
982 block_Release(p_block_in
);
985 /* S2 DTivo has invalid long AC3 packets */
986 if (p_sys
->tivo_series
== TIVO_SERIES2
) {
987 if (p_block_in
->i_buffer
> AC3_PKT_LENGTH
) {
988 p_block_in
->i_buffer
-= 2;
989 p_sys
->l_ac3_pkt_size
= 0;
991 p_sys
->l_ac3_pkt_size
= p_block_in
->i_buffer
;
997 /* Unsupported/Unknown */
998 block_Release(p_block_in
);
1002 /* set PCR before we send (if PTS found) */
1003 if( p_block_in
->i_pts
> VLC_TS_INVALID
)
1004 es_out_Control( p_demux
->out
, ES_OUT_SET_PCR
,
1005 p_block_in
->i_pts
);
1007 es_out_Send( p_demux
->out
, p_sys
->p_audio
, p_block_in
);
1011 static int DemuxRecCc( demux_t
*p_demux
, ty_rec_hdr_t
*rec_hdr
, block_t
*p_block_in
)
1013 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1017 block_Release(p_block_in
);
1019 if( rec_hdr
->rec_type
== 0x01 )
1021 else if( rec_hdr
->rec_type
== 0x02 )
1026 /* XDS data (extract programs infos) transmitted on field 2 only */
1028 DemuxDecodeXds( p_demux
, rec_hdr
->ex
[0], rec_hdr
->ex
[1] );
1030 if( p_sys
->cc
.i_data
+ 3 > CC_MAX_DATA_SIZE
)
1033 cc_AppendData( &p_sys
->cc
, CC_PKT_BYTE0(i_field
), rec_hdr
->ex
);
1037 /* seek to a position within the stream, if possible */
1038 static int ty_stream_seek_pct(demux_t
*p_demux
, double seek_pct
)
1040 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1041 int64_t seek_pos
= p_sys
->i_stream_size
* seek_pct
;
1042 uint64_t l_skip_amt
;
1043 unsigned i_cur_part
;
1045 /* if we're not seekable, there's nothing to do */
1046 if (!p_sys
->b_seekable
)
1047 return VLC_EGENERIC
;
1049 /* figure out which part & chunk we want & go there */
1050 i_cur_part
= seek_pos
/ TIVO_PART_LENGTH
;
1051 p_sys
->i_cur_chunk
= seek_pos
/ CHUNK_SIZE
;
1053 /* try to read the part header (master chunk) if it's there */
1054 if ( vlc_stream_Seek( p_demux
->s
, i_cur_part
* TIVO_PART_LENGTH
))
1056 /* can't seek stream */
1057 return VLC_EGENERIC
;
1059 parse_master(p_demux
);
1061 /* now for the actual chunk */
1062 if ( vlc_stream_Seek( p_demux
->s
, p_sys
->i_cur_chunk
* CHUNK_SIZE
))
1064 /* can't seek stream */
1065 return VLC_EGENERIC
;
1067 /* load the chunk */
1068 p_sys
->i_stuff_cnt
= 0;
1069 get_chunk_header(p_demux
);
1071 /* seek within the chunk to get roughly to where we want */
1072 p_sys
->i_cur_rec
= (int)
1073 ((double) ((seek_pos
% CHUNK_SIZE
) / (double) (CHUNK_SIZE
)) * p_sys
->i_num_recs
);
1074 msg_Dbg(p_demux
, "Seeked to file pos %"PRId64
, seek_pos
);
1075 msg_Dbg(p_demux
, " (chunk %d, record %d)",
1076 p_sys
->i_cur_chunk
- 1, p_sys
->i_cur_rec
);
1078 /* seek to the start of this record's data.
1079 * to do that, we have to skip past all prior records */
1081 for ( int i
=0; i
<p_sys
->i_cur_rec
; i
++)
1082 l_skip_amt
+= p_sys
->rec_hdrs
[i
].l_rec_size
;
1083 vlc_stream_Seek(p_demux
->s
, ((p_sys
->i_cur_chunk
-1) * CHUNK_SIZE
) +
1084 (p_sys
->i_num_recs
* 16) + l_skip_amt
+ 4);
1086 /* to hell with syncing any audio or video, just start reading records... :) */
1087 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TS_INVALID;*/
1092 //#define TY_XDS_DEBUG
1093 static void XdsInit( xds_t
*h
)
1096 h
->i_class
= XDS_MAX_CLASS_COUNT
;
1098 h
->b_future
= false;
1099 for( int i
= 0; i
< XDS_MAX_CLASS_COUNT
; i
++ )
1101 for( int j
= 0; j
< 128; j
++ )
1102 h
->pkt
[i
][j
].b_started
= false;
1104 h
->b_meta_changed
= false;
1105 memset( &h
->meta
, 0, sizeof(h
->meta
) );
1107 static void XdsExit( xds_t
*h
)
1110 free( h
->meta
.psz_channel_name
);
1111 free( h
->meta
.psz_channel_call_letter
);
1112 free( h
->meta
.psz_channel_number
);
1115 free( h
->meta
.current
.psz_name
);
1116 free( h
->meta
.current
.psz_rating
);
1118 free( h
->meta
.future
.psz_name
);
1119 free( h
->meta
.future
.psz_rating
);
1121 static void XdsStringUtf8( char dst
[2*32+1], const uint8_t *p_src
, size_t i_src
)
1124 for( size_t i
= 0; i
< i_src
; i
++ )
1128 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1129 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1130 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1131 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1132 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1133 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1134 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1135 E2( 0x7c, 0xc3,0xb7); // division symbol
1136 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1137 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1140 dst
[i_dst
++] = p_src
[i
];
1144 dst
[i_dst
++] = '\0';
1146 static bool XdsChangeString( xds_t
*h
, char **ppsz_dst
, const char *psz_new
)
1148 if( *ppsz_dst
&& psz_new
&& !strcmp( *ppsz_dst
, psz_new
) )
1150 if( *ppsz_dst
== NULL
&& psz_new
== NULL
)
1155 *ppsz_dst
= strdup( psz_new
);
1159 h
->b_meta_changed
= true;
1163 static void XdsDecodeCurrentFuture( xds_t
*h
, xds_packet_t
*pk
)
1165 xds_meta_program_t
*p_prg
= h
->b_future
? &h
->meta
.future
: &h
->meta
.current
;
1172 XdsStringUtf8( name
, pk
->p_data
, pk
->i_data
);
1173 if( XdsChangeString( h
, &p_prg
->psz_name
, name
) )
1175 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1176 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1180 i_rating
= (pk
->p_data
[0] & 0x18);
1181 if( i_rating
== 0x08 )
1184 static const char *pppsz_ratings
[8][2] = {
1185 { "None", "No rating (no content advisory)" },
1186 { "TV-Y", "All Children (no content advisory)" },
1187 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1188 { "TV-G", "General Audience (no content advisory)" },
1189 { "TV-PG", "Parental Guidance Suggested" },
1190 { "TV-14", "Parents Strongly Cautioned" },
1191 { "TV-MA", "Mature Audience Only" },
1192 { "None", "No rating (no content advisory)" }
1194 p_prg
->rating
= XDS_META_PROGRAM_RATING_TPG
;
1195 if( XdsChangeString( h
, &p_prg
->psz_rating
, pppsz_ratings
[pk
->p_data
[1]&0x07][0] ) )
1197 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1198 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1199 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1202 else if( i_rating
== 0x00 || i_rating
== 0x10 )
1205 static const char *pppsz_ratings
[8][2] = {
1207 { "G", "General Audiences" },
1208 { "PG", "Parental Guidance Suggested" },
1209 { "PG-13", "Parents Strongly Cautioned" },
1210 { "R", "Restricted" },
1211 { "NC-17", "No one 17 and under admitted" },
1212 { "X", "No one under 17 admitted" },
1213 { "NR", "Not Rated" },
1215 p_prg
->rating
= XDS_META_PROGRAM_RATING_MPAA
;
1216 if( XdsChangeString( h
, &p_prg
->psz_rating
, pppsz_ratings
[pk
->p_data
[0]&0x07][0] ) )
1218 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1219 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1220 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1225 /* Non US Rating TODO */
1226 assert( i_rating
== 0x18 ); // only left value possible */
1227 p_prg
->rating
= XDS_META_PROGRAM_RATING_NONE
;
1228 if( XdsChangeString( h
, &p_prg
->psz_rating
, NULL
) )
1230 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1231 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1238 fprintf( stderr
, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h
->i_type
);
1244 static void XdsDecodeChannel( xds_t
*h
, xds_packet_t
*pk
)
1252 if( pk
->i_data
< 2 )
1254 XdsStringUtf8( name
, pk
->p_data
, pk
->i_data
);
1255 if( XdsChangeString( h
, &h
->meta
.psz_channel_name
, name
) )
1257 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1258 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1263 if( pk
->i_data
< 4 )
1266 XdsStringUtf8( name
, pk
->p_data
, 4 );
1267 if( XdsChangeString( h
, &h
->meta
.psz_channel_call_letter
, name
) )
1269 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1270 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1272 if( pk
->i_data
>= 6 )
1274 XdsStringUtf8( chan
, &pk
->p_data
[4], 2 );
1275 if( XdsChangeString( h
, &h
->meta
.psz_channel_number
, chan
) )
1277 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1278 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1283 if( XdsChangeString( h
, &h
->meta
.psz_channel_number
, NULL
) )
1285 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1286 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1291 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1294 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1298 fprintf( stderr
, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h
->i_type
);
1304 static void XdsDecode( xds_t
*h
, xds_packet_t
*pk
)
1306 switch( h
->i_class
)
1308 case XDS_CLASS_CURRENT
:
1309 case XDS_CLASS_FUTURE
:
1310 XdsDecodeCurrentFuture( h
, pk
);
1312 case XDS_CLASS_CHANNEL
:
1313 XdsDecodeChannel( h
, pk
);
1315 case XDS_CLASS_MISCELLANEOUS
:
1317 fprintf( stderr
, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1320 case XDS_CLASS_PUBLIC_SERVICE
:
1322 fprintf( stderr
, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1326 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1331 static void XdsParse( xds_t
*h
, uint8_t d1
, uint8_t d2
)
1333 /* TODO check parity */
1338 if( d1
>= 0x01 && d1
<= 0x0e )
1340 const xds_class_t i_class
= ( d1
- 1 ) >> 1;
1341 const int i_type
= d2
;
1342 const bool b_start
= d1
& 0x01;
1343 xds_packet_t
*pk
= &h
->pkt
[i_class
][i_type
];
1345 if( !b_start
&& !pk
->b_started
)
1347 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1353 h
->i_class
= i_class
;
1355 h
->b_future
= !b_start
;
1356 pk
->b_started
= true;
1360 pk
->i_sum
= d1
+ d2
;
1363 else if( d1
== 0x0f && h
->b_xds
)
1365 xds_packet_t
*pk
= &h
->pkt
[h
->i_class
][h
->i_type
];
1367 /* TODO checksum and decode */
1368 pk
->i_sum
+= d1
+ d2
;
1369 if( pk
->i_sum
& 0x7f )
1371 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1372 pk
->b_started
= false;
1375 if( pk
->i_data
<= 0 )
1377 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1378 pk
->b_started
= false;
1382 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1387 pk
->b_started
= false;
1389 else if( d1
>= 0x20 && h
->b_xds
)
1391 xds_packet_t
*pk
= &h
->pkt
[h
->i_class
][h
->i_type
];
1393 if( pk
->i_data
+2 > XDS_MAX_DATA_SIZE
)
1395 /* Broken -> reinit */
1396 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1398 pk
->b_started
= false;
1401 /* TODO check parity bit */
1402 pk
->p_data
[pk
->i_data
++] = d1
& 0x7f;
1403 pk
->p_data
[pk
->i_data
++] = d2
& 0x7f;
1412 static void DemuxDecodeXds( demux_t
*p_demux
, uint8_t d1
, uint8_t d2
)
1414 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1416 XdsParse( &p_demux
->p_sys
->xds
, d1
, d2
);
1417 if( p_demux
->p_sys
->xds
.b_meta_changed
)
1419 xds_meta_t
*m
= &p_sys
->xds
.meta
;
1422 /* Channel meta data */
1423 p_meta
= vlc_meta_New();
1424 if( m
->psz_channel_name
)
1425 vlc_meta_SetPublisher( p_meta
, m
->psz_channel_name
);
1426 if( m
->psz_channel_call_letter
)
1427 vlc_meta_SetTitle( p_meta
, m
->psz_channel_call_letter
);
1428 if( m
->psz_channel_number
)
1429 vlc_meta_AddExtra( p_meta
, "Channel number", m
->psz_channel_number
);
1430 es_out_Control( p_demux
->out
, ES_OUT_SET_GROUP_META
, TY_ES_GROUP
, p_meta
);
1431 vlc_meta_Delete( p_meta
);
1433 /* Event meta data (current/future) */
1434 if( m
->current
.psz_name
)
1436 vlc_epg_t
*p_epg
= vlc_epg_New( TY_ES_GROUP
, TY_ES_GROUP
);
1439 vlc_epg_event_t
*p_evt
= vlc_epg_event_New( 0, 0, 0 );
1442 if( m
->current
.psz_name
)
1443 p_evt
->psz_name
= strdup( m
->current
.psz_name
);
1444 if( !vlc_epg_AddEvent( p_epg
, p_evt
) )
1445 vlc_epg_event_Delete( p_evt
);
1447 //if( m->current.psz_rating )
1448 // TODO but VLC cannot yet handle rating per epg event
1449 vlc_epg_SetCurrent( p_epg
, 0 );
1451 if( m
->future
.psz_name
)
1454 if( p_epg
->i_event
> 0 )
1455 es_out_Control( p_demux
->out
, ES_OUT_SET_GROUP_EPG
,
1456 TY_ES_GROUP
, p_epg
);
1457 vlc_epg_Delete( p_epg
);
1461 p_demux
->p_sys
->xds
.b_meta_changed
= false;
1464 /* seek to an exact time position within the stream, if possible.
1465 * l_seek_time is in nanoseconds, the TIVO time standard.
1467 static int ty_stream_seek_time(demux_t
*p_demux
, uint64_t l_seek_time
)
1469 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1470 unsigned i_seq_entry
= 0;
1473 int64_t l_cur_pos
= vlc_stream_Tell(p_demux
->s
);
1474 unsigned i_cur_part
= l_cur_pos
/ TIVO_PART_LENGTH
;
1475 uint64_t l_seek_secs
= l_seek_time
/ 1000000000;
1476 uint64_t l_fwd_stamp
= 1;
1478 /* if we're not seekable, there's nothing to do */
1479 if (!p_sys
->b_seekable
|| !p_sys
->b_have_master
)
1480 return VLC_EGENERIC
;
1482 msg_Dbg(p_demux
, "Skipping to time %02"PRIu64
":%02"PRIu64
":%02"PRIu64
,
1483 l_seek_secs
/ 3600, (l_seek_secs
/ 60) % 60, l_seek_secs
% 60);
1485 /* seek to the proper segment if necessary */
1486 /* first see if we need to go back */
1487 while (l_seek_time
< p_sys
->l_first_ty_pts
) {
1488 msg_Dbg(p_demux
, "skipping to prior segment.");
1489 /* load previous part */
1490 if (i_cur_part
== 0) {
1491 vlc_stream_Seek(p_demux
->s
, l_cur_pos
);
1492 msg_Err(p_demux
, "Attempt to seek past BOF");
1493 return VLC_EGENERIC
;
1495 vlc_stream_Seek(p_demux
->s
, (i_cur_part
- 1) * TIVO_PART_LENGTH
);
1497 parse_master(p_demux
);
1499 /* maybe we need to go forward */
1500 while (l_seek_time
> p_sys
->l_final_ty_pts
) {
1501 msg_Dbg(p_demux
, "skipping to next segment.");
1502 /* load next part */
1503 if ((i_cur_part
+ 1) * TIVO_PART_LENGTH
> p_sys
->i_stream_size
) {
1504 /* error; restore previous file position */
1505 vlc_stream_Seek(p_demux
->s
, l_cur_pos
);
1506 msg_Err(p_demux
, "seek error");
1507 return VLC_EGENERIC
;
1509 vlc_stream_Seek(p_demux
->s
, (i_cur_part
+ 1) * TIVO_PART_LENGTH
);
1511 parse_master(p_demux
);
1514 /* our target is somewhere within this part;
1515 find the proper chunk using seq_table */
1516 for (i
=1; i
<p_sys
->i_seq_table_size
; i
++) {
1517 if (p_sys
->seq_table
[i
].l_timestamp
> l_seek_time
) {
1518 /* i-1 is the section we want; remember the next timestamp in case
1519 we have to use it (this section may not have a proper SEQ hdr
1520 for the time we're seeking) */
1521 msg_Dbg(p_demux
, "stopping at seq entry %d.", i
);
1522 l_fwd_stamp
= p_sys
->seq_table
[i
].l_timestamp
;
1528 /* if we went through the entire last loop and didn't find our target,
1529 then we skip to the next part. What has happened is that the actual
1530 time we're seeking is within this part, but there isn't a SEQ hdr
1531 for it here. So we skip to the next part */
1532 if (i
== p_sys
->i_seq_table_size
) {
1533 if ((i_cur_part
+ 1) * TIVO_PART_LENGTH
> p_sys
->i_stream_size
) {
1534 /* error; restore previous file position */
1535 vlc_stream_Seek(p_demux
->s
, l_cur_pos
);
1536 msg_Err(p_demux
, "seek error");
1537 return VLC_EGENERIC
;
1539 vlc_stream_Seek(p_demux
->s
, (i_cur_part
+ 1) * TIVO_PART_LENGTH
);
1541 parse_master(p_demux
);
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 vlc_stream_Seek(p_demux
->s
, (i_cur_part
* TIVO_PART_LENGTH
) +
1558 // TODO: we don't have to parse the full header set;
1559 // just test the seq_rec entry for its timestamp
1560 p_sys
->i_stuff_cnt
= 0;
1561 get_chunk_header(p_demux
);
1562 // check ty PTS for the SEQ entry in this chunk
1563 if (p_sys
->i_seq_rec
< 0 || p_sys
->i_seq_rec
> p_sys
->i_num_recs
) {
1564 msg_Err(p_demux
, "no SEQ hdr in chunk; table had one.");
1565 /* Seek to beginning of original chunk & reload it */
1566 vlc_stream_Seek(p_demux
->s
, (l_cur_pos
/ CHUNK_SIZE
) * CHUNK_SIZE
);
1567 p_sys
->i_stuff_cnt
= 0;
1568 get_chunk_header(p_demux
);
1569 return VLC_EGENERIC
;
1571 l_seek_secs
= p_sys
->rec_hdrs
[p_sys
->i_seq_rec
].l_ty_pts
/
1573 msg_Dbg(p_demux
, "found SEQ hdr for timestamp %02"PRIu64
":%02"PRIu64
":%02"PRIu64
,
1575 (l_seek_secs
/ 60) % 60, l_seek_secs
% 60);
1576 if (p_sys
->rec_hdrs
[p_sys
->i_seq_rec
].l_ty_pts
>= l_seek_time
) {
1577 // keep this one? go back?
1578 /* for now, we take this one. it's the first SEQ hdr AFTER
1579 the time we were searching for. */
1580 msg_Dbg(p_demux
, "seek target found.");
1583 msg_Dbg(p_demux
, "timestamp too early. still scanning.");
1586 /* if we made it through this entire loop without finding our target,
1587 then we skip to the next section. What has happened is that the actual
1588 time we're seeking is within this section, but there isn't a SEQ hdr
1589 for it here. So we skip to the next closest one (l_fwd_stamp) */
1590 if (i
== p_sys
->i_bits_per_seq_entry
)
1591 return ty_stream_seek_time(p_demux
, l_fwd_stamp
);
1593 /* current stream ptr is at beginning of data for this chunk,
1594 so we need to skip past any stream data prior to the seq_rec
1597 for (int j
=0; j
<p_sys
->i_seq_rec
; j
++)
1598 i_skip_cnt
+= p_sys
->rec_hdrs
[j
].l_rec_size
;
1599 vlc_stream_Read(p_demux
->s
, NULL
, i_skip_cnt
);
1600 p_sys
->i_cur_rec
= p_sys
->i_seq_rec
;
1601 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1602 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1608 /* parse a master chunk, filling the SEQ table and other variables.
1609 * We assume the stream is currently pointing to it.
1611 static void parse_master(demux_t
*p_demux
)
1613 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1614 uint8_t mst_buf
[32];
1615 int64_t i_save_pos
= vlc_stream_Tell(p_demux
->s
);
1618 /* Note that the entries in the SEQ table in the stream may have
1619 different sizes depending on the bits per entry. We store them
1620 all in the same size structure, so we have to parse them out one
1621 by one. If we had a dynamic structure, we could simply read the
1622 entire table directly from the stream into memory in place. */
1624 /* clear the SEQ table */
1625 free(p_sys
->seq_table
);
1627 /* parse header info */
1628 vlc_stream_Read(p_demux
->s
, mst_buf
, 32);
1630 uint32_t i_map_size
= U32_AT(&mst_buf
[20]); /* size of bitmask, in bytes */
1631 uint32_t i
= U32_AT(&mst_buf
[28]); /* size of SEQ table, in bytes */
1633 p_sys
->i_bits_per_seq_entry
= i_map_size
* 8;
1634 p_sys
->i_seq_table_size
= i
/ (8 + i_map_size
);
1636 if(p_sys
->i_seq_table_size
== 0)
1638 p_sys
->seq_table
= NULL
;
1642 /* parse all the entries */
1643 p_sys
->seq_table
= calloc(p_sys
->i_seq_table_size
, sizeof(ty_seq_table_t
));
1644 if (p_sys
->seq_table
== NULL
)
1646 p_sys
->i_seq_table_size
= 0;
1649 for (unsigned j
=0; j
<p_sys
->i_seq_table_size
; j
++) {
1650 vlc_stream_Read(p_demux
->s
, mst_buf
, 8);
1651 p_sys
->seq_table
[j
].l_timestamp
= U64_AT(&mst_buf
[0]);
1652 if (i_map_size
> 8) {
1653 msg_Err(p_demux
, "Unsupported SEQ bitmap size in master chunk");
1654 vlc_stream_Read(p_demux
->s
, NULL
, i_map_size
);
1656 vlc_stream_Read(p_demux
->s
, mst_buf
+ 8, i_map_size
);
1657 memcpy(p_sys
->seq_table
[j
].chunk_bitmask
, &mst_buf
[8], i_map_size
);
1661 /* set up a few of our variables */
1662 p_sys
->l_first_ty_pts
= p_sys
->seq_table
[0].l_timestamp
;
1663 p_sys
->l_final_ty_pts
=
1664 p_sys
->seq_table
[p_sys
->i_seq_table_size
- 1].l_timestamp
;
1665 p_sys
->b_have_master
= true;
1667 i_pts_secs
= p_sys
->l_first_ty_pts
/ 1000000000;
1669 "first TY pts in master is %02"PRId64
":%02"PRId64
":%02"PRId64
,
1670 i_pts_secs
/ 3600, (i_pts_secs
/ 60) % 60, i_pts_secs
% 60 );
1671 i_pts_secs
= p_sys
->l_final_ty_pts
/ 1000000000;
1673 "final TY pts in master is %02"PRId64
":%02"PRId64
":%02"PRId64
,
1674 i_pts_secs
/ 3600, (i_pts_secs
/ 60) % 60, i_pts_secs
% 60 );
1676 /* seek past this chunk */
1677 vlc_stream_Seek(p_demux
->s
, i_save_pos
+ CHUNK_SIZE
);
1681 /* ======================================================================== */
1682 /* "Peek" at some chunks. Skip over the Part header if we find it.
1683 * We parse the peeked data and determine audio type,
1684 * SA vs. DTivo, & Tivo Series.
1685 * Set global vars i_Pes_Length, i_Pts_Offset,
1686 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1687 static int probe_stream(demux_t
*p_demux
)
1689 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1690 const uint8_t *p_buf
;
1692 bool b_probe_error
= false;
1694 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1695 if (vlc_stream_Peek( p_demux
->s
, &p_buf
, CHUNK_PEEK_COUNT
* CHUNK_SIZE
) <
1696 CHUNK_PEEK_COUNT
* CHUNK_SIZE
) {
1697 msg_Err(p_demux
, "Can't peek %d chunks", CHUNK_PEEK_COUNT
);
1698 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1699 return VLC_EGENERIC
;
1702 /* the real work: analyze this chunk */
1703 for (i
= 0; i
< CHUNK_PEEK_COUNT
; i
++) {
1704 analyze_chunk(p_demux
, p_buf
);
1705 if (p_sys
->tivo_series
!= TIVO_SERIES_UNKNOWN
&&
1706 p_sys
->audio_type
!= TIVO_AUDIO_UNKNOWN
&&
1707 p_sys
->tivo_type
!= TIVO_TYPE_UNKNOWN
)
1709 p_buf
+= CHUNK_SIZE
;
1712 /* the final tally */
1713 if (p_sys
->tivo_series
== TIVO_SERIES_UNKNOWN
) {
1714 msg_Err(p_demux
, "Can't determine Tivo Series.");
1715 b_probe_error
= true;
1717 if (p_sys
->audio_type
== TIVO_AUDIO_UNKNOWN
) {
1718 msg_Err(p_demux
, "Can't determine Tivo Audio Type.");
1719 b_probe_error
= true;
1721 if (p_sys
->tivo_type
== TIVO_TYPE_UNKNOWN
) {
1722 msg_Err(p_demux
, "Can't determine Tivo Type (SA/DTivo).");
1723 b_probe_error
= true;
1725 return b_probe_error
?VLC_EGENERIC
:VLC_SUCCESS
;
1729 /* ======================================================================== */
1730 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1731 static void analyze_chunk(demux_t
*p_demux
, const uint8_t *p_chunk
)
1733 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1735 ty_rec_hdr_t
*p_hdrs
;
1736 int i_num_6e0
, i_num_be0
, i_num_9c0
, i_num_3c0
;
1739 /* skip if it's a Part header */
1740 if( U32_AT( &p_chunk
[ 0 ] ) == TIVO_PES_FILEID
)
1743 /* number of records in chunk (we ignore high order byte;
1744 * rarely are there > 256 chunks & we don't need that many anyway) */
1745 i_num_recs
= p_chunk
[0];
1746 if (i_num_recs
< 5) {
1747 /* try again with the next chunk. Sometimes there are dead ones */
1751 p_chunk
+= 4; /* skip past rec count & SEQ bytes */
1752 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1753 p_hdrs
= parse_chunk_headers(p_chunk
, i_num_recs
, &i_payload_size
);
1755 * 1. check video packets. Presence of 0x6e0 means S1.
1756 * No 6e0 but have be0 means S2.
1757 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1758 * If AC-3, then we have DTivo.
1759 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1761 i_num_6e0
= i_num_be0
= i_num_9c0
= i_num_3c0
= 0;
1762 for (i
=0; i
<i_num_recs
; i
++) {
1763 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1764 //p_hdrs[i].rec_type,
1765 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1766 switch (p_hdrs
[i
].subrec_type
<< 8 | p_hdrs
[i
].rec_type
) {
1781 msg_Dbg(p_demux
, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1782 i_num_6e0
, i_num_be0
);
1784 /* set up our variables */
1785 if (i_num_6e0
> 0) {
1786 msg_Dbg(p_demux
, "detected Series 1 Tivo");
1787 p_sys
->tivo_series
= TIVO_SERIES1
;
1788 p_sys
->i_Pes_Length
= SERIES1_PES_LENGTH
;
1789 } else if (i_num_be0
> 0) {
1790 msg_Dbg(p_demux
, "detected Series 2 Tivo");
1791 p_sys
->tivo_series
= TIVO_SERIES2
;
1792 p_sys
->i_Pes_Length
= SERIES2_PES_LENGTH
;
1794 if (i_num_9c0
> 0) {
1795 msg_Dbg(p_demux
, "detected AC-3 Audio (DTivo)" );
1796 p_sys
->audio_type
= TIVO_AUDIO_AC3
;
1797 p_sys
->tivo_type
= TIVO_TYPE_DTIVO
;
1798 p_sys
->i_Pts_Offset
= AC3_PTS_OFFSET
;
1799 p_sys
->i_Pes_Length
= AC3_PES_LENGTH
;
1800 } else if (i_num_3c0
> 0) {
1801 p_sys
->audio_type
= TIVO_AUDIO_MPEG
;
1802 msg_Dbg(p_demux
, "detected MPEG Audio" );
1805 /* if tivo_type still unknown, we can check PTS location
1806 * in MPEG packets to determine tivo_type */
1807 if (p_sys
->tivo_type
== TIVO_TYPE_UNKNOWN
) {
1808 uint32_t i_data_offset
= (16 * i_num_recs
);
1809 for (i
=0; i
<i_num_recs
; i
++) {
1810 if ((p_hdrs
[i
].subrec_type
<< 0x08 | p_hdrs
[i
].rec_type
) == 0x3c0 &&
1811 p_hdrs
[i
].l_rec_size
> 15) {
1812 /* first make sure we're aligned */
1813 int i_pes_offset
= find_es_header(ty_MPEGAudioPacket
,
1814 &p_chunk
[i_data_offset
], 5);
1815 if (i_pes_offset
>= 0) {
1816 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1817 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1819 if ((p_chunk
[i_data_offset
+ 6 + i_pes_offset
] & 0x80) == 0x80) {
1820 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1821 if (p_sys
->tivo_series
== TIVO_SERIES1
)
1822 msg_Dbg(p_demux
, "detected Stand-Alone Tivo" );
1823 p_sys
->tivo_type
= TIVO_TYPE_SA
;
1824 p_sys
->i_Pts_Offset
= SA_PTS_OFFSET
;
1826 if (p_sys
->tivo_series
== TIVO_SERIES1
)
1827 msg_Dbg(p_demux
, "detected DirecTV Tivo" );
1828 p_sys
->tivo_type
= TIVO_TYPE_DTIVO
;
1829 p_sys
->i_Pts_Offset
= DTIVO_PTS_OFFSET
;
1834 i_data_offset
+= p_hdrs
[i
].l_rec_size
;
1841 /* =========================================================================== */
1842 static int get_chunk_header(demux_t
*p_demux
)
1844 int i_readSize
, i_num_recs
;
1846 const uint8_t *p_peek
;
1847 demux_sys_t
*p_sys
= p_demux
->p_sys
;
1848 int i_payload_size
; /* sum of all records' sizes */
1850 msg_Dbg(p_demux
, "parsing ty chunk #%d", p_sys
->i_cur_chunk
);
1852 /* if we have left-over filler space from the last chunk, get that */
1853 if (p_sys
->i_stuff_cnt
> 0) {
1854 vlc_stream_Read( p_demux
->s
, NULL
, p_sys
->i_stuff_cnt
);
1855 p_sys
->i_stuff_cnt
= 0;
1858 /* read the TY packet header */
1859 i_readSize
= vlc_stream_Peek( p_demux
->s
, &p_peek
, 4 );
1860 p_sys
->i_cur_chunk
++;
1862 if ( (i_readSize
< 4) || ( U32_AT(&p_peek
[ 0 ] ) == 0 ))
1869 /* check if it's a PART Header */
1870 if( U32_AT( &p_peek
[ 0 ] ) == TIVO_PES_FILEID
)
1872 /* parse master chunk */
1873 parse_master(p_demux
);
1874 return get_chunk_header(p_demux
);
1877 /* number of records in chunk (8- or 16-bit number) */
1878 if (p_peek
[3] & 0x80)
1880 /* 16 bit rec cnt */
1881 p_sys
->i_num_recs
= i_num_recs
= (p_peek
[1] << 8) + p_peek
[0];
1882 p_sys
->i_seq_rec
= (p_peek
[3] << 8) + p_peek
[2];
1883 if (p_sys
->i_seq_rec
!= 0xffff)
1885 p_sys
->i_seq_rec
&= ~0x8000;
1890 /* 8 bit reclen - tivo 1.3 format */
1891 p_sys
->i_num_recs
= i_num_recs
= p_peek
[0];
1892 p_sys
->i_seq_rec
= p_peek
[1];
1894 p_sys
->i_cur_rec
= 0;
1895 p_sys
->b_first_chunk
= false;
1897 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1899 free(p_sys
->rec_hdrs
);
1900 p_sys
->rec_hdrs
= NULL
;
1902 /* skip past the 4 bytes we "peeked" earlier */
1903 vlc_stream_Read( p_demux
->s
, NULL
, 4 );
1905 /* read the record headers into a temp buffer */
1906 p_hdr_buf
= xmalloc(i_num_recs
* 16);
1907 if (vlc_stream_Read(p_demux
->s
, p_hdr_buf
, i_num_recs
* 16) < i_num_recs
* 16) {
1913 p_sys
->rec_hdrs
= parse_chunk_headers(p_hdr_buf
, i_num_recs
,
1917 p_sys
->i_stuff_cnt
= CHUNK_SIZE
- 4 -
1918 (p_sys
->i_num_recs
* 16) - i_payload_size
;
1919 if (p_sys
->i_stuff_cnt
> 0)
1920 msg_Dbg( p_demux
, "chunk has %d stuff bytes at end",
1921 p_sys
->i_stuff_cnt
);
1926 static ty_rec_hdr_t
*parse_chunk_headers( const uint8_t *p_buf
,
1927 int i_num_recs
, int *pi_payload_size
)
1930 ty_rec_hdr_t
*p_hdrs
, *p_rec_hdr
;
1932 *pi_payload_size
= 0;
1933 p_hdrs
= xmalloc(i_num_recs
* sizeof(ty_rec_hdr_t
));
1935 for (i
= 0; i
< i_num_recs
; i
++)
1937 const uint8_t *record_header
= p_buf
+ (i
* 16);
1938 p_rec_hdr
= &p_hdrs
[i
]; /* for brevity */
1939 p_rec_hdr
->rec_type
= record_header
[3];
1940 p_rec_hdr
->subrec_type
= record_header
[2] & 0x0f;
1941 if ((record_header
[ 0 ] & 0x80) == 0x80)
1944 /* marker bit 2 set, so read extended data */
1945 b1
= ( ( ( record_header
[ 0 ] & 0x0f ) << 4 ) |
1946 ( ( record_header
[ 1 ] & 0xf0 ) >> 4 ) );
1947 b2
= ( ( ( record_header
[ 1 ] & 0x0f ) << 4 ) |
1948 ( ( record_header
[ 2 ] & 0xf0 ) >> 4 ) );
1950 p_rec_hdr
->ex
[0] = b1
;
1951 p_rec_hdr
->ex
[1] = b2
;
1952 p_rec_hdr
->l_rec_size
= 0;
1953 p_rec_hdr
->l_ty_pts
= 0;
1954 p_rec_hdr
->b_ext
= true;
1958 p_rec_hdr
->l_rec_size
= ( record_header
[ 0 ] << 8 |
1959 record_header
[ 1 ] ) << 4 | ( record_header
[ 2 ] >> 4 );
1960 *pi_payload_size
+= p_rec_hdr
->l_rec_size
;
1961 p_rec_hdr
->b_ext
= false;
1962 p_rec_hdr
->l_ty_pts
= U64_AT( &record_header
[ 8 ] );
1964 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1965 } /* end of record-header loop */