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 *****************************************************************************/
39 #include <vlc_demux.h>
40 #include "vlc_codec.h"
42 #include "vlc_input.h"
43 #include "../codec/cc.h"
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t * );
49 static void Close( vlc_object_t * );
52 set_shortname( _("TY") );
53 set_description(_("TY Stream audio/video demux"));
54 set_category( CAT_INPUT );
55 set_subcategory( SUBCAT_INPUT_DEMUX );
56 set_capability("demux2", 6);
57 /* FIXME: there seems to be a segfault when using PVR access
58 * and TY demux has a bigger priority than PS
59 * Something must be wrong.
61 set_callbacks( Open, Close );
66 /*****************************************************************************
68 *****************************************************************************/
69 static int Demux ( demux_t * );
70 static int Control( demux_t *, int, va_list );
72 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
73 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
74 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
75 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
76 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
77 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
78 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
79 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
80 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
81 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
82 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
83 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
85 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
87 /* packet types for reference:
88 2/c0: audio data continued
89 3/c0: audio packet header (PES header)
90 4/c0: audio data (S/A only?)
91 9/c0: audio packet header, AC-3 audio
92 2/e0: video data continued
93 6/e0: video packet header (PES header)
94 7/e0: video sequence header start
95 8/e0: video I-frame header start
96 a/e0: video P-frame header start
97 b/e0: video B-frame header start
98 c/e0: video GOP header start
99 e/01: closed-caption data
100 e/02: Extended data services data
101 e/03: ipreview data ("thumbs up to record" signal)
105 #define TIVO_PES_FILEID ( 0xf5467abd )
106 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
107 #define CHUNK_SIZE ( 128 * 1024 )
116 uint64_t l_ty_pts; /* TY PTS in the record header */
121 uint64_t l_timestamp;
122 uint8_t chunk_bitmask[8];
146 #define XDS_MAX_DATA_SIZE (32)
149 XDS_CLASS_CURRENT = 0,
150 XDS_CLASS_FUTURE = 1,
151 XDS_CLASS_CHANNEL = 2,
152 XDS_CLASS_MISCELLANEOUS = 3,
153 XDS_CLASS_PUBLIC_SERVICE = 4,
154 XDS_CLASS_RESERVED = 5,
155 XDS_CLASS_UNDEFINED = 6,
162 vlc_bool_t b_started;
164 uint8_t p_data[XDS_MAX_DATA_SIZE];
169 XDS_META_PROGRAM_RATING_NONE,
170 XDS_META_PROGRAM_RATING_MPAA,
171 XDS_META_PROGRAM_RATING_TPG,
172 /* TODO add CA/CE rating */
173 } xds_meta_program_rating_t;
177 xds_meta_program_rating_t rating;
179 /* Add the other fields once I have the samples */
180 } xds_meta_program_t;
183 char *psz_channel_name;
184 char *psz_channel_call_letter;
185 char *psz_channel_number;
187 xds_meta_program_t current;
188 xds_meta_program_t future;
192 /* Are we in XDS mode */
195 /* Current class type */
201 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
204 vlc_bool_t b_meta_changed;
211 es_out_id_t *p_video; /* ptr to video codec */
212 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
215 es_out_id_t *p_cc[4];
221 size_t i_stream_size; /* size of input stream (if known) */
222 //uint64_t l_program_len; /* length of this stream in msec */
223 vlc_bool_t b_seekable; /* is this stream seekable? */
224 vlc_bool_t b_have_master; /* are master chunks present? */
225 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
226 tivo_series_t tivo_series; /* Series1 or Series2 */
227 tivo_audio_t audio_type; /* AC3 or MPEG */
228 int i_Pes_Length; /* Length of Audio PES header */
229 int i_Pts_Offset; /* offset into audio PES of PTS */
230 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
231 int i_pes_buf_cnt; /* how many bytes in our buffer */
232 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
233 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
234 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
235 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
236 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
237 int i_seq_table_size; /* number of entries in SEQ table */
238 int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
240 mtime_t firstAudioPTS;
241 mtime_t lastAudioPTS;
242 mtime_t lastVideoPTS;
244 ty_rec_hdr_t *rec_hdrs; /* record headers array */
245 int i_cur_rec; /* current record in this chunk */
246 int i_num_recs; /* number of recs in this chunk */
247 int i_seq_rec; /* record number where seq start is */
248 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
250 vlc_bool_t b_first_chunk;
253 static int get_chunk_header(demux_t *);
254 static mtime_t get_pts( const uint8_t *buf );
255 static int find_es_header( const uint8_t *header,
256 const uint8_t *buffer, int i_search_len );
257 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
258 static int ty_stream_seek_time(demux_t *, uint64_t);
260 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
261 int i_num_recs, int *pi_payload_size);
262 static int probe_stream(demux_t *p_demux);
263 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
264 static void parse_master(demux_t *p_demux);
266 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
267 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
268 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
270 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
272 static void XdsInit( xds_t * );
273 static void XdsExit( xds_t * );
275 #define TY_ES_GROUP (1)
278 * Open: check file and initialize demux structures
281 * 1. peek at the first 12 bytes of the stream for the
282 * magic TiVo PART header & stream type & chunk size
283 * 2. if it's not there, error with VLC_EGENERIC
284 * 3. set up video (mpgv) codec
285 * 4. return VLC_SUCCESS
287 static int Open(vlc_object_t *p_this)
289 demux_t *p_demux = (demux_t *)p_this;
292 const uint8_t *p_peek;
295 /* peek at the first 12 bytes. */
296 /* for TY streams, they're always the same */
297 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
300 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
301 U32_AT(&p_peek[4]) != 0x02 ||
302 U32_AT(&p_peek[8]) != CHUNK_SIZE )
304 if( !p_demux->b_force &&
305 !demux2_IsPathExtension( p_demux, ".ty" ) &&
306 !demux2_IsPathExtension( p_demux, ".ty+" ) )
308 msg_Warn( p_demux, "this does not look like a TY file, "
309 "continuing anyway..." );
312 /* at this point, we assume we have a valid TY stream */
313 msg_Dbg( p_demux, "valid TY stream detected" );
315 /* Set exported functions */
316 p_demux->pf_demux = Demux;
317 p_demux->pf_control = Control;
319 /* create our structure that will hold all data */
320 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
321 memset(p_sys, 0, sizeof(demux_sys_t));
323 /* set up our struct (most were zero'd out with the memset above) */
324 p_sys->b_first_chunk = VLC_TRUE;
325 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
326 p_sys->firstAudioPTS = -1;
327 p_sys->i_stream_size = stream_Size(p_demux->s);
328 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
329 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
330 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
331 p_sys->i_Pes_Length = 0;
332 p_sys->i_Pts_Offset = 0;
333 p_sys->l_ac3_pkt_size = 0;
335 /* see if this stream is seekable */
336 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
338 if (probe_stream(p_demux) != VLC_SUCCESS) {
343 if (!p_sys->b_have_master)
344 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
346 /* register the proper audio codec */
347 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
348 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
350 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
352 fmt.i_group = TY_ES_GROUP;
353 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
355 /* register the video stream */
356 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
357 fmt.i_group = TY_ES_GROUP;
358 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
361 for( i = 0; i < 4; i++ )
362 p_sys->p_cc[i] = NULL;
363 cc_Init( &p_sys->cc );
365 XdsInit( &p_sys->xds );
370 /* =========================================================================== */
371 /* Demux: Read & Demux one record from the chunk
373 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
375 * NOTE: I think we can return the number of packets sent instead of just 1.
376 * that means we can demux an entire chunk and shoot it back (may be more efficient)
377 * -- should try that some day :) --
379 static int Demux( demux_t *p_demux )
381 demux_sys_t *p_sys = p_demux->p_sys;
383 block_t *p_block_in = NULL;
385 /*msg_Dbg(p_demux, "ty demux processing" );*/
387 /* did we hit EOF earlier? */
392 * what we do (1 record now.. maybe more later):
393 * - use stream_Read() to read the chunk header & record headers
394 * - discard entire chunk if it is a PART header chunk
395 * - parse all the headers into record header array
396 * - keep a pointer of which record we're on
397 * - use stream_Block() to fetch each record
398 * - parse out PTS from PES headers
399 * - set PTS for data packets
400 * - pass the data on to the proper codec via es_out_Send()
402 * if this is the first time or
403 * if we're at the end of this chunk, start a new one
405 /* parse the next chunk's record headers */
406 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
408 if( get_chunk_header(p_demux) == 0 )
412 /*======================================================================
413 * parse & send one record of the chunk
414 *====================================================================== */
415 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
419 const long l_rec_size = p_rec->l_rec_size;
420 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
421 subrec_type, p_rec->rec_type, l_rec_size );*/
423 /* some normal records are 0 length, so check for that... */
424 if( l_rec_size <= 0 )
426 /* no data in payload; we're done */
431 /* read in this record's payload */
432 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
435 /* set these as 'unknown' for now */
437 p_block_in->i_dts = 0;
441 -- don't read any data from the stream, data was in the record header --
443 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
444 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
447 if( p_rec->rec_type == 0xe0 )
450 DemuxRecVideo( p_demux, p_rec, p_block_in );
452 else if ( p_rec->rec_type == 0xc0 )
455 DemuxRecAudio( p_demux, p_rec, p_block_in );
457 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
459 /* Closed Captions/XDS */
460 DemuxRecCc( p_demux, p_rec, p_block_in );
462 else if ( p_rec->rec_type == 0x03 )
464 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
466 block_Release(p_block_in);
468 else if ( p_rec->rec_type == 0x05 )
470 /* Unknown, but seen regularly */
472 block_Release(p_block_in);
476 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
478 block_Release(p_block_in);
487 static int Control(demux_t *p_demux, int i_query, va_list args)
489 demux_sys_t *p_sys = p_demux->p_sys;
493 /*msg_Info(p_demux, "control cmd %d", i_query);*/
496 case DEMUX_GET_POSITION:
497 /* arg is 0.0 - 1.0 percent of overall file position */
498 if( ( i64 = p_sys->i_stream_size ) > 0 )
500 pf = (double*) va_arg( args, double* );
501 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
506 case DEMUX_SET_POSITION:
507 /* arg is 0.0 - 1.0 percent of overall file position */
508 f = (double) va_arg( args, double );
509 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
510 if ((i64 = p_sys->i_stream_size) > 0)
511 return ty_stream_seek_pct(p_demux, f);
514 /* return TiVo timestamp */
515 p_i64 = (int64_t *) va_arg(args, int64_t *);
516 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
517 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
518 // p_sys->l_last_ty_pts_sync);
519 *p_i64 = (p_sys->l_last_ty_pts / 1000);
521 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
523 p_i64 = (int64_t *) va_arg(args, int64_t *);
526 case DEMUX_SET_TIME: /* arg is time in microsecs */
527 i64 = (int64_t) va_arg( args, int64_t );
528 return ty_stream_seek_time(p_demux, i64 * 1000);
536 static void Close( vlc_object_t *p_this )
538 demux_t *p_demux = (demux_t*)p_this;
539 demux_sys_t *p_sys = p_demux->p_sys;
541 XdsExit( &p_sys->xds );
542 cc_Exit( &p_sys->cc );
543 free( p_sys->rec_hdrs );
544 if( p_sys->seq_table )
545 free( p_sys->seq_table );
550 /* =========================================================================== */
551 /* Compute Presentation Time Stamp (PTS)
552 * Assume buf points to beginning of PTS */
553 static mtime_t get_pts( const uint8_t *buf )
557 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
558 (mtime_t)(buf[1] << 22)|
559 ((mtime_t)(buf[2]&0xfe) << 14)|
560 (mtime_t)(buf[3] << 7)|
561 (mtime_t)(buf[4] >> 1);
562 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
567 /* =========================================================================== */
568 static int find_es_header( const uint8_t *header,
569 const uint8_t *buffer, int i_search_len )
573 for( count = 0; count < i_search_len; count++ )
575 if( !memcmp( &buffer[count], header, 4 ) )
582 /* =========================================================================== */
583 /* check if we have a full PES header, if not, then save what we have.
584 * this is called when audio-start packets are encountered.
586 * 1 partial PES hdr found, some audio data found (buffer adjusted),
587 * -1 partial PES hdr found, no audio data found
588 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
589 /* TODO: HD support -- nothing known about those streams */
590 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
591 int32_t offset, int32_t rec_len )
593 demux_sys_t *p_sys = p_demux->p_sys;
595 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
597 /* entire PES header not present */
598 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
600 /* save the partial pes header */
603 /* no header found, fake some 00's (this works, believe me) */
604 memset( p_sys->pes_buffer, 4, 0 );
605 p_sys->i_pes_buf_cnt = 4;
607 msg_Err( p_demux, "PES header not found in record of %d bytes!",
611 /* copy the partial pes header we found */
612 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
614 p_sys->i_pes_buf_cnt = rec_len - offset;
618 /* PES Header was found, but not complete, so trim the end of this record */
619 p_block->i_buffer -= rec_len - offset;
622 return -1; /* partial PES, no audio data */
624 /* full PES header present, extract PTS */
625 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
626 p_sys->i_Pts_Offset ] );
627 if (p_sys->firstAudioPTS < 0)
628 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
629 p_block->i_pts = p_sys->lastAudioPTS;
630 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
631 /* adjust audio record to remove PES header */
632 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
633 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
634 p_block->i_buffer -= p_sys->i_Pes_Length;
636 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
637 "%02x %02x %02x %02x %02x %02x %02x %02x "
638 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
639 p_block->p_buffer[0], p_block->p_buffer[1],
640 p_block->p_buffer[2], p_block->p_buffer[3],
641 p_block->p_buffer[4], p_block->p_buffer[5],
642 p_block->p_buffer[6], p_block->p_buffer[7],
643 p_block->p_buffer[8], p_block->p_buffer[9],
644 p_block->p_buffer[10], p_block->p_buffer[11],
645 p_block->p_buffer[12], p_block->p_buffer[13],
646 p_block->p_buffer[14], p_block->p_buffer[15]);
651 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
653 demux_sys_t *p_sys = p_demux->p_sys;
654 const int subrec_type = rec_hdr->subrec_type;
655 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
659 assert( rec_hdr->rec_type == 0xe0 );
664 msg_Dbg(p_demux, "packet buffer has "
665 "%02x %02x %02x %02x %02x %02x %02x %02x "
666 "%02x %02x %02x %02x %02x %02x %02x %02x",
667 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
668 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
669 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
670 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
671 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
672 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
673 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
674 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
676 //if( subrec_type == 0x06 || subrec_type == 0x07 )
677 if( subrec_type != 0x02 && subrec_type != 0x0c &&
678 subrec_type != 0x08 && l_rec_size > 4 )
680 /* get the PTS from this packet if it has one.
681 * on S1, only 0x06 has PES. On S2, however, most all do.
682 * Do NOT Pass the PES Header to the MPEG2 codec */
683 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
684 if( esOffset1 != -1 )
686 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
687 //subrec_type, esOffset1);
688 p_sys->lastVideoPTS = get_pts(
689 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
690 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
691 p_sys->lastVideoPTS );*/
692 if (subrec_type != 0x06) {
693 /* if we found a PES, and it's not type 6, then we're S2 */
694 /* The packet will have video data (& other headers) so we
695 * chop out the PES header and send the rest */
696 if (l_rec_size >= VIDEO_PES_LENGTH) {
697 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
698 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
700 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
701 " (%ld bytes)", subrec_type, l_rec_size);
702 /* nuke this block; it's too short, but has PES marker */
703 p_block_in->i_buffer = 0;
707 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
711 if(subrec_type == 0x06 )
713 /* type 6 (S1 DTivo) has no data, so we're done */
714 block_Release(p_block_in);
718 /* if it's not a continue blk, then set PTS */
719 if( subrec_type != 0x02 )
721 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
723 /* if it's a GOP header, make sure it's legal
724 * (if we have enough data) */
725 /* Some ty files don't have this bit set
726 * and it causes problems */
727 if (subrec_type == 0x0c && l_rec_size >= 6)
728 p_block_in->p_buffer[5] |= 0x08;
729 /* store the TY PTS if there is one */
730 if (subrec_type == 0x07) {
731 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
732 /* should we use audio or video PTS? */
733 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
735 /* yes I know this is a cheap hack. It's the timestamp
736 used for display and skipping fwd/back, so it
737 doesn't have to be accurate to the millisecond.
738 I adjust it here by roughly one 1/30 sec. Yes it
739 will be slightly off for UK streams, but it's OK.
741 p_sys->l_last_ty_pts += 35000000;
742 //p_sys->l_last_ty_pts += 33366667;
744 /* set PTS for this block before we send */
745 if (p_sys->lastVideoPTS > 0)
747 p_block_in->i_pts = p_sys->lastVideoPTS;
748 /* PTS gets used ONCE.
749 * Any subsequent frames we get BEFORE next PES
750 * header will have their PTS computed in the codec */
751 p_sys->lastVideoPTS = 0;
755 /* Register the CC decoders when needed */
756 for( i = 0; i < 4; i++ )
758 static const vlc_fourcc_t fcc[4] = {
759 VLC_FOURCC('c', 'c', '1', ' '),
760 VLC_FOURCC('c', 'c', '2', ' '),
761 VLC_FOURCC('c', 'c', '3', ' '),
762 VLC_FOURCC('c', 'c', '4', ' ')
764 static const char *ppsz_description[4] = {
765 N_("Closed captions 1"),
766 N_("Closed captions 2"),
767 N_("Closed captions 3"),
768 N_("Closed captions 4"),
773 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
776 es_format_Init( &fmt, SPU_ES, fcc[i] );
777 fmt.psz_description = strdup( _(ppsz_description[i]) );
778 fmt.i_group = TY_ES_GROUP;
779 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
780 es_format_Clean( &fmt );
783 /* Send the CC data */
784 if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
788 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
789 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
790 p_cc->i_pts = p_block_in->i_pts;
791 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
793 for( i = 0, i_cc_count = 0; i < 4; i++ )
794 i_cc_count += p_sys->p_cc[i] ? 1 : 0;
796 for( i = 0; i < 4; i++ )
798 if( !p_sys->p_cc[i] )
801 es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
803 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 printf( "Audio Packet Header " );
826 for( i = 0 ; i < 24 ; i++ )
827 printf( "%2.2x ", p_block_in->p_buffer[i] );
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 = 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 = get_pts( &p_block_in->p_buffer[
916 if (p_sys->firstAudioPTS < 0)
917 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
919 block_Release(p_block_in);
921 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
922 p_sys->lastAudioPTS );*/
924 /* DTiVo Audio with PES Header */
925 /* ================================================ */
927 /* Check for complete PES */
928 if (check_sync_pes(p_demux, p_block_in, esOffset1,
931 /* partial PES header found, nothing else.
933 block_Release(p_block_in);
937 msg_Dbg(p_demux, "packet buffer has "
938 "%02x %02x %02x %02x %02x %02x %02x %02x "
939 "%02x %02x %02x %02x %02x %02x %02x %02x",
940 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
941 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
942 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
943 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
944 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
945 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
946 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
947 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
950 else if( subrec_type == 0x04 )
952 /* SA Audio with no PES Header */
953 /* ================================================ */
955 "Adding SA Audio Packet Size %ld", l_rec_size ); */
957 if (p_sys->lastAudioPTS > 0)
958 p_block_in->i_pts = p_sys->lastAudioPTS;
960 else if( subrec_type == 0x09 )
962 /* DTiVo AC3 Audio Data with PES Header */
963 /* ================================================ */
964 esOffset1 = find_es_header( ty_AC3AudioPacket,
965 p_block_in->p_buffer, 5 );
968 msg_Dbg(p_demux, "buffer has "
969 "%02x %02x %02x %02x %02x %02x %02x %02x "
970 "%02x %02x %02x %02x %02x %02x %02x %02x",
971 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
972 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
973 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
974 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
975 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
976 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
977 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
978 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
979 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
982 /* Check for complete PES */
983 if (check_sync_pes(p_demux, p_block_in, esOffset1,
986 /* partial PES header found, nothing else. we're done. */
987 block_Release(p_block_in);
990 /* S2 DTivo has invalid long AC3 packets */
991 if (p_sys->tivo_series == TIVO_SERIES2) {
992 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
993 p_block_in->i_buffer -= 2;
994 p_sys->l_ac3_pkt_size = 0;
996 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1002 /* Unsupported/Unknown */
1003 block_Release(p_block_in);
1007 /* set PCR before we send (if PTS found) */
1008 if( p_block_in->i_pts > 0 )
1009 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1010 p_block_in->i_pts );
1012 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1016 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1018 demux_sys_t *p_sys = p_demux->p_sys;
1023 block_Release(p_block_in);
1025 if( rec_hdr->rec_type == 0x01 )
1027 else if( rec_hdr->rec_type == 0x02 )
1032 /* XDS data (extract programs infos) transmitted on field 2 only */
1034 DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
1036 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1039 p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
1040 p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
1041 p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
1042 p_sys->cc.i_data += 3;
1044 i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
1045 if( i_channel >= 0 && i_channel < 4 )
1046 p_sys->cc.pb_present[i_channel] = VLC_TRUE;
1050 /* seek to a position within the stream, if possible */
1051 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1053 demux_sys_t *p_sys = p_demux->p_sys;
1054 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1058 /* if we're not seekable, there's nothing to do */
1059 if (!p_sys->b_seekable)
1060 return VLC_EGENERIC;
1062 /* figure out which part & chunk we want & go there */
1063 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1064 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1066 /* try to read the part header (master chunk) if it's there */
1067 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1069 /* can't seek stream */
1070 return VLC_EGENERIC;
1072 parse_master(p_demux);
1074 /* now for the actual chunk */
1075 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1077 /* can't seek stream */
1078 return VLC_EGENERIC;
1080 /* load the chunk */
1081 p_sys->i_stuff_cnt = 0;
1082 get_chunk_header(p_demux);
1084 /* seek within the chunk to get roughly to where we want */
1085 p_sys->i_cur_rec = (int)
1086 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1087 msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
1088 msg_Dbg(p_demux, " (chunk %d, record %d)",
1089 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1091 /* seek to the start of this record's data.
1092 * to do that, we have to skip past all prior records */
1094 for (i=0; i<p_sys->i_cur_rec; i++)
1095 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1096 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1097 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1099 /* to hell with syncing any audio or video, just start reading records... :) */
1100 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
1101 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1106 //#define TY_XDS_DEBUG
1107 static void XdsInit( xds_t *h )
1111 h->b_xds = VLC_FALSE;
1112 h->i_class = XDS_MAX_CLASS_COUNT;
1114 h->b_future = VLC_FALSE;
1115 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1117 for( j = 0; j < 128; j++ )
1118 h->pkt[i][j].b_started = VLC_FALSE;
1120 h->b_meta_changed = VLC_FALSE;
1121 memset( &h->meta, 0, sizeof(h->meta) );
1123 static void XdsExit( xds_t *h )
1126 if( h->meta.psz_channel_name )
1127 free( h->meta.psz_channel_name );
1128 if( h->meta.psz_channel_call_letter )
1129 free( h->meta.psz_channel_call_letter );
1130 if( h->meta.psz_channel_number )
1131 free( h->meta.psz_channel_number );
1134 if( h->meta.current.psz_name )
1135 free( h->meta.current.psz_name );
1136 if( h->meta.current.psz_rating )
1137 free( h->meta.current.psz_rating );
1139 if( h->meta.future.psz_name )
1140 free( h->meta.future.psz_name );
1141 if( h->meta.future.psz_rating )
1142 free( h->meta.future.psz_rating );
1144 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1149 for( i = 0, i_dst = 0; i < i_src; i++ )
1153 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1154 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1155 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1156 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1157 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1158 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1159 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1160 E2( 0x7c, 0xc3,0xb7); // division symbol
1161 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1162 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1165 dst[i_dst++] = p_src[i];
1169 dst[i_dst++] = '\0';
1171 static vlc_bool_t XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1173 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1175 if( *ppsz_dst == NULL && psz_new == NULL )
1181 *ppsz_dst = strdup( psz_new );
1185 h->b_meta_changed = VLC_TRUE;
1189 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1191 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1198 XdsStringUtf8( name, pk->p_data, pk->i_data );
1199 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1201 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1202 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1206 i_rating = (pk->p_data[0] & 0x18);
1207 if( i_rating == 0x08 )
1210 static const char *pppsz_ratings[8][2] = {
1211 { "None", "No rating (no content advisory)" },
1212 { "TV-Y", "All Children (no content advisory)" },
1213 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1214 { "TV-G", "General Audience (no content advisory)" },
1215 { "TV-PG", "Parental Guidance Suggested" },
1216 { "TV-14", "Parents Strongly Cautioned" },
1217 { "TV-MA", "Mature Audience Only" },
1218 { "None", "No rating (no content advisory)" }
1220 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1221 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1223 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1224 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1225 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1228 else if( i_rating == 0x00 || i_rating == 0x10 )
1231 static const char *pppsz_ratings[8][2] = {
1233 { "G", "General Audiences" },
1234 { "PG", "Parental Guidance Suggested" },
1235 { "PG-13", "Parents Strongly Cautioned" },
1236 { "R", "Restricted" },
1237 { "NC-17", "No one 17 and under admitted" },
1238 { "X", "No one under 17 admitted" },
1239 { "NR", "Not Rated" },
1241 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1242 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1244 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1245 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1246 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1251 /* Non US Rating TODO */
1252 assert( i_rating == 0x18 ); // only left value possible */
1253 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1254 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1256 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1257 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1264 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1270 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1278 if( pk->i_data < 2 )
1280 XdsStringUtf8( name, pk->p_data, pk->i_data );
1281 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1283 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1284 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1289 if( pk->i_data < 4 )
1292 XdsStringUtf8( name, pk->p_data, 4 );
1293 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1295 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1296 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1298 if( pk->i_data >= 6 )
1300 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1301 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1303 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1304 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1309 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1311 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1312 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1317 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1320 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1324 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1330 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1332 switch( h->i_class )
1334 case XDS_CLASS_CURRENT:
1335 case XDS_CLASS_FUTURE:
1336 XdsDecodeCurrentFuture( h, pk );
1338 case XDS_CLASS_CHANNEL:
1339 XdsDecodeChannel( h, pk );
1341 case XDS_CLASS_MISCELLANEOUS:
1343 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1346 case XDS_CLASS_PUBLIC_SERVICE:
1348 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1352 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1357 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1359 /* TODO check parity */
1364 if( d1 >= 0x01 && d1 <= 0x0e )
1366 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1367 const int i_type = d2;
1368 const vlc_bool_t b_start = d1 & 0x01;
1369 xds_packet_t *pk = &h->pkt[i_class][i_type];
1371 if( !b_start && !pk->b_started )
1373 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1374 h->b_xds = VLC_FALSE;
1378 h->b_xds = VLC_TRUE;
1379 h->i_class = i_class;
1381 h->b_future = !b_start;
1382 pk->b_started = VLC_TRUE;
1386 pk->i_sum = d1 + d2;
1389 else if( d1 == 0x0f && h->b_xds )
1391 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1393 /* TODO checksum and decode */
1394 pk->i_sum += d1 + d2;
1395 if( pk->i_sum & 0x7f )
1397 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1398 pk->b_started = VLC_FALSE;
1401 if( pk->i_data <= 0 )
1403 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1404 pk->b_started = VLC_FALSE;
1408 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1413 pk->b_started = VLC_FALSE;
1415 else if( d1 >= 0x20 && h->b_xds )
1417 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1419 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1421 /* Broken -> reinit */
1422 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1423 h->b_xds = VLC_FALSE;
1424 pk->b_started = VLC_FALSE;
1427 /* TODO check parity bit */
1428 pk->p_data[pk->i_data++] = d1 & 0x7f;
1429 pk->p_data[pk->i_data++] = d2 & 0x7f;
1434 h->b_xds = VLC_FALSE;
1438 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1440 demux_sys_t *p_sys = p_demux->p_sys;
1442 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1443 if( p_demux->p_sys->xds.b_meta_changed )
1445 xds_meta_t *m = &p_sys->xds.meta;
1449 /* Channel meta data */
1450 p_meta = vlc_meta_New();
1451 if( m->psz_channel_name )
1452 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1453 if( m->psz_channel_call_letter )
1454 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1455 if( m->psz_channel_number )
1456 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1457 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1458 vlc_meta_Delete( p_meta );
1460 /* Event meta data (current/future) */
1461 p_epg = vlc_epg_New( NULL );
1462 if( m->current.psz_name )
1464 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1465 //if( m->current.psz_rating )
1466 // TODO but VLC cannot yet handle rating per epg event
1467 vlc_epg_SetCurrent( p_epg, 0 );
1469 if( m->future.psz_name )
1472 if( p_epg->i_event > 0 )
1473 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1474 vlc_epg_Delete( p_epg );
1476 p_demux->p_sys->xds.b_meta_changed = VLC_FALSE;
1479 /* seek to an exact time position within the stream, if possible.
1480 * l_seek_time is in nanoseconds, the TIVO time standard.
1482 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1484 demux_sys_t *p_sys = p_demux->p_sys;
1485 int i, i_seq_entry = 0;
1487 long l_cur_pos = stream_Tell(p_demux->s);
1488 int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1489 long l_seek_secs = l_seek_time / 1000000000;
1490 uint64_t l_fwd_stamp = 1;
1492 /* if we're not seekable, there's nothing to do */
1493 if (!p_sys->b_seekable || !p_sys->b_have_master)
1494 return VLC_EGENERIC;
1496 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1497 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1499 /* seek to the proper segment if necessary */
1500 /* first see if we need to go back */
1501 while (l_seek_time < p_sys->l_first_ty_pts) {
1502 msg_Dbg(p_demux, "skipping to prior segment.");
1503 /* load previous part */
1504 if (i_cur_part == 0) {
1505 stream_Seek(p_demux->s, l_cur_pos);
1506 msg_Err(p_demux, "Attempt to seek past BOF");
1507 return VLC_EGENERIC;
1509 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1511 parse_master(p_demux);
1513 /* maybe we need to go forward */
1514 while (l_seek_time > p_sys->l_final_ty_pts) {
1515 msg_Dbg(p_demux, "skipping to next segment.");
1516 /* load next part */
1517 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1518 /* error; restore previous file position */
1519 stream_Seek(p_demux->s, l_cur_pos);
1520 msg_Err(p_demux, "seek error");
1521 return VLC_EGENERIC;
1523 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1525 parse_master(p_demux);
1528 /* our target is somewhere within this part;
1529 find the proper chunk using seq_table */
1530 for (i=1; i<p_sys->i_seq_table_size; i++) {
1531 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1532 /* i-1 is the section we want; remember the next timestamp in case
1533 we have to use it (this section may not have a proper SEQ hdr
1534 for the time we're seeking) */
1535 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1536 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1542 /* if we went through the entire last loop and didn't find our target,
1543 then we skip to the next part. What has happened is that the actual
1544 time we're seeking is within this part, but there isn't a SEQ hdr
1545 for it here. So we skip to the next part */
1546 if (i == p_sys->i_seq_table_size) {
1547 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1548 /* error; restore previous file position */
1549 stream_Seek(p_demux->s, l_cur_pos);
1550 msg_Err(p_demux, "seek error");
1551 return VLC_EGENERIC;
1553 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1555 parse_master(p_demux);
1559 /* determine which chunk has our seek_time */
1560 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1561 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1562 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1563 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1564 i_cur_part, l_chunk_nr,
1565 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1566 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1567 /* check this chunk's SEQ header timestamp */
1568 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1569 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1570 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1572 // TODO: we don't have to parse the full header set;
1573 // just test the seq_rec entry for its timestamp
1574 p_sys->i_stuff_cnt = 0;
1575 get_chunk_header(p_demux);
1576 // check ty PTS for the SEQ entry in this chunk
1577 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1578 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1579 /* Seek to beginning of original chunk & reload it */
1580 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1581 p_sys->i_stuff_cnt = 0;
1582 get_chunk_header(p_demux);
1583 return VLC_EGENERIC;
1585 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1587 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1589 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1590 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1591 // keep this one? go back?
1592 /* for now, we take this one. it's the first SEQ hdr AFTER
1593 the time we were searching for. */
1594 msg_Dbg(p_demux, "seek target found.");
1597 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1600 /* if we made it through this entire loop without finding our target,
1601 then we skip to the next section. What has happened is that the actual
1602 time we're seeking is within this section, but there isn't a SEQ hdr
1603 for it here. So we skip to the next closest one (l_fwd_stamp) */
1604 if (i == p_sys->i_bits_per_seq_entry)
1605 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1607 /* current stream ptr is at beginning of data for this chunk,
1608 so we need to skip past any stream data prior to the seq_rec
1611 for (i=0; i<p_sys->i_seq_rec; i++)
1612 i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
1613 stream_Read(p_demux->s, NULL, i_skip_cnt);
1614 p_sys->i_cur_rec = p_sys->i_seq_rec;
1615 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1616 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1622 /* parse a master chunk, filling the SEQ table and other variables.
1623 * We assume the stream is currently pointing to it.
1625 static void parse_master(demux_t *p_demux)
1627 demux_sys_t *p_sys = p_demux->p_sys;
1628 uint8_t mst_buf[32];
1630 int64_t i_save_pos = stream_Tell(p_demux->s);
1633 /* Note that the entries in the SEQ table in the stream may have
1634 different sizes depending on the bits per entry. We store them
1635 all in the same size structure, so we have to parse them out one
1636 by one. If we had a dynamic structure, we could simply read the
1637 entire table directly from the stream into memory in place. */
1639 /* clear the SEQ table */
1640 if (p_sys->seq_table != NULL)
1641 free(p_sys->seq_table);
1643 /* parse header info */
1644 stream_Read(p_demux->s, mst_buf, 32);
1645 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1646 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1647 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1648 p_sys->i_seq_table_size = i / (8 + i_map_size);
1650 /* parse all the entries */
1651 p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
1652 for (i=0; i<p_sys->i_seq_table_size; i++) {
1653 stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
1654 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1655 if (i_map_size > 8) {
1656 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1657 memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
1659 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1663 /* set up a few of our variables */
1664 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1665 p_sys->l_final_ty_pts =
1666 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1667 p_sys->b_have_master = VLC_TRUE;
1669 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1670 msg_Dbg( p_demux, "first 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) );
1672 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1673 msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
1674 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1676 /* seek past this chunk */
1677 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1681 /* ======================================================================== */
1682 /* "Peek" at some chunks. Skip over the Part header if we find it.
1683 * We parse the peeked data and determine audio type,
1684 * SA vs. DTivo, & Tivo Series.
1685 * Set global vars i_Pes_Length, i_Pts_Offset,
1686 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1687 static int probe_stream(demux_t *p_demux)
1689 demux_sys_t *p_sys = p_demux->p_sys;
1690 const uint8_t *p_buf;
1692 vlc_bool_t b_probe_error = VLC_FALSE;
1694 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1695 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1696 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1697 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1698 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1699 return VLC_EGENERIC;
1702 /* the real work: analyze this chunk */
1703 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1704 analyze_chunk(p_demux, p_buf);
1705 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1706 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1707 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1709 p_buf += CHUNK_SIZE;
1712 /* the final tally */
1713 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1714 msg_Err(p_demux, "Can't determine Tivo Series.");
1715 b_probe_error = VLC_TRUE;
1717 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1718 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1719 b_probe_error = VLC_TRUE;
1721 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1722 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1723 b_probe_error = VLC_TRUE;
1725 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1729 /* ======================================================================== */
1730 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1731 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1733 demux_sys_t *p_sys = p_demux->p_sys;
1735 ty_rec_hdr_t *p_hdrs;
1736 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1737 uint32_t i_payload_size;
1739 /* skip if it's a Part header */
1740 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1743 /* number of records in chunk (we ignore high order byte;
1744 * rarely are there > 256 chunks & we don't need that many anyway) */
1745 i_num_recs = p_chunk[0];
1746 if (i_num_recs < 5) {
1747 /* try again with the next chunk. Sometimes there are dead ones */
1751 p_chunk += 4; /* skip past rec count & SEQ bytes */
1752 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1753 p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
1755 * 1. check video packets. Presence of 0x6e0 means S1.
1756 * No 6e0 but have be0 means S2.
1757 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1758 * If AC-3, then we have DTivo.
1759 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1761 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1762 for (i=0; i<i_num_recs; i++) {
1763 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1764 //p_hdrs[i].rec_type,
1765 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1766 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1781 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1782 i_num_6e0, i_num_be0);
1784 /* set up our variables */
1785 if (i_num_6e0 > 0) {
1786 msg_Dbg(p_demux, "detected Series 1 Tivo");
1787 p_sys->tivo_series = TIVO_SERIES1;
1788 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1789 } else if (i_num_be0 > 0) {
1790 msg_Dbg(p_demux, "detected Series 2 Tivo");
1791 p_sys->tivo_series = TIVO_SERIES2;
1792 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1794 if (i_num_9c0 > 0) {
1795 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1796 p_sys->audio_type = TIVO_AUDIO_AC3;
1797 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1798 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1799 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1800 } else if (i_num_3c0 > 0) {
1801 p_sys->audio_type = TIVO_AUDIO_MPEG;
1802 msg_Dbg(p_demux, "detected MPEG Audio" );
1805 /* if tivo_type still unknown, we can check PTS location
1806 * in MPEG packets to determine tivo_type */
1807 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1808 uint32_t i_data_offset = (16 * i_num_recs);
1809 for (i=0; i<i_num_recs; i++) {
1810 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1811 p_hdrs[i].l_rec_size > 15) {
1812 /* first make sure we're aligned */
1813 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1814 &p_chunk[i_data_offset], 5);
1815 if (i_pes_offset >= 0) {
1816 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1817 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1819 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1820 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1821 if (p_sys->tivo_series == TIVO_SERIES1)
1822 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1823 p_sys->tivo_type = TIVO_TYPE_SA;
1824 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1826 if (p_sys->tivo_series == TIVO_SERIES1)
1827 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1828 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1829 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1834 i_data_offset += p_hdrs[i].l_rec_size;
1841 /* =========================================================================== */
1842 static int get_chunk_header(demux_t *p_demux)
1844 int i_readSize, i_num_recs;
1846 const uint8_t *p_peek;
1847 demux_sys_t *p_sys = p_demux->p_sys;
1848 int i_payload_size; /* sum of all records' sizes */
1850 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1852 /* if we have left-over filler space from the last chunk, get that */
1853 if (p_sys->i_stuff_cnt > 0) {
1854 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1855 p_sys->i_stuff_cnt = 0;
1858 /* read the TY packet header */
1859 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1860 p_sys->i_cur_chunk++;
1862 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1869 /* check if it's a PART Header */
1870 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1872 /* parse master chunk */
1873 parse_master(p_demux);
1874 return get_chunk_header(p_demux);
1877 /* number of records in chunk (8- or 16-bit number) */
1878 if (p_peek[3] & 0x80)
1880 /* 16 bit rec cnt */
1881 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1882 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1883 if (p_sys->i_seq_rec != 0xffff)
1885 p_sys->i_seq_rec &= ~0x8000;
1890 /* 8 bit reclen - tivo 1.3 format */
1891 p_sys->i_num_recs = i_num_recs = p_peek[0];
1892 p_sys->i_seq_rec = p_peek[1];
1894 p_sys->i_cur_rec = 0;
1895 p_sys->b_first_chunk = VLC_FALSE;
1897 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1899 if (p_sys->rec_hdrs)
1900 free(p_sys->rec_hdrs);
1902 /* skip past the 4 bytes we "peeked" earlier */
1903 stream_Read( p_demux->s, NULL, 4 );
1905 /* read the record headers into a temp buffer */
1906 p_hdr_buf = malloc(i_num_recs * 16);
1907 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1909 p_sys->eof = VLC_TRUE;
1913 p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
1917 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1918 (p_sys->i_num_recs * 16) - i_payload_size;
1919 if (p_sys->i_stuff_cnt > 0)
1920 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1921 p_sys->i_stuff_cnt );
1926 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
1927 int i_num_recs, int *pi_payload_size)
1930 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1932 *pi_payload_size = 0;
1933 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1935 for (i = 0; i < i_num_recs; i++)
1937 const uint8_t *record_header = p_buf + (i * 16);
1938 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1939 p_rec_hdr->rec_type = record_header[3];
1940 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1941 if ((record_header[ 0 ] & 0x80) == 0x80)
1944 /* marker bit 2 set, so read extended data */
1945 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1946 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1948 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1949 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1952 p_rec_hdr->ex1 = b1;
1953 p_rec_hdr->ex2 = b2;
1954 p_rec_hdr->l_rec_size = 0;
1955 p_rec_hdr->l_ty_pts = 0;
1956 p_rec_hdr->b_ext = VLC_TRUE;
1960 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1961 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1962 *pi_payload_size += p_rec_hdr->l_rec_size;
1963 p_rec_hdr->b_ext = VLC_FALSE;
1964 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1966 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1967 } /* end of record-header loop */