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_demux.h>
44 #include "vlc_codec.h"
46 #include "vlc_input.h"
47 #include "../codec/cc.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Open ( vlc_object_t * );
53 static void Close( vlc_object_t * );
56 set_shortname( _("TY") );
57 set_description(_("TY Stream audio/video demux"));
58 set_category( CAT_INPUT );
59 set_subcategory( SUBCAT_INPUT_DEMUX );
60 set_capability("demux2", 6);
61 /* FIXME: there seems to be a segfault when using PVR access
62 * and TY demux has a bigger priority than PS
63 * Something must be wrong.
65 set_callbacks( Open, Close );
70 /*****************************************************************************
72 *****************************************************************************/
73 static int Demux ( demux_t * );
74 static int Control( demux_t *, int, va_list );
76 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
77 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
78 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
79 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
80 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
81 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
82 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
83 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
84 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
85 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
86 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
87 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
89 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
91 /* packet types for reference:
92 2/c0: audio data continued
93 3/c0: audio packet header (PES header)
94 4/c0: audio data (S/A only?)
95 9/c0: audio packet header, AC-3 audio
96 2/e0: video data continued
97 6/e0: video packet header (PES header)
98 7/e0: video sequence header start
99 8/e0: video I-frame header start
100 a/e0: video P-frame header start
101 b/e0: video B-frame header start
102 c/e0: video GOP header start
103 e/01: closed-caption data
104 e/02: Extended data services data
105 e/03: ipreview data ("thumbs up to record" signal)
109 #define TIVO_PES_FILEID ( 0xf5467abd )
110 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
111 #define CHUNK_SIZE ( 128 * 1024 )
120 uint64_t l_ty_pts; /* TY PTS in the record header */
125 uint64_t l_timestamp;
126 uint8_t chunk_bitmask[8];
150 #define XDS_MAX_DATA_SIZE (32)
153 XDS_CLASS_CURRENT = 0,
154 XDS_CLASS_FUTURE = 1,
155 XDS_CLASS_CHANNEL = 2,
156 XDS_CLASS_MISCELLANEOUS = 3,
157 XDS_CLASS_PUBLIC_SERVICE = 4,
158 XDS_CLASS_RESERVED = 5,
159 XDS_CLASS_UNDEFINED = 6,
168 uint8_t p_data[XDS_MAX_DATA_SIZE];
173 XDS_META_PROGRAM_RATING_NONE,
174 XDS_META_PROGRAM_RATING_MPAA,
175 XDS_META_PROGRAM_RATING_TPG,
176 /* TODO add CA/CE rating */
177 } xds_meta_program_rating_t;
181 xds_meta_program_rating_t rating;
183 /* Add the other fields once I have the samples */
184 } xds_meta_program_t;
187 char *psz_channel_name;
188 char *psz_channel_call_letter;
189 char *psz_channel_number;
191 xds_meta_program_t current;
192 xds_meta_program_t future;
196 /* Are we in XDS mode */
199 /* Current class type */
205 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
215 es_out_id_t *p_video; /* ptr to video codec */
216 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
219 es_out_id_t *p_cc[4];
225 size_t i_stream_size; /* size of input stream (if known) */
226 //uint64_t l_program_len; /* length of this stream in msec */
227 bool b_seekable; /* is this stream seekable? */
228 bool b_have_master; /* are master chunks present? */
229 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
230 tivo_series_t tivo_series; /* Series1 or Series2 */
231 tivo_audio_t audio_type; /* AC3 or MPEG */
232 int i_Pes_Length; /* Length of Audio PES header */
233 int i_Pts_Offset; /* offset into audio PES of PTS */
234 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
235 int i_pes_buf_cnt; /* how many bytes in our buffer */
236 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
237 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
238 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
239 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
240 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
241 int i_seq_table_size; /* number of entries in SEQ table */
242 int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
244 mtime_t firstAudioPTS;
245 mtime_t lastAudioPTS;
246 mtime_t lastVideoPTS;
248 ty_rec_hdr_t *rec_hdrs; /* record headers array */
249 int i_cur_rec; /* current record in this chunk */
250 int i_num_recs; /* number of recs in this chunk */
251 int i_seq_rec; /* record number where seq start is */
252 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
257 static int get_chunk_header(demux_t *);
258 static mtime_t get_pts( const uint8_t *buf );
259 static int find_es_header( const uint8_t *header,
260 const uint8_t *buffer, int i_search_len );
261 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
262 static int ty_stream_seek_time(demux_t *, uint64_t);
264 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
265 int i_num_recs, int *pi_payload_size);
266 static int probe_stream(demux_t *p_demux);
267 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
268 static void parse_master(demux_t *p_demux);
270 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
271 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
272 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
274 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
276 static void XdsInit( xds_t * );
277 static void XdsExit( xds_t * );
279 #define TY_ES_GROUP (1)
282 * Open: check file and initialize demux structures
285 * 1. peek at the first 12 bytes of the stream for the
286 * magic TiVo PART header & stream type & chunk size
287 * 2. if it's not there, error with VLC_EGENERIC
288 * 3. set up video (mpgv) codec
289 * 4. return VLC_SUCCESS
291 static int Open(vlc_object_t *p_this)
293 demux_t *p_demux = (demux_t *)p_this;
296 const uint8_t *p_peek;
299 /* peek at the first 12 bytes. */
300 /* for TY streams, they're always the same */
301 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
304 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
305 U32_AT(&p_peek[4]) != 0x02 ||
306 U32_AT(&p_peek[8]) != CHUNK_SIZE )
308 if( !p_demux->b_force &&
309 !demux2_IsPathExtension( p_demux, ".ty" ) &&
310 !demux2_IsPathExtension( p_demux, ".ty+" ) )
312 msg_Warn( p_demux, "this does not look like a TY file, "
313 "continuing anyway..." );
316 /* at this point, we assume we have a valid TY stream */
317 msg_Dbg( p_demux, "valid TY stream detected" );
319 /* Set exported functions */
320 p_demux->pf_demux = Demux;
321 p_demux->pf_control = Control;
323 /* create our structure that will hold all data */
324 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
325 memset(p_sys, 0, sizeof(demux_sys_t));
327 /* set up our struct (most were zero'd out with the memset above) */
328 p_sys->b_first_chunk = true;
329 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
330 p_sys->firstAudioPTS = -1;
331 p_sys->i_stream_size = stream_Size(p_demux->s);
332 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
333 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
334 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
335 p_sys->i_Pes_Length = 0;
336 p_sys->i_Pts_Offset = 0;
337 p_sys->l_ac3_pkt_size = 0;
339 /* see if this stream is seekable */
340 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
342 if (probe_stream(p_demux) != VLC_SUCCESS) {
347 if (!p_sys->b_have_master)
348 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
350 /* register the proper audio codec */
351 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
352 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
354 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
356 fmt.i_group = TY_ES_GROUP;
357 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
359 /* register the video stream */
360 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
361 fmt.i_group = TY_ES_GROUP;
362 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
365 for( i = 0; i < 4; i++ )
366 p_sys->p_cc[i] = NULL;
367 cc_Init( &p_sys->cc );
369 XdsInit( &p_sys->xds );
374 /* =========================================================================== */
375 /* Demux: Read & Demux one record from the chunk
377 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
379 * NOTE: I think we can return the number of packets sent instead of just 1.
380 * that means we can demux an entire chunk and shoot it back (may be more efficient)
381 * -- should try that some day :) --
383 static int Demux( demux_t *p_demux )
385 demux_sys_t *p_sys = p_demux->p_sys;
387 block_t *p_block_in = NULL;
389 /*msg_Dbg(p_demux, "ty demux processing" );*/
391 /* did we hit EOF earlier? */
396 * what we do (1 record now.. maybe more later):
397 * - use stream_Read() to read the chunk header & record headers
398 * - discard entire chunk if it is a PART header chunk
399 * - parse all the headers into record header array
400 * - keep a pointer of which record we're on
401 * - use stream_Block() to fetch each record
402 * - parse out PTS from PES headers
403 * - set PTS for data packets
404 * - pass the data on to the proper codec via es_out_Send()
406 * if this is the first time or
407 * if we're at the end of this chunk, start a new one
409 /* parse the next chunk's record headers */
410 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
412 if( get_chunk_header(p_demux) == 0 )
416 /*======================================================================
417 * parse & send one record of the chunk
418 *====================================================================== */
419 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
423 const long l_rec_size = p_rec->l_rec_size;
424 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
425 subrec_type, p_rec->rec_type, l_rec_size );*/
427 /* some normal records are 0 length, so check for that... */
428 if( l_rec_size <= 0 )
430 /* no data in payload; we're done */
435 /* read in this record's payload */
436 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
439 /* set these as 'unknown' for now */
441 p_block_in->i_dts = 0;
445 -- don't read any data from the stream, data was in the record header --
447 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
448 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
451 if( p_rec->rec_type == 0xe0 )
454 DemuxRecVideo( p_demux, p_rec, p_block_in );
456 else if ( p_rec->rec_type == 0xc0 )
459 DemuxRecAudio( p_demux, p_rec, p_block_in );
461 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
463 /* Closed Captions/XDS */
464 DemuxRecCc( p_demux, p_rec, p_block_in );
466 else if ( p_rec->rec_type == 0x03 )
468 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
470 block_Release(p_block_in);
472 else if ( p_rec->rec_type == 0x05 )
474 /* Unknown, but seen regularly */
476 block_Release(p_block_in);
480 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
482 block_Release(p_block_in);
491 static int Control(demux_t *p_demux, int i_query, va_list args)
493 demux_sys_t *p_sys = p_demux->p_sys;
497 /*msg_Info(p_demux, "control cmd %d", i_query);*/
500 case DEMUX_GET_POSITION:
501 /* arg is 0.0 - 1.0 percent of overall file position */
502 if( ( i64 = p_sys->i_stream_size ) > 0 )
504 pf = (double*) va_arg( args, double* );
505 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
510 case DEMUX_SET_POSITION:
511 /* arg is 0.0 - 1.0 percent of overall file position */
512 f = (double) va_arg( args, double );
513 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
514 if ((i64 = p_sys->i_stream_size) > 0)
515 return ty_stream_seek_pct(p_demux, f);
518 /* return TiVo timestamp */
519 p_i64 = (int64_t *) va_arg(args, int64_t *);
520 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
521 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
522 // p_sys->l_last_ty_pts_sync);
523 *p_i64 = (p_sys->l_last_ty_pts / 1000);
525 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
527 p_i64 = (int64_t *) va_arg(args, int64_t *);
530 case DEMUX_SET_TIME: /* arg is time in microsecs */
531 i64 = (int64_t) va_arg( args, int64_t );
532 return ty_stream_seek_time(p_demux, i64 * 1000);
540 static void Close( vlc_object_t *p_this )
542 demux_t *p_demux = (demux_t*)p_this;
543 demux_sys_t *p_sys = p_demux->p_sys;
545 XdsExit( &p_sys->xds );
546 cc_Exit( &p_sys->cc );
547 free( p_sys->rec_hdrs );
548 free( p_sys->seq_table );
553 /* =========================================================================== */
554 /* Compute Presentation Time Stamp (PTS)
555 * Assume buf points to beginning of PTS */
556 static mtime_t get_pts( const uint8_t *buf )
560 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
561 (mtime_t)(buf[1] << 22)|
562 ((mtime_t)(buf[2]&0xfe) << 14)|
563 (mtime_t)(buf[3] << 7)|
564 (mtime_t)(buf[4] >> 1);
565 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
570 /* =========================================================================== */
571 static int find_es_header( const uint8_t *header,
572 const uint8_t *buffer, int i_search_len )
576 for( count = 0; count < i_search_len; count++ )
578 if( !memcmp( &buffer[count], header, 4 ) )
585 /* =========================================================================== */
586 /* check if we have a full PES header, if not, then save what we have.
587 * this is called when audio-start packets are encountered.
589 * 1 partial PES hdr found, some audio data found (buffer adjusted),
590 * -1 partial PES hdr found, no audio data found
591 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
592 /* TODO: HD support -- nothing known about those streams */
593 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
594 int32_t offset, int32_t rec_len )
596 demux_sys_t *p_sys = p_demux->p_sys;
598 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
600 /* entire PES header not present */
601 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
603 /* save the partial pes header */
606 /* no header found, fake some 00's (this works, believe me) */
607 memset( p_sys->pes_buffer, 4, 0 );
608 p_sys->i_pes_buf_cnt = 4;
610 msg_Err( p_demux, "PES header not found in record of %d bytes!",
614 /* copy the partial pes header we found */
615 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
617 p_sys->i_pes_buf_cnt = rec_len - offset;
621 /* PES Header was found, but not complete, so trim the end of this record */
622 p_block->i_buffer -= rec_len - offset;
625 return -1; /* partial PES, no audio data */
627 /* full PES header present, extract PTS */
628 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
629 p_sys->i_Pts_Offset ] );
630 if (p_sys->firstAudioPTS < 0)
631 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
632 p_block->i_pts = p_sys->lastAudioPTS;
633 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
634 /* adjust audio record to remove PES header */
635 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
636 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
637 p_block->i_buffer -= p_sys->i_Pes_Length;
639 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
640 "%02x %02x %02x %02x %02x %02x %02x %02x "
641 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
642 p_block->p_buffer[0], p_block->p_buffer[1],
643 p_block->p_buffer[2], p_block->p_buffer[3],
644 p_block->p_buffer[4], p_block->p_buffer[5],
645 p_block->p_buffer[6], p_block->p_buffer[7],
646 p_block->p_buffer[8], p_block->p_buffer[9],
647 p_block->p_buffer[10], p_block->p_buffer[11],
648 p_block->p_buffer[12], p_block->p_buffer[13],
649 p_block->p_buffer[14], p_block->p_buffer[15]);
654 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
656 demux_sys_t *p_sys = p_demux->p_sys;
657 const int subrec_type = rec_hdr->subrec_type;
658 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
662 assert( rec_hdr->rec_type == 0xe0 );
667 msg_Dbg(p_demux, "packet buffer has "
668 "%02x %02x %02x %02x %02x %02x %02x %02x "
669 "%02x %02x %02x %02x %02x %02x %02x %02x",
670 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
671 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
672 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
673 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
674 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
675 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
676 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
677 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
679 //if( subrec_type == 0x06 || subrec_type == 0x07 )
680 if( subrec_type != 0x02 && subrec_type != 0x0c &&
681 subrec_type != 0x08 && l_rec_size > 4 )
683 /* get the PTS from this packet if it has one.
684 * on S1, only 0x06 has PES. On S2, however, most all do.
685 * Do NOT Pass the PES Header to the MPEG2 codec */
686 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
687 if( esOffset1 != -1 )
689 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
690 //subrec_type, esOffset1);
691 p_sys->lastVideoPTS = get_pts(
692 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
693 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
694 p_sys->lastVideoPTS );*/
695 if (subrec_type != 0x06) {
696 /* if we found a PES, and it's not type 6, then we're S2 */
697 /* The packet will have video data (& other headers) so we
698 * chop out the PES header and send the rest */
699 if (l_rec_size >= VIDEO_PES_LENGTH) {
700 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
701 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
703 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
704 " (%ld bytes)", subrec_type, l_rec_size);
705 /* nuke this block; it's too short, but has PES marker */
706 p_block_in->i_buffer = 0;
710 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
714 if(subrec_type == 0x06 )
716 /* type 6 (S1 DTivo) has no data, so we're done */
717 block_Release(p_block_in);
721 /* if it's not a continue blk, then set PTS */
722 if( subrec_type != 0x02 )
724 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
726 /* if it's a GOP header, make sure it's legal
727 * (if we have enough data) */
728 /* Some ty files don't have this bit set
729 * and it causes problems */
730 if (subrec_type == 0x0c && l_rec_size >= 6)
731 p_block_in->p_buffer[5] |= 0x08;
732 /* store the TY PTS if there is one */
733 if (subrec_type == 0x07) {
734 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
735 /* should we use audio or video PTS? */
736 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
738 /* yes I know this is a cheap hack. It's the timestamp
739 used for display and skipping fwd/back, so it
740 doesn't have to be accurate to the millisecond.
741 I adjust it here by roughly one 1/30 sec. Yes it
742 will be slightly off for UK streams, but it's OK.
744 p_sys->l_last_ty_pts += 35000000;
745 //p_sys->l_last_ty_pts += 33366667;
747 /* set PTS for this block before we send */
748 if (p_sys->lastVideoPTS > 0)
750 p_block_in->i_pts = p_sys->lastVideoPTS;
751 /* PTS gets used ONCE.
752 * Any subsequent frames we get BEFORE next PES
753 * header will have their PTS computed in the codec */
754 p_sys->lastVideoPTS = 0;
758 /* Register the CC decoders when needed */
759 for( i = 0; i < 4; i++ )
761 static const vlc_fourcc_t fcc[4] = {
762 VLC_FOURCC('c', 'c', '1', ' '),
763 VLC_FOURCC('c', 'c', '2', ' '),
764 VLC_FOURCC('c', 'c', '3', ' '),
765 VLC_FOURCC('c', 'c', '4', ' ')
767 static const char *ppsz_description[4] = {
768 N_("Closed captions 1"),
769 N_("Closed captions 2"),
770 N_("Closed captions 3"),
771 N_("Closed captions 4"),
776 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
779 es_format_Init( &fmt, SPU_ES, fcc[i] );
780 fmt.psz_description = strdup( _(ppsz_description[i]) );
781 fmt.i_group = TY_ES_GROUP;
782 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
783 es_format_Clean( &fmt );
786 /* Send the CC data */
787 if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
791 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
792 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
793 p_cc->i_pts = p_block_in->i_pts;
794 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
796 for( i = 0, i_cc_count = 0; i < 4; i++ )
797 i_cc_count += p_sys->p_cc[i] ? 1 : 0;
799 for( i = 0; i < 4; i++ )
801 if( !p_sys->p_cc[i] )
804 es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
806 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
808 cc_Flush( &p_sys->cc );
811 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
812 //p_sys->i_cur_rec, subrec_type);
813 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
816 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
818 demux_sys_t *p_sys = p_demux->p_sys;
819 const int subrec_type = rec_hdr->subrec_type;
820 const long l_rec_size = rec_hdr->l_rec_size;
823 assert( rec_hdr->rec_type == 0xc0 );
828 printf( "Audio Packet Header " );
829 for( i = 0 ; i < 24 ; i++ )
830 printf( "%2.2x ", p_block_in->p_buffer[i] );
834 if( subrec_type == 2 )
836 /* SA or DTiVo Audio Data, no PES (continued block)
837 * ================================================
840 /* continue PES if previous was incomplete */
841 if (p_sys->i_pes_buf_cnt > 0)
843 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
845 msg_Dbg(p_demux, "continuing PES header");
846 /* do we have enough data to complete? */
847 if (i_need >= l_rec_size)
849 /* don't have complete PES hdr; save what we have and return */
850 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
851 p_block_in->p_buffer, l_rec_size);
852 p_sys->i_pes_buf_cnt += l_rec_size;
854 block_Release(p_block_in);
858 /* we have enough; reconstruct this p_frame with the new hdr */
859 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
860 p_block_in->p_buffer, i_need);
861 /* advance the block past the PES header (don't want to send it) */
862 p_block_in->p_buffer += i_need;
863 p_block_in->i_buffer -= i_need;
864 /* get the PTS out of this PES header (MPEG or AC3) */
865 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
866 esOffset1 = find_es_header(ty_MPEGAudioPacket,
867 p_sys->pes_buffer, 5);
869 esOffset1 = find_es_header(ty_AC3AudioPacket,
870 p_sys->pes_buffer, 5);
873 /* god help us; something's really wrong */
874 msg_Err(p_demux, "can't find audio PES header in packet");
878 p_sys->lastAudioPTS = get_pts(
879 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
880 p_block_in->i_pts = p_sys->lastAudioPTS;
882 p_sys->i_pes_buf_cnt = 0;
884 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
885 * not allowed in the AC3 spec and will cause problems. So here
886 * we try to trim things. */
887 /* Also, S1 DTivo has alternating short / long AC3 packets. That
888 * is, one packet is short (incomplete) and the next packet has
889 * the first one's missing data, plus all of its own. Strange. */
890 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
891 p_sys->tivo_series == TIVO_SERIES2) {
892 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
894 p_block_in->i_buffer -= 2;
895 p_sys->l_ac3_pkt_size = 0;
897 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
901 else if( subrec_type == 0x03 )
903 /* MPEG Audio with PES Header, either SA or DTiVo */
904 /* ================================================ */
905 esOffset1 = find_es_header( ty_MPEGAudioPacket,
906 p_block_in->p_buffer, 5 );
908 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
909 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
910 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
911 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
913 /* SA PES Header, No Audio Data */
914 /* ================================================ */
915 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
917 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
919 if (p_sys->firstAudioPTS < 0)
920 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
922 block_Release(p_block_in);
924 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
925 p_sys->lastAudioPTS );*/
927 /* DTiVo Audio with PES Header */
928 /* ================================================ */
930 /* Check for complete PES */
931 if (check_sync_pes(p_demux, p_block_in, esOffset1,
934 /* partial PES header found, nothing else.
936 block_Release(p_block_in);
940 msg_Dbg(p_demux, "packet buffer has "
941 "%02x %02x %02x %02x %02x %02x %02x %02x "
942 "%02x %02x %02x %02x %02x %02x %02x %02x",
943 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
944 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
945 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
946 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
947 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
948 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
949 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
950 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
953 else if( subrec_type == 0x04 )
955 /* SA Audio with no PES Header */
956 /* ================================================ */
958 "Adding SA Audio Packet Size %ld", l_rec_size ); */
960 if (p_sys->lastAudioPTS > 0)
961 p_block_in->i_pts = p_sys->lastAudioPTS;
963 else if( subrec_type == 0x09 )
965 /* DTiVo AC3 Audio Data with PES Header */
966 /* ================================================ */
967 esOffset1 = find_es_header( ty_AC3AudioPacket,
968 p_block_in->p_buffer, 5 );
971 msg_Dbg(p_demux, "buffer has "
972 "%02x %02x %02x %02x %02x %02x %02x %02x "
973 "%02x %02x %02x %02x %02x %02x %02x %02x",
974 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
975 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
976 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
977 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
978 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
979 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
980 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
981 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
982 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
985 /* Check for complete PES */
986 if (check_sync_pes(p_demux, p_block_in, esOffset1,
989 /* partial PES header found, nothing else. we're done. */
990 block_Release(p_block_in);
993 /* S2 DTivo has invalid long AC3 packets */
994 if (p_sys->tivo_series == TIVO_SERIES2) {
995 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
996 p_block_in->i_buffer -= 2;
997 p_sys->l_ac3_pkt_size = 0;
999 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1005 /* Unsupported/Unknown */
1006 block_Release(p_block_in);
1010 /* set PCR before we send (if PTS found) */
1011 if( p_block_in->i_pts > 0 )
1012 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1013 p_block_in->i_pts );
1015 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1019 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1021 demux_sys_t *p_sys = p_demux->p_sys;
1026 block_Release(p_block_in);
1028 if( rec_hdr->rec_type == 0x01 )
1030 else if( rec_hdr->rec_type == 0x02 )
1035 /* XDS data (extract programs infos) transmitted on field 2 only */
1037 DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
1039 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1042 p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
1043 p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
1044 p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
1045 p_sys->cc.i_data += 3;
1047 i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
1048 if( i_channel >= 0 && i_channel < 4 )
1049 p_sys->cc.pb_present[i_channel] = true;
1053 /* seek to a position within the stream, if possible */
1054 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1056 demux_sys_t *p_sys = p_demux->p_sys;
1057 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1061 /* if we're not seekable, there's nothing to do */
1062 if (!p_sys->b_seekable)
1063 return VLC_EGENERIC;
1065 /* figure out which part & chunk we want & go there */
1066 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1067 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1069 /* try to read the part header (master chunk) if it's there */
1070 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1072 /* can't seek stream */
1073 return VLC_EGENERIC;
1075 parse_master(p_demux);
1077 /* now for the actual chunk */
1078 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1080 /* can't seek stream */
1081 return VLC_EGENERIC;
1083 /* load the chunk */
1084 p_sys->i_stuff_cnt = 0;
1085 get_chunk_header(p_demux);
1087 /* seek within the chunk to get roughly to where we want */
1088 p_sys->i_cur_rec = (int)
1089 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1090 msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
1091 msg_Dbg(p_demux, " (chunk %d, record %d)",
1092 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1094 /* seek to the start of this record's data.
1095 * to do that, we have to skip past all prior records */
1097 for (i=0; i<p_sys->i_cur_rec; i++)
1098 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1099 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1100 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1102 /* to hell with syncing any audio or video, just start reading records... :) */
1103 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
1104 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1109 //#define TY_XDS_DEBUG
1110 static void XdsInit( xds_t *h )
1115 h->i_class = XDS_MAX_CLASS_COUNT;
1117 h->b_future = false;
1118 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1120 for( j = 0; j < 128; j++ )
1121 h->pkt[i][j].b_started = false;
1123 h->b_meta_changed = false;
1124 memset( &h->meta, 0, sizeof(h->meta) );
1126 static void XdsExit( xds_t *h )
1129 free( h->meta.psz_channel_name );
1130 free( h->meta.psz_channel_call_letter );
1131 free( h->meta.psz_channel_number );
1134 free( h->meta.current.psz_name );
1135 free( h->meta.current.psz_rating );
1137 free( h->meta.future.psz_name );
1138 free( h->meta.future.psz_rating );
1140 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1145 for( i = 0, i_dst = 0; i < i_src; i++ )
1149 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1150 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1151 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1152 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1153 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1154 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1155 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1156 E2( 0x7c, 0xc3,0xb7); // division symbol
1157 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1158 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1161 dst[i_dst++] = p_src[i];
1165 dst[i_dst++] = '\0';
1167 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1169 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1171 if( *ppsz_dst == NULL && psz_new == NULL )
1176 *ppsz_dst = strdup( psz_new );
1180 h->b_meta_changed = true;
1184 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1186 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1193 XdsStringUtf8( name, pk->p_data, pk->i_data );
1194 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1196 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1197 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1201 i_rating = (pk->p_data[0] & 0x18);
1202 if( i_rating == 0x08 )
1205 static const char *pppsz_ratings[8][2] = {
1206 { "None", "No rating (no content advisory)" },
1207 { "TV-Y", "All Children (no content advisory)" },
1208 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1209 { "TV-G", "General Audience (no content advisory)" },
1210 { "TV-PG", "Parental Guidance Suggested" },
1211 { "TV-14", "Parents Strongly Cautioned" },
1212 { "TV-MA", "Mature Audience Only" },
1213 { "None", "No rating (no content advisory)" }
1215 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1216 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1218 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1219 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1220 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1223 else if( i_rating == 0x00 || i_rating == 0x10 )
1226 static const char *pppsz_ratings[8][2] = {
1228 { "G", "General Audiences" },
1229 { "PG", "Parental Guidance Suggested" },
1230 { "PG-13", "Parents Strongly Cautioned" },
1231 { "R", "Restricted" },
1232 { "NC-17", "No one 17 and under admitted" },
1233 { "X", "No one under 17 admitted" },
1234 { "NR", "Not Rated" },
1236 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1237 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1239 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1240 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1241 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1246 /* Non US Rating TODO */
1247 assert( i_rating == 0x18 ); // only left value possible */
1248 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1249 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1251 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1252 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1259 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1265 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1273 if( pk->i_data < 2 )
1275 XdsStringUtf8( name, pk->p_data, pk->i_data );
1276 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1278 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1279 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1284 if( pk->i_data < 4 )
1287 XdsStringUtf8( name, pk->p_data, 4 );
1288 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1290 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1291 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1293 if( pk->i_data >= 6 )
1295 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1296 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1298 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1299 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1304 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1306 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1307 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1312 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1315 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1319 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1325 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1327 switch( h->i_class )
1329 case XDS_CLASS_CURRENT:
1330 case XDS_CLASS_FUTURE:
1331 XdsDecodeCurrentFuture( h, pk );
1333 case XDS_CLASS_CHANNEL:
1334 XdsDecodeChannel( h, pk );
1336 case XDS_CLASS_MISCELLANEOUS:
1338 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1341 case XDS_CLASS_PUBLIC_SERVICE:
1343 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1347 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1352 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1354 /* TODO check parity */
1359 if( d1 >= 0x01 && d1 <= 0x0e )
1361 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1362 const int i_type = d2;
1363 const bool b_start = d1 & 0x01;
1364 xds_packet_t *pk = &h->pkt[i_class][i_type];
1366 if( !b_start && !pk->b_started )
1368 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1374 h->i_class = i_class;
1376 h->b_future = !b_start;
1377 pk->b_started = true;
1381 pk->i_sum = d1 + d2;
1384 else if( d1 == 0x0f && h->b_xds )
1386 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1388 /* TODO checksum and decode */
1389 pk->i_sum += d1 + d2;
1390 if( pk->i_sum & 0x7f )
1392 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1393 pk->b_started = false;
1396 if( pk->i_data <= 0 )
1398 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1399 pk->b_started = false;
1403 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1408 pk->b_started = false;
1410 else if( d1 >= 0x20 && h->b_xds )
1412 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1414 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1416 /* Broken -> reinit */
1417 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1419 pk->b_started = false;
1422 /* TODO check parity bit */
1423 pk->p_data[pk->i_data++] = d1 & 0x7f;
1424 pk->p_data[pk->i_data++] = d2 & 0x7f;
1433 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1435 demux_sys_t *p_sys = p_demux->p_sys;
1437 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1438 if( p_demux->p_sys->xds.b_meta_changed )
1440 xds_meta_t *m = &p_sys->xds.meta;
1444 /* Channel meta data */
1445 p_meta = vlc_meta_New();
1446 if( m->psz_channel_name )
1447 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1448 if( m->psz_channel_call_letter )
1449 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1450 if( m->psz_channel_number )
1451 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1452 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1453 vlc_meta_Delete( p_meta );
1455 /* Event meta data (current/future) */
1456 p_epg = vlc_epg_New( NULL );
1457 if( m->current.psz_name )
1459 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1460 //if( m->current.psz_rating )
1461 // TODO but VLC cannot yet handle rating per epg event
1462 vlc_epg_SetCurrent( p_epg, 0 );
1464 if( m->future.psz_name )
1467 if( p_epg->i_event > 0 )
1468 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1469 vlc_epg_Delete( p_epg );
1471 p_demux->p_sys->xds.b_meta_changed = false;
1474 /* seek to an exact time position within the stream, if possible.
1475 * l_seek_time is in nanoseconds, the TIVO time standard.
1477 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1479 demux_sys_t *p_sys = p_demux->p_sys;
1480 int i, i_seq_entry = 0;
1482 long l_cur_pos = stream_Tell(p_demux->s);
1483 int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1484 long l_seek_secs = l_seek_time / 1000000000;
1485 uint64_t l_fwd_stamp = 1;
1487 /* if we're not seekable, there's nothing to do */
1488 if (!p_sys->b_seekable || !p_sys->b_have_master)
1489 return VLC_EGENERIC;
1491 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1492 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1494 /* seek to the proper segment if necessary */
1495 /* first see if we need to go back */
1496 while (l_seek_time < p_sys->l_first_ty_pts) {
1497 msg_Dbg(p_demux, "skipping to prior segment.");
1498 /* load previous part */
1499 if (i_cur_part == 0) {
1500 stream_Seek(p_demux->s, l_cur_pos);
1501 msg_Err(p_demux, "Attempt to seek past BOF");
1502 return VLC_EGENERIC;
1504 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1506 parse_master(p_demux);
1508 /* maybe we need to go forward */
1509 while (l_seek_time > p_sys->l_final_ty_pts) {
1510 msg_Dbg(p_demux, "skipping to next segment.");
1511 /* load next part */
1512 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1513 /* error; restore previous file position */
1514 stream_Seek(p_demux->s, l_cur_pos);
1515 msg_Err(p_demux, "seek error");
1516 return VLC_EGENERIC;
1518 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1520 parse_master(p_demux);
1523 /* our target is somewhere within this part;
1524 find the proper chunk using seq_table */
1525 for (i=1; i<p_sys->i_seq_table_size; i++) {
1526 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1527 /* i-1 is the section we want; remember the next timestamp in case
1528 we have to use it (this section may not have a proper SEQ hdr
1529 for the time we're seeking) */
1530 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1531 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1537 /* if we went through the entire last loop and didn't find our target,
1538 then we skip to the next part. What has happened is that the actual
1539 time we're seeking is within this part, but there isn't a SEQ hdr
1540 for it here. So we skip to the next part */
1541 if (i == p_sys->i_seq_table_size) {
1542 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1543 /* error; restore previous file position */
1544 stream_Seek(p_demux->s, l_cur_pos);
1545 msg_Err(p_demux, "seek error");
1546 return VLC_EGENERIC;
1548 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1550 parse_master(p_demux);
1554 /* determine which chunk has our seek_time */
1555 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1556 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1557 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1558 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1559 i_cur_part, l_chunk_nr,
1560 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1561 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1562 /* check this chunk's SEQ header timestamp */
1563 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1564 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1565 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1567 // TODO: we don't have to parse the full header set;
1568 // just test the seq_rec entry for its timestamp
1569 p_sys->i_stuff_cnt = 0;
1570 get_chunk_header(p_demux);
1571 // check ty PTS for the SEQ entry in this chunk
1572 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1573 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1574 /* Seek to beginning of original chunk & reload it */
1575 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1576 p_sys->i_stuff_cnt = 0;
1577 get_chunk_header(p_demux);
1578 return VLC_EGENERIC;
1580 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1582 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1584 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1585 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1586 // keep this one? go back?
1587 /* for now, we take this one. it's the first SEQ hdr AFTER
1588 the time we were searching for. */
1589 msg_Dbg(p_demux, "seek target found.");
1592 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1595 /* if we made it through this entire loop without finding our target,
1596 then we skip to the next section. What has happened is that the actual
1597 time we're seeking is within this section, but there isn't a SEQ hdr
1598 for it here. So we skip to the next closest one (l_fwd_stamp) */
1599 if (i == p_sys->i_bits_per_seq_entry)
1600 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1602 /* current stream ptr is at beginning of data for this chunk,
1603 so we need to skip past any stream data prior to the seq_rec
1606 for (i=0; i<p_sys->i_seq_rec; i++)
1607 i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
1608 stream_Read(p_demux->s, NULL, i_skip_cnt);
1609 p_sys->i_cur_rec = p_sys->i_seq_rec;
1610 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1611 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1617 /* parse a master chunk, filling the SEQ table and other variables.
1618 * We assume the stream is currently pointing to it.
1620 static void parse_master(demux_t *p_demux)
1622 demux_sys_t *p_sys = p_demux->p_sys;
1623 uint8_t mst_buf[32];
1625 int64_t i_save_pos = stream_Tell(p_demux->s);
1628 /* Note that the entries in the SEQ table in the stream may have
1629 different sizes depending on the bits per entry. We store them
1630 all in the same size structure, so we have to parse them out one
1631 by one. If we had a dynamic structure, we could simply read the
1632 entire table directly from the stream into memory in place. */
1634 /* clear the SEQ table */
1635 free(p_sys->seq_table);
1637 /* parse header info */
1638 stream_Read(p_demux->s, mst_buf, 32);
1639 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1640 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1641 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1642 p_sys->i_seq_table_size = i / (8 + i_map_size);
1644 /* parse all the entries */
1645 p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
1646 for (i=0; i<p_sys->i_seq_table_size; i++) {
1647 stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
1648 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1649 if (i_map_size > 8) {
1650 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1651 memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
1653 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1657 /* set up a few of our variables */
1658 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1659 p_sys->l_final_ty_pts =
1660 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1661 p_sys->b_have_master = true;
1663 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1664 msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d",
1665 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1666 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1667 msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
1668 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1670 /* seek past this chunk */
1671 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1675 /* ======================================================================== */
1676 /* "Peek" at some chunks. Skip over the Part header if we find it.
1677 * We parse the peeked data and determine audio type,
1678 * SA vs. DTivo, & Tivo Series.
1679 * Set global vars i_Pes_Length, i_Pts_Offset,
1680 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1681 static int probe_stream(demux_t *p_demux)
1683 demux_sys_t *p_sys = p_demux->p_sys;
1684 const uint8_t *p_buf;
1686 bool b_probe_error = false;
1688 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1689 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1690 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1691 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1692 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1693 return VLC_EGENERIC;
1696 /* the real work: analyze this chunk */
1697 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1698 analyze_chunk(p_demux, p_buf);
1699 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1700 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1701 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1703 p_buf += CHUNK_SIZE;
1706 /* the final tally */
1707 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1708 msg_Err(p_demux, "Can't determine Tivo Series.");
1709 b_probe_error = true;
1711 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1712 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1713 b_probe_error = true;
1715 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1716 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1717 b_probe_error = true;
1719 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1723 /* ======================================================================== */
1724 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1725 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1727 demux_sys_t *p_sys = p_demux->p_sys;
1729 ty_rec_hdr_t *p_hdrs;
1730 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1731 uint32_t i_payload_size;
1733 /* skip if it's a Part header */
1734 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1737 /* number of records in chunk (we ignore high order byte;
1738 * rarely are there > 256 chunks & we don't need that many anyway) */
1739 i_num_recs = p_chunk[0];
1740 if (i_num_recs < 5) {
1741 /* try again with the next chunk. Sometimes there are dead ones */
1745 p_chunk += 4; /* skip past rec count & SEQ bytes */
1746 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1747 p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
1749 * 1. check video packets. Presence of 0x6e0 means S1.
1750 * No 6e0 but have be0 means S2.
1751 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1752 * If AC-3, then we have DTivo.
1753 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1755 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1756 for (i=0; i<i_num_recs; i++) {
1757 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1758 //p_hdrs[i].rec_type,
1759 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1760 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1775 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1776 i_num_6e0, i_num_be0);
1778 /* set up our variables */
1779 if (i_num_6e0 > 0) {
1780 msg_Dbg(p_demux, "detected Series 1 Tivo");
1781 p_sys->tivo_series = TIVO_SERIES1;
1782 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1783 } else if (i_num_be0 > 0) {
1784 msg_Dbg(p_demux, "detected Series 2 Tivo");
1785 p_sys->tivo_series = TIVO_SERIES2;
1786 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1788 if (i_num_9c0 > 0) {
1789 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1790 p_sys->audio_type = TIVO_AUDIO_AC3;
1791 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1792 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1793 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1794 } else if (i_num_3c0 > 0) {
1795 p_sys->audio_type = TIVO_AUDIO_MPEG;
1796 msg_Dbg(p_demux, "detected MPEG Audio" );
1799 /* if tivo_type still unknown, we can check PTS location
1800 * in MPEG packets to determine tivo_type */
1801 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1802 uint32_t i_data_offset = (16 * i_num_recs);
1803 for (i=0; i<i_num_recs; i++) {
1804 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1805 p_hdrs[i].l_rec_size > 15) {
1806 /* first make sure we're aligned */
1807 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1808 &p_chunk[i_data_offset], 5);
1809 if (i_pes_offset >= 0) {
1810 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1811 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1813 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1814 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1815 if (p_sys->tivo_series == TIVO_SERIES1)
1816 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1817 p_sys->tivo_type = TIVO_TYPE_SA;
1818 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1820 if (p_sys->tivo_series == TIVO_SERIES1)
1821 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1822 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1823 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1828 i_data_offset += p_hdrs[i].l_rec_size;
1835 /* =========================================================================== */
1836 static int get_chunk_header(demux_t *p_demux)
1838 int i_readSize, i_num_recs;
1840 const uint8_t *p_peek;
1841 demux_sys_t *p_sys = p_demux->p_sys;
1842 int i_payload_size; /* sum of all records' sizes */
1844 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1846 /* if we have left-over filler space from the last chunk, get that */
1847 if (p_sys->i_stuff_cnt > 0) {
1848 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1849 p_sys->i_stuff_cnt = 0;
1852 /* read the TY packet header */
1853 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1854 p_sys->i_cur_chunk++;
1856 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1863 /* check if it's a PART Header */
1864 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1866 /* parse master chunk */
1867 parse_master(p_demux);
1868 return get_chunk_header(p_demux);
1871 /* number of records in chunk (8- or 16-bit number) */
1872 if (p_peek[3] & 0x80)
1874 /* 16 bit rec cnt */
1875 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1876 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1877 if (p_sys->i_seq_rec != 0xffff)
1879 p_sys->i_seq_rec &= ~0x8000;
1884 /* 8 bit reclen - tivo 1.3 format */
1885 p_sys->i_num_recs = i_num_recs = p_peek[0];
1886 p_sys->i_seq_rec = p_peek[1];
1888 p_sys->i_cur_rec = 0;
1889 p_sys->b_first_chunk = false;
1891 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1893 free(p_sys->rec_hdrs);
1895 /* skip past the 4 bytes we "peeked" earlier */
1896 stream_Read( p_demux->s, NULL, 4 );
1898 /* read the record headers into a temp buffer */
1899 p_hdr_buf = malloc(i_num_recs * 16);
1900 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1906 p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
1910 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1911 (p_sys->i_num_recs * 16) - i_payload_size;
1912 if (p_sys->i_stuff_cnt > 0)
1913 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1914 p_sys->i_stuff_cnt );
1919 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
1920 int i_num_recs, int *pi_payload_size)
1923 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1925 *pi_payload_size = 0;
1926 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1928 for (i = 0; i < i_num_recs; i++)
1930 const uint8_t *record_header = p_buf + (i * 16);
1931 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1932 p_rec_hdr->rec_type = record_header[3];
1933 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1934 if ((record_header[ 0 ] & 0x80) == 0x80)
1937 /* marker bit 2 set, so read extended data */
1938 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1939 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1941 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1942 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1945 p_rec_hdr->ex1 = b1;
1946 p_rec_hdr->ex2 = b2;
1947 p_rec_hdr->l_rec_size = 0;
1948 p_rec_hdr->l_ty_pts = 0;
1949 p_rec_hdr->b_ext = true;
1953 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1954 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1955 *pi_payload_size += p_rec_hdr->l_rec_size;
1956 p_rec_hdr->b_ext = false;
1957 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1959 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1960 } /* end of record-header loop */