1 /*****************************************************************************
2 * ty.c - TiVo ty stream video demuxer for VLC
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
5 * Copyright (C) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005
6 * based on code by Christopher Wingert for tivo-mplayer
7 * tivo(at)wingert.org, February 2003
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 * v1.0.0 - 24-Feb-2005 - Initial release - Series 1 support ONLY!
27 * v1.0.1 - 25-Feb-2005 - Added fix for bad GOP headers - Neal
28 * v1.0.2 - 26-Feb-2005 - No longer require "seekable" input stream - Neal
29 * v2.0.0 - 21-Mar-2005 - Series 2 support! No AC-3 on S2 DTivo yet.
30 * v2.1.0 - 22-Mar-2005 - Support for AC-3 on S2 DTivo (long ac3 packets)
31 * v3.0.0 - 14-Jul-2005 - Support for skipping fwd/back via VLC hotkeys
32 *****************************************************************************/
34 /*****************************************************************************
36 *****************************************************************************/
43 #include <vlc_demux.h>
44 #include "vlc_codec.h"
46 #include "vlc_input.h"
47 #include "../codec/cc.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Open ( vlc_object_t * );
53 static void Close( vlc_object_t * );
56 set_shortname( _("TY") );
57 set_description(_("TY Stream audio/video demux"));
58 set_category( CAT_INPUT );
59 set_subcategory( SUBCAT_INPUT_DEMUX );
60 set_capability("demux2", 6);
61 /* FIXME: there seems to be a segfault when using PVR access
62 * and TY demux has a bigger priority than PS
63 * Something must be wrong.
65 set_callbacks( Open, Close );
70 /*****************************************************************************
72 *****************************************************************************/
73 static int Demux ( demux_t * );
74 static int Control( demux_t *, int, va_list );
76 #define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */
77 #define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */
78 #define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */
79 #define VIDEO_PES_LENGTH (16) /* length of video PES header */
80 #define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */
81 #define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */
82 #define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */
83 #define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */
84 #define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */
85 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
86 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
87 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
89 #define CHUNK_PEEK_COUNT (3) /* number of chunks to probe */
91 /* packet types for reference:
92 2/c0: audio data continued
93 3/c0: audio packet header (PES header)
94 4/c0: audio data (S/A only?)
95 9/c0: audio packet header, AC-3 audio
96 2/e0: video data continued
97 6/e0: video packet header (PES header)
98 7/e0: video sequence header start
99 8/e0: video I-frame header start
100 a/e0: video P-frame header start
101 b/e0: video B-frame header start
102 c/e0: video GOP header start
103 e/01: closed-caption data
104 e/02: Extended data services data
105 e/03: ipreview data ("thumbs up to record" signal)
109 #define TIVO_PES_FILEID ( 0xf5467abd )
110 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
111 #define CHUNK_SIZE ( 128 * 1024 )
120 uint64_t l_ty_pts; /* TY PTS in the record header */
125 uint64_t l_timestamp;
126 uint8_t chunk_bitmask[8];
150 #define XDS_MAX_DATA_SIZE (32)
153 XDS_CLASS_CURRENT = 0,
154 XDS_CLASS_FUTURE = 1,
155 XDS_CLASS_CHANNEL = 2,
156 XDS_CLASS_MISCELLANEOUS = 3,
157 XDS_CLASS_PUBLIC_SERVICE = 4,
158 XDS_CLASS_RESERVED = 5,
159 XDS_CLASS_UNDEFINED = 6,
166 vlc_bool_t b_started;
168 uint8_t p_data[XDS_MAX_DATA_SIZE];
173 XDS_META_PROGRAM_RATING_NONE,
174 XDS_META_PROGRAM_RATING_MPAA,
175 XDS_META_PROGRAM_RATING_TPG,
176 /* TODO add CA/CE rating */
177 } xds_meta_program_rating_t;
181 xds_meta_program_rating_t rating;
183 /* Add the other fields once I have the samples */
184 } xds_meta_program_t;
187 char *psz_channel_name;
188 char *psz_channel_call_letter;
189 char *psz_channel_number;
191 xds_meta_program_t current;
192 xds_meta_program_t future;
196 /* Are we in XDS mode */
199 /* Current class type */
205 xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
208 vlc_bool_t b_meta_changed;
215 es_out_id_t *p_video; /* ptr to video codec */
216 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
219 es_out_id_t *p_cc[4];
225 size_t i_stream_size; /* size of input stream (if known) */
226 //uint64_t l_program_len; /* length of this stream in msec */
227 vlc_bool_t b_seekable; /* is this stream seekable? */
228 vlc_bool_t b_have_master; /* are master chunks present? */
229 tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */
230 tivo_series_t tivo_series; /* Series1 or Series2 */
231 tivo_audio_t audio_type; /* AC3 or MPEG */
232 int i_Pes_Length; /* Length of Audio PES header */
233 int i_Pts_Offset; /* offset into audio PES of PTS */
234 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
235 int i_pes_buf_cnt; /* how many bytes in our buffer */
236 size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */
237 uint64_t l_last_ty_pts; /* last TY timestamp we've seen */
238 //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
239 uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */
240 uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */
241 int i_seq_table_size; /* number of entries in SEQ table */
242 int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
244 mtime_t firstAudioPTS;
245 mtime_t lastAudioPTS;
246 mtime_t lastVideoPTS;
248 ty_rec_hdr_t *rec_hdrs; /* record headers array */
249 int i_cur_rec; /* current record in this chunk */
250 int i_num_recs; /* number of recs in this chunk */
251 int i_seq_rec; /* record number where seq start is */
252 ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */
254 vlc_bool_t b_first_chunk;
257 static int get_chunk_header(demux_t *);
258 static mtime_t get_pts( const uint8_t *buf );
259 static int find_es_header( const uint8_t *header,
260 const uint8_t *buffer, int i_search_len );
261 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
262 static int ty_stream_seek_time(demux_t *, uint64_t);
264 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
265 int i_num_recs, int *pi_payload_size);
266 static int probe_stream(demux_t *p_demux);
267 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
268 static void parse_master(demux_t *p_demux);
270 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
271 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
272 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
274 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
276 static void XdsInit( xds_t * );
277 static void XdsExit( xds_t * );
279 #define TY_ES_GROUP (1)
282 * Open: check file and initialize demux structures
285 * 1. peek at the first 12 bytes of the stream for the
286 * magic TiVo PART header & stream type & chunk size
287 * 2. if it's not there, error with VLC_EGENERIC
288 * 3. set up video (mpgv) codec
289 * 4. return VLC_SUCCESS
291 static int Open(vlc_object_t *p_this)
293 demux_t *p_demux = (demux_t *)p_this;
296 const uint8_t *p_peek;
299 /* peek at the first 12 bytes. */
300 /* for TY streams, they're always the same */
301 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
304 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
305 U32_AT(&p_peek[4]) != 0x02 ||
306 U32_AT(&p_peek[8]) != CHUNK_SIZE )
308 if( !p_demux->b_force &&
309 !demux2_IsPathExtension( p_demux, ".ty" ) &&
310 !demux2_IsPathExtension( p_demux, ".ty+" ) )
312 msg_Warn( p_demux, "this does not look like a TY file, "
313 "continuing anyway..." );
316 /* at this point, we assume we have a valid TY stream */
317 msg_Dbg( p_demux, "valid TY stream detected" );
319 /* Set exported functions */
320 p_demux->pf_demux = Demux;
321 p_demux->pf_control = Control;
323 /* create our structure that will hold all data */
324 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
325 memset(p_sys, 0, sizeof(demux_sys_t));
327 /* set up our struct (most were zero'd out with the memset above) */
328 p_sys->b_first_chunk = VLC_TRUE;
329 p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
330 p_sys->firstAudioPTS = -1;
331 p_sys->i_stream_size = stream_Size(p_demux->s);
332 p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
333 p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
334 p_sys->tivo_series = TIVO_SERIES_UNKNOWN;
335 p_sys->i_Pes_Length = 0;
336 p_sys->i_Pts_Offset = 0;
337 p_sys->l_ac3_pkt_size = 0;
339 /* see if this stream is seekable */
340 stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
342 if (probe_stream(p_demux) != VLC_SUCCESS) {
347 if (!p_sys->b_have_master)
348 msg_Warn(p_demux, "No master chunk found; seeking will be limited.");
350 /* register the proper audio codec */
351 if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
352 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
354 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
356 fmt.i_group = TY_ES_GROUP;
357 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
359 /* register the video stream */
360 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
361 fmt.i_group = TY_ES_GROUP;
362 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
365 for( i = 0; i < 4; i++ )
366 p_sys->p_cc[i] = NULL;
367 cc_Init( &p_sys->cc );
369 XdsInit( &p_sys->xds );
374 /* =========================================================================== */
375 /* Demux: Read & Demux one record from the chunk
377 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
379 * NOTE: I think we can return the number of packets sent instead of just 1.
380 * that means we can demux an entire chunk and shoot it back (may be more efficient)
381 * -- should try that some day :) --
383 static int Demux( demux_t *p_demux )
385 demux_sys_t *p_sys = p_demux->p_sys;
387 block_t *p_block_in = NULL;
389 /*msg_Dbg(p_demux, "ty demux processing" );*/
391 /* did we hit EOF earlier? */
396 * what we do (1 record now.. maybe more later):
397 * - use stream_Read() to read the chunk header & record headers
398 * - discard entire chunk if it is a PART header chunk
399 * - parse all the headers into record header array
400 * - keep a pointer of which record we're on
401 * - use stream_Block() to fetch each record
402 * - parse out PTS from PES headers
403 * - set PTS for data packets
404 * - pass the data on to the proper codec via es_out_Send()
406 * if this is the first time or
407 * if we're at the end of this chunk, start a new one
409 /* parse the next chunk's record headers */
410 if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
412 if( get_chunk_header(p_demux) == 0 )
416 /*======================================================================
417 * parse & send one record of the chunk
418 *====================================================================== */
419 p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
423 const long l_rec_size = p_rec->l_rec_size;
424 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
425 subrec_type, p_rec->rec_type, l_rec_size );*/
427 /* some normal records are 0 length, so check for that... */
428 if( l_rec_size <= 0 )
430 /* no data in payload; we're done */
435 /* read in this record's payload */
436 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
439 /* set these as 'unknown' for now */
441 p_block_in->i_dts = 0;
445 -- don't read any data from the stream, data was in the record header --
447 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
448 p_rec->rec_type, p_rec->ex1, p_rec->ex2);
451 if( p_rec->rec_type == 0xe0 )
454 DemuxRecVideo( p_demux, p_rec, p_block_in );
456 else if ( p_rec->rec_type == 0xc0 )
459 DemuxRecAudio( p_demux, p_rec, p_block_in );
461 else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
463 /* Closed Captions/XDS */
464 DemuxRecCc( p_demux, p_rec, p_block_in );
466 else if ( p_rec->rec_type == 0x03 )
468 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
470 block_Release(p_block_in);
472 else if ( p_rec->rec_type == 0x05 )
474 /* Unknown, but seen regularly */
476 block_Release(p_block_in);
480 msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
482 block_Release(p_block_in);
491 static int Control(demux_t *p_demux, int i_query, va_list args)
493 demux_sys_t *p_sys = p_demux->p_sys;
497 /*msg_Info(p_demux, "control cmd %d", i_query);*/
500 case DEMUX_GET_POSITION:
501 /* arg is 0.0 - 1.0 percent of overall file position */
502 if( ( i64 = p_sys->i_stream_size ) > 0 )
504 pf = (double*) va_arg( args, double* );
505 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
510 case DEMUX_SET_POSITION:
511 /* arg is 0.0 - 1.0 percent of overall file position */
512 f = (double) va_arg( args, double );
513 /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
514 if ((i64 = p_sys->i_stream_size) > 0)
515 return ty_stream_seek_pct(p_demux, f);
518 /* return TiVo timestamp */
519 p_i64 = (int64_t *) va_arg(args, int64_t *);
520 //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
521 //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
522 // p_sys->l_last_ty_pts_sync);
523 *p_i64 = (p_sys->l_last_ty_pts / 1000);
525 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
527 p_i64 = (int64_t *) va_arg(args, int64_t *);
530 case DEMUX_SET_TIME: /* arg is time in microsecs */
531 i64 = (int64_t) va_arg( args, int64_t );
532 return ty_stream_seek_time(p_demux, i64 * 1000);
540 static void Close( vlc_object_t *p_this )
542 demux_t *p_demux = (demux_t*)p_this;
543 demux_sys_t *p_sys = p_demux->p_sys;
545 XdsExit( &p_sys->xds );
546 cc_Exit( &p_sys->cc );
547 free( p_sys->rec_hdrs );
548 if( p_sys->seq_table )
549 free( p_sys->seq_table );
554 /* =========================================================================== */
555 /* Compute Presentation Time Stamp (PTS)
556 * Assume buf points to beginning of PTS */
557 static mtime_t get_pts( const uint8_t *buf )
561 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
562 (mtime_t)(buf[1] << 22)|
563 ((mtime_t)(buf[2]&0xfe) << 14)|
564 (mtime_t)(buf[3] << 7)|
565 (mtime_t)(buf[4] >> 1);
566 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
571 /* =========================================================================== */
572 static int find_es_header( const uint8_t *header,
573 const uint8_t *buffer, int i_search_len )
577 for( count = 0; count < i_search_len; count++ )
579 if( !memcmp( &buffer[count], header, 4 ) )
586 /* =========================================================================== */
587 /* check if we have a full PES header, if not, then save what we have.
588 * this is called when audio-start packets are encountered.
590 * 1 partial PES hdr found, some audio data found (buffer adjusted),
591 * -1 partial PES hdr found, no audio data found
592 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
593 /* TODO: HD support -- nothing known about those streams */
594 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
595 int32_t offset, int32_t rec_len )
597 demux_sys_t *p_sys = p_demux->p_sys;
599 if ( offset < 0 || offset + p_sys->i_Pes_Length > rec_len )
601 /* entire PES header not present */
602 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
604 /* save the partial pes header */
607 /* no header found, fake some 00's (this works, believe me) */
608 memset( p_sys->pes_buffer, 4, 0 );
609 p_sys->i_pes_buf_cnt = 4;
611 msg_Err( p_demux, "PES header not found in record of %d bytes!",
615 /* copy the partial pes header we found */
616 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
618 p_sys->i_pes_buf_cnt = rec_len - offset;
622 /* PES Header was found, but not complete, so trim the end of this record */
623 p_block->i_buffer -= rec_len - offset;
626 return -1; /* partial PES, no audio data */
628 /* full PES header present, extract PTS */
629 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
630 p_sys->i_Pts_Offset ] );
631 if (p_sys->firstAudioPTS < 0)
632 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
633 p_block->i_pts = p_sys->lastAudioPTS;
634 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
635 /* adjust audio record to remove PES header */
636 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
637 p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
638 p_block->i_buffer -= p_sys->i_Pes_Length;
640 msg_Dbg(p_demux, "pes hdr removed; buffer len=%d and has "
641 "%02x %02x %02x %02x %02x %02x %02x %02x "
642 "%02x %02x %02x %02x %02x %02x %02x %02x", p_block->i_buffer,
643 p_block->p_buffer[0], p_block->p_buffer[1],
644 p_block->p_buffer[2], p_block->p_buffer[3],
645 p_block->p_buffer[4], p_block->p_buffer[5],
646 p_block->p_buffer[6], p_block->p_buffer[7],
647 p_block->p_buffer[8], p_block->p_buffer[9],
648 p_block->p_buffer[10], p_block->p_buffer[11],
649 p_block->p_buffer[12], p_block->p_buffer[13],
650 p_block->p_buffer[14], p_block->p_buffer[15]);
655 static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
657 demux_sys_t *p_sys = p_demux->p_sys;
658 const int subrec_type = rec_hdr->subrec_type;
659 const long l_rec_size = rec_hdr->l_rec_size; // p_block_in->i_buffer might be better
663 assert( rec_hdr->rec_type == 0xe0 );
668 msg_Dbg(p_demux, "packet buffer has "
669 "%02x %02x %02x %02x %02x %02x %02x %02x "
670 "%02x %02x %02x %02x %02x %02x %02x %02x",
671 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
672 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
673 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
674 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
675 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
676 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
677 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
678 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
680 //if( subrec_type == 0x06 || subrec_type == 0x07 )
681 if( subrec_type != 0x02 && subrec_type != 0x0c &&
682 subrec_type != 0x08 && l_rec_size > 4 )
684 /* get the PTS from this packet if it has one.
685 * on S1, only 0x06 has PES. On S2, however, most all do.
686 * Do NOT Pass the PES Header to the MPEG2 codec */
687 esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
688 if( esOffset1 != -1 )
690 //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
691 //subrec_type, esOffset1);
692 p_sys->lastVideoPTS = get_pts(
693 &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
694 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
695 p_sys->lastVideoPTS );*/
696 if (subrec_type != 0x06) {
697 /* if we found a PES, and it's not type 6, then we're S2 */
698 /* The packet will have video data (& other headers) so we
699 * chop out the PES header and send the rest */
700 if (l_rec_size >= VIDEO_PES_LENGTH) {
701 p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
702 p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
704 msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
705 " (%ld bytes)", subrec_type, l_rec_size);
706 /* nuke this block; it's too short, but has PES marker */
707 p_block_in->i_buffer = 0;
711 msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
715 if(subrec_type == 0x06 )
717 /* type 6 (S1 DTivo) has no data, so we're done */
718 block_Release(p_block_in);
722 /* if it's not a continue blk, then set PTS */
723 if( subrec_type != 0x02 )
725 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
727 /* if it's a GOP header, make sure it's legal
728 * (if we have enough data) */
729 /* Some ty files don't have this bit set
730 * and it causes problems */
731 if (subrec_type == 0x0c && l_rec_size >= 6)
732 p_block_in->p_buffer[5] |= 0x08;
733 /* store the TY PTS if there is one */
734 if (subrec_type == 0x07) {
735 p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
736 /* should we use audio or video PTS? */
737 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
739 /* yes I know this is a cheap hack. It's the timestamp
740 used for display and skipping fwd/back, so it
741 doesn't have to be accurate to the millisecond.
742 I adjust it here by roughly one 1/30 sec. Yes it
743 will be slightly off for UK streams, but it's OK.
745 p_sys->l_last_ty_pts += 35000000;
746 //p_sys->l_last_ty_pts += 33366667;
748 /* set PTS for this block before we send */
749 if (p_sys->lastVideoPTS > 0)
751 p_block_in->i_pts = p_sys->lastVideoPTS;
752 /* PTS gets used ONCE.
753 * Any subsequent frames we get BEFORE next PES
754 * header will have their PTS computed in the codec */
755 p_sys->lastVideoPTS = 0;
759 /* Register the CC decoders when needed */
760 for( i = 0; i < 4; i++ )
762 static const vlc_fourcc_t fcc[4] = {
763 VLC_FOURCC('c', 'c', '1', ' '),
764 VLC_FOURCC('c', 'c', '2', ' '),
765 VLC_FOURCC('c', 'c', '3', ' '),
766 VLC_FOURCC('c', 'c', '4', ' ')
768 static const char *ppsz_description[4] = {
769 N_("Closed captions 1"),
770 N_("Closed captions 2"),
771 N_("Closed captions 3"),
772 N_("Closed captions 4"),
777 if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
780 es_format_Init( &fmt, SPU_ES, fcc[i] );
781 fmt.psz_description = strdup( _(ppsz_description[i]) );
782 fmt.i_group = TY_ES_GROUP;
783 p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
784 es_format_Clean( &fmt );
787 /* Send the CC data */
788 if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
792 block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
793 p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
794 p_cc->i_pts = p_block_in->i_pts;
795 memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
797 for( i = 0, i_cc_count = 0; i < 4; i++ )
798 i_cc_count += p_sys->p_cc[i] ? 1 : 0;
800 for( i = 0; i < 4; i++ )
802 if( !p_sys->p_cc[i] )
805 es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
807 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
809 cc_Flush( &p_sys->cc );
812 //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
813 //p_sys->i_cur_rec, subrec_type);
814 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
817 static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
819 demux_sys_t *p_sys = p_demux->p_sys;
820 const int subrec_type = rec_hdr->subrec_type;
821 const long l_rec_size = rec_hdr->l_rec_size;
824 assert( rec_hdr->rec_type == 0xc0 );
829 printf( "Audio Packet Header " );
830 for( i = 0 ; i < 24 ; i++ )
831 printf( "%2.2x ", p_block_in->p_buffer[i] );
835 if( subrec_type == 2 )
837 /* SA or DTiVo Audio Data, no PES (continued block)
838 * ================================================
841 /* continue PES if previous was incomplete */
842 if (p_sys->i_pes_buf_cnt > 0)
844 const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
846 msg_Dbg(p_demux, "continuing PES header");
847 /* do we have enough data to complete? */
848 if (i_need >= l_rec_size)
850 /* don't have complete PES hdr; save what we have and return */
851 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
852 p_block_in->p_buffer, l_rec_size);
853 p_sys->i_pes_buf_cnt += l_rec_size;
855 block_Release(p_block_in);
859 /* we have enough; reconstruct this p_frame with the new hdr */
860 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
861 p_block_in->p_buffer, i_need);
862 /* advance the block past the PES header (don't want to send it) */
863 p_block_in->p_buffer += i_need;
864 p_block_in->i_buffer -= i_need;
865 /* get the PTS out of this PES header (MPEG or AC3) */
866 if (p_sys->audio_type == TIVO_AUDIO_MPEG)
867 esOffset1 = find_es_header(ty_MPEGAudioPacket,
868 p_sys->pes_buffer, 5);
870 esOffset1 = find_es_header(ty_AC3AudioPacket,
871 p_sys->pes_buffer, 5);
874 /* god help us; something's really wrong */
875 msg_Err(p_demux, "can't find audio PES header in packet");
879 p_sys->lastAudioPTS = get_pts(
880 &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
881 p_block_in->i_pts = p_sys->lastAudioPTS;
883 p_sys->i_pes_buf_cnt = 0;
885 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
886 * not allowed in the AC3 spec and will cause problems. So here
887 * we try to trim things. */
888 /* Also, S1 DTivo has alternating short / long AC3 packets. That
889 * is, one packet is short (incomplete) and the next packet has
890 * the first one's missing data, plus all of its own. Strange. */
891 if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
892 p_sys->tivo_series == TIVO_SERIES2) {
893 if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
895 p_block_in->i_buffer -= 2;
896 p_sys->l_ac3_pkt_size = 0;
898 p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
902 else if( subrec_type == 0x03 )
904 /* MPEG Audio with PES Header, either SA or DTiVo */
905 /* ================================================ */
906 esOffset1 = find_es_header( ty_MPEGAudioPacket,
907 p_block_in->p_buffer, 5 );
909 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
910 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
911 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
912 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
914 /* SA PES Header, No Audio Data */
915 /* ================================================ */
916 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
918 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
920 if (p_sys->firstAudioPTS < 0)
921 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
923 block_Release(p_block_in);
925 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
926 p_sys->lastAudioPTS );*/
928 /* DTiVo Audio with PES Header */
929 /* ================================================ */
931 /* Check for complete PES */
932 if (check_sync_pes(p_demux, p_block_in, esOffset1,
935 /* partial PES header found, nothing else.
937 block_Release(p_block_in);
941 msg_Dbg(p_demux, "packet buffer has "
942 "%02x %02x %02x %02x %02x %02x %02x %02x "
943 "%02x %02x %02x %02x %02x %02x %02x %02x",
944 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
945 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
946 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
947 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
948 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
949 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
950 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
951 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
954 else if( subrec_type == 0x04 )
956 /* SA Audio with no PES Header */
957 /* ================================================ */
959 "Adding SA Audio Packet Size %ld", l_rec_size ); */
961 if (p_sys->lastAudioPTS > 0)
962 p_block_in->i_pts = p_sys->lastAudioPTS;
964 else if( subrec_type == 0x09 )
966 /* DTiVo AC3 Audio Data with PES Header */
967 /* ================================================ */
968 esOffset1 = find_es_header( ty_AC3AudioPacket,
969 p_block_in->p_buffer, 5 );
972 msg_Dbg(p_demux, "buffer has "
973 "%02x %02x %02x %02x %02x %02x %02x %02x "
974 "%02x %02x %02x %02x %02x %02x %02x %02x",
975 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
976 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
977 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
978 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
979 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
980 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
981 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
982 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
983 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
986 /* Check for complete PES */
987 if (check_sync_pes(p_demux, p_block_in, esOffset1,
990 /* partial PES header found, nothing else. we're done. */
991 block_Release(p_block_in);
994 /* S2 DTivo has invalid long AC3 packets */
995 if (p_sys->tivo_series == TIVO_SERIES2) {
996 if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
997 p_block_in->i_buffer -= 2;
998 p_sys->l_ac3_pkt_size = 0;
1000 p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
1006 /* Unsupported/Unknown */
1007 block_Release(p_block_in);
1011 /* set PCR before we send (if PTS found) */
1012 if( p_block_in->i_pts > 0 )
1013 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
1014 p_block_in->i_pts );
1016 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
1020 static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
1022 demux_sys_t *p_sys = p_demux->p_sys;
1027 block_Release(p_block_in);
1029 if( rec_hdr->rec_type == 0x01 )
1031 else if( rec_hdr->rec_type == 0x02 )
1036 /* XDS data (extract programs infos) transmitted on field 2 only */
1038 DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
1040 if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
1043 p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
1044 p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
1045 p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
1046 p_sys->cc.i_data += 3;
1048 i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
1049 if( i_channel >= 0 && i_channel < 4 )
1050 p_sys->cc.pb_present[i_channel] = VLC_TRUE;
1054 /* seek to a position within the stream, if possible */
1055 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
1057 demux_sys_t *p_sys = p_demux->p_sys;
1058 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
1062 /* if we're not seekable, there's nothing to do */
1063 if (!p_sys->b_seekable)
1064 return VLC_EGENERIC;
1066 /* figure out which part & chunk we want & go there */
1067 i_cur_part = seek_pos / TIVO_PART_LENGTH;
1068 p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
1070 /* try to read the part header (master chunk) if it's there */
1071 if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
1073 /* can't seek stream */
1074 return VLC_EGENERIC;
1076 parse_master(p_demux);
1078 /* now for the actual chunk */
1079 if ( stream_Seek( p_demux->s, p_sys->i_cur_chunk * CHUNK_SIZE))
1081 /* can't seek stream */
1082 return VLC_EGENERIC;
1084 /* load the chunk */
1085 p_sys->i_stuff_cnt = 0;
1086 get_chunk_header(p_demux);
1088 /* seek within the chunk to get roughly to where we want */
1089 p_sys->i_cur_rec = (int)
1090 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
1091 msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
1092 msg_Dbg(p_demux, " (chunk %d, record %d)",
1093 p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
1095 /* seek to the start of this record's data.
1096 * to do that, we have to skip past all prior records */
1098 for (i=0; i<p_sys->i_cur_rec; i++)
1099 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
1100 stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
1101 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
1103 /* to hell with syncing any audio or video, just start reading records... :) */
1104 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
1105 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1110 //#define TY_XDS_DEBUG
1111 static void XdsInit( xds_t *h )
1115 h->b_xds = VLC_FALSE;
1116 h->i_class = XDS_MAX_CLASS_COUNT;
1118 h->b_future = VLC_FALSE;
1119 for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
1121 for( j = 0; j < 128; j++ )
1122 h->pkt[i][j].b_started = VLC_FALSE;
1124 h->b_meta_changed = VLC_FALSE;
1125 memset( &h->meta, 0, sizeof(h->meta) );
1127 static void XdsExit( xds_t *h )
1130 if( h->meta.psz_channel_name )
1131 free( h->meta.psz_channel_name );
1132 if( h->meta.psz_channel_call_letter )
1133 free( h->meta.psz_channel_call_letter );
1134 if( h->meta.psz_channel_number )
1135 free( h->meta.psz_channel_number );
1138 if( h->meta.current.psz_name )
1139 free( h->meta.current.psz_name );
1140 if( h->meta.current.psz_rating )
1141 free( h->meta.current.psz_rating );
1143 if( h->meta.future.psz_name )
1144 free( h->meta.future.psz_name );
1145 if( h->meta.future.psz_rating )
1146 free( h->meta.future.psz_rating );
1148 static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
1153 for( i = 0, i_dst = 0; i < i_src; i++ )
1157 #define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
1158 E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
1159 E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
1160 E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
1161 E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
1162 E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
1163 E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
1164 E2( 0x7c, 0xc3,0xb7); // division symbol
1165 E2( 0x7d, 0xc3,0x91); // uppercase N tilde
1166 E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
1169 dst[i_dst++] = p_src[i];
1173 dst[i_dst++] = '\0';
1175 static vlc_bool_t XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
1177 if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
1179 if( *ppsz_dst == NULL && psz_new == NULL )
1185 *ppsz_dst = strdup( psz_new );
1189 h->b_meta_changed = VLC_TRUE;
1193 static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
1195 xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
1202 XdsStringUtf8( name, pk->p_data, pk->i_data );
1203 if( XdsChangeString( h, &p_prg->psz_name, name ) )
1205 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
1206 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
1210 i_rating = (pk->p_data[0] & 0x18);
1211 if( i_rating == 0x08 )
1214 static const char *pppsz_ratings[8][2] = {
1215 { "None", "No rating (no content advisory)" },
1216 { "TV-Y", "All Children (no content advisory)" },
1217 { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
1218 { "TV-G", "General Audience (no content advisory)" },
1219 { "TV-PG", "Parental Guidance Suggested" },
1220 { "TV-14", "Parents Strongly Cautioned" },
1221 { "TV-MA", "Mature Audience Only" },
1222 { "None", "No rating (no content advisory)" }
1224 p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
1225 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
1227 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1228 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1229 // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
1232 else if( i_rating == 0x00 || i_rating == 0x10 )
1235 static const char *pppsz_ratings[8][2] = {
1237 { "G", "General Audiences" },
1238 { "PG", "Parental Guidance Suggested" },
1239 { "PG-13", "Parents Strongly Cautioned" },
1240 { "R", "Restricted" },
1241 { "NC-17", "No one 17 and under admitted" },
1242 { "X", "No one under 17 admitted" },
1243 { "NR", "Not Rated" },
1245 p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
1246 if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
1248 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1249 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
1250 // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
1255 /* Non US Rating TODO */
1256 assert( i_rating == 0x18 ); // only left value possible */
1257 p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
1258 if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
1260 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
1261 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
1268 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
1274 static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
1282 if( pk->i_data < 2 )
1284 XdsStringUtf8( name, pk->p_data, pk->i_data );
1285 if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
1287 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
1288 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
1293 if( pk->i_data < 4 )
1296 XdsStringUtf8( name, pk->p_data, 4 );
1297 if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
1299 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1300 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
1302 if( pk->i_data >= 6 )
1304 XdsStringUtf8( chan, &pk->p_data[4], 2 );
1305 if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
1307 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1308 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
1313 if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
1315 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
1316 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
1321 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
1324 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
1328 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
1334 static void XdsDecode( xds_t *h, xds_packet_t *pk )
1336 switch( h->i_class )
1338 case XDS_CLASS_CURRENT:
1339 case XDS_CLASS_FUTURE:
1340 XdsDecodeCurrentFuture( h, pk );
1342 case XDS_CLASS_CHANNEL:
1343 XdsDecodeChannel( h, pk );
1345 case XDS_CLASS_MISCELLANEOUS:
1347 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
1350 case XDS_CLASS_PUBLIC_SERVICE:
1352 fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
1356 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
1361 static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
1363 /* TODO check parity */
1368 if( d1 >= 0x01 && d1 <= 0x0e )
1370 const xds_class_t i_class = ( d1 - 1 ) >> 1;
1371 const int i_type = d2;
1372 const vlc_bool_t b_start = d1 & 0x01;
1373 xds_packet_t *pk = &h->pkt[i_class][i_type];
1375 if( !b_start && !pk->b_started )
1377 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
1378 h->b_xds = VLC_FALSE;
1382 h->b_xds = VLC_TRUE;
1383 h->i_class = i_class;
1385 h->b_future = !b_start;
1386 pk->b_started = VLC_TRUE;
1390 pk->i_sum = d1 + d2;
1393 else if( d1 == 0x0f && h->b_xds )
1395 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1397 /* TODO checksum and decode */
1398 pk->i_sum += d1 + d2;
1399 if( pk->i_sum & 0x7f )
1401 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
1402 pk->b_started = VLC_FALSE;
1405 if( pk->i_data <= 0 )
1407 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
1408 pk->b_started = VLC_FALSE;
1412 //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
1417 pk->b_started = VLC_FALSE;
1419 else if( d1 >= 0x20 && h->b_xds )
1421 xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
1423 if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
1425 /* Broken -> reinit */
1426 //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
1427 h->b_xds = VLC_FALSE;
1428 pk->b_started = VLC_FALSE;
1431 /* TODO check parity bit */
1432 pk->p_data[pk->i_data++] = d1 & 0x7f;
1433 pk->p_data[pk->i_data++] = d2 & 0x7f;
1438 h->b_xds = VLC_FALSE;
1442 static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
1444 demux_sys_t *p_sys = p_demux->p_sys;
1446 XdsParse( &p_demux->p_sys->xds, d1, d2 );
1447 if( p_demux->p_sys->xds.b_meta_changed )
1449 xds_meta_t *m = &p_sys->xds.meta;
1453 /* Channel meta data */
1454 p_meta = vlc_meta_New();
1455 if( m->psz_channel_name )
1456 vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
1457 if( m->psz_channel_call_letter )
1458 vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
1459 if( m->psz_channel_number )
1460 vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
1461 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
1462 vlc_meta_Delete( p_meta );
1464 /* Event meta data (current/future) */
1465 p_epg = vlc_epg_New( NULL );
1466 if( m->current.psz_name )
1468 vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
1469 //if( m->current.psz_rating )
1470 // TODO but VLC cannot yet handle rating per epg event
1471 vlc_epg_SetCurrent( p_epg, 0 );
1473 if( m->future.psz_name )
1476 if( p_epg->i_event > 0 )
1477 es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
1478 vlc_epg_Delete( p_epg );
1480 p_demux->p_sys->xds.b_meta_changed = VLC_FALSE;
1483 /* seek to an exact time position within the stream, if possible.
1484 * l_seek_time is in nanoseconds, the TIVO time standard.
1486 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
1488 demux_sys_t *p_sys = p_demux->p_sys;
1489 int i, i_seq_entry = 0;
1491 long l_cur_pos = stream_Tell(p_demux->s);
1492 int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
1493 long l_seek_secs = l_seek_time / 1000000000;
1494 uint64_t l_fwd_stamp = 1;
1496 /* if we're not seekable, there's nothing to do */
1497 if (!p_sys->b_seekable || !p_sys->b_have_master)
1498 return VLC_EGENERIC;
1500 msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
1501 l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
1503 /* seek to the proper segment if necessary */
1504 /* first see if we need to go back */
1505 while (l_seek_time < p_sys->l_first_ty_pts) {
1506 msg_Dbg(p_demux, "skipping to prior segment.");
1507 /* load previous part */
1508 if (i_cur_part == 0) {
1509 stream_Seek(p_demux->s, l_cur_pos);
1510 msg_Err(p_demux, "Attempt to seek past BOF");
1511 return VLC_EGENERIC;
1513 stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH);
1515 parse_master(p_demux);
1517 /* maybe we need to go forward */
1518 while (l_seek_time > p_sys->l_final_ty_pts) {
1519 msg_Dbg(p_demux, "skipping to next segment.");
1520 /* load next part */
1521 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1522 /* error; restore previous file position */
1523 stream_Seek(p_demux->s, l_cur_pos);
1524 msg_Err(p_demux, "seek error");
1525 return VLC_EGENERIC;
1527 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1529 parse_master(p_demux);
1532 /* our target is somewhere within this part;
1533 find the proper chunk using seq_table */
1534 for (i=1; i<p_sys->i_seq_table_size; i++) {
1535 if (p_sys->seq_table[i].l_timestamp > l_seek_time) {
1536 /* i-1 is the section we want; remember the next timestamp in case
1537 we have to use it (this section may not have a proper SEQ hdr
1538 for the time we're seeking) */
1539 msg_Dbg(p_demux, "stopping at seq entry %d.", i);
1540 l_fwd_stamp = p_sys->seq_table[i].l_timestamp;
1546 /* if we went through the entire last loop and didn't find our target,
1547 then we skip to the next part. What has happened is that the actual
1548 time we're seeking is within this part, but there isn't a SEQ hdr
1549 for it here. So we skip to the next part */
1550 if (i == p_sys->i_seq_table_size) {
1551 if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) {
1552 /* error; restore previous file position */
1553 stream_Seek(p_demux->s, l_cur_pos);
1554 msg_Err(p_demux, "seek error");
1555 return VLC_EGENERIC;
1557 stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH);
1559 parse_master(p_demux);
1563 /* determine which chunk has our seek_time */
1564 for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
1565 long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
1566 long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
1567 msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
1568 i_cur_part, l_chunk_nr,
1569 p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
1570 if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
1571 /* check this chunk's SEQ header timestamp */
1572 msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
1573 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
1574 stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
1576 // TODO: we don't have to parse the full header set;
1577 // just test the seq_rec entry for its timestamp
1578 p_sys->i_stuff_cnt = 0;
1579 get_chunk_header(p_demux);
1580 // check ty PTS for the SEQ entry in this chunk
1581 if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) {
1582 msg_Err(p_demux, "no SEQ hdr in chunk; table had one.");
1583 /* Seek to beginning of original chunk & reload it */
1584 stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE);
1585 p_sys->i_stuff_cnt = 0;
1586 get_chunk_header(p_demux);
1587 return VLC_EGENERIC;
1589 l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
1591 msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
1593 (l_seek_secs / 60) % 60, l_seek_secs % 60);
1594 if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
1595 // keep this one? go back?
1596 /* for now, we take this one. it's the first SEQ hdr AFTER
1597 the time we were searching for. */
1598 msg_Dbg(p_demux, "seek target found.");
1601 msg_Dbg(p_demux, "timestamp too early. still scanning.");
1604 /* if we made it through this entire loop without finding our target,
1605 then we skip to the next section. What has happened is that the actual
1606 time we're seeking is within this section, but there isn't a SEQ hdr
1607 for it here. So we skip to the next closest one (l_fwd_stamp) */
1608 if (i == p_sys->i_bits_per_seq_entry)
1609 return ty_stream_seek_time(p_demux, l_fwd_stamp);
1611 /* current stream ptr is at beginning of data for this chunk,
1612 so we need to skip past any stream data prior to the seq_rec
1615 for (i=0; i<p_sys->i_seq_rec; i++)
1616 i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
1617 stream_Read(p_demux->s, NULL, i_skip_cnt);
1618 p_sys->i_cur_rec = p_sys->i_seq_rec;
1619 //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
1620 //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
1626 /* parse a master chunk, filling the SEQ table and other variables.
1627 * We assume the stream is currently pointing to it.
1629 static void parse_master(demux_t *p_demux)
1631 demux_sys_t *p_sys = p_demux->p_sys;
1632 uint8_t mst_buf[32];
1634 int64_t i_save_pos = stream_Tell(p_demux->s);
1637 /* Note that the entries in the SEQ table in the stream may have
1638 different sizes depending on the bits per entry. We store them
1639 all in the same size structure, so we have to parse them out one
1640 by one. If we had a dynamic structure, we could simply read the
1641 entire table directly from the stream into memory in place. */
1643 /* clear the SEQ table */
1644 if (p_sys->seq_table != NULL)
1645 free(p_sys->seq_table);
1647 /* parse header info */
1648 stream_Read(p_demux->s, mst_buf, 32);
1649 i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */
1650 p_sys->i_bits_per_seq_entry = i_map_size * 8;
1651 i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */
1652 p_sys->i_seq_table_size = i / (8 + i_map_size);
1654 /* parse all the entries */
1655 p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
1656 for (i=0; i<p_sys->i_seq_table_size; i++) {
1657 stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
1658 p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
1659 if (i_map_size > 8) {
1660 msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
1661 memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
1663 memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
1667 /* set up a few of our variables */
1668 p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
1669 p_sys->l_final_ty_pts =
1670 p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
1671 p_sys->b_have_master = VLC_TRUE;
1673 i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
1674 msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d",
1675 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1676 i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
1677 msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
1678 (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
1680 /* seek past this chunk */
1681 stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
1685 /* ======================================================================== */
1686 /* "Peek" at some chunks. Skip over the Part header if we find it.
1687 * We parse the peeked data and determine audio type,
1688 * SA vs. DTivo, & Tivo Series.
1689 * Set global vars i_Pes_Length, i_Pts_Offset,
1690 * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */
1691 static int probe_stream(demux_t *p_demux)
1693 demux_sys_t *p_sys = p_demux->p_sys;
1694 const uint8_t *p_buf;
1696 vlc_bool_t b_probe_error = VLC_FALSE;
1698 /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
1699 if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
1700 CHUNK_PEEK_COUNT * CHUNK_SIZE) {
1701 msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT);
1702 /* TODO: if seekable, then loop reading chunks into a temp buffer */
1703 return VLC_EGENERIC;
1706 /* the real work: analyze this chunk */
1707 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
1708 analyze_chunk(p_demux, p_buf);
1709 if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN &&
1710 p_sys->audio_type != TIVO_AUDIO_UNKNOWN &&
1711 p_sys->tivo_type != TIVO_TYPE_UNKNOWN)
1713 p_buf += CHUNK_SIZE;
1716 /* the final tally */
1717 if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
1718 msg_Err(p_demux, "Can't determine Tivo Series.");
1719 b_probe_error = VLC_TRUE;
1721 if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
1722 msg_Err(p_demux, "Can't determine Tivo Audio Type.");
1723 b_probe_error = VLC_TRUE;
1725 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1726 msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
1727 b_probe_error = VLC_TRUE;
1729 return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
1733 /* ======================================================================== */
1734 /* gather statistics for this chunk & set our tivo-type vars accordingly */
1735 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
1737 demux_sys_t *p_sys = p_demux->p_sys;
1739 ty_rec_hdr_t *p_hdrs;
1740 int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
1741 uint32_t i_payload_size;
1743 /* skip if it's a Part header */
1744 if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
1747 /* number of records in chunk (we ignore high order byte;
1748 * rarely are there > 256 chunks & we don't need that many anyway) */
1749 i_num_recs = p_chunk[0];
1750 if (i_num_recs < 5) {
1751 /* try again with the next chunk. Sometimes there are dead ones */
1755 p_chunk += 4; /* skip past rec count & SEQ bytes */
1756 //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
1757 p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
1759 * 1. check video packets. Presence of 0x6e0 means S1.
1760 * No 6e0 but have be0 means S2.
1761 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
1762 * If AC-3, then we have DTivo.
1763 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
1765 i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0;
1766 for (i=0; i<i_num_recs; i++) {
1767 //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type,
1768 //p_hdrs[i].rec_type,
1769 //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type);
1770 switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) {
1785 msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.",
1786 i_num_6e0, i_num_be0);
1788 /* set up our variables */
1789 if (i_num_6e0 > 0) {
1790 msg_Dbg(p_demux, "detected Series 1 Tivo");
1791 p_sys->tivo_series = TIVO_SERIES1;
1792 p_sys->i_Pes_Length = SERIES1_PES_LENGTH;
1793 } else if (i_num_be0 > 0) {
1794 msg_Dbg(p_demux, "detected Series 2 Tivo");
1795 p_sys->tivo_series = TIVO_SERIES2;
1796 p_sys->i_Pes_Length = SERIES2_PES_LENGTH;
1798 if (i_num_9c0 > 0) {
1799 msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" );
1800 p_sys->audio_type = TIVO_AUDIO_AC3;
1801 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1802 p_sys->i_Pts_Offset = AC3_PTS_OFFSET;
1803 p_sys->i_Pes_Length = AC3_PES_LENGTH;
1804 } else if (i_num_3c0 > 0) {
1805 p_sys->audio_type = TIVO_AUDIO_MPEG;
1806 msg_Dbg(p_demux, "detected MPEG Audio" );
1809 /* if tivo_type still unknown, we can check PTS location
1810 * in MPEG packets to determine tivo_type */
1811 if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
1812 uint32_t i_data_offset = (16 * i_num_recs);
1813 for (i=0; i<i_num_recs; i++) {
1814 if ((p_hdrs[i].subrec_type << 0x08 | p_hdrs[i].rec_type) == 0x3c0 &&
1815 p_hdrs[i].l_rec_size > 15) {
1816 /* first make sure we're aligned */
1817 int i_pes_offset = find_es_header(ty_MPEGAudioPacket,
1818 &p_chunk[i_data_offset], 5);
1819 if (i_pes_offset >= 0) {
1820 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
1821 //msg_Dbg(p_demux, "probe: mpeg es header found in rec %d at offset %d",
1823 if ((p_chunk[i_data_offset + 6 + i_pes_offset] & 0x80) == 0x80) {
1824 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
1825 if (p_sys->tivo_series == TIVO_SERIES1)
1826 msg_Dbg(p_demux, "detected Stand-Alone Tivo" );
1827 p_sys->tivo_type = TIVO_TYPE_SA;
1828 p_sys->i_Pts_Offset = SA_PTS_OFFSET;
1830 if (p_sys->tivo_series == TIVO_SERIES1)
1831 msg_Dbg(p_demux, "detected DirecTV Tivo" );
1832 p_sys->tivo_type = TIVO_TYPE_DTIVO;
1833 p_sys->i_Pts_Offset = DTIVO_PTS_OFFSET;
1838 i_data_offset += p_hdrs[i].l_rec_size;
1845 /* =========================================================================== */
1846 static int get_chunk_header(demux_t *p_demux)
1848 int i_readSize, i_num_recs;
1850 const uint8_t *p_peek;
1851 demux_sys_t *p_sys = p_demux->p_sys;
1852 int i_payload_size; /* sum of all records' sizes */
1854 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_cur_chunk );
1856 /* if we have left-over filler space from the last chunk, get that */
1857 if (p_sys->i_stuff_cnt > 0) {
1858 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
1859 p_sys->i_stuff_cnt = 0;
1862 /* read the TY packet header */
1863 i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
1864 p_sys->i_cur_chunk++;
1866 if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
1873 /* check if it's a PART Header */
1874 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
1876 /* parse master chunk */
1877 parse_master(p_demux);
1878 return get_chunk_header(p_demux);
1881 /* number of records in chunk (8- or 16-bit number) */
1882 if (p_peek[3] & 0x80)
1884 /* 16 bit rec cnt */
1885 p_sys->i_num_recs = i_num_recs = (p_peek[1] << 8) + p_peek[0];
1886 p_sys->i_seq_rec = (p_peek[3] << 8) + p_peek[2];
1887 if (p_sys->i_seq_rec != 0xffff)
1889 p_sys->i_seq_rec &= ~0x8000;
1894 /* 8 bit reclen - tivo 1.3 format */
1895 p_sys->i_num_recs = i_num_recs = p_peek[0];
1896 p_sys->i_seq_rec = p_peek[1];
1898 p_sys->i_cur_rec = 0;
1899 p_sys->b_first_chunk = VLC_FALSE;
1901 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
1903 if (p_sys->rec_hdrs)
1904 free(p_sys->rec_hdrs);
1906 /* skip past the 4 bytes we "peeked" earlier */
1907 stream_Read( p_demux->s, NULL, 4 );
1909 /* read the record headers into a temp buffer */
1910 p_hdr_buf = malloc(i_num_recs * 16);
1911 if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
1913 p_sys->eof = VLC_TRUE;
1917 p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
1921 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
1922 (p_sys->i_num_recs * 16) - i_payload_size;
1923 if (p_sys->i_stuff_cnt > 0)
1924 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
1925 p_sys->i_stuff_cnt );
1930 static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
1931 int i_num_recs, int *pi_payload_size)
1934 ty_rec_hdr_t *p_hdrs, *p_rec_hdr;
1936 *pi_payload_size = 0;
1937 p_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
1939 for (i = 0; i < i_num_recs; i++)
1941 const uint8_t *record_header = p_buf + (i * 16);
1942 p_rec_hdr = &p_hdrs[i]; /* for brevity */
1943 p_rec_hdr->rec_type = record_header[3];
1944 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
1945 if ((record_header[ 0 ] & 0x80) == 0x80)
1948 /* marker bit 2 set, so read extended data */
1949 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
1950 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
1952 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
1953 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
1956 p_rec_hdr->ex1 = b1;
1957 p_rec_hdr->ex2 = b2;
1958 p_rec_hdr->l_rec_size = 0;
1959 p_rec_hdr->l_ty_pts = 0;
1960 p_rec_hdr->b_ext = VLC_TRUE;
1964 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
1965 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
1966 *pi_payload_size += p_rec_hdr->l_rec_size;
1967 p_rec_hdr->b_ext = VLC_FALSE;
1968 p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
1970 //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
1971 } /* end of record-header loop */