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 );
73 /*****************************************************************************
75 *****************************************************************************/
76 static int Demux ( demux_t * );
77 static int Control( demux_t *, int, va_list );
79 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
80 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
81 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
82 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
83 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
84 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
85 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
86 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
87 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
88 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
89 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
90 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
92 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
94 /* packet types for reference:
95 2/c0: audio data continued
96 3/c0: audio packet header (PES header)
97 4/c0: audio data (S/A only?)
98 9/c0: audio packet header, AC-3 audio
99 2/e0: video data continued
100 6/e0: video packet header (PES header)
101 7/e0: video sequence header start
102 8/e0: video I-frame header start
103 a/e0: video P-frame header start
104 b/e0: video B-frame header start
105 c/e0: video GOP header start
106 e/01: closed-caption data
107 e/02: Extended data services data
108 e/03: ipreview data ("thumbs up to record" signal)
112 #define TIVO_PES_FILEID ( 0xf5467abd )
113 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
114 #define CHUNK_SIZE ( 128 * 1024 )
123 uint64_t l_ty_pts; /* TY PTS in the record header */
128 uint64_t l_timestamp;
129 uint8_t chunk_bitmask[8];
153 #define XDS_MAX_DATA_SIZE (32)
156 XDS_CLASS_CURRENT = 0,
157 XDS_CLASS_FUTURE = 1,
158 XDS_CLASS_CHANNEL = 2,
159 XDS_CLASS_MISCELLANEOUS = 3,
160 XDS_CLASS_PUBLIC_SERVICE = 4,
161 XDS_CLASS_RESERVED = 5,
162 XDS_CLASS_UNDEFINED = 6,
171 uint8_t p_data[XDS_MAX_DATA_SIZE];
176 XDS_META_PROGRAM_RATING_NONE,
177 XDS_META_PROGRAM_RATING_MPAA,
178 XDS_META_PROGRAM_RATING_TPG,
179 /* TODO add CA/CE rating */
180 } xds_meta_program_rating_t;
184 xds_meta_program_rating_t rating;
186 /* Add the other fields once I have the samples */
187 } xds_meta_program_t;
190 char *psz_channel_name;
191 char *psz_channel_call_letter;
192 char *psz_channel_number;
194 xds_meta_program_t current;
195 xds_meta_program_t future;
199 /* Are we in XDS mode */
202 /* Current class type */
208 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
218 es_out_id_t *p_video; /* ptr to video codec */
219 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
222 es_out_id_t *p_cc[4];
228 size_t i_stream_size; /* size of input stream (if known) */
229 //uint64_t l_program_len; /* length of this stream in msec */
230 bool b_seekable; /* is this stream seekable? */
231 bool b_have_master; /* are master chunks present? */
232 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
233 tivo_series_t tivo_series; /* Series1 or Series2 */
234 tivo_audio_t audio_type; /* AC3 or MPEG */
235 int i_Pes_Length; /* Length of Audio PES header */
236 int i_Pts_Offset; /* offset into audio PES of PTS */
237 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
238 int i_pes_buf_cnt; /* how many bytes in our buffer */
239 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
240 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
241 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
242 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
243 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
244 int i_seq_table_size; /* number of entries in SEQ table */
245 int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
247 mtime_t firstAudioPTS;
248 mtime_t lastAudioPTS;
249 mtime_t lastVideoPTS;
251 ty_rec_hdr_t *rec_hdrs; /* record headers array */
252 int i_cur_rec; /* current record in this chunk */
253 int i_num_recs; /* number of recs in this chunk */
254 int i_seq_rec; /* record number where seq start is */
255 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
260 static int get_chunk_header(demux_t *);
261 static mtime_t get_pts( const uint8_t *buf );
262 static int find_es_header( const uint8_t *header,
263 const uint8_t *buffer, int i_search_len );
264 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
265 static int ty_stream_seek_time(demux_t *, uint64_t);
267 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
268 int i_num_recs, int *pi_payload_size);
269 static int probe_stream(demux_t *p_demux);
270 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
271 static void parse_master(demux_t *p_demux);
273 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
274 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
275 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
277 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
279 static void XdsInit( xds_t * );
280 static void XdsExit( xds_t * );
282 #define TY_ES_GROUP (1)
285 * Open: check file and initialize demux structures
288 * 1. peek at the first 12 bytes of the stream for the
289 * magic TiVo PART header & stream type & chunk size
290 * 2. if it's not there, error with VLC_EGENERIC
291 * 3. set up video (mpgv) codec
292 * 4. return VLC_SUCCESS
294 static int Open(vlc_object_t *p_this)
296 demux_t *p_demux = (demux_t *)p_this;
299 const uint8_t *p_peek;
302 /* peek at the first 12 bytes. */
303 /* for TY streams, they're always the same */
304 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
307 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
308 U32_AT(&p_peek[4]) != 0x02 ||
309 U32_AT(&p_peek[8]) != CHUNK_SIZE )
311 if( !p_demux->b_force &&
312 !demux_IsPathExtension( p_demux, ".ty" ) &&
313 !demux_IsPathExtension( p_demux, ".ty+" ) )
315 msg_Warn( p_demux, "this does not look like a TY file, "
316 "continuing anyway..." );
319 /* at this point, we assume we have a valid TY stream */
320 msg_Dbg( p_demux, "valid TY stream detected" );
322 /* Set exported functions */
323 p_demux->pf_demux = Demux;
324 p_demux->pf_control = Control;
326 /* create our structure that will hold all data */
327 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
328 memset(p_sys, 0, sizeof(demux_sys_t));
330 /* set up our struct (most were zero'd out with the memset above) */
331 p_sys->b_first_chunk = true;
332 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
333 p_sys->firstAudioPTS = -1;
334 p_sys->i_stream_size = stream_Size(p_demux->s);
335 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
336 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
337 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
338 p_sys->i_Pes_Length = 0;
339 p_sys->i_Pts_Offset = 0;
340 p_sys->l_ac3_pkt_size = 0;
342 /* see if this stream is seekable */
343 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
345 if (probe_stream(p_demux) != VLC_SUCCESS) {
350 if (!p_sys->b_have_master)
351 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
353 /* register the proper audio codec */
354 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
355 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
357 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
359 fmt.i_group = TY_ES_GROUP;
360 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
362 /* register the video stream */
363 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
364 fmt.i_group = TY_ES_GROUP;
365 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
368 for( i = 0; i < 4; i++ )
369 p_sys->p_cc[i] = NULL;
370 cc_Init( &p_sys->cc );
372 XdsInit( &p_sys->xds );
377 /* =========================================================================== */
378 /* Demux: Read & Demux one record from the chunk
380 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
382 * NOTE: I think we can return the number of packets sent instead of just 1.
383 * that means we can demux an entire chunk and shoot it back (may be more efficient)
384 * -- should try that some day :) --
386 static int Demux( demux_t *p_demux )
388 demux_sys_t *p_sys = p_demux->p_sys;
390 block_t *p_block_in = NULL;
392 /*msg_Dbg(p_demux, "ty demux processing" );*/
394 /* did we hit EOF earlier? */
399 * what we do (1 record now.. maybe more later):
400 * - use stream_Read() to read the chunk header & record headers
401 * - discard entire chunk if it is a PART header chunk
402 * - parse all the headers into record header array
403 * - keep a pointer of which record we're on
404 * - use stream_Block() to fetch each record
405 * - parse out PTS from PES headers
406 * - set PTS for data packets
407 * - pass the data on to the proper codec via es_out_Send()
409 * if this is the first time or
410 * if we're at the end of this chunk, start a new one
412 /* parse the next chunk's record headers */
413 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
415 if( get_chunk_header(p_demux) == 0 )
419 /*======================================================================
420 * parse & send one record of the chunk
421 *====================================================================== */
422 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
426 const long l_rec_size = p_rec->l_rec_size;
427 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
428 subrec_type, p_rec->rec_type, l_rec_size );*/
430 /* some normal records are 0 length, so check for that... */
431 if( l_rec_size <= 0 )
433 /* no data in payload; we're done */
438 /* read in this record's payload */
439 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
442 /* set these as 'unknown' for now */
444 p_block_in->i_dts = 0;
448 -- don't read any data from the stream, data was in the record header --
450 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
451 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
454 if( p_rec->rec_type == 0xe0 )
457 DemuxRecVideo( p_demux, p_rec, p_block_in );
459 else if ( p_rec->rec_type == 0xc0 )
462 DemuxRecAudio( p_demux, p_rec, p_block_in );
464 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
466 /* Closed Captions/XDS */
467 DemuxRecCc( p_demux, p_rec, p_block_in );
469 else if ( p_rec->rec_type == 0x03 )
471 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
473 block_Release(p_block_in);
475 else if ( p_rec->rec_type == 0x05 )
477 /* Unknown, but seen regularly */
479 block_Release(p_block_in);
483 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
485 block_Release(p_block_in);
494 static int Control(demux_t *p_demux, int i_query, va_list args)
496 demux_sys_t *p_sys = p_demux->p_sys;
500 /*msg_Info(p_demux, "control cmd %d", i_query);*/
503 case DEMUX_GET_POSITION:
504 /* arg is 0.0 - 1.0 percent of overall file position */
505 if( ( i64 = p_sys->i_stream_size ) > 0 )
507 pf = (double*) va_arg( args, double* );
508 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
513 case DEMUX_SET_POSITION:
514 /* arg is 0.0 - 1.0 percent of overall file position */
515 f = (double) va_arg( args, double );
516 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
517 if ((i64 = p_sys->i_stream_size) > 0)
518 return ty_stream_seek_pct(p_demux, f);
521 /* return TiVo timestamp */
522 p_i64 = (int64_t *) va_arg(args, int64_t *);
523 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
524 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
525 // p_sys->l_last_ty_pts_sync);
526 *p_i64 = (p_sys->l_last_ty_pts / 1000);
528 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
530 p_i64 = (int64_t *) va_arg(args, int64_t *);
533 case DEMUX_SET_TIME: /* arg is time in microsecs */
534 i64 = (int64_t) va_arg( args, int64_t );
535 return ty_stream_seek_time(p_demux, i64 * 1000);
543 static void Close( vlc_object_t *p_this )
545 demux_t *p_demux = (demux_t*)p_this;
546 demux_sys_t *p_sys = p_demux->p_sys;
548 XdsExit( &p_sys->xds );
549 cc_Exit( &p_sys->cc );
550 free( p_sys->rec_hdrs );
551 free( p_sys->seq_table );
556 /* =========================================================================== */
557 /* Compute Presentation Time Stamp (PTS)
558 * Assume buf points to beginning of PTS */
559 static mtime_t get_pts( const uint8_t *buf )
563 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
564 (mtime_t)(buf[1] << 22)|
565 ((mtime_t)(buf[2]&0xfe) << 14)|
566 (mtime_t)(buf[3] << 7)|
567 (mtime_t)(buf[4] >> 1);
568 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
573 /* =========================================================================== */
574 static int find_es_header( const uint8_t *header,
575 const uint8_t *buffer, int i_search_len )
579 for( count = 0; count < i_search_len; count++ )
581 if( !memcmp( &buffer[count], header, 4 ) )
588 /* =========================================================================== */
589 /* check if we have a full PES header, if not, then save what we have.
590 * this is called when audio-start packets are encountered.
592 * 1 partial PES hdr found, some audio data found (buffer adjusted),
593 * -1 partial PES hdr found, no audio data found
594 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
595 /* TODO: HD support -- nothing known about those streams */
596 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
597 int32_t offset, int32_t rec_len )
599 demux_sys_t *p_sys = p_demux->p_sys;
601 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
603 /* entire PES header not present */
604 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
606 /* save the partial pes header */
609 /* no header found, fake some 00's (this works, believe me) */
610 memset( p_sys->pes_buffer, 4, 0 );
611 p_sys->i_pes_buf_cnt = 4;
613 msg_Err( p_demux, "PES header not found in record of %d bytes!",
617 /* copy the partial pes header we found */
618 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
620 p_sys->i_pes_buf_cnt = rec_len - offset;
624 /* PES Header was found, but not complete, so trim the end of this record */
625 p_block->i_buffer -= rec_len - offset;
628 return -1; /* partial PES, no audio data */
630 /* full PES header present, extract PTS */
631 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
632 p_sys->i_Pts_Offset ] );
633 if (p_sys->firstAudioPTS < 0)
634 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
635 p_block->i_pts = p_sys->lastAudioPTS;
636 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
637 /* adjust audio record to remove PES header */
638 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
639 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
640 p_block->i_buffer -= p_sys->i_Pes_Length;
642 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
643 "%02x %02x %02x %02x %02x %02x %02x %02x "
644 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
645 p_block->p_buffer[0], p_block->p_buffer[1],
646 p_block->p_buffer[2], p_block->p_buffer[3],
647 p_block->p_buffer[4], p_block->p_buffer[5],
648 p_block->p_buffer[6], p_block->p_buffer[7],
649 p_block->p_buffer[8], p_block->p_buffer[9],
650 p_block->p_buffer[10], p_block->p_buffer[11],
651 p_block->p_buffer[12], p_block->p_buffer[13],
652 p_block->p_buffer[14], p_block->p_buffer[15]);
657 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
659 demux_sys_t *p_sys = p_demux->p_sys;
660 const int subrec_type = rec_hdr->subrec_type;
661 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
665 assert( rec_hdr->rec_type == 0xe0 );
670 msg_Dbg(p_demux, "packet buffer has "
671 "%02x %02x %02x %02x %02x %02x %02x %02x "
672 "%02x %02x %02x %02x %02x %02x %02x %02x",
673 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
674 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
675 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
676 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
677 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
678 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
679 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
680 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
682 //if( subrec_type == 0x06 || subrec_type == 0x07 )
683 if( subrec_type != 0x02 && subrec_type != 0x0c &&
684 subrec_type != 0x08 && l_rec_size > 4 )
686 /* get the PTS from this packet if it has one.
687 * on S1, only 0x06 has PES. On S2, however, most all do.
688 * Do NOT Pass the PES Header to the MPEG2 codec */
689 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
690 if( esOffset1 != -1 )
692 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
693 //subrec_type, esOffset1);
694 p_sys->lastVideoPTS = get_pts(
695 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
696 /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
697 p_sys->lastVideoPTS );*/
698 if (subrec_type != 0x06) {
699 /* if we found a PES, and it's not type 6, then we're S2 */
700 /* The packet will have video data (& other headers) so we
701 * chop out the PES header and send the rest */
702 if (l_rec_size >= VIDEO_PES_LENGTH) {
703 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
704 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
706 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
707 " (%ld bytes)", subrec_type, l_rec_size);
708 /* nuke this block; it's too short, but has PES marker */
709 p_block_in->i_buffer = 0;
713 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
717 if(subrec_type == 0x06 )
719 /* type 6 (S1 DTivo) has no data, so we're done */
720 block_Release(p_block_in);
724 /* if it's not a continue blk, then set PTS */
725 if( subrec_type != 0x02 )
727 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
729 /* if it's a GOP header, make sure it's legal
730 * (if we have enough data) */
731 /* Some ty files don't have this bit set
732 * and it causes problems */
733 if (subrec_type == 0x0c && l_rec_size >= 6)
734 p_block_in->p_buffer[5] |= 0x08;
735 /* store the TY PTS if there is one */
736 if (subrec_type == 0x07) {
737 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
738 /* should we use audio or video PTS? */
739 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
741 /* yes I know this is a cheap hack. It's the timestamp
742 used for display and skipping fwd/back, so it
743 doesn't have to be accurate to the millisecond.
744 I adjust it here by roughly one 1/30 sec. Yes it
745 will be slightly off for UK streams, but it's OK.
747 p_sys->l_last_ty_pts += 35000000;
748 //p_sys->l_last_ty_pts += 33366667;
750 /* set PTS for this block before we send */
751 if (p_sys->lastVideoPTS > 0)
753 p_block_in->i_pts = p_sys->lastVideoPTS;
754 /* PTS gets used ONCE.
755 * Any subsequent frames we get BEFORE next PES
756 * header will have their PTS computed in the codec */
757 p_sys->lastVideoPTS = 0;
761 /* Register the CC decoders when needed */
762 for( i = 0; i < 4; i++ )
764 static const vlc_fourcc_t fcc[4] = {
765 VLC_FOURCC('c', 'c', '1', ' '),
766 VLC_FOURCC('c', 'c', '2', ' '),
767 VLC_FOURCC('c', 'c', '3', ' '),
768 VLC_FOURCC('c', 'c', '4', ' ')
770 static const char *ppsz_description[4] = {
771 N_("Closed captions 1"),
772 N_("Closed captions 2"),
773 N_("Closed captions 3"),
774 N_("Closed captions 4"),
779 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
782 es_format_Init( &fmt, SPU_ES, fcc[i] );
783 fmt.psz_description = strdup( _(ppsz_description[i]) );
784 fmt.i_group = TY_ES_GROUP;
785 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
786 es_format_Clean( &fmt );
789 /* Send the CC data */
790 if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
794 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
795 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
796 p_cc->i_pts = p_block_in->i_pts;
797 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
799 for( i = 0, i_cc_count = 0; i < 4; i++ )
800 i_cc_count += p_sys->p_cc[i] ? 1 : 0;
802 for( i = 0; i < 4; i++ )
804 if( !p_sys->p_cc[i] )
807 es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
809 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
811 cc_Flush( &p_sys->cc );
814 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
815 //p_sys->i_cur_rec, subrec_type);
816 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
819 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
821 demux_sys_t *p_sys = p_demux->p_sys;
822 const int subrec_type = rec_hdr->subrec_type;
823 const long l_rec_size = rec_hdr->l_rec_size;
826 assert( rec_hdr->rec_type == 0xc0 );
831 printf( "Audio Packet Header " );
832 for( i = 0 ; i < 24 ; i++ )
833 printf( "%2.2x ", p_block_in->p_buffer[i] );
837 if( subrec_type == 2 )
839 /* SA or DTiVo Audio Data, no PES (continued block)
840 * ================================================
843 /* continue PES if previous was incomplete */
844 if (p_sys->i_pes_buf_cnt > 0)
846 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
848 msg_Dbg(p_demux, "continuing PES header");
849 /* do we have enough data to complete? */
850 if (i_need >= l_rec_size)
852 /* don't have complete PES hdr; save what we have and return */
853 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
854 p_block_in->p_buffer, l_rec_size);
855 p_sys->i_pes_buf_cnt += l_rec_size;
857 block_Release(p_block_in);
861 /* we have enough; reconstruct this p_frame with the new hdr */
862 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
863 p_block_in->p_buffer, i_need);
864 /* advance the block past the PES header (don't want to send it) */
865 p_block_in->p_buffer += i_need;
866 p_block_in->i_buffer -= i_need;
867 /* get the PTS out of this PES header (MPEG or AC3) */
868 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
869 esOffset1 = find_es_header(ty_MPEGAudioPacket,
870 p_sys->pes_buffer, 5);
872 esOffset1 = find_es_header(ty_AC3AudioPacket,
873 p_sys->pes_buffer, 5);
876 /* god help us; something's really wrong */
877 msg_Err(p_demux, "can't find audio PES header in packet");
881 p_sys->lastAudioPTS = get_pts(
882 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
883 p_block_in->i_pts = p_sys->lastAudioPTS;
885 p_sys->i_pes_buf_cnt = 0;
887 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
888 * not allowed in the AC3 spec and will cause problems. So here
889 * we try to trim things. */
890 /* Also, S1 DTivo has alternating short / long AC3 packets. That
891 * is, one packet is short (incomplete) and the next packet has
892 * the first one's missing data, plus all of its own. Strange. */
893 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
894 p_sys->tivo_series == TIVO_SERIES2) {
895 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
897 p_block_in->i_buffer -= 2;
898 p_sys->l_ac3_pkt_size = 0;
900 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
904 else if( subrec_type == 0x03 )
906 /* MPEG Audio with PES Header, either SA or DTiVo */
907 /* ================================================ */
908 esOffset1 = find_es_header( ty_MPEGAudioPacket,
909 p_block_in->p_buffer, 5 );
911 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
912 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
913 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
914 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
916 /* SA PES Header, No Audio Data */
917 /* ================================================ */
918 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
920 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
922 if (p_sys->firstAudioPTS < 0)
923 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
925 block_Release(p_block_in);
927 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
928 p_sys->lastAudioPTS );*/
930 /* DTiVo Audio with PES Header */
931 /* ================================================ */
933 /* Check for complete PES */
934 if (check_sync_pes(p_demux, p_block_in, esOffset1,
937 /* partial PES header found, nothing else.
939 block_Release(p_block_in);
943 msg_Dbg(p_demux, "packet buffer has "
944 "%02x %02x %02x %02x %02x %02x %02x %02x "
945 "%02x %02x %02x %02x %02x %02x %02x %02x",
946 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
947 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
948 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
949 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
950 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
951 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
952 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
953 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
956 else if( subrec_type == 0x04 )
958 /* SA Audio with no PES Header */
959 /* ================================================ */
961 "Adding SA Audio Packet Size %ld", l_rec_size ); */
963 if (p_sys->lastAudioPTS > 0)
964 p_block_in->i_pts = p_sys->lastAudioPTS;
966 else if( subrec_type == 0x09 )
968 /* DTiVo AC3 Audio Data with PES Header */
969 /* ================================================ */
970 esOffset1 = find_es_header( ty_AC3AudioPacket,
971 p_block_in->p_buffer, 5 );
974 msg_Dbg(p_demux, "buffer has "
975 "%02x %02x %02x %02x %02x %02x %02x %02x "
976 "%02x %02x %02x %02x %02x %02x %02x %02x",
977 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
978 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
979 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
980 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
981 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
982 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
983 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
984 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
985 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
988 /* Check for complete PES */
989 if (check_sync_pes(p_demux, p_block_in, esOffset1,
992 /* partial PES header found, nothing else. we're done. */
993 block_Release(p_block_in);
996 /* S2 DTivo has invalid long AC3 packets */
997 if (p_sys->tivo_series == TIVO_SERIES2) {
998 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
999 p_block_in->i_buffer -= 2;
1000 p_sys->l_ac3_pkt_size = 0;
1002 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1008 /* Unsupported/Unknown */
1009 block_Release(p_block_in);
1013 /* set PCR before we send (if PTS found) */
1014 if( p_block_in->i_pts > 0 )
1015 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1016 p_block_in->i_pts );
1018 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1022 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1024 demux_sys_t *p_sys = p_demux->p_sys;
1029 block_Release(p_block_in);
1031 if( rec_hdr->rec_type == 0x01 )
1033 else if( rec_hdr->rec_type == 0x02 )
1038 /* XDS data (extract programs infos) transmitted on field 2 only */
1040 DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
1042 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1045 p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
1046 p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
1047 p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
1048 p_sys->cc.i_data += 3;
1050 i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
1051 if( i_channel >= 0 && i_channel < 4 )
1052 p_sys->cc.pb_present[i_channel] = true;
1056 /* seek to a position within the stream, if possible */
1057 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1059 demux_sys_t *p_sys = p_demux->p_sys;
1060 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1064 /* if we're not seekable, there's nothing to do */
1065 if (!p_sys->b_seekable)
1066 return VLC_EGENERIC;
1068 /* figure out which part & chunk we want & go there */
1069 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1070 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1072 /* try to read the part header (master chunk) if it's there */
1073 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1075 /* can't seek stream */
1076 return VLC_EGENERIC;
1078 parse_master(p_demux);
1080 /* now for the actual chunk */
1081 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1083 /* can't seek stream */
1084 return VLC_EGENERIC;
1086 /* load the chunk */
1087 p_sys->i_stuff_cnt = 0;
1088 get_chunk_header(p_demux);
1090 /* seek within the chunk to get roughly to where we want */
1091 p_sys->i_cur_rec = (int)
1092 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1093 msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
1094 msg_Dbg(p_demux, " (chunk %d, record %d)",
1095 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1097 /* seek to the start of this record's data.
1098 * to do that, we have to skip past all prior records */
1100 for (i=0; i<p_sys->i_cur_rec; i++)
1101 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1102 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1103 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1105 /* to hell with syncing any audio or video, just start reading records... :) */
1106 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
1107 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1112 //#define TY_XDS_DEBUG
1113 static void XdsInit( xds_t *h )
1118 h->i_class = XDS_MAX_CLASS_COUNT;
1120 h->b_future = false;
1121 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1123 for( j = 0; j < 128; j++ )
1124 h->pkt[i][j].b_started = false;
1126 h->b_meta_changed = false;
1127 memset( &h->meta, 0, sizeof(h->meta) );
1129 static void XdsExit( xds_t *h )
1132 free( h->meta.psz_channel_name );
1133 free( h->meta.psz_channel_call_letter );
1134 free( h->meta.psz_channel_number );
1137 free( h->meta.current.psz_name );
1138 free( h->meta.current.psz_rating );
1140 free( h->meta.future.psz_name );
1141 free( h->meta.future.psz_rating );
1143 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1148 for( i = 0, i_dst = 0; i < i_src; i++ )
1152 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1153 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1154 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1155 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1156 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1157 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1158 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1159 E2( 0x7c, 0xc3,0xb7); // division symbol
1160 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1161 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1164 dst[i_dst++] = p_src[i];
1168 dst[i_dst++] = '\0';
1170 static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1172 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1174 if( *ppsz_dst == NULL && psz_new == NULL )
1179 *ppsz_dst = strdup( psz_new );
1183 h->b_meta_changed = true;
1187 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1189 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1196 XdsStringUtf8( name, pk->p_data, pk->i_data );
1197 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1199 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1200 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1204 i_rating = (pk->p_data[0] & 0x18);
1205 if( i_rating == 0x08 )
1208 static const char *pppsz_ratings[8][2] = {
1209 { "None", "No rating (no content advisory)" },
1210 { "TV-Y", "All Children (no content advisory)" },
1211 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1212 { "TV-G", "General Audience (no content advisory)" },
1213 { "TV-PG", "Parental Guidance Suggested" },
1214 { "TV-14", "Parents Strongly Cautioned" },
1215 { "TV-MA", "Mature Audience Only" },
1216 { "None", "No rating (no content advisory)" }
1218 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1219 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1221 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1222 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1223 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1226 else if( i_rating == 0x00 || i_rating == 0x10 )
1229 static const char *pppsz_ratings[8][2] = {
1231 { "G", "General Audiences" },
1232 { "PG", "Parental Guidance Suggested" },
1233 { "PG-13", "Parents Strongly Cautioned" },
1234 { "R", "Restricted" },
1235 { "NC-17", "No one 17 and under admitted" },
1236 { "X", "No one under 17 admitted" },
1237 { "NR", "Not Rated" },
1239 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1240 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1242 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1243 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1244 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1249 /* Non US Rating TODO */
1250 assert( i_rating == 0x18 ); // only left value possible */
1251 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1252 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1254 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1255 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1262 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1268 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1276 if( pk->i_data < 2 )
1278 XdsStringUtf8( name, pk->p_data, pk->i_data );
1279 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1281 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1282 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1287 if( pk->i_data < 4 )
1290 XdsStringUtf8( name, pk->p_data, 4 );
1291 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1293 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1294 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1296 if( pk->i_data >= 6 )
1298 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1299 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1301 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1302 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1307 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1309 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1310 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1315 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1318 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1322 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1328 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1330 switch( h->i_class )
1332 case XDS_CLASS_CURRENT:
1333 case XDS_CLASS_FUTURE:
1334 XdsDecodeCurrentFuture( h, pk );
1336 case XDS_CLASS_CHANNEL:
1337 XdsDecodeChannel( h, pk );
1339 case XDS_CLASS_MISCELLANEOUS:
1341 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1344 case XDS_CLASS_PUBLIC_SERVICE:
1346 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1350 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1355 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1357 /* TODO check parity */
1362 if( d1 >= 0x01 && d1 <= 0x0e )
1364 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1365 const int i_type = d2;
1366 const bool b_start = d1 & 0x01;
1367 xds_packet_t *pk = &h->pkt[i_class][i_type];
1369 if( !b_start && !pk->b_started )
1371 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1377 h->i_class = i_class;
1379 h->b_future = !b_start;
1380 pk->b_started = true;
1384 pk->i_sum = d1 + d2;
1387 else if( d1 == 0x0f && h->b_xds )
1389 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1391 /* TODO checksum and decode */
1392 pk->i_sum += d1 + d2;
1393 if( pk->i_sum & 0x7f )
1395 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1396 pk->b_started = false;
1399 if( pk->i_data <= 0 )
1401 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1402 pk->b_started = false;
1406 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1411 pk->b_started = false;
1413 else if( d1 >= 0x20 && h->b_xds )
1415 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1417 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1419 /* Broken -> reinit */
1420 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1422 pk->b_started = false;
1425 /* TODO check parity bit */
1426 pk->p_data[pk->i_data++] = d1 & 0x7f;
1427 pk->p_data[pk->i_data++] = d2 & 0x7f;
1436 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1438 demux_sys_t *p_sys = p_demux->p_sys;
1440 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1441 if( p_demux->p_sys->xds.b_meta_changed )
1443 xds_meta_t *m = &p_sys->xds.meta;
1447 /* Channel meta data */
1448 p_meta = vlc_meta_New();
1449 if( m->psz_channel_name )
1450 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1451 if( m->psz_channel_call_letter )
1452 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1453 if( m->psz_channel_number )
1454 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1455 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1456 vlc_meta_Delete( p_meta );
1458 /* Event meta data (current/future) */
1459 p_epg = vlc_epg_New( NULL );
1460 if( m->current.psz_name )
1462 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1463 //if( m->current.psz_rating )
1464 // TODO but VLC cannot yet handle rating per epg event
1465 vlc_epg_SetCurrent( p_epg, 0 );
1467 if( m->future.psz_name )
1470 if( p_epg->i_event > 0 )
1471 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1472 vlc_epg_Delete( p_epg );
1474 p_demux->p_sys->xds.b_meta_changed = false;
1477 /* seek to an exact time position within the stream, if possible.
1478 * l_seek_time is in nanoseconds, the TIVO time standard.
1480 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1482 demux_sys_t *p_sys = p_demux->p_sys;
1483 int i, i_seq_entry = 0;
1485 long l_cur_pos = stream_Tell(p_demux->s);
1486 int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1487 long l_seek_secs = l_seek_time / 1000000000;
1488 uint64_t l_fwd_stamp = 1;
1490 /* if we're not seekable, there's nothing to do */
1491 if (!p_sys->b_seekable || !p_sys->b_have_master)
1492 return VLC_EGENERIC;
1494 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1495 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1497 /* seek to the proper segment if necessary */
1498 /* first see if we need to go back */
1499 while (l_seek_time < p_sys->l_first_ty_pts) {
1500 msg_Dbg(p_demux, "skipping to prior segment.");
1501 /* load previous part */
1502 if (i_cur_part == 0) {
1503 stream_Seek(p_demux->s, l_cur_pos);
1504 msg_Err(p_demux, "Attempt to seek past BOF");
1505 return VLC_EGENERIC;
1507 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1509 parse_master(p_demux);
1511 /* maybe we need to go forward */
1512 while (l_seek_time > p_sys->l_final_ty_pts) {
1513 msg_Dbg(p_demux, "skipping to next segment.");
1514 /* load next part */
1515 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1516 /* error; restore previous file position */
1517 stream_Seek(p_demux->s, l_cur_pos);
1518 msg_Err(p_demux, "seek error");
1519 return VLC_EGENERIC;
1521 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1523 parse_master(p_demux);
1526 /* our target is somewhere within this part;
1527 find the proper chunk using seq_table */
1528 for (i=1; i<p_sys->i_seq_table_size; i++) {
1529 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1530 /* i-1 is the section we want; remember the next timestamp in case
1531 we have to use it (this section may not have a proper SEQ hdr
1532 for the time we're seeking) */
1533 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1534 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1540 /* if we went through the entire last loop and didn't find our target,
1541 then we skip to the next part. What has happened is that the actual
1542 time we're seeking is within this part, but there isn't a SEQ hdr
1543 for it here. So we skip to the next part */
1544 if (i == p_sys->i_seq_table_size) {
1545 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1546 /* error; restore previous file position */
1547 stream_Seek(p_demux->s, l_cur_pos);
1548 msg_Err(p_demux, "seek error");
1549 return VLC_EGENERIC;
1551 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1553 parse_master(p_demux);
1557 /* determine which chunk has our seek_time */
1558 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1559 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1560 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1561 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1562 i_cur_part, l_chunk_nr,
1563 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1564 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1565 /* check this chunk's SEQ header timestamp */
1566 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1567 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1568 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1570 // TODO: we don't have to parse the full header set;
1571 // just test the seq_rec entry for its timestamp
1572 p_sys->i_stuff_cnt = 0;
1573 get_chunk_header(p_demux);
1574 // check ty PTS for the SEQ entry in this chunk
1575 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1576 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1577 /* Seek to beginning of original chunk & reload it */
1578 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1579 p_sys->i_stuff_cnt = 0;
1580 get_chunk_header(p_demux);
1581 return VLC_EGENERIC;
1583 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1585 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1587 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1588 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1589 // keep this one? go back?
1590 /* for now, we take this one. it's the first SEQ hdr AFTER
1591 the time we were searching for. */
1592 msg_Dbg(p_demux, "seek target found.");
1595 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1598 /* if we made it through this entire loop without finding our target,
1599 then we skip to the next section. What has happened is that the actual
1600 time we're seeking is within this section, but there isn't a SEQ hdr
1601 for it here. So we skip to the next closest one (l_fwd_stamp) */
1602 if (i == p_sys->i_bits_per_seq_entry)
1603 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1605 /* current stream ptr is at beginning of data for this chunk,
1606 so we need to skip past any stream data prior to the seq_rec
1609 for (i=0; i<p_sys->i_seq_rec; i++)
1610 i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
1611 stream_Read(p_demux->s, NULL, i_skip_cnt);
1612 p_sys->i_cur_rec = p_sys->i_seq_rec;
1613 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1614 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1620 /* parse a master chunk, filling the SEQ table and other variables.
1621 * We assume the stream is currently pointing to it.
1623 static void parse_master(demux_t *p_demux)
1625 demux_sys_t *p_sys = p_demux->p_sys;
1626 uint8_t mst_buf[32];
1628 int64_t i_save_pos = stream_Tell(p_demux->s);
1631 /* Note that the entries in the SEQ table in the stream may have
1632 different sizes depending on the bits per entry. We store them
1633 all in the same size structure, so we have to parse them out one
1634 by one. If we had a dynamic structure, we could simply read the
1635 entire table directly from the stream into memory in place. */
1637 /* clear the SEQ table */
1638 free(p_sys->seq_table);
1640 /* parse header info */
1641 stream_Read(p_demux->s, mst_buf, 32);
1642 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1643 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1644 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1645 p_sys->i_seq_table_size = i / (8 + i_map_size);
1647 /* parse all the entries */
1648 p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
1649 for (i=0; i<p_sys->i_seq_table_size; i++) {
1650 stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
1651 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1652 if (i_map_size > 8) {
1653 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1654 memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
1656 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1660 /* set up a few of our variables */
1661 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1662 p_sys->l_final_ty_pts =
1663 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1664 p_sys->b_have_master = true;
1666 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1667 msg_Dbg( p_demux, "first 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) );
1669 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1670 msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
1671 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1673 /* seek past this chunk */
1674 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1678 /* ======================================================================== */
1679 /* "Peek" at some chunks. Skip over the Part header if we find it.
1680 * We parse the peeked data and determine audio type,
1681 * SA vs. DTivo, & Tivo Series.
1682 * Set global vars i_Pes_Length, i_Pts_Offset,
1683 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1684 static int probe_stream(demux_t *p_demux)
1686 demux_sys_t *p_sys = p_demux->p_sys;
1687 const uint8_t *p_buf;
1689 bool b_probe_error = false;
1691 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1692 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1693 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1694 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1695 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1696 return VLC_EGENERIC;
1699 /* the real work: analyze this chunk */
1700 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1701 analyze_chunk(p_demux, p_buf);
1702 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1703 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1704 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1706 p_buf += CHUNK_SIZE;
1709 /* the final tally */
1710 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1711 msg_Err(p_demux, "Can't determine Tivo Series.");
1712 b_probe_error = true;
1714 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1715 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1716 b_probe_error = true;
1718 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1719 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1720 b_probe_error = true;
1722 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1726 /* ======================================================================== */
1727 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1728 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1730 demux_sys_t *p_sys = p_demux->p_sys;
1732 ty_rec_hdr_t *p_hdrs;
1733 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1734 uint32_t i_payload_size;
1736 /* skip if it's a Part header */
1737 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1740 /* number of records in chunk (we ignore high order byte;
1741 * rarely are there > 256 chunks & we don't need that many anyway) */
1742 i_num_recs = p_chunk[0];
1743 if (i_num_recs < 5) {
1744 /* try again with the next chunk. Sometimes there are dead ones */
1748 p_chunk += 4; /* skip past rec count & SEQ bytes */
1749 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1750 p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
1752 * 1. check video packets. Presence of 0x6e0 means S1.
1753 * No 6e0 but have be0 means S2.
1754 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1755 * If AC-3, then we have DTivo.
1756 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1758 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1759 for (i=0; i<i_num_recs; i++) {
1760 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1761 //p_hdrs[i].rec_type,
1762 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1763 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1778 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1779 i_num_6e0, i_num_be0);
1781 /* set up our variables */
1782 if (i_num_6e0 > 0) {
1783 msg_Dbg(p_demux, "detected Series 1 Tivo");
1784 p_sys->tivo_series = TIVO_SERIES1;
1785 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1786 } else if (i_num_be0 > 0) {
1787 msg_Dbg(p_demux, "detected Series 2 Tivo");
1788 p_sys->tivo_series = TIVO_SERIES2;
1789 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1791 if (i_num_9c0 > 0) {
1792 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1793 p_sys->audio_type = TIVO_AUDIO_AC3;
1794 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1795 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1796 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1797 } else if (i_num_3c0 > 0) {
1798 p_sys->audio_type = TIVO_AUDIO_MPEG;
1799 msg_Dbg(p_demux, "detected MPEG Audio" );
1802 /* if tivo_type still unknown, we can check PTS location
1803 * in MPEG packets to determine tivo_type */
1804 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1805 uint32_t i_data_offset = (16 * i_num_recs);
1806 for (i=0; i<i_num_recs; i++) {
1807 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1808 p_hdrs[i].l_rec_size > 15) {
1809 /* first make sure we're aligned */
1810 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1811 &p_chunk[i_data_offset], 5);
1812 if (i_pes_offset >= 0) {
1813 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1814 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1816 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1817 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1818 if (p_sys->tivo_series == TIVO_SERIES1)
1819 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1820 p_sys->tivo_type = TIVO_TYPE_SA;
1821 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1823 if (p_sys->tivo_series == TIVO_SERIES1)
1824 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1825 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1826 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1831 i_data_offset += p_hdrs[i].l_rec_size;
1838 /* =========================================================================== */
1839 static int get_chunk_header(demux_t *p_demux)
1841 int i_readSize, i_num_recs;
1843 const uint8_t *p_peek;
1844 demux_sys_t *p_sys = p_demux->p_sys;
1845 int i_payload_size; /* sum of all records' sizes */
1847 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1849 /* if we have left-over filler space from the last chunk, get that */
1850 if (p_sys->i_stuff_cnt > 0) {
1851 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1852 p_sys->i_stuff_cnt = 0;
1855 /* read the TY packet header */
1856 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1857 p_sys->i_cur_chunk++;
1859 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1866 /* check if it's a PART Header */
1867 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1869 /* parse master chunk */
1870 parse_master(p_demux);
1871 return get_chunk_header(p_demux);
1874 /* number of records in chunk (8- or 16-bit number) */
1875 if (p_peek[3] & 0x80)
1877 /* 16 bit rec cnt */
1878 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1879 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1880 if (p_sys->i_seq_rec != 0xffff)
1882 p_sys->i_seq_rec &= ~0x8000;
1887 /* 8 bit reclen - tivo 1.3 format */
1888 p_sys->i_num_recs = i_num_recs = p_peek[0];
1889 p_sys->i_seq_rec = p_peek[1];
1891 p_sys->i_cur_rec = 0;
1892 p_sys->b_first_chunk = false;
1894 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1896 free(p_sys->rec_hdrs);
1898 /* skip past the 4 bytes we "peeked" earlier */
1899 stream_Read( p_demux->s, NULL, 4 );
1901 /* read the record headers into a temp buffer */
1902 p_hdr_buf = malloc(i_num_recs * 16);
1903 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1909 p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
1913 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1914 (p_sys->i_num_recs * 16) - i_payload_size;
1915 if (p_sys->i_stuff_cnt > 0)
1916 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1917 p_sys->i_stuff_cnt );
1922 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
1923 int i_num_recs, int *pi_payload_size)
1926 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1928 *pi_payload_size = 0;
1929 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1931 for (i = 0; i < i_num_recs; i++)
1933 const uint8_t *record_header = p_buf + (i * 16);
1934 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1935 p_rec_hdr->rec_type = record_header[3];
1936 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1937 if ((record_header[ 0 ] & 0x80) == 0x80)
1940 /* marker bit 2 set, so read extended data */
1941 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1942 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1944 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1945 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1948 p_rec_hdr->ex1 = b1;
1949 p_rec_hdr->ex2 = b2;
1950 p_rec_hdr->l_rec_size = 0;
1951 p_rec_hdr->l_ty_pts = 0;
1952 p_rec_hdr->b_ext = true;
1956 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1957 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1958 *pi_payload_size += p_rec_hdr->l_rec_size;
1959 p_rec_hdr->b_ext = false;
1960 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1962 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1963 } /* end of record-header loop */