1 /*****************************************************************************
2 * ty.c - TiVo ty stream video demuxer for VLC
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
5 * Copyright (C) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005
6 * based on code by Christopher Wingert for tivo-mplayer
7 * tivo(at)wingert.org, February 2003
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 * v1.0.0 - 24-Feb-2005 - Initial release - Series 1 support ONLY!
27 * v1.0.1 - 25-Feb-2005 - Added fix for bad GOP headers - Neal
28 * v1.0.2 - 26-Feb-2005 - No longer require "seekable" input stream - Neal
29 * v2.0.0 - 21-Mar-2005 - Series 2 support! No AC-3 on S2 DTivo yet.
30 * v2.1.0 - 22-Mar-2005 - Support for AC-3 on S2 DTivo (long ac3 packets)
31 * v3.0.0 - 14-Jul-2005 - Support for skipping fwd/back via VLC hotkeys
32 *****************************************************************************/
34 /*****************************************************************************
36 *****************************************************************************/
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_demux.h>
45 #include <vlc_codec.h>
47 #include <vlc_input.h>
48 #include "../codec/cc.h"
52 /*****************************************************************************
54 *****************************************************************************/
55 static int Open ( vlc_object_t * );
56 static void Close( vlc_object_t * );
59 set_shortname( N_("TY") )
60 set_description(N_("TY Stream audio/video demux"))
61 set_category( CAT_INPUT )
62 set_subcategory( SUBCAT_INPUT_DEMUX )
63 set_capability("demux", 6)
64 /* FIXME: there seems to be a segfault when using PVR access
65 * and TY demux has a bigger priority than PS
66 * Something must be wrong.
68 set_callbacks( Open, Close )
69 add_shortcut("ty", "tivo")
72 /*****************************************************************************
74 *****************************************************************************/
75 static int Demux ( demux_t * );
76 static int Control( demux_t *, int, va_list );
78 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
79 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
80 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
81 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
82 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
83 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
84 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
85 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
86 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
87 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
88 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
89 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
91 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
93 /* packet types for reference:
94 2/c0: audio data continued
95 3/c0: audio packet header (PES header)
96 4/c0: audio data (S/A only?)
97 9/c0: audio packet header, AC-3 audio
98 2/e0: video data continued
99 6/e0: video packet header (PES header)
100 7/e0: video sequence header start
101 8/e0: video I-frame header start
102 a/e0: video P-frame header start
103 b/e0: video B-frame header start
104 c/e0: video GOP header start
105 e/01: closed-caption data
106 e/02: Extended data services data
107 e/03: ipreview data ("thumbs up to record" signal)
111 #define TIVO_PES_FILEID ( 0xf5467abd )
112 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
113 #define CHUNK_SIZE ( 128 * 1024 )
122 uint64_t l_ty_pts; /* TY PTS in the record header */
127 uint64_t l_timestamp;
128 uint8_t chunk_bitmask[8];
152 #define XDS_MAX_DATA_SIZE (32)
155 XDS_CLASS_CURRENT = 0,
156 XDS_CLASS_FUTURE = 1,
157 XDS_CLASS_CHANNEL = 2,
158 XDS_CLASS_MISCELLANEOUS = 3,
159 XDS_CLASS_PUBLIC_SERVICE = 4,
160 XDS_CLASS_RESERVED = 5,
161 XDS_CLASS_UNDEFINED = 6,
170 uint8_t p_data[XDS_MAX_DATA_SIZE];
175 XDS_META_PROGRAM_RATING_NONE,
176 XDS_META_PROGRAM_RATING_MPAA,
177 XDS_META_PROGRAM_RATING_TPG,
178 /* TODO add CA/CE rating */
179 } xds_meta_program_rating_t;
183 xds_meta_program_rating_t rating;
185 /* Add the other fields once I have the samples */
186 } xds_meta_program_t;
189 char *psz_channel_name;
190 char *psz_channel_call_letter;
191 char *psz_channel_number;
193 xds_meta_program_t current;
194 xds_meta_program_t future;
198 /* Are we in XDS mode */
201 /* Current class type */
207 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
217 es_out_id_t *p_video; /* ptr to video codec */
218 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
221 es_out_id_t *p_cc[4];
227 size_t i_stream_size; /* size of input stream (if known) */
228 //uint64_t l_program_len; /* length of this stream in msec */
229 bool b_seekable; /* is this stream seekable? */
230 bool b_have_master; /* are master chunks present? */
231 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
232 tivo_series_t tivo_series; /* Series1 or Series2 */
233 tivo_audio_t audio_type; /* AC3 or MPEG */
234 int i_Pes_Length; /* Length of Audio PES header */
235 int i_Pts_Offset; /* offset into audio PES of PTS */
236 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
237 int i_pes_buf_cnt; /* how many bytes in our buffer */
238 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
239 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
240 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
241 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
242 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
243 unsigned i_seq_table_size; /* number of entries in SEQ table */
244 unsigned i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
246 mtime_t firstAudioPTS;
247 mtime_t lastAudioPTS;
248 mtime_t lastVideoPTS;
250 ty_rec_hdr_t *rec_hdrs; /* record headers array */
251 int i_cur_rec; /* current record in this chunk */
252 int i_num_recs; /* number of recs in this chunk */
253 int i_seq_rec; /* record number where seq start is */
254 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
259 static int get_chunk_header(demux_t *);
260 static mtime_t get_pts( const uint8_t *buf );
261 static int find_es_header( const uint8_t *header,
262 const uint8_t *buffer, int i_search_len );
263 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
264 static int ty_stream_seek_time(demux_t *, uint64_t);
266 static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
267 int i_num_recs, int *pi_payload_size);
268 static int probe_stream(demux_t *p_demux);
269 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
270 static void parse_master(demux_t *p_demux);
272 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
273 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
274 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
276 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
278 static void XdsInit( xds_t * );
279 static void XdsExit( xds_t * );
281 #define TY_ES_GROUP (1)
284 * Open: check file and initialize demux structures
287 * 1. peek at the first 12 bytes of the stream for the
288 * magic TiVo PART header & stream type & chunk size
289 * 2. if it's not there, error with VLC_EGENERIC
290 * 3. set up video (mpgv) codec
291 * 4. return VLC_SUCCESS
293 static int Open(vlc_object_t *p_this)
295 demux_t *p_demux = (demux_t *)p_this;
298 const uint8_t *p_peek;
301 /* peek at the first 12 bytes. */
302 /* for TY streams, they're always the same */
303 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
306 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
307 U32_AT(&p_peek[4]) != 0x02 ||
308 U32_AT(&p_peek[8]) != CHUNK_SIZE )
310 if( !p_demux->b_force &&
311 !demux_IsPathExtension( p_demux, ".ty" ) &&
312 !demux_IsPathExtension( p_demux, ".ty+" ) )
314 msg_Warn( p_demux, "this does not look like a TY file, "
315 "continuing anyway..." );
318 /* at this point, we assume we have a valid TY stream */
319 msg_Dbg( p_demux, "valid TY stream detected" );
321 /* Set exported functions */
322 p_demux->pf_demux = Demux;
323 p_demux->pf_control = Control;
325 /* create our structure that will hold all data */
326 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
327 memset(p_sys, 0, sizeof(demux_sys_t));
329 /* set up our struct (most were zero'd out with the memset above) */
330 p_sys->b_first_chunk = true;
331 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
332 p_sys->firstAudioPTS = -1;
333 p_sys->lastAudioPTS = VLC_TS_INVALID;
334 p_sys->lastVideoPTS = VLC_TS_INVALID;
335 p_sys->i_stream_size = stream_Size(p_demux->s);
336 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
337 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
338 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
339 p_sys->i_Pes_Length = 0;
340 p_sys->i_Pts_Offset = 0;
341 p_sys->l_ac3_pkt_size = 0;
343 /* see if this stream is seekable */
344 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
346 if (probe_stream(p_demux) != VLC_SUCCESS) {
351 if (!p_sys->b_have_master)
352 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
354 /* register the proper audio codec */
355 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
356 es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA );
358 es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_A52 );
360 fmt.i_group = TY_ES_GROUP;
361 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
363 /* register the video stream */
364 es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV );
365 fmt.i_group = TY_ES_GROUP;
366 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
369 for( i = 0; i < 4; i++ )
370 p_sys->p_cc[i] = NULL;
371 cc_Init( &p_sys->cc );
373 XdsInit( &p_sys->xds );
378 /* =========================================================================== */
379 /* Demux: Read & Demux one record from the chunk
381 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
383 * NOTE: I think we can return the number of packets sent instead of just 1.
384 * that means we can demux an entire chunk and shoot it back (may be more efficient)
385 * -- should try that some day :) --
387 static int Demux( demux_t *p_demux )
389 demux_sys_t *p_sys = p_demux->p_sys;
391 block_t *p_block_in = NULL;
393 /*msg_Dbg(p_demux, "ty demux processing" );*/
395 /* did we hit EOF earlier? */
400 * what we do (1 record now.. maybe more later):
401 * - use stream_Read() to read the chunk header & record headers
402 * - discard entire chunk if it is a PART header chunk
403 * - parse all the headers into record header array
404 * - keep a pointer of which record we're on
405 * - use stream_Block() to fetch each record
406 * - parse out PTS from PES headers
407 * - set PTS for data packets
408 * - pass the data on to the proper codec via es_out_Send()
410 * if this is the first time or
411 * if we're at the end of this chunk, start a new one
413 /* parse the next chunk's record headers */
414 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
416 if( get_chunk_header(p_demux) == 0 )
420 /*======================================================================
421 * parse & send one record of the chunk
422 *====================================================================== */
423 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
427 const long l_rec_size = p_rec->l_rec_size;
428 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
429 subrec_type, p_rec->rec_type, l_rec_size );*/
431 /* some normal records are 0 length, so check for that... */
432 if( l_rec_size <= 0 )
434 /* no data in payload; we're done */
439 /* read in this record's payload */
440 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
443 /* set these as 'unknown' for now */
445 p_block_in->i_dts = VLC_TS_INVALID;
449 -- don't read any data from the stream, data was in the record header --
451 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
452 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
455 if( p_rec->rec_type == 0xe0 )
458 DemuxRecVideo( p_demux, p_rec, p_block_in );
460 else if ( p_rec->rec_type == 0xc0 )
463 DemuxRecAudio( p_demux, p_rec, p_block_in );
465 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
467 /* Closed Captions/XDS */
468 DemuxRecCc( p_demux, p_rec, p_block_in );
470 else if ( p_rec->rec_type == 0x03 )
472 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
474 block_Release(p_block_in);
476 else if ( p_rec->rec_type == 0x05 )
478 /* Unknown, but seen regularly */
480 block_Release(p_block_in);
484 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
486 block_Release(p_block_in);
495 static int Control(demux_t *p_demux, int i_query, va_list args)
497 demux_sys_t *p_sys = p_demux->p_sys;
501 /*msg_Info(p_demux, "control cmd %d", i_query);*/
504 case DEMUX_GET_POSITION:
505 /* arg is 0.0 - 1.0 percent of overall file position */
506 if( ( i64 = p_sys->i_stream_size ) > 0 )
508 pf = (double*) va_arg( args, double* );
509 *pf = ((double)1.0) * stream_Tell( p_demux->s ) / (double) i64;
514 case DEMUX_SET_POSITION:
515 /* arg is 0.0 - 1.0 percent of overall file position */
516 f = (double) va_arg( args, double );
517 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
518 if ((i64 = p_sys->i_stream_size) > 0)
519 return ty_stream_seek_pct(p_demux, f);
522 /* return TiVo timestamp */
523 p_i64 = (int64_t *) va_arg(args, int64_t *);
524 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
525 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
526 // p_sys->l_last_ty_pts_sync);
527 *p_i64 = (p_sys->l_last_ty_pts / 1000);
529 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
531 p_i64 = (int64_t *) va_arg(args, int64_t *);
534 case DEMUX_SET_TIME: /* arg is time in microsecs */
535 i64 = (int64_t) va_arg( args, int64_t );
536 return ty_stream_seek_time(p_demux, i64 * 1000);
544 static void Close( vlc_object_t *p_this )
546 demux_t *p_demux = (demux_t*)p_this;
547 demux_sys_t *p_sys = p_demux->p_sys;
549 XdsExit( &p_sys->xds );
550 cc_Exit( &p_sys->cc );
551 free( p_sys->rec_hdrs );
552 free( p_sys->seq_table );
557 /* =========================================================================== */
558 /* Compute Presentation Time Stamp (PTS)
559 * Assume buf points to beginning of PTS */
560 static mtime_t get_pts( const uint8_t *buf )
564 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
565 (mtime_t)(buf[1] << 22)|
566 ((mtime_t)(buf[2]&0xfe) << 14)|
567 (mtime_t)(buf[3] << 7)|
568 (mtime_t)(buf[4] >> 1);
569 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
574 /* =========================================================================== */
575 static int find_es_header( const uint8_t *header,
576 const uint8_t *buffer, int i_search_len )
580 for( count = 0; count < i_search_len; count++ )
582 if( !memcmp( &buffer[count], header, 4 ) )
589 /* =========================================================================== */
590 /* check if we have a full PES header, if not, then save what we have.
591 * this is called when audio-start packets are encountered.
593 * 1 partial PES hdr found, some audio data found (buffer adjusted),
594 * -1 partial PES hdr found, no audio data found
595 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
596 /* TODO: HD support -- nothing known about those streams */
597 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
598 int32_t offset, int32_t rec_len )
600 demux_sys_t *p_sys = p_demux->p_sys;
602 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
604 /* entire PES header not present */
605 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
607 /* save the partial pes header */
610 /* no header found, fake some 00's (this works, believe me) */
611 memset( p_sys->pes_buffer, 0, 4 );
612 p_sys->i_pes_buf_cnt = 4;
614 msg_Err( p_demux, "PES header not found in record of %d bytes!",
618 /* copy the partial pes header we found */
619 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
621 p_sys->i_pes_buf_cnt = rec_len - offset;
625 /* PES Header was found, but not complete, so trim the end of this record */
626 p_block->i_buffer -= rec_len - offset;
629 return -1; /* partial PES, no audio data */
631 /* full PES header present, extract PTS */
632 p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block->p_buffer[ offset +
633 p_sys->i_Pts_Offset ] );
634 if (p_sys->firstAudioPTS < 0)
635 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
636 p_block->i_pts = p_sys->lastAudioPTS;
637 /*msg_Dbg(p_demux, "Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
638 /* adjust audio record to remove PES header */
639 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
640 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
641 p_block->i_buffer -= p_sys->i_Pes_Length;
643 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
644 "%02x %02x %02x %02x %02x %02x %02x %02x "
645 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
646 p_block->p_buffer[0], p_block->p_buffer[1],
647 p_block->p_buffer[2], p_block->p_buffer[3],
648 p_block->p_buffer[4], p_block->p_buffer[5],
649 p_block->p_buffer[6], p_block->p_buffer[7],
650 p_block->p_buffer[8], p_block->p_buffer[9],
651 p_block->p_buffer[10], p_block->p_buffer[11],
652 p_block->p_buffer[12], p_block->p_buffer[13],
653 p_block->p_buffer[14], p_block->p_buffer[15]);
658 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
660 demux_sys_t *p_sys = p_demux->p_sys;
661 const int subrec_type = rec_hdr->subrec_type;
662 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
666 assert( rec_hdr->rec_type == 0xe0 );
671 msg_Dbg(p_demux, "packet buffer has "
672 "%02x %02x %02x %02x %02x %02x %02x %02x "
673 "%02x %02x %02x %02x %02x %02x %02x %02x",
674 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
675 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
676 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
677 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
678 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
679 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
680 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
681 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
683 //if( subrec_type == 0x06 || subrec_type == 0x07 )
684 if( subrec_type != 0x02 && subrec_type != 0x0c &&
685 subrec_type != 0x08 && l_rec_size > 4 )
687 /* get the PTS from this packet if it has one.
688 * on S1, only 0x06 has PES. On S2, however, most all do.
689 * Do NOT Pass the PES Header to the MPEG2 codec */
690 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
691 if( esOffset1 != -1 )
693 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
694 //subrec_type, esOffset1);
695 p_sys->lastVideoPTS = VLC_TS_0 + get_pts(
696 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
697 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
698 p_sys->lastVideoPTS );*/
699 if (subrec_type != 0x06) {
700 /* if we found a PES, and it's not type 6, then we're S2 */
701 /* The packet will have video data (& other headers) so we
702 * chop out the PES header and send the rest */
703 if (l_rec_size >= VIDEO_PES_LENGTH) {
704 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
705 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
707 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
708 " (%ld bytes)", subrec_type, l_rec_size);
709 /* nuke this block; it's too short, but has PES marker */
710 p_block_in->i_buffer = 0;
714 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
718 if(subrec_type == 0x06 )
720 /* type 6 (S1 DTivo) has no data, so we're done */
721 block_Release(p_block_in);
725 /* if it's not a continue blk, then set PTS */
726 if( subrec_type != 0x02 )
728 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
730 /* if it's a GOP header, make sure it's legal
731 * (if we have enough data) */
732 /* Some ty files don't have this bit set
733 * and it causes problems */
734 if (subrec_type == 0x0c && l_rec_size >= 6)
735 p_block_in->p_buffer[5] |= 0x08;
736 /* store the TY PTS if there is one */
737 if (subrec_type == 0x07) {
738 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
739 /* should we use audio or video PTS? */
740 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
742 /* yes I know this is a cheap hack. It's the timestamp
743 used for display and skipping fwd/back, so it
744 doesn't have to be accurate to the millisecond.
745 I adjust it here by roughly one 1/30 sec. Yes it
746 will be slightly off for UK streams, but it's OK.
748 p_sys->l_last_ty_pts += 35000000;
749 //p_sys->l_last_ty_pts += 33366667;
751 /* set PTS for this block before we send */
752 if (p_sys->lastVideoPTS > VLC_TS_INVALID)
754 p_block_in->i_pts = p_sys->lastVideoPTS;
755 /* PTS gets used ONCE.
756 * Any subsequent frames we get BEFORE next PES
757 * header will have their PTS computed in the codec */
758 p_sys->lastVideoPTS = VLC_TS_INVALID;
762 /* Register the CC decoders when needed */
763 for( i = 0; i < 4; i++ )
765 static const vlc_fourcc_t fcc[4] = {
766 VLC_FOURCC('c', 'c', '1', ' '),
767 VLC_FOURCC('c', 'c', '2', ' '),
768 VLC_FOURCC('c', 'c', '3', ' '),
769 VLC_FOURCC('c', 'c', '4', ' ')
771 static const char *ppsz_description[4] = {
772 N_("Closed captions 1"),
773 N_("Closed captions 2"),
774 N_("Closed captions 3"),
775 N_("Closed captions 4"),
780 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
783 es_format_Init( &fmt, SPU_ES, fcc[i] );
784 fmt.psz_description = strdup( vlc_gettext(ppsz_description[i]) );
785 fmt.i_group = TY_ES_GROUP;
786 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
787 es_format_Clean( &fmt );
790 /* Send the CC data */
791 if( p_block_in->i_pts > VLC_TS_INVALID && p_sys->cc.i_data > 0 )
793 for( i = 0; i < 4; i++ )
797 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
798 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
799 p_cc->i_pts = p_block_in->i_pts;
800 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
802 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
805 cc_Flush( &p_sys->cc );
808 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
809 //p_sys->i_cur_rec, subrec_type);
810 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
813 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
815 demux_sys_t *p_sys = p_demux->p_sys;
816 const int subrec_type = rec_hdr->subrec_type;
817 const long l_rec_size = rec_hdr->l_rec_size;
820 assert( rec_hdr->rec_type == 0xc0 );
825 fprintf( stderr, "Audio Packet Header " );
826 for( i = 0 ; i < 24 ; i++ )
827 fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] );
828 fprintf( stderr, "\n" );
831 if( subrec_type == 2 )
833 /* SA or DTiVo Audio Data, no PES (continued block)
834 * ================================================
837 /* continue PES if previous was incomplete */
838 if (p_sys->i_pes_buf_cnt > 0)
840 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
842 msg_Dbg(p_demux, "continuing PES header");
843 /* do we have enough data to complete? */
844 if (i_need >= l_rec_size)
846 /* don't have complete PES hdr; save what we have and return */
847 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
848 p_block_in->p_buffer, l_rec_size);
849 p_sys->i_pes_buf_cnt += l_rec_size;
851 block_Release(p_block_in);
855 /* we have enough; reconstruct this p_frame with the new hdr */
856 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
857 p_block_in->p_buffer, i_need);
858 /* advance the block past the PES header (don't want to send it) */
859 p_block_in->p_buffer += i_need;
860 p_block_in->i_buffer -= i_need;
861 /* get the PTS out of this PES header (MPEG or AC3) */
862 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
863 esOffset1 = find_es_header(ty_MPEGAudioPacket,
864 p_sys->pes_buffer, 5);
866 esOffset1 = find_es_header(ty_AC3AudioPacket,
867 p_sys->pes_buffer, 5);
870 /* god help us; something's really wrong */
871 msg_Err(p_demux, "can't find audio PES header in packet");
875 p_sys->lastAudioPTS = VLC_TS_0 + get_pts(
876 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
877 p_block_in->i_pts = p_sys->lastAudioPTS;
879 p_sys->i_pes_buf_cnt = 0;
881 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
882 * not allowed in the AC3 spec and will cause problems. So here
883 * we try to trim things. */
884 /* Also, S1 DTivo has alternating short / long AC3 packets. That
885 * is, one packet is short (incomplete) and the next packet has
886 * the first one's missing data, plus all of its own. Strange. */
887 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
888 p_sys->tivo_series == TIVO_SERIES2) {
889 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
891 p_block_in->i_buffer -= 2;
892 p_sys->l_ac3_pkt_size = 0;
894 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
898 else if( subrec_type == 0x03 )
900 /* MPEG Audio with PES Header, either SA or DTiVo */
901 /* ================================================ */
902 esOffset1 = find_es_header( ty_MPEGAudioPacket,
903 p_block_in->p_buffer, 5 );
905 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
906 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
907 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
908 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
910 /* SA PES Header, No Audio Data */
911 /* ================================================ */
912 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
914 p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block_in->p_buffer[
916 if (p_sys->firstAudioPTS < 0)
917 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
919 block_Release(p_block_in);
921 /*msg_Dbg(p_demux, "SA Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
923 /* DTiVo Audio with PES Header */
924 /* ================================================ */
926 /* Check for complete PES */
927 if (check_sync_pes(p_demux, p_block_in, esOffset1,
930 /* partial PES header found, nothing else.
932 block_Release(p_block_in);
936 msg_Dbg(p_demux, "packet buffer has "
937 "%02x %02x %02x %02x %02x %02x %02x %02x "
938 "%02x %02x %02x %02x %02x %02x %02x %02x",
939 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
940 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
941 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
942 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
943 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
944 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
945 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
946 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
949 else if( subrec_type == 0x04 )
951 /* SA Audio with no PES Header */
952 /* ================================================ */
954 "Adding SA Audio Packet Size %ld", l_rec_size ); */
956 if (p_sys->lastAudioPTS > VLC_TS_INVALID )
957 p_block_in->i_pts = p_sys->lastAudioPTS;
959 else if( subrec_type == 0x09 )
961 /* DTiVo AC3 Audio Data with PES Header */
962 /* ================================================ */
963 esOffset1 = find_es_header( ty_AC3AudioPacket,
964 p_block_in->p_buffer, 5 );
967 msg_Dbg(p_demux, "buffer has "
968 "%02x %02x %02x %02x %02x %02x %02x %02x "
969 "%02x %02x %02x %02x %02x %02x %02x %02x",
970 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
971 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
972 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
973 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
974 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
975 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
976 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
977 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
978 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
981 /* Check for complete PES */
982 if (check_sync_pes(p_demux, p_block_in, esOffset1,
985 /* partial PES header found, nothing else. we're done. */
986 block_Release(p_block_in);
989 /* S2 DTivo has invalid long AC3 packets */
990 if (p_sys->tivo_series == TIVO_SERIES2) {
991 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
992 p_block_in->i_buffer -= 2;
993 p_sys->l_ac3_pkt_size = 0;
995 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1001 /* Unsupported/Unknown */
1002 block_Release(p_block_in);
1006 /* set PCR before we send (if PTS found) */
1007 if( p_block_in->i_pts > VLC_TS_INVALID )
1008 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1009 p_block_in->i_pts );
1011 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1015 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1017 demux_sys_t *p_sys = p_demux->p_sys;
1021 block_Release(p_block_in);
1023 if( rec_hdr->rec_type == 0x01 )
1025 else if( rec_hdr->rec_type == 0x02 )
1030 /* XDS data (extract programs infos) transmitted on field 2 only */
1032 DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] );
1034 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1037 cc_AppendData( &p_sys->cc, i_field, rec_hdr->ex );
1041 /* seek to a position within the stream, if possible */
1042 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1044 demux_sys_t *p_sys = p_demux->p_sys;
1045 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1046 uint64_t l_skip_amt;
1047 unsigned i_cur_part;
1049 /* if we're not seekable, there's nothing to do */
1050 if (!p_sys->b_seekable)
1051 return VLC_EGENERIC;
1053 /* figure out which part & chunk we want & go there */
1054 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1055 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1057 /* try to read the part header (master chunk) if it's there */
1058 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1060 /* can't seek stream */
1061 return VLC_EGENERIC;
1063 parse_master(p_demux);
1065 /* now for the actual chunk */
1066 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1068 /* can't seek stream */
1069 return VLC_EGENERIC;
1071 /* load the chunk */
1072 p_sys->i_stuff_cnt = 0;
1073 get_chunk_header(p_demux);
1075 /* seek within the chunk to get roughly to where we want */
1076 p_sys->i_cur_rec = (int)
1077 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1078 msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1079 msg_Dbg(p_demux, " (chunk %d, record %d)",
1080 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1082 /* seek to the start of this record's data.
1083 * to do that, we have to skip past all prior records */
1085 for ( int i=0; i<p_sys->i_cur_rec; i++)
1086 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1087 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1088 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1090 /* to hell with syncing any audio or video, just start reading records... :) */
1091 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TS_INVALID;*/
1096 //#define TY_XDS_DEBUG
1097 static void XdsInit( xds_t *h )
1100 h->i_class = XDS_MAX_CLASS_COUNT;
1102 h->b_future = false;
1103 for( int i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1105 for( int j = 0; j < 128; j++ )
1106 h->pkt[i][j].b_started = false;
1108 h->b_meta_changed = false;
1109 memset( &h->meta, 0, sizeof(h->meta) );
1111 static void XdsExit( xds_t *h )
1114 free( h->meta.psz_channel_name );
1115 free( h->meta.psz_channel_call_letter );
1116 free( h->meta.psz_channel_number );
1119 free( h->meta.current.psz_name );
1120 free( h->meta.current.psz_rating );
1122 free( h->meta.future.psz_name );
1123 free( h->meta.future.psz_rating );
1125 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1128 for( int i = 0; i < i_src; i++ )
1132 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1133 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1134 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1135 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1136 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1137 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1138 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1139 E2( 0x7c, 0xc3,0xb7); // division symbol
1140 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1141 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1144 dst[i_dst++] = p_src[i];
1148 dst[i_dst++] = '\0';
1150 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1152 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1154 if( *ppsz_dst == NULL && psz_new == NULL )
1159 *ppsz_dst = strdup( psz_new );
1163 h->b_meta_changed = true;
1167 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1169 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1176 XdsStringUtf8( name, pk->p_data, pk->i_data );
1177 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1179 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1180 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1184 i_rating = (pk->p_data[0] & 0x18);
1185 if( i_rating == 0x08 )
1188 static const char *pppsz_ratings[8][2] = {
1189 { "None", "No rating (no content advisory)" },
1190 { "TV-Y", "All Children (no content advisory)" },
1191 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1192 { "TV-G", "General Audience (no content advisory)" },
1193 { "TV-PG", "Parental Guidance Suggested" },
1194 { "TV-14", "Parents Strongly Cautioned" },
1195 { "TV-MA", "Mature Audience Only" },
1196 { "None", "No rating (no content advisory)" }
1198 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1199 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1201 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1202 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1203 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1206 else if( i_rating == 0x00 || i_rating == 0x10 )
1209 static const char *pppsz_ratings[8][2] = {
1211 { "G", "General Audiences" },
1212 { "PG", "Parental Guidance Suggested" },
1213 { "PG-13", "Parents Strongly Cautioned" },
1214 { "R", "Restricted" },
1215 { "NC-17", "No one 17 and under admitted" },
1216 { "X", "No one under 17 admitted" },
1217 { "NR", "Not Rated" },
1219 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1220 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1222 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1223 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1224 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1229 /* Non US Rating TODO */
1230 assert( i_rating == 0x18 ); // only left value possible */
1231 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1232 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1234 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1235 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1242 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1248 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1256 if( pk->i_data < 2 )
1258 XdsStringUtf8( name, pk->p_data, pk->i_data );
1259 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1261 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1262 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1267 if( pk->i_data < 4 )
1270 XdsStringUtf8( name, pk->p_data, 4 );
1271 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1273 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1274 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1276 if( pk->i_data >= 6 )
1278 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1279 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1281 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1282 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1287 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1289 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1290 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1295 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1298 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1302 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1308 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1310 switch( h->i_class )
1312 case XDS_CLASS_CURRENT:
1313 case XDS_CLASS_FUTURE:
1314 XdsDecodeCurrentFuture( h, pk );
1316 case XDS_CLASS_CHANNEL:
1317 XdsDecodeChannel( h, pk );
1319 case XDS_CLASS_MISCELLANEOUS:
1321 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1324 case XDS_CLASS_PUBLIC_SERVICE:
1326 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1330 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1335 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1337 /* TODO check parity */
1342 if( d1 >= 0x01 && d1 <= 0x0e )
1344 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1345 const int i_type = d2;
1346 const bool b_start = d1 & 0x01;
1347 xds_packet_t *pk = &h->pkt[i_class][i_type];
1349 if( !b_start && !pk->b_started )
1351 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1357 h->i_class = i_class;
1359 h->b_future = !b_start;
1360 pk->b_started = true;
1364 pk->i_sum = d1 + d2;
1367 else if( d1 == 0x0f && h->b_xds )
1369 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1371 /* TODO checksum and decode */
1372 pk->i_sum += d1 + d2;
1373 if( pk->i_sum & 0x7f )
1375 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1376 pk->b_started = false;
1379 if( pk->i_data <= 0 )
1381 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1382 pk->b_started = false;
1386 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1391 pk->b_started = false;
1393 else if( d1 >= 0x20 && h->b_xds )
1395 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1397 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1399 /* Broken -> reinit */
1400 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1402 pk->b_started = false;
1405 /* TODO check parity bit */
1406 pk->p_data[pk->i_data++] = d1 & 0x7f;
1407 pk->p_data[pk->i_data++] = d2 & 0x7f;
1416 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1418 demux_sys_t *p_sys = p_demux->p_sys;
1420 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1421 if( p_demux->p_sys->xds.b_meta_changed )
1423 xds_meta_t *m = &p_sys->xds.meta;
1427 /* Channel meta data */
1428 p_meta = vlc_meta_New();
1429 if( m->psz_channel_name )
1430 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1431 if( m->psz_channel_call_letter )
1432 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1433 if( m->psz_channel_number )
1434 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1435 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1436 vlc_meta_Delete( p_meta );
1438 /* Event meta data (current/future) */
1439 p_epg = vlc_epg_New( NULL );
1440 if( m->current.psz_name )
1442 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1443 //if( m->current.psz_rating )
1444 // TODO but VLC cannot yet handle rating per epg event
1445 vlc_epg_SetCurrent( p_epg, 0 );
1447 if( m->future.psz_name )
1450 if( p_epg->i_event > 0 )
1451 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1452 vlc_epg_Delete( p_epg );
1454 p_demux->p_sys->xds.b_meta_changed = false;
1457 /* seek to an exact time position within the stream, if possible.
1458 * l_seek_time is in nanoseconds, the TIVO time standard.
1460 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1462 demux_sys_t *p_sys = p_demux->p_sys;
1463 unsigned i_seq_entry = 0;
1466 int64_t l_cur_pos = stream_Tell(p_demux->s);
1467 unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1468 uint64_t l_seek_secs = l_seek_time / 1000000000;
1469 uint64_t l_fwd_stamp = 1;
1471 /* if we're not seekable, there's nothing to do */
1472 if (!p_sys->b_seekable || !p_sys->b_have_master)
1473 return VLC_EGENERIC;
1475 msg_Dbg(p_demux, "Skipping to time %02"PRIu64":%02"PRIu64":%02"PRIu64,
1476 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1478 /* seek to the proper segment if necessary */
1479 /* first see if we need to go back */
1480 while (l_seek_time < p_sys->l_first_ty_pts) {
1481 msg_Dbg(p_demux, "skipping to prior segment.");
1482 /* load previous part */
1483 if (i_cur_part == 0) {
1484 stream_Seek(p_demux->s, l_cur_pos);
1485 msg_Err(p_demux, "Attempt to seek past BOF");
1486 return VLC_EGENERIC;
1488 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1490 parse_master(p_demux);
1492 /* maybe we need to go forward */
1493 while (l_seek_time > p_sys->l_final_ty_pts) {
1494 msg_Dbg(p_demux, "skipping to next segment.");
1495 /* load next part */
1496 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1497 /* error; restore previous file position */
1498 stream_Seek(p_demux->s, l_cur_pos);
1499 msg_Err(p_demux, "seek error");
1500 return VLC_EGENERIC;
1502 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1504 parse_master(p_demux);
1507 /* our target is somewhere within this part;
1508 find the proper chunk using seq_table */
1509 for (i=1; i<p_sys->i_seq_table_size; i++) {
1510 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1511 /* i-1 is the section we want; remember the next timestamp in case
1512 we have to use it (this section may not have a proper SEQ hdr
1513 for the time we're seeking) */
1514 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1515 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1521 /* if we went through the entire last loop and didn't find our target,
1522 then we skip to the next part. What has happened is that the actual
1523 time we're seeking is within this part, but there isn't a SEQ hdr
1524 for it here. So we skip to the next part */
1525 if (i == p_sys->i_seq_table_size) {
1526 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1527 /* error; restore previous file position */
1528 stream_Seek(p_demux->s, l_cur_pos);
1529 msg_Err(p_demux, "seek error");
1530 return VLC_EGENERIC;
1532 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1534 parse_master(p_demux);
1538 /* determine which chunk has our seek_time */
1539 for (unsigned i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1540 uint64_t l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1541 uint64_t l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1542 msg_Dbg(p_demux, "testing part %d chunk %"PRIu64" mask 0x%02X bit %d",
1543 i_cur_part, l_chunk_nr,
1544 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1545 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1546 /* check this chunk's SEQ header timestamp */
1547 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%"PRIu64,
1548 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1549 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1551 // TODO: we don't have to parse the full header set;
1552 // just test the seq_rec entry for its timestamp
1553 p_sys->i_stuff_cnt = 0;
1554 get_chunk_header(p_demux);
1555 // check ty PTS for the SEQ entry in this chunk
1556 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1557 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1558 /* Seek to beginning of original chunk & reload it */
1559 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1560 p_sys->i_stuff_cnt = 0;
1561 get_chunk_header(p_demux);
1562 return VLC_EGENERIC;
1564 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1566 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02"PRIu64":%02"PRIu64":%02"PRIu64,
1568 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1569 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1570 // keep this one? go back?
1571 /* for now, we take this one. it's the first SEQ hdr AFTER
1572 the time we were searching for. */
1573 msg_Dbg(p_demux, "seek target found.");
1576 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1579 /* if we made it through this entire loop without finding our target,
1580 then we skip to the next section. What has happened is that the actual
1581 time we're seeking is within this section, but there isn't a SEQ hdr
1582 for it here. So we skip to the next closest one (l_fwd_stamp) */
1583 if (i == p_sys->i_bits_per_seq_entry)
1584 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1586 /* current stream ptr is at beginning of data for this chunk,
1587 so we need to skip past any stream data prior to the seq_rec
1590 for (int j=0; j<p_sys->i_seq_rec; j++)
1591 i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size;
1592 stream_Read(p_demux->s, NULL, i_skip_cnt);
1593 p_sys->i_cur_rec = p_sys->i_seq_rec;
1594 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1595 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1601 /* parse a master chunk, filling the SEQ table and other variables.
1602 * We assume the stream is currently pointing to it.
1604 static void parse_master(demux_t *p_demux)
1606 demux_sys_t *p_sys = p_demux->p_sys;
1607 uint8_t mst_buf[32];
1608 uint32_t i, i_map_size;
1609 int64_t i_save_pos = stream_Tell(p_demux->s);
1612 /* Note that the entries in the SEQ table in the stream may have
1613 different sizes depending on the bits per entry. We store them
1614 all in the same size structure, so we have to parse them out one
1615 by one. If we had a dynamic structure, we could simply read the
1616 entire table directly from the stream into memory in place. */
1618 /* clear the SEQ table */
1619 free(p_sys->seq_table);
1621 /* parse header info */
1622 stream_Read(p_demux->s, mst_buf, 32);
1623 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1624 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1625 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1626 p_sys->i_seq_table_size = i / (8 + i_map_size);
1628 /* parse all the entries */
1629 p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t));
1630 if (p_sys->seq_table == NULL)
1632 p_sys->i_seq_table_size = 0;
1635 for (unsigned i=0; i<p_sys->i_seq_table_size; i++) {
1636 stream_Read(p_demux->s, mst_buf, 8);
1637 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1638 if (i_map_size > 8) {
1639 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1640 stream_Read(p_demux->s, NULL, i_map_size);
1642 stream_Read(p_demux->s, mst_buf + 8, i_map_size);
1643 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1647 /* set up a few of our variables */
1648 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1649 p_sys->l_final_ty_pts =
1650 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1651 p_sys->b_have_master = true;
1653 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1655 "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1656 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1657 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1659 "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
1660 i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
1662 /* seek past this chunk */
1663 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1667 /* ======================================================================== */
1668 /* "Peek" at some chunks. Skip over the Part header if we find it.
1669 * We parse the peeked data and determine audio type,
1670 * SA vs. DTivo, & Tivo Series.
1671 * Set global vars i_Pes_Length, i_Pts_Offset,
1672 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1673 static int probe_stream(demux_t *p_demux)
1675 demux_sys_t *p_sys = p_demux->p_sys;
1676 const uint8_t *p_buf;
1678 bool b_probe_error = false;
1680 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1681 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1682 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1683 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1684 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1685 return VLC_EGENERIC;
1688 /* the real work: analyze this chunk */
1689 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1690 analyze_chunk(p_demux, p_buf);
1691 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1692 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1693 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1695 p_buf += CHUNK_SIZE;
1698 /* the final tally */
1699 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1700 msg_Err(p_demux, "Can't determine Tivo Series.");
1701 b_probe_error = true;
1703 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1704 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1705 b_probe_error = true;
1707 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1708 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1709 b_probe_error = true;
1711 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1715 /* ======================================================================== */
1716 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1717 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1719 demux_sys_t *p_sys = p_demux->p_sys;
1721 ty_rec_hdr_t *p_hdrs;
1722 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1725 /* skip if it's a Part header */
1726 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1729 /* number of records in chunk (we ignore high order byte;
1730 * rarely are there > 256 chunks & we don't need that many anyway) */
1731 i_num_recs = p_chunk[0];
1732 if (i_num_recs < 5) {
1733 /* try again with the next chunk. Sometimes there are dead ones */
1737 p_chunk += 4; /* skip past rec count & SEQ bytes */
1738 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1739 p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
1741 * 1. check video packets. Presence of 0x6e0 means S1.
1742 * No 6e0 but have be0 means S2.
1743 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1744 * If AC-3, then we have DTivo.
1745 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1747 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1748 for (i=0; i<i_num_recs; i++) {
1749 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1750 //p_hdrs[i].rec_type,
1751 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1752 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1767 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1768 i_num_6e0, i_num_be0);
1770 /* set up our variables */
1771 if (i_num_6e0 > 0) {
1772 msg_Dbg(p_demux, "detected Series 1 Tivo");
1773 p_sys->tivo_series = TIVO_SERIES1;
1774 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1775 } else if (i_num_be0 > 0) {
1776 msg_Dbg(p_demux, "detected Series 2 Tivo");
1777 p_sys->tivo_series = TIVO_SERIES2;
1778 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1780 if (i_num_9c0 > 0) {
1781 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1782 p_sys->audio_type = TIVO_AUDIO_AC3;
1783 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1784 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1785 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1786 } else if (i_num_3c0 > 0) {
1787 p_sys->audio_type = TIVO_AUDIO_MPEG;
1788 msg_Dbg(p_demux, "detected MPEG Audio" );
1791 /* if tivo_type still unknown, we can check PTS location
1792 * in MPEG packets to determine tivo_type */
1793 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1794 uint32_t i_data_offset = (16 * i_num_recs);
1795 for (i=0; i<i_num_recs; i++) {
1796 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1797 p_hdrs[i].l_rec_size > 15) {
1798 /* first make sure we're aligned */
1799 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1800 &p_chunk[i_data_offset], 5);
1801 if (i_pes_offset >= 0) {
1802 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1803 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1805 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1806 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1807 if (p_sys->tivo_series == TIVO_SERIES1)
1808 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1809 p_sys->tivo_type = TIVO_TYPE_SA;
1810 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1812 if (p_sys->tivo_series == TIVO_SERIES1)
1813 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1814 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1815 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1820 i_data_offset += p_hdrs[i].l_rec_size;
1827 /* =========================================================================== */
1828 static int get_chunk_header(demux_t *p_demux)
1830 int i_readSize, i_num_recs;
1832 const uint8_t *p_peek;
1833 demux_sys_t *p_sys = p_demux->p_sys;
1834 int i_payload_size; /* sum of all records' sizes */
1836 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1838 /* if we have left-over filler space from the last chunk, get that */
1839 if (p_sys->i_stuff_cnt > 0) {
1840 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1841 p_sys->i_stuff_cnt = 0;
1844 /* read the TY packet header */
1845 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1846 p_sys->i_cur_chunk++;
1848 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1855 /* check if it's a PART Header */
1856 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1858 /* parse master chunk */
1859 parse_master(p_demux);
1860 return get_chunk_header(p_demux);
1863 /* number of records in chunk (8- or 16-bit number) */
1864 if (p_peek[3] & 0x80)
1866 /* 16 bit rec cnt */
1867 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1868 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1869 if (p_sys->i_seq_rec != 0xffff)
1871 p_sys->i_seq_rec &= ~0x8000;
1876 /* 8 bit reclen - tivo 1.3 format */
1877 p_sys->i_num_recs = i_num_recs = p_peek[0];
1878 p_sys->i_seq_rec = p_peek[1];
1880 p_sys->i_cur_rec = 0;
1881 p_sys->b_first_chunk = false;
1883 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1885 free(p_sys->rec_hdrs);
1887 /* skip past the 4 bytes we "peeked" earlier */
1888 stream_Read( p_demux->s, NULL, 4 );
1890 /* read the record headers into a temp buffer */
1891 p_hdr_buf = malloc(i_num_recs * 16);
1892 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1898 p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs,
1902 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1903 (p_sys->i_num_recs * 16) - i_payload_size;
1904 if (p_sys->i_stuff_cnt > 0)
1905 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1906 p_sys->i_stuff_cnt );
1911 static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
1912 int i_num_recs, int *pi_payload_size)
1915 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1917 *pi_payload_size = 0;
1918 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1920 for (i = 0; i < i_num_recs; i++)
1922 const uint8_t *record_header = p_buf + (i * 16);
1923 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1924 p_rec_hdr->rec_type = record_header[3];
1925 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1926 if ((record_header[ 0 ] & 0x80) == 0x80)
1929 /* marker bit 2 set, so read extended data */
1930 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1931 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1932 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1933 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1935 p_rec_hdr->ex[0] = b1;
1936 p_rec_hdr->ex[1] = b2;
1937 p_rec_hdr->l_rec_size = 0;
1938 p_rec_hdr->l_ty_pts = 0;
1939 p_rec_hdr->b_ext = true;
1943 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1944 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1945 *pi_payload_size += p_rec_hdr->l_rec_size;
1946 p_rec_hdr->b_ext = false;
1947 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1949 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1950 } /* end of record-header loop */