1 /*****************************************************************************
2 * ty.c - TiVo ty stream video demuxer for VLC
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
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
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 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 General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, 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( 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->b_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 /* Set exported functions */
322 p_demux->pf_demux = Demux;
323 p_demux->pf_control = Control;
325 /* create our structure that will hold all data */
326 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
327 memset(p_sys, 0, sizeof(demux_sys_t));
329 /* set up our struct (most were zero'd out with the memset above) */
330 p_sys->b_first_chunk = true;
331 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
332 p_sys->firstAudioPTS = -1;
333 p_sys->lastAudioPTS = VLC_TS_INVALID;
334 p_sys->lastVideoPTS = VLC_TS_INVALID;
335 p_sys->i_stream_size = stream_Size(p_demux->s);
336 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
337 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
338 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
339 p_sys->i_Pes_Length = 0;
340 p_sys->i_Pts_Offset = 0;
341 p_sys->l_ac3_pkt_size = 0;
343 /* see if this stream is seekable */
344 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
346 if (probe_stream(p_demux) != VLC_SUCCESS) {
351 if (!p_sys->b_have_master)
352 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
354 /* register the proper audio codec */
355 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
356 es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA );
358 es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_A52 );
360 fmt.i_group = TY_ES_GROUP;
361 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
363 /* register the video stream */
364 es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV );
365 fmt.i_group = TY_ES_GROUP;
366 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
369 for( i = 0; i < 4; i++ )
370 p_sys->p_cc[i] = NULL;
371 cc_Init( &p_sys->cc );
373 XdsInit( &p_sys->xds );
378 /* =========================================================================== */
379 /* Demux: Read & Demux one record from the chunk
381 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
383 * NOTE: I think we can return the number of packets sent instead of just 1.
384 * that means we can demux an entire chunk and shoot it back (may be more efficient)
385 * -- should try that some day :) --
387 static int Demux( demux_t *p_demux )
389 demux_sys_t *p_sys = p_demux->p_sys;
391 block_t *p_block_in = NULL;
393 /*msg_Dbg(p_demux, "ty demux processing" );*/
395 /* did we hit EOF earlier? */
400 * what we do (1 record now.. maybe more later):
401 * - use stream_Read() to read the chunk header & record headers
402 * - discard entire chunk if it is a PART header chunk
403 * - parse all the headers into record header array
404 * - keep a pointer of which record we're on
405 * - use stream_Block() to fetch each record
406 * - parse out PTS from PES headers
407 * - set PTS for data packets
408 * - pass the data on to the proper codec via es_out_Send()
410 * if this is the first time or
411 * if we're at the end of this chunk, start a new one
413 /* parse the next chunk's record headers */
414 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
416 if( get_chunk_header(p_demux) == 0 )
420 /*======================================================================
421 * parse & send one record of the chunk
422 *====================================================================== */
423 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
427 const long l_rec_size = p_rec->l_rec_size;
428 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
429 subrec_type, p_rec->rec_type, l_rec_size );*/
431 /* some normal records are 0 length, so check for that... */
432 if( l_rec_size <= 0 )
434 /* no data in payload; we're done */
439 /* read in this record's payload */
440 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
443 /* set these as 'unknown' for now */
445 p_block_in->i_dts = VLC_TS_INVALID;
449 -- don't read any data from the stream, data was in the record header --
451 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
452 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
455 if( p_rec->rec_type == 0xe0 )
458 DemuxRecVideo( p_demux, p_rec, p_block_in );
460 else if ( p_rec->rec_type == 0xc0 )
463 DemuxRecAudio( p_demux, p_rec, p_block_in );
465 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
467 /* Closed Captions/XDS */
468 DemuxRecCc( p_demux, p_rec, p_block_in );
470 else if ( p_rec->rec_type == 0x03 )
472 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
474 block_Release(p_block_in);
476 else if ( p_rec->rec_type == 0x05 )
478 /* Unknown, but seen regularly */
480 block_Release(p_block_in);
484 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
486 block_Release(p_block_in);
495 static int Control(demux_t *p_demux, int i_query, va_list args)
497 demux_sys_t *p_sys = p_demux->p_sys;
501 /*msg_Info(p_demux, "control cmd %d", i_query);*/
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 = (double*) va_arg( args, double* );
509 *pf = ((double)1.0) * stream_Tell( p_demux->s ) / (double) i64;
514 case DEMUX_SET_POSITION:
515 /* arg is 0.0 - 1.0 percent of overall file position */
516 f = (double) 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);
522 /* return TiVo timestamp */
523 p_i64 = (int64_t *) va_arg(args, int64_t *);
524 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
525 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
526 // p_sys->l_last_ty_pts_sync);
527 *p_i64 = (p_sys->l_last_ty_pts / 1000);
529 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
531 p_i64 = (int64_t *) va_arg(args, int64_t *);
534 case DEMUX_SET_TIME: /* arg is time in microsecs */
535 i64 = (int64_t) va_arg( args, int64_t );
536 return ty_stream_seek_time(p_demux, i64 * 1000);
544 static void Close( vlc_object_t *p_this )
546 demux_t *p_demux = (demux_t*)p_this;
547 demux_sys_t *p_sys = p_demux->p_sys;
549 XdsExit( &p_sys->xds );
550 cc_Exit( &p_sys->cc );
551 free( p_sys->rec_hdrs );
552 free( p_sys->seq_table );
557 /* =========================================================================== */
558 /* Compute Presentation Time Stamp (PTS)
559 * Assume buf points to beginning of PTS */
560 static mtime_t get_pts( const uint8_t *buf )
564 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
565 (mtime_t)(buf[1] << 22)|
566 ((mtime_t)(buf[2]&0xfe) << 14)|
567 (mtime_t)(buf[3] << 7)|
568 (mtime_t)(buf[4] >> 1);
569 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
574 /* =========================================================================== */
575 static int find_es_header( const uint8_t *header,
576 const uint8_t *buffer, int i_search_len )
580 for( count = 0; count < i_search_len; count++ )
582 if( !memcmp( &buffer[count], header, 4 ) )
589 /* =========================================================================== */
590 /* check if we have a full PES header, if not, then save what we have.
591 * this is called when audio-start packets are encountered.
593 * 1 partial PES hdr found, some audio data found (buffer adjusted),
594 * -1 partial PES hdr found, no audio data found
595 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
596 /* TODO: HD support -- nothing known about those streams */
597 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
598 int32_t offset, int32_t rec_len )
600 demux_sys_t *p_sys = p_demux->p_sys;
602 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
604 /* entire PES header not present */
605 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
607 /* save the partial pes header */
610 /* no header found, fake some 00's (this works, believe me) */
611 memset( p_sys->pes_buffer, 0, 4 );
612 p_sys->i_pes_buf_cnt = 4;
614 msg_Err( p_demux, "PES header not found in record of %d bytes!",
618 /* copy the partial pes header we found */
619 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
621 p_sys->i_pes_buf_cnt = rec_len - offset;
625 /* PES Header was found, but not complete, so trim the end of this record */
626 p_block->i_buffer -= rec_len - offset;
629 return -1; /* partial PES, no audio data */
631 /* full PES header present, extract PTS */
632 p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block->p_buffer[ offset +
633 p_sys->i_Pts_Offset ] );
634 if (p_sys->firstAudioPTS < 0)
635 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
636 p_block->i_pts = p_sys->lastAudioPTS;
637 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
638 /* adjust audio record to remove PES header */
639 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
640 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
641 p_block->i_buffer -= p_sys->i_Pes_Length;
643 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
644 "%02x %02x %02x %02x %02x %02x %02x %02x "
645 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
646 p_block->p_buffer[0], p_block->p_buffer[1],
647 p_block->p_buffer[2], p_block->p_buffer[3],
648 p_block->p_buffer[4], p_block->p_buffer[5],
649 p_block->p_buffer[6], p_block->p_buffer[7],
650 p_block->p_buffer[8], p_block->p_buffer[9],
651 p_block->p_buffer[10], p_block->p_buffer[11],
652 p_block->p_buffer[12], p_block->p_buffer[13],
653 p_block->p_buffer[14], p_block->p_buffer[15]);
658 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
660 demux_sys_t *p_sys = p_demux->p_sys;
661 const int subrec_type = rec_hdr->subrec_type;
662 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
666 assert( rec_hdr->rec_type == 0xe0 );
671 msg_Dbg(p_demux, "packet buffer has "
672 "%02x %02x %02x %02x %02x %02x %02x %02x "
673 "%02x %02x %02x %02x %02x %02x %02x %02x",
674 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
675 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
676 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
677 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
678 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
679 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
680 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
681 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
683 //if( subrec_type == 0x06 || subrec_type == 0x07 )
684 if( subrec_type != 0x02 && subrec_type != 0x0c &&
685 subrec_type != 0x08 && l_rec_size > 4 )
687 /* get the PTS from this packet if it has one.
688 * on S1, only 0x06 has PES. On S2, however, most all do.
689 * Do NOT Pass the PES Header to the MPEG2 codec */
690 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
691 if( esOffset1 != -1 )
693 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
694 //subrec_type, esOffset1);
695 p_sys->lastVideoPTS = VLC_TS_0 + get_pts(
696 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
697 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
698 p_sys->lastVideoPTS );*/
699 if (subrec_type != 0x06) {
700 /* if we found a PES, and it's not type 6, then we're S2 */
701 /* The packet will have video data (& other headers) so we
702 * chop out the PES header and send the rest */
703 if (l_rec_size >= VIDEO_PES_LENGTH) {
704 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
705 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
707 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
708 " (%ld bytes)", subrec_type, l_rec_size);
709 /* nuke this block; it's too short, but has PES marker */
710 p_block_in->i_buffer = 0;
714 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
718 if(subrec_type == 0x06 )
720 /* type 6 (S1 DTivo) has no data, so we're done */
721 block_Release(p_block_in);
725 /* if it's not a continue blk, then set PTS */
726 if( subrec_type != 0x02 )
728 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
730 /* if it's a GOP header, make sure it's legal
731 * (if we have enough data) */
732 /* Some ty files don't have this bit set
733 * and it causes problems */
734 if (subrec_type == 0x0c && l_rec_size >= 6)
735 p_block_in->p_buffer[5] |= 0x08;
736 /* store the TY PTS if there is one */
737 if (subrec_type == 0x07) {
738 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
739 /* should we use audio or video PTS? */
740 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
742 /* yes I know this is a cheap hack. It's the timestamp
743 used for display and skipping fwd/back, so it
744 doesn't have to be accurate to the millisecond.
745 I adjust it here by roughly one 1/30 sec. Yes it
746 will be slightly off for UK streams, but it's OK.
748 p_sys->l_last_ty_pts += 35000000;
749 //p_sys->l_last_ty_pts += 33366667;
751 /* set PTS for this block before we send */
752 if (p_sys->lastVideoPTS > VLC_TS_INVALID)
754 p_block_in->i_pts = p_sys->lastVideoPTS;
755 /* PTS gets used ONCE.
756 * Any subsequent frames we get BEFORE next PES
757 * header will have their PTS computed in the codec */
758 p_sys->lastVideoPTS = VLC_TS_INVALID;
762 /* Register the CC decoders when needed */
763 for( i = 0; i < 4; i++ )
765 static const vlc_fourcc_t fcc[4] = {
766 VLC_FOURCC('c', 'c', '1', ' '),
767 VLC_FOURCC('c', 'c', '2', ' '),
768 VLC_FOURCC('c', 'c', '3', ' '),
769 VLC_FOURCC('c', 'c', '4', ' ')
771 static const char *ppsz_description[4] = {
772 N_("Closed captions 1"),
773 N_("Closed captions 2"),
774 N_("Closed captions 3"),
775 N_("Closed captions 4"),
780 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
783 es_format_Init( &fmt, SPU_ES, fcc[i] );
784 fmt.psz_description = strdup( vlc_gettext(ppsz_description[i]) );
785 fmt.i_group = TY_ES_GROUP;
786 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
787 es_format_Clean( &fmt );
790 /* Send the CC data */
791 if( p_block_in->i_pts > VLC_TS_INVALID && p_sys->cc.i_data > 0 )
793 for( i = 0; i < 4; i++ )
797 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
798 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
799 p_cc->i_pts = p_block_in->i_pts;
800 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
802 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
805 cc_Flush( &p_sys->cc );
808 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
809 //p_sys->i_cur_rec, subrec_type);
810 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
813 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
815 demux_sys_t *p_sys = p_demux->p_sys;
816 const int subrec_type = rec_hdr->subrec_type;
817 const long l_rec_size = rec_hdr->l_rec_size;
820 assert( rec_hdr->rec_type == 0xc0 );
825 fprintf( stderr, "Audio Packet Header " );
826 for( i = 0 ; i < 24 ; i++ )
827 fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] );
828 fprintf( stderr, "\n" );
831 if( subrec_type == 2 )
833 /* SA or DTiVo Audio Data, no PES (continued block)
834 * ================================================
837 /* continue PES if previous was incomplete */
838 if (p_sys->i_pes_buf_cnt > 0)
840 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
842 msg_Dbg(p_demux, "continuing PES header");
843 /* do we have enough data to complete? */
844 if (i_need >= l_rec_size)
846 /* don't have complete PES hdr; save what we have and return */
847 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
848 p_block_in->p_buffer, l_rec_size);
849 p_sys->i_pes_buf_cnt += l_rec_size;
851 block_Release(p_block_in);
855 /* we have enough; reconstruct this p_frame with the new hdr */
856 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
857 p_block_in->p_buffer, i_need);
858 /* advance the block past the PES header (don't want to send it) */
859 p_block_in->p_buffer += i_need;
860 p_block_in->i_buffer -= i_need;
861 /* get the PTS out of this PES header (MPEG or AC3) */
862 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
863 esOffset1 = find_es_header(ty_MPEGAudioPacket,
864 p_sys->pes_buffer, 5);
866 esOffset1 = find_es_header(ty_AC3AudioPacket,
867 p_sys->pes_buffer, 5);
870 /* god help us; something's really wrong */
871 msg_Err(p_demux, "can't find audio PES header in packet");
875 p_sys->lastAudioPTS = VLC_TS_0 + get_pts(
876 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
877 p_block_in->i_pts = p_sys->lastAudioPTS;
879 p_sys->i_pes_buf_cnt = 0;
881 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
882 * not allowed in the AC3 spec and will cause problems. So here
883 * we try to trim things. */
884 /* Also, S1 DTivo has alternating short / long AC3 packets. That
885 * is, one packet is short (incomplete) and the next packet has
886 * the first one's missing data, plus all of its own. Strange. */
887 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
888 p_sys->tivo_series == TIVO_SERIES2) {
889 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
891 p_block_in->i_buffer -= 2;
892 p_sys->l_ac3_pkt_size = 0;
894 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
898 else if( subrec_type == 0x03 )
900 /* MPEG Audio with PES Header, either SA or DTiVo */
901 /* ================================================ */
902 esOffset1 = find_es_header( ty_MPEGAudioPacket,
903 p_block_in->p_buffer, 5 );
905 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
906 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
907 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
908 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
910 /* SA PES Header, No Audio Data */
911 /* ================================================ */
912 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
914 p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block_in->p_buffer[
916 if (p_sys->firstAudioPTS < 0)
917 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
919 block_Release(p_block_in);
921 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
922 p_sys->lastAudioPTS );*/
924 /* DTiVo Audio with PES Header */
925 /* ================================================ */
927 /* Check for complete PES */
928 if (check_sync_pes(p_demux, p_block_in, esOffset1,
931 /* partial PES header found, nothing else.
933 block_Release(p_block_in);
937 msg_Dbg(p_demux, "packet buffer has "
938 "%02x %02x %02x %02x %02x %02x %02x %02x "
939 "%02x %02x %02x %02x %02x %02x %02x %02x",
940 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
941 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
942 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
943 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
944 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
945 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
946 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
947 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
950 else if( subrec_type == 0x04 )
952 /* SA Audio with no PES Header */
953 /* ================================================ */
955 "Adding SA Audio Packet Size %ld", l_rec_size ); */
957 if (p_sys->lastAudioPTS > VLC_TS_INVALID )
958 p_block_in->i_pts = p_sys->lastAudioPTS;
960 else if( subrec_type == 0x09 )
962 /* DTiVo AC3 Audio Data with PES Header */
963 /* ================================================ */
964 esOffset1 = find_es_header( ty_AC3AudioPacket,
965 p_block_in->p_buffer, 5 );
968 msg_Dbg(p_demux, "buffer has "
969 "%02x %02x %02x %02x %02x %02x %02x %02x "
970 "%02x %02x %02x %02x %02x %02x %02x %02x",
971 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
972 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
973 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
974 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
975 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
976 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
977 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
978 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
979 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
982 /* Check for complete PES */
983 if (check_sync_pes(p_demux, p_block_in, esOffset1,
986 /* partial PES header found, nothing else. we're done. */
987 block_Release(p_block_in);
990 /* S2 DTivo has invalid long AC3 packets */
991 if (p_sys->tivo_series == TIVO_SERIES2) {
992 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
993 p_block_in->i_buffer -= 2;
994 p_sys->l_ac3_pkt_size = 0;
996 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1002 /* Unsupported/Unknown */
1003 block_Release(p_block_in);
1007 /* set PCR before we send (if PTS found) */
1008 if( p_block_in->i_pts > VLC_TS_INVALID )
1009 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1010 p_block_in->i_pts );
1012 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1016 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1018 demux_sys_t *p_sys = p_demux->p_sys;
1022 block_Release(p_block_in);
1024 if( rec_hdr->rec_type == 0x01 )
1026 else if( rec_hdr->rec_type == 0x02 )
1031 /* XDS data (extract programs infos) transmitted on field 2 only */
1033 DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] );
1035 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1038 cc_AppendData( &p_sys->cc, i_field, rec_hdr->ex );
1042 /* seek to a position within the stream, if possible */
1043 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1045 demux_sys_t *p_sys = p_demux->p_sys;
1046 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1049 unsigned i_cur_part;
1051 /* if we're not seekable, there's nothing to do */
1052 if (!p_sys->b_seekable)
1053 return VLC_EGENERIC;
1055 /* figure out which part & chunk we want & go there */
1056 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1057 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1059 /* try to read the part header (master chunk) if it's there */
1060 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1062 /* can't seek stream */
1063 return VLC_EGENERIC;
1065 parse_master(p_demux);
1067 /* now for the actual chunk */
1068 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1070 /* can't seek stream */
1071 return VLC_EGENERIC;
1073 /* load the chunk */
1074 p_sys->i_stuff_cnt = 0;
1075 get_chunk_header(p_demux);
1077 /* seek within the chunk to get roughly to where we want */
1078 p_sys->i_cur_rec = (int)
1079 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1080 msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1081 msg_Dbg(p_demux, " (chunk %d, record %d)",
1082 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1084 /* seek to the start of this record's data.
1085 * to do that, we have to skip past all prior records */
1087 for (i=0; i<p_sys->i_cur_rec; i++)
1088 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1089 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1090 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1092 /* to hell with syncing any audio or video, just start reading records... :) */
1093 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TS_INVALID;*/
1098 //#define TY_XDS_DEBUG
1099 static void XdsInit( xds_t *h )
1104 h->i_class = XDS_MAX_CLASS_COUNT;
1106 h->b_future = false;
1107 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1109 for( j = 0; j < 128; j++ )
1110 h->pkt[i][j].b_started = false;
1112 h->b_meta_changed = false;
1113 memset( &h->meta, 0, sizeof(h->meta) );
1115 static void XdsExit( xds_t *h )
1118 free( h->meta.psz_channel_name );
1119 free( h->meta.psz_channel_call_letter );
1120 free( h->meta.psz_channel_number );
1123 free( h->meta.current.psz_name );
1124 free( h->meta.current.psz_rating );
1126 free( h->meta.future.psz_name );
1127 free( h->meta.future.psz_rating );
1129 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1134 for( i = 0, i_dst = 0; i < i_src; i++ )
1138 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1139 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1140 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1141 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1142 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1143 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1144 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1145 E2( 0x7c, 0xc3,0xb7); // division symbol
1146 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1147 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1150 dst[i_dst++] = p_src[i];
1154 dst[i_dst++] = '\0';
1156 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1158 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1160 if( *ppsz_dst == NULL && psz_new == NULL )
1165 *ppsz_dst = strdup( psz_new );
1169 h->b_meta_changed = true;
1173 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1175 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1182 XdsStringUtf8( name, pk->p_data, pk->i_data );
1183 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1185 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1186 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1190 i_rating = (pk->p_data[0] & 0x18);
1191 if( i_rating == 0x08 )
1194 static const char *pppsz_ratings[8][2] = {
1195 { "None", "No rating (no content advisory)" },
1196 { "TV-Y", "All Children (no content advisory)" },
1197 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1198 { "TV-G", "General Audience (no content advisory)" },
1199 { "TV-PG", "Parental Guidance Suggested" },
1200 { "TV-14", "Parents Strongly Cautioned" },
1201 { "TV-MA", "Mature Audience Only" },
1202 { "None", "No rating (no content advisory)" }
1204 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1205 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1207 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1208 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1209 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1212 else if( i_rating == 0x00 || i_rating == 0x10 )
1215 static const char *pppsz_ratings[8][2] = {
1217 { "G", "General Audiences" },
1218 { "PG", "Parental Guidance Suggested" },
1219 { "PG-13", "Parents Strongly Cautioned" },
1220 { "R", "Restricted" },
1221 { "NC-17", "No one 17 and under admitted" },
1222 { "X", "No one under 17 admitted" },
1223 { "NR", "Not Rated" },
1225 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1226 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1228 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1229 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1230 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1235 /* Non US Rating TODO */
1236 assert( i_rating == 0x18 ); // only left value possible */
1237 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1238 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1240 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1241 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1248 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1254 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1262 if( pk->i_data < 2 )
1264 XdsStringUtf8( name, pk->p_data, pk->i_data );
1265 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1267 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1268 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1273 if( pk->i_data < 4 )
1276 XdsStringUtf8( name, pk->p_data, 4 );
1277 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1279 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1280 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1282 if( pk->i_data >= 6 )
1284 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1285 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1287 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1288 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1293 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1295 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1296 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1301 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1304 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1308 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1314 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1316 switch( h->i_class )
1318 case XDS_CLASS_CURRENT:
1319 case XDS_CLASS_FUTURE:
1320 XdsDecodeCurrentFuture( h, pk );
1322 case XDS_CLASS_CHANNEL:
1323 XdsDecodeChannel( h, pk );
1325 case XDS_CLASS_MISCELLANEOUS:
1327 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1330 case XDS_CLASS_PUBLIC_SERVICE:
1332 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1336 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1341 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1343 /* TODO check parity */
1348 if( d1 >= 0x01 && d1 <= 0x0e )
1350 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1351 const int i_type = d2;
1352 const bool b_start = d1 & 0x01;
1353 xds_packet_t *pk = &h->pkt[i_class][i_type];
1355 if( !b_start && !pk->b_started )
1357 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1363 h->i_class = i_class;
1365 h->b_future = !b_start;
1366 pk->b_started = true;
1370 pk->i_sum = d1 + d2;
1373 else if( d1 == 0x0f && h->b_xds )
1375 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1377 /* TODO checksum and decode */
1378 pk->i_sum += d1 + d2;
1379 if( pk->i_sum & 0x7f )
1381 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1382 pk->b_started = false;
1385 if( pk->i_data <= 0 )
1387 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1388 pk->b_started = false;
1392 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1397 pk->b_started = false;
1399 else if( d1 >= 0x20 && h->b_xds )
1401 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1403 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1405 /* Broken -> reinit */
1406 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1408 pk->b_started = false;
1411 /* TODO check parity bit */
1412 pk->p_data[pk->i_data++] = d1 & 0x7f;
1413 pk->p_data[pk->i_data++] = d2 & 0x7f;
1422 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1424 demux_sys_t *p_sys = p_demux->p_sys;
1426 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1427 if( p_demux->p_sys->xds.b_meta_changed )
1429 xds_meta_t *m = &p_sys->xds.meta;
1433 /* Channel meta data */
1434 p_meta = vlc_meta_New();
1435 if( m->psz_channel_name )
1436 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1437 if( m->psz_channel_call_letter )
1438 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1439 if( m->psz_channel_number )
1440 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1441 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1442 vlc_meta_Delete( p_meta );
1444 /* Event meta data (current/future) */
1445 p_epg = vlc_epg_New( NULL );
1446 if( m->current.psz_name )
1448 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1449 //if( m->current.psz_rating )
1450 // TODO but VLC cannot yet handle rating per epg event
1451 vlc_epg_SetCurrent( p_epg, 0 );
1453 if( m->future.psz_name )
1456 if( p_epg->i_event > 0 )
1457 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1458 vlc_epg_Delete( p_epg );
1460 p_demux->p_sys->xds.b_meta_changed = false;
1463 /* seek to an exact time position within the stream, if possible.
1464 * l_seek_time is in nanoseconds, the TIVO time standard.
1466 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1468 demux_sys_t *p_sys = p_demux->p_sys;
1469 int i_seq_entry = 0;
1472 long l_cur_pos = stream_Tell(p_demux->s);
1473 unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1474 long l_seek_secs = l_seek_time / 1000000000;
1475 uint64_t l_fwd_stamp = 1;
1477 /* if we're not seekable, there's nothing to do */
1478 if (!p_sys->b_seekable || !p_sys->b_have_master)
1479 return VLC_EGENERIC;
1481 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1482 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1484 /* seek to the proper segment if necessary */
1485 /* first see if we need to go back */
1486 while (l_seek_time < p_sys->l_first_ty_pts) {
1487 msg_Dbg(p_demux, "skipping to prior segment.");
1488 /* load previous part */
1489 if (i_cur_part == 0) {
1490 stream_Seek(p_demux->s, l_cur_pos);
1491 msg_Err(p_demux, "Attempt to seek past BOF");
1492 return VLC_EGENERIC;
1494 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1496 parse_master(p_demux);
1498 /* maybe we need to go forward */
1499 while (l_seek_time > p_sys->l_final_ty_pts) {
1500 msg_Dbg(p_demux, "skipping to next segment.");
1501 /* load next part */
1502 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1503 /* error; restore previous file position */
1504 stream_Seek(p_demux->s, l_cur_pos);
1505 msg_Err(p_demux, "seek error");
1506 return VLC_EGENERIC;
1508 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1510 parse_master(p_demux);
1513 /* our target is somewhere within this part;
1514 find the proper chunk using seq_table */
1515 for (i=1; i<p_sys->i_seq_table_size; i++) {
1516 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1517 /* i-1 is the section we want; remember the next timestamp in case
1518 we have to use it (this section may not have a proper SEQ hdr
1519 for the time we're seeking) */
1520 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1521 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1527 /* if we went through the entire last loop and didn't find our target,
1528 then we skip to the next part. What has happened is that the actual
1529 time we're seeking is within this part, but there isn't a SEQ hdr
1530 for it here. So we skip to the next part */
1531 if (i == p_sys->i_seq_table_size) {
1532 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1533 /* error; restore previous file position */
1534 stream_Seek(p_demux->s, l_cur_pos);
1535 msg_Err(p_demux, "seek error");
1536 return VLC_EGENERIC;
1538 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1540 parse_master(p_demux);
1544 /* determine which chunk has our seek_time */
1545 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1546 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1547 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1548 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1549 i_cur_part, l_chunk_nr,
1550 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1551 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1552 /* check this chunk's SEQ header timestamp */
1553 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1554 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1555 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1557 // TODO: we don't have to parse the full header set;
1558 // just test the seq_rec entry for its timestamp
1559 p_sys->i_stuff_cnt = 0;
1560 get_chunk_header(p_demux);
1561 // check ty PTS for the SEQ entry in this chunk
1562 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1563 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1564 /* Seek to beginning of original chunk & reload it */
1565 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1566 p_sys->i_stuff_cnt = 0;
1567 get_chunk_header(p_demux);
1568 return VLC_EGENERIC;
1570 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1572 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1574 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1575 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1576 // keep this one? go back?
1577 /* for now, we take this one. it's the first SEQ hdr AFTER
1578 the time we were searching for. */
1579 msg_Dbg(p_demux, "seek target found.");
1582 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1585 /* if we made it through this entire loop without finding our target,
1586 then we skip to the next section. What has happened is that the actual
1587 time we're seeking is within this section, but there isn't a SEQ hdr
1588 for it here. So we skip to the next closest one (l_fwd_stamp) */
1589 if (i == p_sys->i_bits_per_seq_entry)
1590 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1592 /* current stream ptr is at beginning of data for this chunk,
1593 so we need to skip past any stream data prior to the seq_rec
1596 for (int j=0; j<p_sys->i_seq_rec; j++)
1597 i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size;
1598 stream_Read(p_demux->s, NULL, i_skip_cnt);
1599 p_sys->i_cur_rec = p_sys->i_seq_rec;
1600 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1601 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1607 /* parse a master chunk, filling the SEQ table and other variables.
1608 * We assume the stream is currently pointing to it.
1610 static void parse_master(demux_t *p_demux)
1612 demux_sys_t *p_sys = p_demux->p_sys;
1613 uint8_t mst_buf[32];
1614 uint32_t i, i_map_size;
1615 int64_t i_save_pos = 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 stream_Read(p_demux->s, mst_buf, 32);
1629 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1630 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1631 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1632 p_sys->i_seq_table_size = i / (8 + i_map_size);
1634 /* parse all the entries */
1635 p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t));
1636 if (p_sys->seq_table == NULL)
1638 p_sys->i_seq_table_size = 0;
1641 for (unsigned i=0; i<p_sys->i_seq_table_size; i++) {
1642 stream_Read(p_demux->s, mst_buf, 8);
1643 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1644 if (i_map_size > 8) {
1645 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1646 stream_Read(p_demux->s, NULL, i_map_size);
1648 stream_Read(p_demux->s, mst_buf + 8, i_map_size);
1649 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1653 /* set up a few of our variables */
1654 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1655 p_sys->l_final_ty_pts =
1656 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1657 p_sys->b_have_master = true;
1659 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1661 "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1662 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1663 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1665 "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1666 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1668 /* seek past this chunk */
1669 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1673 /* ======================================================================== */
1674 /* "Peek" at some chunks. Skip over the Part header if we find it.
1675 * We parse the peeked data and determine audio type,
1676 * SA vs. DTivo, & Tivo Series.
1677 * Set global vars i_Pes_Length, i_Pts_Offset,
1678 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1679 static int probe_stream(demux_t *p_demux)
1681 demux_sys_t *p_sys = p_demux->p_sys;
1682 const uint8_t *p_buf;
1684 bool b_probe_error = false;
1686 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1687 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1688 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1689 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1690 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1691 return VLC_EGENERIC;
1694 /* the real work: analyze this chunk */
1695 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1696 analyze_chunk(p_demux, p_buf);
1697 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1698 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1699 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1701 p_buf += CHUNK_SIZE;
1704 /* the final tally */
1705 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1706 msg_Err(p_demux, "Can't determine Tivo Series.");
1707 b_probe_error = true;
1709 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1710 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1711 b_probe_error = true;
1713 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1714 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1715 b_probe_error = true;
1717 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1721 /* ======================================================================== */
1722 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1723 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1725 demux_sys_t *p_sys = p_demux->p_sys;
1727 ty_rec_hdr_t *p_hdrs;
1728 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1731 /* skip if it's a Part header */
1732 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1735 /* number of records in chunk (we ignore high order byte;
1736 * rarely are there > 256 chunks & we don't need that many anyway) */
1737 i_num_recs = p_chunk[0];
1738 if (i_num_recs < 5) {
1739 /* try again with the next chunk. Sometimes there are dead ones */
1743 p_chunk += 4; /* skip past rec count & SEQ bytes */
1744 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1745 p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
1747 * 1. check video packets. Presence of 0x6e0 means S1.
1748 * No 6e0 but have be0 means S2.
1749 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1750 * If AC-3, then we have DTivo.
1751 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1753 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1754 for (i=0; i<i_num_recs; i++) {
1755 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1756 //p_hdrs[i].rec_type,
1757 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1758 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1773 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1774 i_num_6e0, i_num_be0);
1776 /* set up our variables */
1777 if (i_num_6e0 > 0) {
1778 msg_Dbg(p_demux, "detected Series 1 Tivo");
1779 p_sys->tivo_series = TIVO_SERIES1;
1780 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1781 } else if (i_num_be0 > 0) {
1782 msg_Dbg(p_demux, "detected Series 2 Tivo");
1783 p_sys->tivo_series = TIVO_SERIES2;
1784 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1786 if (i_num_9c0 > 0) {
1787 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1788 p_sys->audio_type = TIVO_AUDIO_AC3;
1789 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1790 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1791 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1792 } else if (i_num_3c0 > 0) {
1793 p_sys->audio_type = TIVO_AUDIO_MPEG;
1794 msg_Dbg(p_demux, "detected MPEG Audio" );
1797 /* if tivo_type still unknown, we can check PTS location
1798 * in MPEG packets to determine tivo_type */
1799 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1800 uint32_t i_data_offset = (16 * i_num_recs);
1801 for (i=0; i<i_num_recs; i++) {
1802 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1803 p_hdrs[i].l_rec_size > 15) {
1804 /* first make sure we're aligned */
1805 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1806 &p_chunk[i_data_offset], 5);
1807 if (i_pes_offset >= 0) {
1808 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1809 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1811 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1812 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1813 if (p_sys->tivo_series == TIVO_SERIES1)
1814 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1815 p_sys->tivo_type = TIVO_TYPE_SA;
1816 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1818 if (p_sys->tivo_series == TIVO_SERIES1)
1819 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1820 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1821 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1826 i_data_offset += p_hdrs[i].l_rec_size;
1833 /* =========================================================================== */
1834 static int get_chunk_header(demux_t *p_demux)
1836 int i_readSize, i_num_recs;
1838 const uint8_t *p_peek;
1839 demux_sys_t *p_sys = p_demux->p_sys;
1840 int i_payload_size; /* sum of all records' sizes */
1842 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1844 /* if we have left-over filler space from the last chunk, get that */
1845 if (p_sys->i_stuff_cnt > 0) {
1846 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1847 p_sys->i_stuff_cnt = 0;
1850 /* read the TY packet header */
1851 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1852 p_sys->i_cur_chunk++;
1854 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1861 /* check if it's a PART Header */
1862 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1864 /* parse master chunk */
1865 parse_master(p_demux);
1866 return get_chunk_header(p_demux);
1869 /* number of records in chunk (8- or 16-bit number) */
1870 if (p_peek[3] & 0x80)
1872 /* 16 bit rec cnt */
1873 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1874 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1875 if (p_sys->i_seq_rec != 0xffff)
1877 p_sys->i_seq_rec &= ~0x8000;
1882 /* 8 bit reclen - tivo 1.3 format */
1883 p_sys->i_num_recs = i_num_recs = p_peek[0];
1884 p_sys->i_seq_rec = p_peek[1];
1886 p_sys->i_cur_rec = 0;
1887 p_sys->b_first_chunk = false;
1889 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1891 free(p_sys->rec_hdrs);
1893 /* skip past the 4 bytes we "peeked" earlier */
1894 stream_Read( p_demux->s, NULL, 4 );
1896 /* read the record headers into a temp buffer */
1897 p_hdr_buf = malloc(i_num_recs * 16);
1898 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1904 p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs,
1908 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1909 (p_sys->i_num_recs * 16) - i_payload_size;
1910 if (p_sys->i_stuff_cnt > 0)
1911 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1912 p_sys->i_stuff_cnt );
1917 static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
1918 int i_num_recs, int *pi_payload_size)
1921 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1923 *pi_payload_size = 0;
1924 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1926 for (i = 0; i < i_num_recs; i++)
1928 const uint8_t *record_header = p_buf + (i * 16);
1929 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1930 p_rec_hdr->rec_type = record_header[3];
1931 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1932 if ((record_header[ 0 ] & 0x80) == 0x80)
1935 /* marker bit 2 set, so read extended data */
1936 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1937 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1938 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1939 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1941 p_rec_hdr->ex[0] = b1;
1942 p_rec_hdr->ex[1] = b2;
1943 p_rec_hdr->l_rec_size = 0;
1944 p_rec_hdr->l_ty_pts = 0;
1945 p_rec_hdr->b_ext = true;
1949 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1950 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1951 *pi_payload_size += p_rec_hdr->l_rec_size;
1952 p_rec_hdr->b_ext = false;
1953 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1955 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1956 } /* end of record-header loop */