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 *****************************************************************************/
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"
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Open ( vlc_object_t * );
54 static void Close( vlc_object_t * );
57 set_shortname( _("TY") );
58 set_description(_("TY Stream audio/video demux"));
59 set_category( CAT_INPUT );
60 set_subcategory( SUBCAT_INPUT_DEMUX );
61 set_capability("demux", 6);
62 /* FIXME: there seems to be a segfault when using PVR access
63 * and TY demux has a bigger priority than PS
64 * Something must be wrong.
66 set_callbacks( Open, Close );
71 /*****************************************************************************
73 *****************************************************************************/
74 static int Demux ( demux_t * );
75 static int Control( demux_t *, int, va_list );
77 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
78 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
79 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
80 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
81 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
82 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
83 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
84 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
85 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
86 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
87 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
88 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
90 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
92 /* packet types for reference:
93 2/c0: audio data continued
94 3/c0: audio packet header (PES header)
95 4/c0: audio data (S/A only?)
96 9/c0: audio packet header, AC-3 audio
97 2/e0: video data continued
98 6/e0: video packet header (PES header)
99 7/e0: video sequence header start
100 8/e0: video I-frame header start
101 a/e0: video P-frame header start
102 b/e0: video B-frame header start
103 c/e0: video GOP header start
104 e/01: closed-caption data
105 e/02: Extended data services data
106 e/03: ipreview data ("thumbs up to record" signal)
110 #define TIVO_PES_FILEID ( 0xf5467abd )
111 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
112 #define CHUNK_SIZE ( 128 * 1024 )
121 uint64_t l_ty_pts; /* TY PTS in the record header */
126 uint64_t l_timestamp;
127 uint8_t chunk_bitmask[8];
151 #define XDS_MAX_DATA_SIZE (32)
154 XDS_CLASS_CURRENT = 0,
155 XDS_CLASS_FUTURE = 1,
156 XDS_CLASS_CHANNEL = 2,
157 XDS_CLASS_MISCELLANEOUS = 3,
158 XDS_CLASS_PUBLIC_SERVICE = 4,
159 XDS_CLASS_RESERVED = 5,
160 XDS_CLASS_UNDEFINED = 6,
169 uint8_t p_data[XDS_MAX_DATA_SIZE];
174 XDS_META_PROGRAM_RATING_NONE,
175 XDS_META_PROGRAM_RATING_MPAA,
176 XDS_META_PROGRAM_RATING_TPG,
177 /* TODO add CA/CE rating */
178 } xds_meta_program_rating_t;
182 xds_meta_program_rating_t rating;
184 /* Add the other fields once I have the samples */
185 } xds_meta_program_t;
188 char *psz_channel_name;
189 char *psz_channel_call_letter;
190 char *psz_channel_number;
192 xds_meta_program_t current;
193 xds_meta_program_t future;
197 /* Are we in XDS mode */
200 /* Current class type */
206 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
216 es_out_id_t *p_video; /* ptr to video codec */
217 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
220 es_out_id_t *p_cc[4];
226 size_t i_stream_size; /* size of input stream (if known) */
227 //uint64_t l_program_len; /* length of this stream in msec */
228 bool b_seekable; /* is this stream seekable? */
229 bool b_have_master; /* are master chunks present? */
230 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
231 tivo_series_t tivo_series; /* Series1 or Series2 */
232 tivo_audio_t audio_type; /* AC3 or MPEG */
233 int i_Pes_Length; /* Length of Audio PES header */
234 int i_Pts_Offset; /* offset into audio PES of PTS */
235 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
236 int i_pes_buf_cnt; /* how many bytes in our buffer */
237 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
238 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
239 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
240 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
241 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
242 int i_seq_table_size; /* number of entries in SEQ table */
243 int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
245 mtime_t firstAudioPTS;
246 mtime_t lastAudioPTS;
247 mtime_t lastVideoPTS;
249 ty_rec_hdr_t *rec_hdrs; /* record headers array */
250 int i_cur_rec; /* current record in this chunk */
251 int i_num_recs; /* number of recs in this chunk */
252 int i_seq_rec; /* record number where seq start is */
253 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
258 static int get_chunk_header(demux_t *);
259 static mtime_t get_pts( const uint8_t *buf );
260 static int find_es_header( const uint8_t *header,
261 const uint8_t *buffer, int i_search_len );
262 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
263 static int ty_stream_seek_time(demux_t *, uint64_t);
265 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
266 int i_num_recs, int *pi_payload_size);
267 static int probe_stream(demux_t *p_demux);
268 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
269 static void parse_master(demux_t *p_demux);
271 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
272 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
273 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
275 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
277 static void XdsInit( xds_t * );
278 static void XdsExit( xds_t * );
280 #define TY_ES_GROUP (1)
283 * Open: check file and initialize demux structures
286 * 1. peek at the first 12 bytes of the stream for the
287 * magic TiVo PART header & stream type & chunk size
288 * 2. if it's not there, error with VLC_EGENERIC
289 * 3. set up video (mpgv) codec
290 * 4. return VLC_SUCCESS
292 static int Open(vlc_object_t *p_this)
294 demux_t *p_demux = (demux_t *)p_this;
297 const uint8_t *p_peek;
300 /* peek at the first 12 bytes. */
301 /* for TY streams, they're always the same */
302 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
305 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
306 U32_AT(&p_peek[4]) != 0x02 ||
307 U32_AT(&p_peek[8]) != CHUNK_SIZE )
309 if( !p_demux->b_force &&
310 !demux_IsPathExtension( p_demux, ".ty" ) &&
311 !demux_IsPathExtension( p_demux, ".ty+" ) )
313 msg_Warn( p_demux, "this does not look like a TY file, "
314 "continuing anyway..." );
317 /* at this point, we assume we have a valid TY stream */
318 msg_Dbg( p_demux, "valid TY stream detected" );
320 /* Set exported functions */
321 p_demux->pf_demux = Demux;
322 p_demux->pf_control = Control;
324 /* create our structure that will hold all data */
325 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
326 memset(p_sys, 0, sizeof(demux_sys_t));
328 /* set up our struct (most were zero'd out with the memset above) */
329 p_sys->b_first_chunk = true;
330 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
331 p_sys->firstAudioPTS = -1;
332 p_sys->i_stream_size = stream_Size(p_demux->s);
333 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
334 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
335 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
336 p_sys->i_Pes_Length = 0;
337 p_sys->i_Pts_Offset = 0;
338 p_sys->l_ac3_pkt_size = 0;
340 /* see if this stream is seekable */
341 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
343 if (probe_stream(p_demux) != VLC_SUCCESS) {
348 if (!p_sys->b_have_master)
349 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
351 /* register the proper audio codec */
352 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
353 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
355 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
357 fmt.i_group = TY_ES_GROUP;
358 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
360 /* register the video stream */
361 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
362 fmt.i_group = TY_ES_GROUP;
363 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
366 for( i = 0; i < 4; i++ )
367 p_sys->p_cc[i] = NULL;
368 cc_Init( &p_sys->cc );
370 XdsInit( &p_sys->xds );
375 /* =========================================================================== */
376 /* Demux: Read & Demux one record from the chunk
378 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
380 * NOTE: I think we can return the number of packets sent instead of just 1.
381 * that means we can demux an entire chunk and shoot it back (may be more efficient)
382 * -- should try that some day :) --
384 static int Demux( demux_t *p_demux )
386 demux_sys_t *p_sys = p_demux->p_sys;
388 block_t *p_block_in = NULL;
390 /*msg_Dbg(p_demux, "ty demux processing" );*/
392 /* did we hit EOF earlier? */
397 * what we do (1 record now.. maybe more later):
398 * - use stream_Read() to read the chunk header & record headers
399 * - discard entire chunk if it is a PART header chunk
400 * - parse all the headers into record header array
401 * - keep a pointer of which record we're on
402 * - use stream_Block() to fetch each record
403 * - parse out PTS from PES headers
404 * - set PTS for data packets
405 * - pass the data on to the proper codec via es_out_Send()
407 * if this is the first time or
408 * if we're at the end of this chunk, start a new one
410 /* parse the next chunk's record headers */
411 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
413 if( get_chunk_header(p_demux) == 0 )
417 /*======================================================================
418 * parse & send one record of the chunk
419 *====================================================================== */
420 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
424 const long l_rec_size = p_rec->l_rec_size;
425 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
426 subrec_type, p_rec->rec_type, l_rec_size );*/
428 /* some normal records are 0 length, so check for that... */
429 if( l_rec_size <= 0 )
431 /* no data in payload; we're done */
436 /* read in this record's payload */
437 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
440 /* set these as 'unknown' for now */
442 p_block_in->i_dts = 0;
446 -- don't read any data from the stream, data was in the record header --
448 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
449 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
452 if( p_rec->rec_type == 0xe0 )
455 DemuxRecVideo( p_demux, p_rec, p_block_in );
457 else if ( p_rec->rec_type == 0xc0 )
460 DemuxRecAudio( p_demux, p_rec, p_block_in );
462 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
464 /* Closed Captions/XDS */
465 DemuxRecCc( p_demux, p_rec, p_block_in );
467 else if ( p_rec->rec_type == 0x03 )
469 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
471 block_Release(p_block_in);
473 else if ( p_rec->rec_type == 0x05 )
475 /* Unknown, but seen regularly */
477 block_Release(p_block_in);
481 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
483 block_Release(p_block_in);
492 static int Control(demux_t *p_demux, int i_query, va_list args)
494 demux_sys_t *p_sys = p_demux->p_sys;
498 /*msg_Info(p_demux, "control cmd %d", i_query);*/
501 case DEMUX_GET_POSITION:
502 /* arg is 0.0 - 1.0 percent of overall file position */
503 if( ( i64 = p_sys->i_stream_size ) > 0 )
505 pf = (double*) va_arg( args, double* );
506 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
511 case DEMUX_SET_POSITION:
512 /* arg is 0.0 - 1.0 percent of overall file position */
513 f = (double) va_arg( args, double );
514 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
515 if ((i64 = p_sys->i_stream_size) > 0)
516 return ty_stream_seek_pct(p_demux, f);
519 /* return TiVo timestamp */
520 p_i64 = (int64_t *) va_arg(args, int64_t *);
521 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
522 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
523 // p_sys->l_last_ty_pts_sync);
524 *p_i64 = (p_sys->l_last_ty_pts / 1000);
526 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
528 p_i64 = (int64_t *) va_arg(args, int64_t *);
531 case DEMUX_SET_TIME: /* arg is time in microsecs */
532 i64 = (int64_t) va_arg( args, int64_t );
533 return ty_stream_seek_time(p_demux, i64 * 1000);
541 static void Close( vlc_object_t *p_this )
543 demux_t *p_demux = (demux_t*)p_this;
544 demux_sys_t *p_sys = p_demux->p_sys;
546 XdsExit( &p_sys->xds );
547 cc_Exit( &p_sys->cc );
548 free( p_sys->rec_hdrs );
549 free( p_sys->seq_table );
554 /* =========================================================================== */
555 /* Compute Presentation Time Stamp (PTS)
556 * Assume buf points to beginning of PTS */
557 static mtime_t get_pts( const uint8_t *buf )
561 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
562 (mtime_t)(buf[1] << 22)|
563 ((mtime_t)(buf[2]&0xfe) << 14)|
564 (mtime_t)(buf[3] << 7)|
565 (mtime_t)(buf[4] >> 1);
566 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
571 /* =========================================================================== */
572 static int find_es_header( const uint8_t *header,
573 const uint8_t *buffer, int i_search_len )
577 for( count = 0; count < i_search_len; count++ )
579 if( !memcmp( &buffer[count], header, 4 ) )
586 /* =========================================================================== */
587 /* check if we have a full PES header, if not, then save what we have.
588 * this is called when audio-start packets are encountered.
590 * 1 partial PES hdr found, some audio data found (buffer adjusted),
591 * -1 partial PES hdr found, no audio data found
592 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
593 /* TODO: HD support -- nothing known about those streams */
594 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
595 int32_t offset, int32_t rec_len )
597 demux_sys_t *p_sys = p_demux->p_sys;
599 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
601 /* entire PES header not present */
602 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
604 /* save the partial pes header */
607 /* no header found, fake some 00's (this works, believe me) */
608 memset( p_sys->pes_buffer, 4, 0 );
609 p_sys->i_pes_buf_cnt = 4;
611 msg_Err( p_demux, "PES header not found in record of %d bytes!",
615 /* copy the partial pes header we found */
616 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
618 p_sys->i_pes_buf_cnt = rec_len - offset;
622 /* PES Header was found, but not complete, so trim the end of this record */
623 p_block->i_buffer -= rec_len - offset;
626 return -1; /* partial PES, no audio data */
628 /* full PES header present, extract PTS */
629 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
630 p_sys->i_Pts_Offset ] );
631 if (p_sys->firstAudioPTS < 0)
632 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
633 p_block->i_pts = p_sys->lastAudioPTS;
634 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
635 /* adjust audio record to remove PES header */
636 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
637 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
638 p_block->i_buffer -= p_sys->i_Pes_Length;
640 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
641 "%02x %02x %02x %02x %02x %02x %02x %02x "
642 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
643 p_block->p_buffer[0], p_block->p_buffer[1],
644 p_block->p_buffer[2], p_block->p_buffer[3],
645 p_block->p_buffer[4], p_block->p_buffer[5],
646 p_block->p_buffer[6], p_block->p_buffer[7],
647 p_block->p_buffer[8], p_block->p_buffer[9],
648 p_block->p_buffer[10], p_block->p_buffer[11],
649 p_block->p_buffer[12], p_block->p_buffer[13],
650 p_block->p_buffer[14], p_block->p_buffer[15]);
655 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
657 demux_sys_t *p_sys = p_demux->p_sys;
658 const int subrec_type = rec_hdr->subrec_type;
659 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
663 assert( rec_hdr->rec_type == 0xe0 );
668 msg_Dbg(p_demux, "packet buffer has "
669 "%02x %02x %02x %02x %02x %02x %02x %02x "
670 "%02x %02x %02x %02x %02x %02x %02x %02x",
671 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
672 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
673 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
674 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
675 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
676 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
677 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
678 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
680 //if( subrec_type == 0x06 || subrec_type == 0x07 )
681 if( subrec_type != 0x02 && subrec_type != 0x0c &&
682 subrec_type != 0x08 && l_rec_size > 4 )
684 /* get the PTS from this packet if it has one.
685 * on S1, only 0x06 has PES. On S2, however, most all do.
686 * Do NOT Pass the PES Header to the MPEG2 codec */
687 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
688 if( esOffset1 != -1 )
690 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
691 //subrec_type, esOffset1);
692 p_sys->lastVideoPTS = get_pts(
693 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
694 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
695 p_sys->lastVideoPTS );*/
696 if (subrec_type != 0x06) {
697 /* if we found a PES, and it's not type 6, then we're S2 */
698 /* The packet will have video data (& other headers) so we
699 * chop out the PES header and send the rest */
700 if (l_rec_size >= VIDEO_PES_LENGTH) {
701 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
702 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
704 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
705 " (%ld bytes)", subrec_type, l_rec_size);
706 /* nuke this block; it's too short, but has PES marker */
707 p_block_in->i_buffer = 0;
711 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
715 if(subrec_type == 0x06 )
717 /* type 6 (S1 DTivo) has no data, so we're done */
718 block_Release(p_block_in);
722 /* if it's not a continue blk, then set PTS */
723 if( subrec_type != 0x02 )
725 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
727 /* if it's a GOP header, make sure it's legal
728 * (if we have enough data) */
729 /* Some ty files don't have this bit set
730 * and it causes problems */
731 if (subrec_type == 0x0c && l_rec_size >= 6)
732 p_block_in->p_buffer[5] |= 0x08;
733 /* store the TY PTS if there is one */
734 if (subrec_type == 0x07) {
735 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
736 /* should we use audio or video PTS? */
737 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
739 /* yes I know this is a cheap hack. It's the timestamp
740 used for display and skipping fwd/back, so it
741 doesn't have to be accurate to the millisecond.
742 I adjust it here by roughly one 1/30 sec. Yes it
743 will be slightly off for UK streams, but it's OK.
745 p_sys->l_last_ty_pts += 35000000;
746 //p_sys->l_last_ty_pts += 33366667;
748 /* set PTS for this block before we send */
749 if (p_sys->lastVideoPTS > 0)
751 p_block_in->i_pts = p_sys->lastVideoPTS;
752 /* PTS gets used ONCE.
753 * Any subsequent frames we get BEFORE next PES
754 * header will have their PTS computed in the codec */
755 p_sys->lastVideoPTS = 0;
759 /* Register the CC decoders when needed */
760 for( i = 0; i < 4; i++ )
762 static const vlc_fourcc_t fcc[4] = {
763 VLC_FOURCC('c', 'c', '1', ' '),
764 VLC_FOURCC('c', 'c', '2', ' '),
765 VLC_FOURCC('c', 'c', '3', ' '),
766 VLC_FOURCC('c', 'c', '4', ' ')
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"),
777 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
780 es_format_Init( &fmt, SPU_ES, fcc[i] );
781 fmt.psz_description = strdup( _(ppsz_description[i]) );
782 fmt.i_group = TY_ES_GROUP;
783 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
784 es_format_Clean( &fmt );
787 /* Send the CC data */
788 if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
792 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
793 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
794 p_cc->i_pts = p_block_in->i_pts;
795 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
797 for( i = 0, i_cc_count = 0; i < 4; i++ )
798 i_cc_count += p_sys->p_cc[i] ? 1 : 0;
800 for( i = 0; i < 4; i++ )
802 if( !p_sys->p_cc[i] )
805 es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
807 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
809 cc_Flush( &p_sys->cc );
812 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
813 //p_sys->i_cur_rec, subrec_type);
814 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
817 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
819 demux_sys_t *p_sys = p_demux->p_sys;
820 const int subrec_type = rec_hdr->subrec_type;
821 const long l_rec_size = rec_hdr->l_rec_size;
824 assert( rec_hdr->rec_type == 0xc0 );
829 printf( "Audio Packet Header " );
830 for( i = 0 ; i < 24 ; i++ )
831 printf( "%2.2x ", p_block_in->p_buffer[i] );
835 if( subrec_type == 2 )
837 /* SA or DTiVo Audio Data, no PES (continued block)
838 * ================================================
841 /* continue PES if previous was incomplete */
842 if (p_sys->i_pes_buf_cnt > 0)
844 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
846 msg_Dbg(p_demux, "continuing PES header");
847 /* do we have enough data to complete? */
848 if (i_need >= l_rec_size)
850 /* don't have complete PES hdr; save what we have and return */
851 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
852 p_block_in->p_buffer, l_rec_size);
853 p_sys->i_pes_buf_cnt += l_rec_size;
855 block_Release(p_block_in);
859 /* we have enough; reconstruct this p_frame with the new hdr */
860 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
861 p_block_in->p_buffer, i_need);
862 /* advance the block past the PES header (don't want to send it) */
863 p_block_in->p_buffer += i_need;
864 p_block_in->i_buffer -= i_need;
865 /* get the PTS out of this PES header (MPEG or AC3) */
866 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
867 esOffset1 = find_es_header(ty_MPEGAudioPacket,
868 p_sys->pes_buffer, 5);
870 esOffset1 = find_es_header(ty_AC3AudioPacket,
871 p_sys->pes_buffer, 5);
874 /* god help us; something's really wrong */
875 msg_Err(p_demux, "can't find audio PES header in packet");
879 p_sys->lastAudioPTS = get_pts(
880 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
881 p_block_in->i_pts = p_sys->lastAudioPTS;
883 p_sys->i_pes_buf_cnt = 0;
885 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
886 * not allowed in the AC3 spec and will cause problems. So here
887 * we try to trim things. */
888 /* Also, S1 DTivo has alternating short / long AC3 packets. That
889 * is, one packet is short (incomplete) and the next packet has
890 * the first one's missing data, plus all of its own. Strange. */
891 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
892 p_sys->tivo_series == TIVO_SERIES2) {
893 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
895 p_block_in->i_buffer -= 2;
896 p_sys->l_ac3_pkt_size = 0;
898 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
902 else if( subrec_type == 0x03 )
904 /* MPEG Audio with PES Header, either SA or DTiVo */
905 /* ================================================ */
906 esOffset1 = find_es_header( ty_MPEGAudioPacket,
907 p_block_in->p_buffer, 5 );
909 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
910 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
911 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
912 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
914 /* SA PES Header, No Audio Data */
915 /* ================================================ */
916 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
918 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
920 if (p_sys->firstAudioPTS < 0)
921 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
923 block_Release(p_block_in);
925 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
926 p_sys->lastAudioPTS );*/
928 /* DTiVo Audio with PES Header */
929 /* ================================================ */
931 /* Check for complete PES */
932 if (check_sync_pes(p_demux, p_block_in, esOffset1,
935 /* partial PES header found, nothing else.
937 block_Release(p_block_in);
941 msg_Dbg(p_demux, "packet buffer has "
942 "%02x %02x %02x %02x %02x %02x %02x %02x "
943 "%02x %02x %02x %02x %02x %02x %02x %02x",
944 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
945 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
946 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
947 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
948 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
949 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
950 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
951 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
954 else if( subrec_type == 0x04 )
956 /* SA Audio with no PES Header */
957 /* ================================================ */
959 "Adding SA Audio Packet Size %ld", l_rec_size ); */
961 if (p_sys->lastAudioPTS > 0)
962 p_block_in->i_pts = p_sys->lastAudioPTS;
964 else if( subrec_type == 0x09 )
966 /* DTiVo AC3 Audio Data with PES Header */
967 /* ================================================ */
968 esOffset1 = find_es_header( ty_AC3AudioPacket,
969 p_block_in->p_buffer, 5 );
972 msg_Dbg(p_demux, "buffer has "
973 "%02x %02x %02x %02x %02x %02x %02x %02x "
974 "%02x %02x %02x %02x %02x %02x %02x %02x",
975 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
976 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
977 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
978 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
979 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
980 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
981 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
982 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
983 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
986 /* Check for complete PES */
987 if (check_sync_pes(p_demux, p_block_in, esOffset1,
990 /* partial PES header found, nothing else. we're done. */
991 block_Release(p_block_in);
994 /* S2 DTivo has invalid long AC3 packets */
995 if (p_sys->tivo_series == TIVO_SERIES2) {
996 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
997 p_block_in->i_buffer -= 2;
998 p_sys->l_ac3_pkt_size = 0;
1000 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1006 /* Unsupported/Unknown */
1007 block_Release(p_block_in);
1011 /* set PCR before we send (if PTS found) */
1012 if( p_block_in->i_pts > 0 )
1013 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1014 p_block_in->i_pts );
1016 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1020 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1022 demux_sys_t *p_sys = p_demux->p_sys;
1027 block_Release(p_block_in);
1029 if( rec_hdr->rec_type == 0x01 )
1031 else if( rec_hdr->rec_type == 0x02 )
1036 /* XDS data (extract programs infos) transmitted on field 2 only */
1038 DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
1040 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1043 p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
1044 p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
1045 p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
1046 p_sys->cc.i_data += 3;
1048 i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
1049 if( i_channel >= 0 && i_channel < 4 )
1050 p_sys->cc.pb_present[i_channel] = true;
1054 /* seek to a position within the stream, if possible */
1055 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1057 demux_sys_t *p_sys = p_demux->p_sys;
1058 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1062 /* if we're not seekable, there's nothing to do */
1063 if (!p_sys->b_seekable)
1064 return VLC_EGENERIC;
1066 /* figure out which part & chunk we want & go there */
1067 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1068 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1070 /* try to read the part header (master chunk) if it's there */
1071 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1073 /* can't seek stream */
1074 return VLC_EGENERIC;
1076 parse_master(p_demux);
1078 /* now for the actual chunk */
1079 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1081 /* can't seek stream */
1082 return VLC_EGENERIC;
1084 /* load the chunk */
1085 p_sys->i_stuff_cnt = 0;
1086 get_chunk_header(p_demux);
1088 /* seek within the chunk to get roughly to where we want */
1089 p_sys->i_cur_rec = (int)
1090 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1091 msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1092 msg_Dbg(p_demux, " (chunk %d, record %d)",
1093 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1095 /* seek to the start of this record's data.
1096 * to do that, we have to skip past all prior records */
1098 for (i=0; i<p_sys->i_cur_rec; i++)
1099 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1100 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1101 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1103 /* to hell with syncing any audio or video, just start reading records... :) */
1104 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
1105 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1110 //#define TY_XDS_DEBUG
1111 static void XdsInit( xds_t *h )
1116 h->i_class = XDS_MAX_CLASS_COUNT;
1118 h->b_future = false;
1119 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1121 for( j = 0; j < 128; j++ )
1122 h->pkt[i][j].b_started = false;
1124 h->b_meta_changed = false;
1125 memset( &h->meta, 0, sizeof(h->meta) );
1127 static void XdsExit( xds_t *h )
1130 free( h->meta.psz_channel_name );
1131 free( h->meta.psz_channel_call_letter );
1132 free( h->meta.psz_channel_number );
1135 free( h->meta.current.psz_name );
1136 free( h->meta.current.psz_rating );
1138 free( h->meta.future.psz_name );
1139 free( h->meta.future.psz_rating );
1141 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1146 for( i = 0, i_dst = 0; i < i_src; i++ )
1150 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1151 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1152 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1153 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1154 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1155 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1156 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1157 E2( 0x7c, 0xc3,0xb7); // division symbol
1158 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1159 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1162 dst[i_dst++] = p_src[i];
1166 dst[i_dst++] = '\0';
1168 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1170 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1172 if( *ppsz_dst == NULL && psz_new == NULL )
1177 *ppsz_dst = strdup( psz_new );
1181 h->b_meta_changed = true;
1185 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1187 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1194 XdsStringUtf8( name, pk->p_data, pk->i_data );
1195 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1197 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1198 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1202 i_rating = (pk->p_data[0] & 0x18);
1203 if( i_rating == 0x08 )
1206 static const char *pppsz_ratings[8][2] = {
1207 { "None", "No rating (no content advisory)" },
1208 { "TV-Y", "All Children (no content advisory)" },
1209 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1210 { "TV-G", "General Audience (no content advisory)" },
1211 { "TV-PG", "Parental Guidance Suggested" },
1212 { "TV-14", "Parents Strongly Cautioned" },
1213 { "TV-MA", "Mature Audience Only" },
1214 { "None", "No rating (no content advisory)" }
1216 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1217 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1219 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1220 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1221 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1224 else if( i_rating == 0x00 || i_rating == 0x10 )
1227 static const char *pppsz_ratings[8][2] = {
1229 { "G", "General Audiences" },
1230 { "PG", "Parental Guidance Suggested" },
1231 { "PG-13", "Parents Strongly Cautioned" },
1232 { "R", "Restricted" },
1233 { "NC-17", "No one 17 and under admitted" },
1234 { "X", "No one under 17 admitted" },
1235 { "NR", "Not Rated" },
1237 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1238 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1240 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1241 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1242 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1247 /* Non US Rating TODO */
1248 assert( i_rating == 0x18 ); // only left value possible */
1249 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1250 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1252 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1253 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1260 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1266 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1274 if( pk->i_data < 2 )
1276 XdsStringUtf8( name, pk->p_data, pk->i_data );
1277 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1279 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1280 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1285 if( pk->i_data < 4 )
1288 XdsStringUtf8( name, pk->p_data, 4 );
1289 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1291 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1292 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1294 if( pk->i_data >= 6 )
1296 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1297 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1299 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1300 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1305 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1307 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1308 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1313 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1316 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1320 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1326 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1328 switch( h->i_class )
1330 case XDS_CLASS_CURRENT:
1331 case XDS_CLASS_FUTURE:
1332 XdsDecodeCurrentFuture( h, pk );
1334 case XDS_CLASS_CHANNEL:
1335 XdsDecodeChannel( h, pk );
1337 case XDS_CLASS_MISCELLANEOUS:
1339 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1342 case XDS_CLASS_PUBLIC_SERVICE:
1344 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1348 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1353 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1355 /* TODO check parity */
1360 if( d1 >= 0x01 && d1 <= 0x0e )
1362 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1363 const int i_type = d2;
1364 const bool b_start = d1 & 0x01;
1365 xds_packet_t *pk = &h->pkt[i_class][i_type];
1367 if( !b_start && !pk->b_started )
1369 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1375 h->i_class = i_class;
1377 h->b_future = !b_start;
1378 pk->b_started = true;
1382 pk->i_sum = d1 + d2;
1385 else if( d1 == 0x0f && h->b_xds )
1387 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1389 /* TODO checksum and decode */
1390 pk->i_sum += d1 + d2;
1391 if( pk->i_sum & 0x7f )
1393 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1394 pk->b_started = false;
1397 if( pk->i_data <= 0 )
1399 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1400 pk->b_started = false;
1404 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1409 pk->b_started = false;
1411 else if( d1 >= 0x20 && h->b_xds )
1413 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1415 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1417 /* Broken -> reinit */
1418 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1420 pk->b_started = false;
1423 /* TODO check parity bit */
1424 pk->p_data[pk->i_data++] = d1 & 0x7f;
1425 pk->p_data[pk->i_data++] = d2 & 0x7f;
1434 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1436 demux_sys_t *p_sys = p_demux->p_sys;
1438 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1439 if( p_demux->p_sys->xds.b_meta_changed )
1441 xds_meta_t *m = &p_sys->xds.meta;
1445 /* Channel meta data */
1446 p_meta = vlc_meta_New();
1447 if( m->psz_channel_name )
1448 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1449 if( m->psz_channel_call_letter )
1450 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1451 if( m->psz_channel_number )
1452 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1453 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1454 vlc_meta_Delete( p_meta );
1456 /* Event meta data (current/future) */
1457 p_epg = vlc_epg_New( NULL );
1458 if( m->current.psz_name )
1460 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1461 //if( m->current.psz_rating )
1462 // TODO but VLC cannot yet handle rating per epg event
1463 vlc_epg_SetCurrent( p_epg, 0 );
1465 if( m->future.psz_name )
1468 if( p_epg->i_event > 0 )
1469 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1470 vlc_epg_Delete( p_epg );
1472 p_demux->p_sys->xds.b_meta_changed = false;
1475 /* seek to an exact time position within the stream, if possible.
1476 * l_seek_time is in nanoseconds, the TIVO time standard.
1478 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1480 demux_sys_t *p_sys = p_demux->p_sys;
1481 int i, i_seq_entry = 0;
1483 long l_cur_pos = stream_Tell(p_demux->s);
1484 int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1485 long l_seek_secs = l_seek_time / 1000000000;
1486 uint64_t l_fwd_stamp = 1;
1488 /* if we're not seekable, there's nothing to do */
1489 if (!p_sys->b_seekable || !p_sys->b_have_master)
1490 return VLC_EGENERIC;
1492 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1493 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1495 /* seek to the proper segment if necessary */
1496 /* first see if we need to go back */
1497 while (l_seek_time < p_sys->l_first_ty_pts) {
1498 msg_Dbg(p_demux, "skipping to prior segment.");
1499 /* load previous part */
1500 if (i_cur_part == 0) {
1501 stream_Seek(p_demux->s, l_cur_pos);
1502 msg_Err(p_demux, "Attempt to seek past BOF");
1503 return VLC_EGENERIC;
1505 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1507 parse_master(p_demux);
1509 /* maybe we need to go forward */
1510 while (l_seek_time > p_sys->l_final_ty_pts) {
1511 msg_Dbg(p_demux, "skipping to next segment.");
1512 /* load next part */
1513 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1514 /* error; restore previous file position */
1515 stream_Seek(p_demux->s, l_cur_pos);
1516 msg_Err(p_demux, "seek error");
1517 return VLC_EGENERIC;
1519 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1521 parse_master(p_demux);
1524 /* our target is somewhere within this part;
1525 find the proper chunk using seq_table */
1526 for (i=1; i<p_sys->i_seq_table_size; i++) {
1527 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1528 /* i-1 is the section we want; remember the next timestamp in case
1529 we have to use it (this section may not have a proper SEQ hdr
1530 for the time we're seeking) */
1531 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1532 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1538 /* if we went through the entire last loop and didn't find our target,
1539 then we skip to the next part. What has happened is that the actual
1540 time we're seeking is within this part, but there isn't a SEQ hdr
1541 for it here. So we skip to the next part */
1542 if (i == p_sys->i_seq_table_size) {
1543 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1544 /* error; restore previous file position */
1545 stream_Seek(p_demux->s, l_cur_pos);
1546 msg_Err(p_demux, "seek error");
1547 return VLC_EGENERIC;
1549 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1551 parse_master(p_demux);
1555 /* determine which chunk has our seek_time */
1556 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1557 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1558 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1559 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1560 i_cur_part, l_chunk_nr,
1561 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1562 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1563 /* check this chunk's SEQ header timestamp */
1564 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1565 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1566 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1568 // TODO: we don't have to parse the full header set;
1569 // just test the seq_rec entry for its timestamp
1570 p_sys->i_stuff_cnt = 0;
1571 get_chunk_header(p_demux);
1572 // check ty PTS for the SEQ entry in this chunk
1573 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1574 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1575 /* Seek to beginning of original chunk & reload it */
1576 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1577 p_sys->i_stuff_cnt = 0;
1578 get_chunk_header(p_demux);
1579 return VLC_EGENERIC;
1581 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1583 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1585 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1586 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1587 // keep this one? go back?
1588 /* for now, we take this one. it's the first SEQ hdr AFTER
1589 the time we were searching for. */
1590 msg_Dbg(p_demux, "seek target found.");
1593 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1596 /* if we made it through this entire loop without finding our target,
1597 then we skip to the next section. What has happened is that the actual
1598 time we're seeking is within this section, but there isn't a SEQ hdr
1599 for it here. So we skip to the next closest one (l_fwd_stamp) */
1600 if (i == p_sys->i_bits_per_seq_entry)
1601 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1603 /* current stream ptr is at beginning of data for this chunk,
1604 so we need to skip past any stream data prior to the seq_rec
1607 for (i=0; i<p_sys->i_seq_rec; i++)
1608 i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
1609 stream_Read(p_demux->s, NULL, i_skip_cnt);
1610 p_sys->i_cur_rec = p_sys->i_seq_rec;
1611 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1612 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1618 /* parse a master chunk, filling the SEQ table and other variables.
1619 * We assume the stream is currently pointing to it.
1621 static void parse_master(demux_t *p_demux)
1623 demux_sys_t *p_sys = p_demux->p_sys;
1624 uint8_t mst_buf[32];
1626 int64_t i_save_pos = stream_Tell(p_demux->s);
1629 /* Note that the entries in the SEQ table in the stream may have
1630 different sizes depending on the bits per entry. We store them
1631 all in the same size structure, so we have to parse them out one
1632 by one. If we had a dynamic structure, we could simply read the
1633 entire table directly from the stream into memory in place. */
1635 /* clear the SEQ table */
1636 free(p_sys->seq_table);
1638 /* parse header info */
1639 stream_Read(p_demux->s, mst_buf, 32);
1640 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1641 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1642 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1643 p_sys->i_seq_table_size = i / (8 + i_map_size);
1645 /* parse all the entries */
1646 p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
1647 for (i=0; i<p_sys->i_seq_table_size; i++) {
1648 stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
1649 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1650 if (i_map_size > 8) {
1651 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1652 memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
1654 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1658 /* set up a few of our variables */
1659 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1660 p_sys->l_final_ty_pts =
1661 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1662 p_sys->b_have_master = true;
1664 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1665 msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d",
1666 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1667 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1668 msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
1669 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1671 /* seek past this chunk */
1672 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1676 /* ======================================================================== */
1677 /* "Peek" at some chunks. Skip over the Part header if we find it.
1678 * We parse the peeked data and determine audio type,
1679 * SA vs. DTivo, & Tivo Series.
1680 * Set global vars i_Pes_Length, i_Pts_Offset,
1681 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1682 static int probe_stream(demux_t *p_demux)
1684 demux_sys_t *p_sys = p_demux->p_sys;
1685 const uint8_t *p_buf;
1687 bool b_probe_error = false;
1689 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1690 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1691 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1692 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1693 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1694 return VLC_EGENERIC;
1697 /* the real work: analyze this chunk */
1698 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1699 analyze_chunk(p_demux, p_buf);
1700 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1701 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1702 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1704 p_buf += CHUNK_SIZE;
1707 /* the final tally */
1708 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1709 msg_Err(p_demux, "Can't determine Tivo Series.");
1710 b_probe_error = true;
1712 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1713 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1714 b_probe_error = true;
1716 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1717 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1718 b_probe_error = true;
1720 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1724 /* ======================================================================== */
1725 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1726 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1728 demux_sys_t *p_sys = p_demux->p_sys;
1730 ty_rec_hdr_t *p_hdrs;
1731 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1732 uint32_t i_payload_size;
1734 /* skip if it's a Part header */
1735 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1738 /* number of records in chunk (we ignore high order byte;
1739 * rarely are there > 256 chunks & we don't need that many anyway) */
1740 i_num_recs = p_chunk[0];
1741 if (i_num_recs < 5) {
1742 /* try again with the next chunk. Sometimes there are dead ones */
1746 p_chunk += 4; /* skip past rec count & SEQ bytes */
1747 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1748 p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
1750 * 1. check video packets. Presence of 0x6e0 means S1.
1751 * No 6e0 but have be0 means S2.
1752 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1753 * If AC-3, then we have DTivo.
1754 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1756 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1757 for (i=0; i<i_num_recs; i++) {
1758 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1759 //p_hdrs[i].rec_type,
1760 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1761 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1776 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1777 i_num_6e0, i_num_be0);
1779 /* set up our variables */
1780 if (i_num_6e0 > 0) {
1781 msg_Dbg(p_demux, "detected Series 1 Tivo");
1782 p_sys->tivo_series = TIVO_SERIES1;
1783 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1784 } else if (i_num_be0 > 0) {
1785 msg_Dbg(p_demux, "detected Series 2 Tivo");
1786 p_sys->tivo_series = TIVO_SERIES2;
1787 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1789 if (i_num_9c0 > 0) {
1790 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1791 p_sys->audio_type = TIVO_AUDIO_AC3;
1792 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1793 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1794 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1795 } else if (i_num_3c0 > 0) {
1796 p_sys->audio_type = TIVO_AUDIO_MPEG;
1797 msg_Dbg(p_demux, "detected MPEG Audio" );
1800 /* if tivo_type still unknown, we can check PTS location
1801 * in MPEG packets to determine tivo_type */
1802 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1803 uint32_t i_data_offset = (16 * i_num_recs);
1804 for (i=0; i<i_num_recs; i++) {
1805 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1806 p_hdrs[i].l_rec_size > 15) {
1807 /* first make sure we're aligned */
1808 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1809 &p_chunk[i_data_offset], 5);
1810 if (i_pes_offset >= 0) {
1811 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1812 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1814 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1815 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1816 if (p_sys->tivo_series == TIVO_SERIES1)
1817 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1818 p_sys->tivo_type = TIVO_TYPE_SA;
1819 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1821 if (p_sys->tivo_series == TIVO_SERIES1)
1822 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1823 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1824 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1829 i_data_offset += p_hdrs[i].l_rec_size;
1836 /* =========================================================================== */
1837 static int get_chunk_header(demux_t *p_demux)
1839 int i_readSize, i_num_recs;
1841 const uint8_t *p_peek;
1842 demux_sys_t *p_sys = p_demux->p_sys;
1843 int i_payload_size; /* sum of all records' sizes */
1845 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1847 /* if we have left-over filler space from the last chunk, get that */
1848 if (p_sys->i_stuff_cnt > 0) {
1849 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1850 p_sys->i_stuff_cnt = 0;
1853 /* read the TY packet header */
1854 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1855 p_sys->i_cur_chunk++;
1857 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1864 /* check if it's a PART Header */
1865 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1867 /* parse master chunk */
1868 parse_master(p_demux);
1869 return get_chunk_header(p_demux);
1872 /* number of records in chunk (8- or 16-bit number) */
1873 if (p_peek[3] & 0x80)
1875 /* 16 bit rec cnt */
1876 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1877 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1878 if (p_sys->i_seq_rec != 0xffff)
1880 p_sys->i_seq_rec &= ~0x8000;
1885 /* 8 bit reclen - tivo 1.3 format */
1886 p_sys->i_num_recs = i_num_recs = p_peek[0];
1887 p_sys->i_seq_rec = p_peek[1];
1889 p_sys->i_cur_rec = 0;
1890 p_sys->b_first_chunk = false;
1892 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1894 free(p_sys->rec_hdrs);
1896 /* skip past the 4 bytes we "peeked" earlier */
1897 stream_Read( p_demux->s, NULL, 4 );
1899 /* read the record headers into a temp buffer */
1900 p_hdr_buf = malloc(i_num_recs * 16);
1901 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1907 p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
1911 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1912 (p_sys->i_num_recs * 16) - i_payload_size;
1913 if (p_sys->i_stuff_cnt > 0)
1914 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1915 p_sys->i_stuff_cnt );
1920 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
1921 int i_num_recs, int *pi_payload_size)
1924 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1926 *pi_payload_size = 0;
1927 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1929 for (i = 0; i < i_num_recs; i++)
1931 const uint8_t *record_header = p_buf + (i * 16);
1932 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1933 p_rec_hdr->rec_type = record_header[3];
1934 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1935 if ((record_header[ 0 ] & 0x80) == 0x80)
1938 /* marker bit 2 set, so read extended data */
1939 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1940 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1942 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1943 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1946 p_rec_hdr->ex1 = b1;
1947 p_rec_hdr->ex2 = b2;
1948 p_rec_hdr->l_rec_size = 0;
1949 p_rec_hdr->l_ty_pts = 0;
1950 p_rec_hdr->b_ext = true;
1954 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1955 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1956 *pi_payload_size += p_rec_hdr->l_rec_size;
1957 p_rec_hdr->b_ext = false;
1958 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1960 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1961 } /* end of record-header loop */