1 /*****************************************************************************
2 * ty.c - TiVo ty stream video demuxer for VLC
3 *****************************************************************************
4 * Copyright (C) 2005 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA 02111, 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 *****************************************************************************/
31 /*****************************************************************************
33 *****************************************************************************/
37 #include <vlc/input.h>
38 #include "vlc_codec.h"
40 #define SERIES1_PES_LENGTH (11)
41 #define SERIES2_PES_LENGTH (16)
42 #define AC3_PES_LENGTH (14)
43 #define DTIVO_PTS_OFFSET (6)
44 #define SA_PTS_OFFSET (9)
45 #define AC3_PTS_OFFSET (9)
46 static const unsigned char ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
47 static const unsigned char ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
48 static const unsigned char ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
50 /*****************************************************************************
52 *****************************************************************************/
53 static int get_chunk_header(demux_t *);
54 static void setup_audio_streams(char, demux_t *);
55 static mtime_t get_pts( unsigned char *buf );
56 static int find_es_header( unsigned const char *header,
57 unsigned char *buffer, int bufferSize, int *esOffset1 );
58 static int ty_stream_seek(demux_t *p_demux, double seek_pct);
60 static int TyOpen (vlc_object_t *);
61 static void TyClose(vlc_object_t *);
62 static int TyDemux(demux_t *);
63 static int Control(demux_t *, int, va_list);
65 /*****************************************************************************
67 *****************************************************************************/
69 set_shortname( _("TY") );
70 set_description(_("TY Stream audio/video demux"));
71 set_category( CAT_INPUT );
72 set_subcategory( SUBCAT_INPUT_DEMUX );
73 set_capability("demux2", 8);
74 /* FIXME: there seems to be a segfault when using PVR access
75 * and TY demux has a bigger priority than PS
76 * Something must be wrong.
78 set_callbacks(TyOpen, TyClose);
83 /* packet types for reference:
84 2/c0: audio data continued
85 3/c0: audio packet header (PES header)
86 4/c0: audio data (S/A only?)
87 9/c0: audio packet header, AC-3 audio
88 2/e0: video data continued
89 6/e0: video packet header (PES header)
90 7/e0: video sequence header start
91 8/e0: video I-frame header start
92 a/e0: video P-frame header start
93 b/e0: video B-frame header start
94 c/e0: video GOP header start
95 e/01: closed-caption data
96 e/02: Extended data services data
97 e/03: ipreview data ("thumbs up to record" signal)
100 #define TIVO_PES_FILEID ( 0xf5467abd )
101 #define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */
102 #define CHUNK_SIZE ( 128 * 1024 )
107 unsigned char ex1, ex2;
108 unsigned char rec_type;
109 unsigned char subrec_type;
115 es_out_id_t *p_video; /* ptr to video codec */
116 es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */
120 size_t i_stream_size; /* size of input stream (if known) */
121 vlc_bool_t b_seekable; /* is this stream seekable? */
122 int tivoType; /* 1 = SA, 2 = DTiVo */
123 vlc_bool_t b_mpeg_audio; /* true if we're using MPEG audio */
124 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
125 int i_pes_buf_cnt; /* how many bytes in our buffer */
127 mtime_t firstAudioPTS;
128 mtime_t lastAudioPTS;
129 mtime_t lastVideoPTS;
131 ty_rec_hdr_t *rec_hdrs; /* record headers array */
132 int i_cur_rec; /* current record in this chunk */
133 int i_num_recs; /* number of recs in this chunk */
134 int i_seq_rec; /* record number where seq start is */
136 vlc_bool_t b_first_chunk;
141 * TyOpen: check file and initialize demux structures
144 * 1. peek at the first 12 bytes of the stream for the
145 * magic TiVo PART header & stream type & chunk size
146 * 2. if it's not there, error with VLC_EGENERIC
147 * 3. set up video (mpgv) codec
148 * 4. return VLC_SUCCESS
150 static int TyOpen(vlc_object_t *p_this)
152 demux_t *p_demux = (demux_t *)p_this;
154 vlc_bool_t b_seekable;
158 /* see if this stream is seekable */
159 stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
161 /* peek at the first 12 bytes. */
162 /* for TY streams, they're always the same */
163 if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
166 if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
167 U32_AT(&p_peek[4]) != 0x02 ||
168 U32_AT(&p_peek[8]) != CHUNK_SIZE )
170 /* doesn't look like a TY file... */
171 char *psz_ext = strrchr(p_demux->psz_path, '.');
172 /* if they specified tydemux, or if the file ends in .ty we try anyway */
173 if (psz_ext && strcmp(p_demux->psz_demux, "tydemux") &&
174 strcasecmp(psz_ext, ".ty"))
176 msg_Warn(p_demux, "this does not look like a TY file, continuing anyway...");
179 /* at this point, we assume we have a valid TY stream */
180 msg_Dbg( p_demux, "valid TY stream detected" );
182 /* Set exported functions */
183 p_demux->pf_demux = TyDemux;
184 p_demux->pf_control = Control;
186 /* create our structure that will hold all data */
187 p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
188 memset(p_sys, 0, sizeof(demux_sys_t));
190 /* set up our struct (most were zero'd out with the memset above) */
191 p_sys->b_first_chunk = VLC_TRUE;
192 p_sys->firstAudioPTS = -1;
193 p_sys->i_stream_size = stream_Size(p_demux->s);
194 p_sys->b_mpeg_audio = VLC_FALSE;
195 p_sys->b_seekable = b_seekable;
197 /* TODO: read first chunk & parse first audio PTS, then (if seekable)
198 * seek to last chunk & last record; read its PTS and compute
199 * overall program time. Also determine Tivo type. */
201 /* NOTE: we wait to create the audio ES until we know what
202 * audio type we have. */
203 p_sys->p_audio = NULL;
205 /* register the video stream */
206 es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
207 p_sys->p_video = es_out_Add( p_demux->out, &fmt );
210 /* register the CC decoder */
211 es_format_Init( &fmt, SPU_ES, VLC_FOURCC('s', 'u', 'b', 't'));
212 p_sys->p_subt_es = es_out_Add(p_demux->out, &fmt);
219 /* set up audio codec.
220 * this will be called once we determine audio type */
221 static void setup_audio_streams(char stream_type, demux_t *p_demux)
223 demux_sys_t *p_sys = p_demux->p_sys;
226 if (stream_type == 'A') {
227 /* AC3 audio detected */
228 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
229 p_sys->tivoType = 2; /* AC3 is only on dtivo */
232 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
233 p_sys->b_mpeg_audio = VLC_TRUE;
235 /* register the chosen audio output codec */
236 p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
240 /* =========================================================================== */
241 /* Compute Presentation Time Stamp (PTS)
242 * Assume buf points to beginning of PTS */
243 static mtime_t get_pts( unsigned char *buf )
247 i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
248 (mtime_t)(buf[1] << 22)|
249 ((mtime_t)(buf[2]&0xfe) << 14)|
250 (mtime_t)(buf[3] << 7)|
251 (mtime_t)(buf[4] >> 1);
252 i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */
257 /* =========================================================================== */
258 static int find_es_header( unsigned const char *header,
259 unsigned char *buffer, int bufferSize, int *esOffset1 )
264 for( count = 0 ; count < bufferSize ; count++ )
266 if ( ( buffer[ count + 0 ] == header[ 0 ] ) &&
267 ( buffer[ count + 1 ] == header[ 1 ] ) &&
268 ( buffer[ count + 2 ] == header[ 2 ] ) &&
269 ( buffer[ count + 3 ] == header[ 3 ] ) )
279 /* =========================================================================== */
280 /* check if we have a full PES header, if not, then save what we have.
281 * this is called when audio-start packets are encountered.
283 * 1 partial PES hdr found, some audio data found (buffer adjusted),
284 * -1 partial PES hdr found, no audio data found
285 * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
286 /* TODO: fix it so it works with S2 / SA / DTivo / HD etc... */
287 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
288 int32_t offset, int32_t rec_len )
290 demux_sys_t *p_sys = p_demux->p_sys;
292 int pes_length = p_sys->b_mpeg_audio?SERIES1_PES_LENGTH:AC3_PES_LENGTH;
294 if( p_sys->tivoType == 1 )
297 pts_offset = SA_PTS_OFFSET;
302 pts_offset = p_sys->b_mpeg_audio?DTIVO_PTS_OFFSET:AC3_PTS_OFFSET;
304 if ( offset < 0 || offset + pes_length > rec_len )
306 /* entire PES header not present */
307 msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
309 /* save the partial pes header */
312 /* no header found, fake some 00's (this works, believe me) */
313 memset( p_sys->pes_buffer, 4, 0 );
314 p_sys->i_pes_buf_cnt = 4;
316 msg_Err( p_demux, "PES header not found in record of %d bytes!",
320 /* copy the partial pes header we found */
321 memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
323 p_sys->i_pes_buf_cnt = rec_len - offset;
327 /* PES Header was found, but not complete, so trim the end of this record */
328 p_block->i_buffer -= rec_len - offset;
331 return -1; /* partial PES, no audio data */
333 /* full PES header present, extract PTS */
334 p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset + pts_offset ] );
335 if (p_sys->firstAudioPTS < 0)
336 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
337 p_block->i_pts = p_sys->lastAudioPTS;
338 /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
339 /* adjust audio record to remove PES header */
340 memmove(p_block->p_buffer + offset, p_block->p_buffer + offset + pes_length,
341 rec_len - pes_length);
342 p_block->i_buffer -= pes_length;
347 /* =========================================================================== */
348 /* TyDemux: Read & Demux one record from the chunk
350 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
352 * NOTE: I think we can return the number of packets sent instead of just 1.
353 * that means we can demux an entire chunk and shoot it back (may be more efficient)
354 * -- should try that some day :) --
356 int TyDemux(demux_t *p_demux)
359 int recordsDecoded = 0;
365 ty_rec_hdr_t *rec_hdr;
367 block_t *p_block_in = NULL;
370 unsigned char lastCC[ 16 ];
371 unsigned char lastXDS[ 16 ];
373 demux_sys_t *p_sys = p_demux->p_sys;
375 /*msg_Dbg(p_demux, "ty demux processing" );*/
377 /* did we hit EOF earlier? */
378 if (p_sys->eof) return 0;
381 * what we do (1 record now.. maybe more later):
382 * - use stream_Read() to read the chunk header & record headers
383 * - discard entire chunk if it is a PART header chunk
384 * - parse all the headers into record header array
385 * - keep a pointer of which record we're on
386 * - use stream_Block() to fetch each record
387 * - parse out PTS from PES headers
388 * - set PTS for data packets
389 * - pass the data on to the proper codec via es_out_Send()
391 * if this is the first time or
392 * if we're at the end of this chunk, start a new one
394 /* parse the next chunk's record headers */
395 if (p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs)
396 if (get_chunk_header(p_demux) == 0)
399 /*======================================================================
400 * parse & send one record of the chunk
401 *====================================================================== */
402 i_cur_rec = p_sys->i_cur_rec;
404 rec_hdr = &p_sys->rec_hdrs[ i_cur_rec ];
405 subrec_type = rec_hdr->subrec_type;
406 rec_type = rec_hdr->rec_type;
407 l_rec_size = rec_hdr->l_rec_size;
411 /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
412 subrec_type, rec_type, l_rec_size );*/
414 /* some normal records are 0 length, so check for that... */
417 /* read in this record's payload */
418 if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
424 /* set these as 'unknown' for now */
425 p_block_in->i_pts = p_block_in->i_dts = 0;
429 /* no data in payload; we're done */
436 -- don't read any data from the stream, data was in the record header --
438 "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
439 rec_type, rec_hdr->ex1, rec_hdr->ex2);
442 /*================================================================*
444 *================================================================*/
445 if ( rec_type == 0xe0 )
447 if( subrec_type == 0x06 )
449 /* get the PTS from this packet.
450 * Do NOT Pass this packet (a PES Header) on to the MPEG2 codec */
451 find_es_header( ty_VideoPacket, p_block_in->p_buffer,
452 l_rec_size, &esOffset1 );
453 if ( esOffset1 != -1 )
455 /* msg_Dbg(p_demux, "Video PES hdr at offset %d", esOffset1); */
456 p_sys->lastVideoPTS = get_pts( &p_block_in->p_buffer[ esOffset1 + 9 ] );
457 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
458 p_sys->lastVideoPTS );*/
460 block_Release(p_block_in);
465 msg_Dbg(p_demux, "packet buffer has "
466 "%02x %02x %02x %02x %02x %02x %02x %02x "
467 "%02x %02x %02x %02x %02x %02x %02x %02x",
468 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
469 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
470 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
471 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
472 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
473 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
474 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
475 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
477 /* if it's not a continue blk, then set PTS */
478 if (subrec_type != 0x02)
480 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
482 /* if it's a GOP header, make sure it's legal
483 * (if we have enough data) */
484 /* Some ty files don't have this bit set
485 * and it causes problems */
486 if (subrec_type == 0x0c && l_rec_size >= 6)
487 p_block_in->p_buffer[5] |= 0x08;
488 /* set PTS for this block before we send */
489 if (p_sys->lastVideoPTS > 0)
491 p_block_in->i_pts = p_sys->lastVideoPTS;
492 /* PTS gets used ONCE.
493 * Any subsequent frames we get BEFORE next PES
494 * header will have their PTS computed in the codec */
495 p_sys->lastVideoPTS = 0;
498 es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
500 } /* end if video rec type */
501 /* ================================================================
503 * ================================================================
504 * parse PES headers and send the rest to the codec
506 else if ( rec_type == 0xc0 )
510 printf( "Audio Packet Header " );
511 for( i = 0 ; i < 24 ; i++ )
512 printf( "%2.2x ", p_block_in->p_buffer[i] );
515 /* load a codec if we haven't yet */
516 if ( p_sys->p_audio == NULL )
518 if ( subrec_type == 0x09 )
520 /* set up for AC-3 audio */
521 msg_Dbg(p_demux, "detected AC-3 Audio" );
522 setup_audio_streams('A', p_demux);
526 /* set up for MPEG audio */
527 msg_Dbg(p_demux, "detected MPEG Audio" );
528 setup_audio_streams('M', p_demux);
532 /* SA or DTiVo Audio Data, no PES (continued block)
533 * ================================================
535 if ( subrec_type == 2 )
537 /* continue PES if previous was incomplete */
538 /* TODO: Make this work for all series & types of tivos */
539 if (p_sys->i_pes_buf_cnt > 0)
541 int i_need = SERIES1_PES_LENGTH - p_sys->i_pes_buf_cnt;
543 msg_Dbg(p_demux, "continuing PES header");
544 /* do we have enough data to complete? */
545 if (i_need < l_rec_size)
547 /* we have enough; reconstruct this p_frame with the new hdr */
548 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
549 p_block_in->p_buffer, i_need);
550 /* advance the block past the PES header (don't want to send it) */
551 p_block_in->p_buffer += i_need;
552 p_block_in->i_buffer -= i_need;
553 /* get the PTS out of this PES header (MPEG or AC3) */
554 if (p_sys->b_mpeg_audio)
555 find_es_header(ty_MPEGAudioPacket, p_sys->pes_buffer,
558 find_es_header(ty_AC3AudioPacket, p_sys->pes_buffer,
562 /* god help us; something's really wrong */
563 msg_Err(p_demux, "can't find audio PES header in packet");
567 p_sys->lastAudioPTS = get_pts(
568 &p_sys->pes_buffer[ esOffset1 + DTIVO_PTS_OFFSET ] );
569 p_block_in->i_pts = p_sys->lastAudioPTS;
571 p_sys->i_pes_buf_cnt = 0;
575 /* don't have complete PES hdr; save what we have and return */
576 memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
577 p_block_in->p_buffer, l_rec_size);
578 p_sys->i_pes_buf_cnt += l_rec_size;
580 block_Release(p_block_in);
584 /* set PCR before we send */
585 /*es_out_Control( p_demux->out, ES_OUT_SET_PCR,
586 p_block_in->i_pts );*/
587 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
590 /* MPEG Audio with PES Header, either SA or DTiVo */
591 /* ================================================ */
592 if ( subrec_type == 0x03 )
594 find_es_header( ty_MPEGAudioPacket, p_block_in->p_buffer,
595 l_rec_size, &esOffset1 );
597 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
598 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
599 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
600 msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
602 /* SA PES Header, No Audio Data */
603 /* ================================================ */
604 if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
607 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
609 if (p_sys->firstAudioPTS < 0)
610 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
611 block_Release(p_block_in);
612 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
613 p_sys->lastAudioPTS );*/
616 /* DTiVo Audio with PES Header */
617 /* ================================================ */
621 /* Check for complete PES
622 * (TODO: handle proper size for tivo version) */
623 if (check_sync_pes(p_demux, p_block_in, esOffset1,
626 /* partial PES header found, nothing else.
629 block_Release(p_block_in);
633 msg_Dbg(p_demux, "packet buffer has "
634 "%02x %02x %02x %02x %02x %02x %02x %02x "
635 "%02x %02x %02x %02x %02x %02x %02x %02x",
636 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
637 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
638 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
639 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
640 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
641 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
642 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
643 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
645 /* set PCR before we send */
646 if( p_block_in->i_pts > 0 )
647 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
649 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
651 } /* if subrec == 0x03 */
653 /* SA Audio with no PES Header */
654 /* ================================================ */
655 if ( subrec_type == 0x04 )
658 "Adding SA Audio Packet Size %ld", l_rec_size ); */
660 /* set PCR before we send */
661 if (p_sys->lastAudioPTS > 0)
663 p_block_in->i_pts = p_sys->lastAudioPTS;
664 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
667 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
670 /* DTiVo AC3 Audio Data with PES Header */
671 /* ================================================ */
672 if ( subrec_type == 0x09 )
674 find_es_header( ty_AC3AudioPacket, p_block_in->p_buffer,
675 l_rec_size, &esOffset1 );
677 /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
678 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
679 p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
680 msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);*/
682 /* Check for complete PES */
683 if (check_sync_pes(p_demux, p_block_in, esOffset1,
686 /* partial PES header found, nothing else. we're done. */
690 /* set PCR before we send (if PTS found) */
691 if( p_block_in->i_pts > 0 )
693 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
696 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
698 } /* end "if audio" */
699 /* ================================================================ */
701 /* ================================================================ */
702 else if ( rec_type == 0x01 )
704 /*msg_Dbg(p_demux, "CC1 %02x %02x [%c%c]", rec_hdr->ex1,
705 rec_hdr->ex2, rec_hdr->ex1, rec_hdr->ex2 );*/
707 /* construct a 'user-data' MPEG packet */
708 lastCC[ 0x00 ] = 0x00;
709 lastCC[ 0x01 ] = 0x00;
710 lastCC[ 0x02 ] = 0x01;
711 lastCC[ 0x03 ] = 0xb2;
712 lastCC[ 0x04 ] = 'T'; /* vcdimager code says this byte should be 0x11 */
713 lastCC[ 0x05 ] = 'Y'; /* (no other notes there) */
714 lastCC[ 0x06 ] = 0x01;
715 lastCC[ 0x07 ] = rec_hdr->ex1;
716 lastCC[ 0x08 ] = rec_hdr->ex2;
717 /* not sure what to send, because VLC doesn't yet support
718 * teletext type of subtitles (only supports the full-sentence type) */
719 /*p_block_in = block_NewEmpty(); ????
720 es_out_Send( p_demux->out, p_sys->p_subt_es, p_block_in );*/
722 else if ( rec_type == 0x02 )
724 /*msg_Dbg(p_demux, "CC2 %02x %02x", rec_hdr->ex1, rec_hdr->ex2 );*/
726 /* construct a 'user-data' MPEG packet */
727 lastXDS[ 0x00 ] = 0x00;
728 lastXDS[ 0x01 ] = 0x00;
729 lastXDS[ 0x02 ] = 0x01;
730 lastXDS[ 0x03 ] = 0xb2;
731 lastXDS[ 0x04 ] = 'T'; /* vcdimager code says this byte should be 0x11 */
732 lastXDS[ 0x05 ] = 'Y'; /* (no other notes there) */
733 lastXDS[ 0x06 ] = 0x02;
734 lastXDS[ 0x07 ] = rec_hdr->ex1;
735 lastXDS[ 0x08 ] = rec_hdr->ex2;
736 /* not sure what to send, because VLC doesn't support this?? */
737 /*p_block_in = block_NewEmpty(); ????
738 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );*/
740 /* ================================================================ */
741 /* Tivo data services (e.g. "thumbs-up to record!") useless for us */
742 /* ================================================================ */
743 else if ( rec_type == 0x03 )
746 /* ================================================================ */
747 /* Unknown, but seen regularly */
748 /* ================================================================ */
749 else if ( rec_type == 0x05 )
754 msg_Dbg(p_demux, "Invalid record type 0x%02x", rec_type );
755 if (p_block_in) block_Release(p_block_in);
763 /* seek to a position within the stream, if possible */
764 static int ty_stream_seek(demux_t *p_demux, double seek_pct)
766 demux_sys_t *p_sys = p_demux->p_sys;
767 int64_t seek_pos = p_sys->i_stream_size * seek_pct;
771 /* if we're not seekable, there's nothing to do */
772 if (!p_sys->b_seekable)
775 /* figure out which chunk we want & go there */
776 p_sys->i_chunk_count = seek_pos / CHUNK_SIZE;
778 if ( stream_Seek( p_demux->s, p_sys->i_chunk_count * CHUNK_SIZE))
780 /* can't seek stream */
784 get_chunk_header(p_demux);
786 /* seek within the chunk to get roughly to where we want */
787 p_sys->i_cur_rec = (int)
788 ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
789 msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
790 msg_Dbg(p_demux, " (chunk %d, record %d)",
791 p_sys->i_chunk_count - 1, p_sys->i_cur_rec);
793 /* seek to the start of this record's data.
794 * to do that, we have to skip past all prior records */
796 for (i=0; i<p_sys->i_cur_rec; i++)
797 l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
798 stream_Seek(p_demux->s, ((p_sys->i_chunk_count-1) * CHUNK_SIZE) +
799 (p_sys->i_num_recs * 16) + l_skip_amt + 4);
801 /* to hell with syncing any audio or video, just start reading records... :) */
802 /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
803 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
808 static int Control(demux_t *p_demux, int i_query, va_list args)
810 demux_sys_t *p_sys = p_demux->p_sys;
814 /*msg_Info(p_demux, "control cmd %d", i_query);*/
817 case DEMUX_GET_POSITION:
818 /* arg is 0.0 - 1.0 percent of overall file position */
819 if( ( i64 = p_sys->i_stream_size ) > 0 )
821 pf = (double*) va_arg( args, double* );
822 *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
827 case DEMUX_SET_POSITION:
828 /* arg is 0.0 - 1.0 percent of overall file position */
829 f = (double) va_arg( args, double );
830 //msg_Dbg(p_demux, "Control - set position to %2.3f", f);
831 if ((i64 = p_sys->i_stream_size) > 0)
832 return ty_stream_seek(p_demux, f);
835 /* return latest PTS - start PTS */
836 p_i64 = (int64_t *) va_arg(args, int64_t *);
837 *p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
839 case DEMUX_SET_TIME: /* arg is time in microsecs */
840 case DEMUX_GET_LENGTH: /* length of program in microseconds, 0 if unk */
848 /* =========================================================================== */
849 static void TyClose( vlc_object_t *p_this )
851 demux_sys_t *p_sys = ((demux_t *) p_this)->p_sys;
853 free(p_sys->rec_hdrs);
858 /* =========================================================================== */
859 static int get_chunk_header(demux_t *p_demux)
861 int i_readSize, i_num_recs, i;
862 uint8_t packet_header[4];
863 uint8_t record_header[16];
864 ty_rec_hdr_t *p_rec_hdr;
865 demux_sys_t *p_sys = p_demux->p_sys;
866 int i_payload_size = 0; /* sum of all records */
868 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_chunk_count );
870 /* if we have left-over filler space from the last chunk, get that */
871 if (p_sys->i_stuff_cnt > 0)
872 stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
874 /* read the TY packet header */
875 i_readSize = stream_Read( p_demux->s, packet_header, 4 );
876 p_sys->i_chunk_count++;
878 if ( i_readSize < 4 )
885 /* if it's a PART Header, then try again. */
886 if( U32_AT( &packet_header[ 0 ] ) == TIVO_PES_FILEID )
888 msg_Dbg( p_demux, "skipping TY PART Header" );
889 /* TODO: if stream is seekable, should we seek() instead of read() ?? */
890 stream_Read( p_demux->s, NULL, CHUNK_SIZE - 4 );
891 return get_chunk_header(p_demux);
894 /* number of records in chunk (8- or 16-bit number) */
895 if (packet_header[3] & 0x80)
898 p_sys->i_num_recs = i_num_recs = (packet_header[1] << 8) + packet_header[0];
899 p_sys->i_seq_rec = (packet_header[3] << 8) + packet_header[2];
900 if (p_sys->i_seq_rec != 0xffff)
902 p_sys->i_seq_rec &= ~0x8000;
907 /* 8 bit reclen - tivo 1.3 format */
908 p_sys->i_num_recs = i_num_recs = packet_header[0];
909 p_sys->i_seq_rec = packet_header[1];
911 p_sys->i_cur_rec = 0;
912 p_sys->b_first_chunk = VLC_FALSE;
914 /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
916 /* parse headers into array */
918 free(p_sys->rec_hdrs);
919 p_sys->rec_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
920 for (i = 0; i < i_num_recs; i++)
922 i_readSize = stream_Read( p_demux->s, record_header, 16 );
926 p_sys->eof = VLC_TRUE;
929 p_rec_hdr = &p_sys->rec_hdrs[i]; /* for brevity */
930 p_rec_hdr->rec_type = record_header[3];
931 p_rec_hdr->subrec_type = record_header[2] & 0x0f;
932 if ((record_header[ 0 ] & 0x80) == 0x80)
934 unsigned char b1, b2;
935 /* marker bit 2 set, so read extended data */
936 b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
937 ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
939 b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
940 ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
945 p_rec_hdr->l_rec_size = 0;
946 p_rec_hdr->b_ext = VLC_TRUE;
950 p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
951 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
952 i_payload_size += p_rec_hdr->l_rec_size;
953 p_rec_hdr->b_ext = VLC_FALSE;
955 } /* end of record-header loop */
956 p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
957 (p_sys->i_num_recs * 16) - i_payload_size;
958 if (p_sys->i_stuff_cnt > 0)
959 msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
960 p_sys->i_stuff_cnt );