]> git.sesse.net Git - vlc/blob - modules/demux/ty.c
1b76192a48d6534aa499d5a434af495515bd1b38
[vlc] / modules / demux / ty.c
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
8  *
9  * $Id$
10  *
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.
15  *
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.
20  *
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.
24  *
25  * CODE CHANGES:
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  *****************************************************************************/
30
31 /*****************************************************************************
32  * Preamble
33  *****************************************************************************/
34
35 #include <vlc/vlc.h>
36 #include <vlc_demux.h>
37 #include "vlc_codec.h"
38
39 #define SERIES1_PES_LENGTH  (11)
40 #define SERIES2_PES_LENGTH  (16)
41 #define AC3_PES_LENGTH      (14)
42 #define DTIVO_PTS_OFFSET    (6)
43 #define SA_PTS_OFFSET       (9)
44 #define AC3_PTS_OFFSET      (9)
45 static const unsigned char ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
46 static const unsigned char ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
47 static const unsigned char ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52 static int get_chunk_header(demux_t *);
53 static void setup_audio_streams(char, demux_t *);
54 static mtime_t get_pts( unsigned char *buf );
55 static int find_es_header( unsigned const char *header,
56    unsigned char *buffer, int bufferSize, int *esOffset1 );
57 static int ty_stream_seek(demux_t *p_demux, double seek_pct);
58
59 static int TyOpen (vlc_object_t *);
60 static void TyClose(vlc_object_t *);
61 static int TyDemux(demux_t *);
62 static int Control(demux_t *, int, va_list);
63
64 /*****************************************************************************
65  * Module descriptor
66  *****************************************************************************/
67 vlc_module_begin();
68     set_shortname( "TY" );
69     set_description(_("TY Stream audio/video demux"));
70     set_category( CAT_INPUT );
71     set_subcategory( SUBCAT_INPUT_DEMUX );
72     set_capability("demux2", 6);
73     /* FIXME: there seems to be a segfault when using PVR access
74      * and TY demux has a bigger priority than PS
75      * Something must be wrong.
76      */
77     set_callbacks(TyOpen, TyClose);
78     add_shortcut("ty");
79     add_shortcut("tivo");
80 vlc_module_end();
81
82 /* packet types for reference:
83  2/c0: audio data continued
84  3/c0: audio packet header (PES header)
85  4/c0: audio data (S/A only?)
86  9/c0: audio packet header, AC-3 audio
87  2/e0: video data continued
88  6/e0: video packet header (PES header)
89  7/e0: video sequence header start
90  8/e0: video I-frame header start
91  a/e0: video P-frame header start
92  b/e0: video B-frame header start
93  c/e0: video GOP header start
94  e/01: closed-caption data
95  e/02: Extended data services data 
96  e/03: ipreview data ("thumbs up to record" signal)
97 */
98
99 #define TIVO_PES_FILEID   ( 0xf5467abd )
100 #define TIVO_PART_LENGTH  ( 0x20000000 )    /* 536,870,912 bytes */
101 #define CHUNK_SIZE        ( 128 * 1024 )
102
103 typedef struct
104 {
105   long l_rec_size;
106   unsigned char ex1, ex2;
107   unsigned char rec_type;
108   unsigned char subrec_type;
109   char b_ext;
110 } ty_rec_hdr_t;
111
112 struct demux_sys_t
113 {
114   es_out_id_t *p_video;               /* ptr to video codec */
115   es_out_id_t *p_audio;               /* holds either ac3 or mpeg codec ptr */
116
117   int             i_chunk_count;
118   int             i_stuff_cnt;
119   size_t          i_stream_size;      /* size of input stream (if known) */
120   vlc_bool_t      b_seekable;         /* is this stream seekable? */
121   int             tivoType;           /* 1 = SA, 2 = DTiVo */
122   vlc_bool_t      b_mpeg_audio;       /* true if we're using MPEG audio */
123   uint8_t         pes_buffer[20];     /* holds incomplete pes headers */
124   int             i_pes_buf_cnt;      /* how many bytes in our buffer */
125
126   mtime_t         firstAudioPTS;
127   mtime_t         lastAudioPTS;
128   mtime_t         lastVideoPTS;
129
130   ty_rec_hdr_t    *rec_hdrs;          /* record headers array */
131   int             i_cur_rec;          /* current record in this chunk */
132   int             i_num_recs;         /* number of recs in this chunk */
133   int             i_seq_rec;          /* record number where seq start is */
134   vlc_bool_t      eof;
135   vlc_bool_t      b_first_chunk;
136 };
137
138
139 /*
140  * TyOpen: check file and initialize demux structures
141  *
142  * here's what we do:
143  * 1. peek at the first 12 bytes of the stream for the
144  *    magic TiVo PART header & stream type & chunk size
145  * 2. if it's not there, error with VLC_EGENERIC
146  * 3. set up video (mpgv) codec
147  * 4. return VLC_SUCCESS
148  */
149 static int TyOpen(vlc_object_t *p_this)
150 {
151     demux_t *p_demux = (demux_t *)p_this;
152     demux_sys_t *p_sys;
153     es_format_t fmt;
154     uint8_t *p_peek;
155
156     /* peek at the first 12 bytes. */
157     /* for TY streams, they're always the same */
158     if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
159         return VLC_EGENERIC;
160
161     if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
162          U32_AT(&p_peek[4]) != 0x02 ||
163          U32_AT(&p_peek[8]) != CHUNK_SIZE )
164     {
165         /* doesn't look like a TY file... */
166         char *psz_ext = strrchr(p_demux->psz_path, '.');
167
168         if( !p_demux->b_force &&
169             (!psz_ext || strcasecmp(psz_ext, ".ty")) ) return VLC_EGENERIC;
170         msg_Warn( p_demux, "this does not look like a TY file, "
171                   "continuing anyway..." );
172     }
173
174     /* at this point, we assume we have a valid TY stream */  
175     msg_Dbg( p_demux, "valid TY stream detected" );
176
177     /* Set exported functions */
178     p_demux->pf_demux = TyDemux;
179     p_demux->pf_control = Control;
180
181     /* create our structure that will hold all data */
182     p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
183     memset(p_sys, 0, sizeof(demux_sys_t));
184
185     /* set up our struct (most were zero'd out with the memset above) */
186     p_sys->b_first_chunk = VLC_TRUE;
187     p_sys->firstAudioPTS = -1;
188     p_sys->i_stream_size = stream_Size(p_demux->s);
189     p_sys->b_mpeg_audio = VLC_FALSE;
190
191     /* see if this stream is seekable */
192     stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
193
194     /* TODO: read first chunk & parse first audio PTS, then (if seekable)
195      *       seek to last chunk & last record; read its PTS and compute
196      *       overall program time.  Also determine Tivo type.   */
197
198     /* NOTE: we wait to create the audio ES until we know what
199      * audio type we have.   */
200     p_sys->p_audio = NULL;
201
202     /* register the video stream */
203     es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
204     p_sys->p_video = es_out_Add( p_demux->out, &fmt );
205
206 #if 0
207     /* register the CC decoder */
208     es_format_Init( &fmt, SPU_ES, VLC_FOURCC('s', 'u', 'b', 't'));
209     p_sys->p_subt_es = es_out_Add(p_demux->out, &fmt);
210 #endif
211
212     return VLC_SUCCESS;
213 }
214
215
216 /* set up audio codec.
217  * this will be called once we determine audio type */
218 static void setup_audio_streams(char stream_type, demux_t *p_demux)
219 {
220     demux_sys_t *p_sys = p_demux->p_sys;
221     es_format_t  fmt;
222
223     if (stream_type == 'A') {
224         /* AC3 audio detected */
225         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
226         p_sys->tivoType = 2;      /* AC3 is only on dtivo */
227     } else {
228         /* assume MPEG */
229         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
230         p_sys->b_mpeg_audio = VLC_TRUE;
231     }
232     /* register the chosen audio output codec */
233     p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
234 }
235
236
237 /* =========================================================================== */
238 /* Compute Presentation Time Stamp (PTS)
239  * Assume buf points to beginning of PTS */
240 static mtime_t get_pts( unsigned char *buf )
241 {
242     mtime_t i_pts;
243
244     i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
245              (mtime_t)(buf[1] << 22)|
246             ((mtime_t)(buf[2]&0xfe) << 14)|
247              (mtime_t)(buf[3] << 7)|
248              (mtime_t)(buf[4] >> 1);
249     i_pts *= 100 / 9;   /* convert PTS (90Khz clock) to microseconds */
250     return i_pts;
251 }
252
253
254 /* =========================================================================== */
255 static int find_es_header( unsigned const char *header,
256    unsigned char *buffer, int bufferSize, int *esOffset1 )
257 {
258     int count;
259
260     *esOffset1 = -1;
261     for( count = 0 ; count < bufferSize ; count++ )
262     {
263         if ( ( buffer[ count + 0 ] == header[ 0 ] ) &&
264              ( buffer[ count + 1 ] == header[ 1 ] ) &&
265              ( buffer[ count + 2 ] == header[ 2 ] ) &&
266              ( buffer[ count + 3 ] == header[ 3 ] ) )
267         {
268             *esOffset1 = count;
269             return 1;
270         }
271     }
272     return( -1 );
273 }
274
275
276 /* =========================================================================== */
277 /* check if we have a full PES header, if not, then save what we have.
278  * this is called when audio-start packets are encountered.
279  * Returns:
280  *     1 partial PES hdr found, some audio data found (buffer adjusted),
281  *    -1 partial PES hdr found, no audio data found
282  *     0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
283 /* TODO: fix it so it works with S2 / SA / DTivo / HD etc... */
284 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
285                            int32_t offset, int32_t rec_len )
286 {
287     demux_sys_t *p_sys = p_demux->p_sys;
288     int pts_offset;
289     int pes_length = p_sys->b_mpeg_audio?SERIES1_PES_LENGTH:AC3_PES_LENGTH;
290
291     if( p_sys->tivoType == 1 )
292     {
293         /* SA tivo */
294         pts_offset = SA_PTS_OFFSET;
295     }
296     else
297     {
298         /* DTivo */
299         pts_offset = p_sys->b_mpeg_audio?DTIVO_PTS_OFFSET:AC3_PTS_OFFSET;
300     }
301     if ( offset < 0 || offset + pes_length > rec_len )
302     {
303         /* entire PES header not present */
304         msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
305                  offset );
306         /* save the partial pes header */
307         if( offset < 0 )
308         {
309             /* no header found, fake some 00's (this works, believe me) */
310             memset( p_sys->pes_buffer, 0, 4 );
311             p_sys->i_pes_buf_cnt = 4;
312             if( rec_len > 4 )
313                 msg_Err( p_demux, "PES header not found in record of %d bytes!",
314                          rec_len );
315             return -1;
316         }
317         /* copy the partial pes header we found */
318         memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
319                 rec_len - offset );
320         p_sys->i_pes_buf_cnt = rec_len - offset;
321
322         if( offset > 0 )
323         {
324             /* PES Header was found, but not complete, so trim the end of this record */
325             p_block->i_buffer -= rec_len - offset;
326             return 1;
327         }
328         return -1;    /* partial PES, no audio data */
329     }
330     /* full PES header present, extract PTS */
331     p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset + pts_offset ] );
332     if (p_sys->firstAudioPTS < 0)
333         p_sys->firstAudioPTS = p_sys->lastAudioPTS;
334     p_block->i_pts = p_sys->lastAudioPTS;
335     /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
336     /* adjust audio record to remove PES header */
337     memmove(p_block->p_buffer + offset, p_block->p_buffer + offset + pes_length,
338             rec_len - pes_length);
339     p_block->i_buffer -= pes_length;
340     return 0;
341 }
342
343
344 /* =========================================================================== */
345 /* TyDemux: Read & Demux one record from the chunk
346  *
347  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
348  *
349  * NOTE: I think we can return the number of packets sent instead of just 1.
350  * that means we can demux an entire chunk and shoot it back (may be more efficient)
351  * -- should try that some day :) --
352  */
353 int TyDemux(demux_t *p_demux)
354 {
355     int              invalidType = 0;
356     int              recordsDecoded = 0;
357
358     int              rec_type;
359     long             l_rec_size;
360     int              i_cur_rec;
361     int              subrec_type;
362     ty_rec_hdr_t     *rec_hdr;
363
364     block_t          *p_block_in = NULL;
365     int              esOffset1;
366
367     unsigned char    lastCC[ 16 ];
368     unsigned char    lastXDS[ 16 ];
369
370     demux_sys_t      *p_sys = p_demux->p_sys;
371
372     /*msg_Dbg(p_demux, "ty demux processing" );*/
373
374     /* did we hit EOF earlier? */
375     if (p_sys->eof) return 0;
376
377     /*
378      * what we do (1 record now.. maybe more later):
379     * - use stream_Read() to read the chunk header & record headers
380     * - discard entire chunk if it is a PART header chunk
381     * - parse all the headers into record header array
382     * - keep a pointer of which record we're on
383     * - use stream_Block() to fetch each record
384     * - parse out PTS from PES headers
385     * - set PTS for data packets
386     * - pass the data on to the proper codec via es_out_Send()
387
388     * if this is the first time or
389     * if we're at the end of this chunk, start a new one
390     */
391     /* parse the next chunk's record headers */
392     if (p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs)
393         if (get_chunk_header(p_demux) == 0)
394             return 0;
395
396     /*======================================================================
397      * parse & send one record of the chunk
398      *====================================================================== */
399     i_cur_rec = p_sys->i_cur_rec;
400     recordsDecoded++;
401     rec_hdr = &p_sys->rec_hdrs[ i_cur_rec ];
402     subrec_type = rec_hdr->subrec_type;
403     rec_type = rec_hdr->rec_type;
404     l_rec_size = rec_hdr->l_rec_size;
405
406     if (!rec_hdr->b_ext)
407     {
408         /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
409                     subrec_type, rec_type, l_rec_size );*/
410   
411         /* some normal records are 0 length, so check for that... */
412         if (l_rec_size > 0)
413         {
414             /* read in this record's payload */
415             if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
416             {
417                 /* EOF */
418                 p_sys->eof = 1;
419                 return 0;
420             }
421             /* set these as 'unknown' for now */
422             p_block_in->i_pts = p_block_in->i_dts = 0;
423         }
424         else
425         {
426             /* no data in payload; we're done */
427             p_sys->i_cur_rec++;
428             return 1;
429         }
430     }
431     /*else
432     {
433         -- don't read any data from the stream, data was in the record header --
434         msg_Dbg(p_demux,
435                "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
436                 rec_type, rec_hdr->ex1, rec_hdr->ex2);
437     }*/
438
439     /*================================================================*
440      * Video Parsing
441      *================================================================*/
442     if ( rec_type == 0xe0 )
443     {
444         if( subrec_type == 0x06 )
445         {
446             /* get the PTS from this packet.
447              * Do NOT Pass this packet (a PES Header) on to the MPEG2 codec */
448             find_es_header( ty_VideoPacket, p_block_in->p_buffer,
449                             l_rec_size, &esOffset1 );
450             if ( esOffset1 != -1 )
451             {
452                 /* msg_Dbg(p_demux, "Video PES hdr at offset %d", esOffset1); */
453                 p_sys->lastVideoPTS = get_pts( &p_block_in->p_buffer[ esOffset1 + 9 ] );
454                 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
455                             p_sys->lastVideoPTS );*/
456             }
457             block_Release(p_block_in);
458         }
459         else
460         {
461 #if 0
462             msg_Dbg(p_demux, "packet buffer has "
463                     "%02x %02x %02x %02x %02x %02x %02x %02x "
464                     "%02x %02x %02x %02x %02x %02x %02x %02x",
465                     p_block_in->p_buffer[0], p_block_in->p_buffer[1],
466                     p_block_in->p_buffer[2], p_block_in->p_buffer[3],
467                     p_block_in->p_buffer[4], p_block_in->p_buffer[5],
468                     p_block_in->p_buffer[6], p_block_in->p_buffer[7],
469                     p_block_in->p_buffer[8], p_block_in->p_buffer[9],
470                     p_block_in->p_buffer[10], p_block_in->p_buffer[11],
471                     p_block_in->p_buffer[12], p_block_in->p_buffer[13],
472                     p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
473 #endif
474             /* if it's not a continue blk, then set PTS */
475             if (subrec_type != 0x02)
476             {
477                 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
478                            subrec_type);*/
479                 /* if it's a GOP header, make sure it's legal
480                  * (if we have enough data) */
481                 /* Some ty files don't have this bit set
482                  * and it causes problems */
483                 if (subrec_type == 0x0c && l_rec_size >= 6)
484                     p_block_in->p_buffer[5] |= 0x08;
485                 /* set PTS for this block before we send */
486                 if (p_sys->lastVideoPTS > 0)
487                 {
488                     p_block_in->i_pts = p_sys->lastVideoPTS;
489                     /* PTS gets used ONCE. 
490                      * Any subsequent frames we get BEFORE next PES
491                      * header will have their PTS computed in the codec */
492                     p_sys->lastVideoPTS = 0;
493                 }
494             } 
495             es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
496         }
497     } /* end if video rec type */
498     /* ================================================================
499      * Audio Parsing
500      * ================================================================
501      * parse PES headers and send the rest to the codec
502      */
503     else if ( rec_type == 0xc0 )
504     {
505 #if 0
506         int i;
507         printf( "Audio Packet Header " );
508         for( i = 0 ; i < 24 ; i++ )
509             printf( "%2.2x ", p_block_in->p_buffer[i] );
510         printf( "\n" );
511 #endif
512         /* load a codec if we haven't yet */
513         if ( p_sys->p_audio == NULL )
514         {
515             if ( subrec_type == 0x09 )
516             {
517                 /* set up for AC-3 audio */
518                 msg_Dbg(p_demux, "detected AC-3 Audio" );
519                         setup_audio_streams('A', p_demux);
520             }
521             else
522             {
523                 /* set up for MPEG audio */
524                 msg_Dbg(p_demux, "detected MPEG Audio" );
525                 setup_audio_streams('M', p_demux);
526             }
527         }
528
529         /* SA or DTiVo Audio Data, no PES (continued block)
530          * ================================================
531          */
532         if ( subrec_type == 2 )
533         {
534             /* continue PES if previous was incomplete */
535             /* TODO: Make this work for all series & types of tivos */
536             if (p_sys->i_pes_buf_cnt > 0)
537             {
538                 int i_need = SERIES1_PES_LENGTH - p_sys->i_pes_buf_cnt;
539
540                 msg_Dbg(p_demux, "continuing PES header");
541                 /* do we have enough data to complete? */
542                 if (i_need < l_rec_size)
543                 {
544                     /* we have enough; reconstruct this p_frame with the new hdr */
545                     memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
546                            p_block_in->p_buffer, i_need);
547                     /* advance the block past the PES header (don't want to send it) */
548                     p_block_in->p_buffer += i_need;
549                     p_block_in->i_buffer -= i_need;
550                     /* get the PTS out of this PES header (MPEG or AC3) */
551                     if (p_sys->b_mpeg_audio)
552                         find_es_header(ty_MPEGAudioPacket, p_sys->pes_buffer,
553                                         10, &esOffset1);
554                     else
555                         find_es_header(ty_AC3AudioPacket, p_sys->pes_buffer,
556                                         10, &esOffset1);
557                     if (esOffset1 < 0)
558                     {
559                         /* god help us; something's really wrong */
560                         msg_Err(p_demux, "can't find audio PES header in packet");
561                     }
562                     else
563                     {
564                         p_sys->lastAudioPTS = get_pts( 
565                             &p_sys->pes_buffer[ esOffset1 + DTIVO_PTS_OFFSET ] );
566                         p_block_in->i_pts = p_sys->lastAudioPTS;
567                     }
568                     p_sys->i_pes_buf_cnt = 0;
569                 }
570                 else
571                 {
572                     /* don't have complete PES hdr; save what we have and return */
573                     memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
574                             p_block_in->p_buffer, l_rec_size);
575                     p_sys->i_pes_buf_cnt += l_rec_size;
576                     p_sys->i_cur_rec++;
577                     block_Release(p_block_in);
578                     return 1;
579                 }
580             }
581             /* set PCR before we send */
582             /*es_out_Control( p_demux->out, ES_OUT_SET_PCR,
583                               p_block_in->i_pts );*/
584             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
585         } /* subrec == 2 */
586
587         /* MPEG Audio with PES Header, either SA or DTiVo   */
588         /* ================================================ */
589         if ( subrec_type == 0x03 )
590         {
591             find_es_header( ty_MPEGAudioPacket, p_block_in->p_buffer,
592             l_rec_size, &esOffset1 );
593
594             /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
595                p_block_in->p_buffer[0], p_block_in->p_buffer[1],
596                p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
597             msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
598
599             /* SA PES Header, No Audio Data                     */
600             /* ================================================ */
601             if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
602             {
603                 p_sys->tivoType = 1;
604                 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
605                             SA_PTS_OFFSET ] );
606                 if (p_sys->firstAudioPTS < 0)
607                     p_sys->firstAudioPTS = p_sys->lastAudioPTS;
608                 block_Release(p_block_in);
609                 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
610                            p_sys->lastAudioPTS );*/
611             }
612             else
613             /* DTiVo Audio with PES Header                      */
614             /* ================================================ */
615             {
616                 p_sys->tivoType = 2;
617
618                 /* Check for complete PES
619                  * (TODO: handle proper size for tivo version) */
620                 if (check_sync_pes(p_demux, p_block_in, esOffset1,
621                                     l_rec_size) == -1)
622                 {
623                     /* partial PES header found, nothing else. 
624                      * we're done. */
625                     p_sys->i_cur_rec++;
626                     block_Release(p_block_in);
627                     return 1;
628                 }
629 #if 0
630                 msg_Dbg(p_demux, "packet buffer has "
631                          "%02x %02x %02x %02x %02x %02x %02x %02x "
632                          "%02x %02x %02x %02x %02x %02x %02x %02x",
633                          p_block_in->p_buffer[0], p_block_in->p_buffer[1],
634                          p_block_in->p_buffer[2], p_block_in->p_buffer[3],
635                          p_block_in->p_buffer[4], p_block_in->p_buffer[5],
636                          p_block_in->p_buffer[6], p_block_in->p_buffer[7],
637                          p_block_in->p_buffer[8], p_block_in->p_buffer[9],
638                          p_block_in->p_buffer[10], p_block_in->p_buffer[11],
639                          p_block_in->p_buffer[12], p_block_in->p_buffer[13],
640                          p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
641 #endif
642                 /* set PCR before we send */
643                 if( p_block_in->i_pts > 0 )
644                     es_out_Control( p_demux->out, ES_OUT_SET_PCR,
645                                     p_block_in->i_pts );
646                 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
647             }   /* if DTiVo */
648         }   /* if subrec == 0x03 */
649
650         /* SA Audio with no PES Header                      */
651         /* ================================================ */
652         if ( subrec_type == 0x04 )
653         {
654             /*msg_Dbg(p_demux,
655                     "Adding SA Audio Packet Size %ld", l_rec_size ); */
656
657             /* set PCR before we send */
658             if (p_sys->lastAudioPTS > 0)
659             {
660                 p_block_in->i_pts = p_sys->lastAudioPTS;
661                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
662                                 p_block_in->i_pts );
663             }
664             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
665         }
666
667         /* DTiVo AC3 Audio Data with PES Header             */
668         /* ================================================ */
669         if ( subrec_type == 0x09 )
670         {
671             find_es_header( ty_AC3AudioPacket, p_block_in->p_buffer,
672                             l_rec_size, &esOffset1 );
673
674             /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
675                        p_block_in->p_buffer[0], p_block_in->p_buffer[1],
676                        p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
677             msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);*/
678
679             /* Check for complete PES */
680             if (check_sync_pes(p_demux, p_block_in, esOffset1,
681                                 l_rec_size) == -1)
682             {
683                 /* partial PES header found, nothing else.  we're done. */
684                 p_sys->i_cur_rec++;
685                 return 1;
686             }
687             /* set PCR before we send (if PTS found) */
688             if( p_block_in->i_pts > 0 )
689             {
690                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
691                                 p_block_in->i_pts );
692             }
693             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
694         }
695     } /* end "if audio" */
696     /* ================================================================ */
697     /* Closed Caption                                                   */
698     /* ================================================================ */
699     else if ( rec_type == 0x01 )
700     {
701         /*msg_Dbg(p_demux, "CC1 %02x %02x [%c%c]", rec_hdr->ex1,
702                    rec_hdr->ex2, rec_hdr->ex1, rec_hdr->ex2 );*/
703
704         /* construct a 'user-data' MPEG packet */
705         lastCC[ 0x00 ] = 0x00;
706         lastCC[ 0x01 ] = 0x00;
707         lastCC[ 0x02 ] = 0x01;
708         lastCC[ 0x03 ] = 0xb2;
709         lastCC[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
710         lastCC[ 0x05 ] = 'Y';    /* (no other notes there) */
711         lastCC[ 0x06 ] = 0x01;
712         lastCC[ 0x07 ] = rec_hdr->ex1;
713         lastCC[ 0x08 ] = rec_hdr->ex2;
714         /* not sure what to send, because VLC doesn't yet support
715          * teletext type of subtitles (only supports the full-sentence type) */
716         /*p_block_in = block_NewEmpty(); ????
717         es_out_Send( p_demux->out, p_sys->p_subt_es, p_block_in );*/
718     }
719     else if ( rec_type == 0x02 )
720     {
721         /*msg_Dbg(p_demux, "CC2 %02x %02x", rec_hdr->ex1, rec_hdr->ex2 );*/
722
723         /* construct a 'user-data' MPEG packet */
724         lastXDS[ 0x00 ] = 0x00;
725         lastXDS[ 0x01 ] = 0x00;
726         lastXDS[ 0x02 ] = 0x01;
727         lastXDS[ 0x03 ] = 0xb2;
728         lastXDS[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
729         lastXDS[ 0x05 ] = 'Y';    /* (no other notes there) */
730         lastXDS[ 0x06 ] = 0x02;
731         lastXDS[ 0x07 ] = rec_hdr->ex1;
732         lastXDS[ 0x08 ] = rec_hdr->ex2;
733         /* not sure what to send, because VLC doesn't support this?? */
734         /*p_block_in = block_NewEmpty(); ????
735         es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );*/
736     }
737     /* ================================================================ */
738     /* Tivo data services (e.g. "thumbs-up to record!")  useless for us */
739     /* ================================================================ */
740     else if ( rec_type == 0x03 )
741     {
742     }
743     /* ================================================================ */
744     /* Unknown, but seen regularly */
745     /* ================================================================ */
746     else if ( rec_type == 0x05 )
747     {
748     }
749     else
750     {
751         msg_Dbg(p_demux, "Invalid record type 0x%02x", rec_type );
752         if (p_block_in) block_Release(p_block_in);
753             invalidType++;
754     }
755     p_sys->i_cur_rec++;
756     return 1;
757 }
758
759
760 /* seek to a position within the stream, if possible */
761 static int ty_stream_seek(demux_t *p_demux, double seek_pct)
762 {
763     demux_sys_t *p_sys = p_demux->p_sys;
764     int64_t seek_pos = p_sys->i_stream_size * seek_pct;
765     int i;
766     long l_skip_amt;
767
768     /* if we're not seekable, there's nothing to do */
769     if (!p_sys->b_seekable)
770         return VLC_EGENERIC;
771
772     /* figure out which chunk we want & go there */
773     p_sys->i_chunk_count = seek_pos / CHUNK_SIZE;
774
775     if ( stream_Seek( p_demux->s, p_sys->i_chunk_count * CHUNK_SIZE))
776     {
777         /* can't seek stream */
778         return VLC_EGENERIC;
779     }
780     /* load the chunk */
781     get_chunk_header(p_demux);
782   
783     /* seek within the chunk to get roughly to where we want */
784     p_sys->i_cur_rec = (int)
785       ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
786     msg_Dbg(p_demux, "seeked to file pos " I64Fd, seek_pos);
787     msg_Dbg(p_demux, " (chunk %d, record %d)",
788              p_sys->i_chunk_count - 1, p_sys->i_cur_rec);
789
790     /* seek to the start of this record's data.
791      * to do that, we have to skip past all prior records */
792     l_skip_amt = 0;
793     for (i=0; i<p_sys->i_cur_rec; i++)
794         l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
795     stream_Seek(p_demux->s, ((p_sys->i_chunk_count-1) * CHUNK_SIZE) +
796                  (p_sys->i_num_recs * 16) + l_skip_amt + 4);
797
798     /* to hell with syncing any audio or video, just start reading records... :) */
799     /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
800     es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
801     return VLC_SUCCESS;
802 }
803
804
805 static int Control(demux_t *p_demux, int i_query, va_list args)
806 {
807     demux_sys_t *p_sys = p_demux->p_sys;
808     double f, *pf;
809     int64_t i64, *p_i64;
810   
811     /*msg_Info(p_demux, "control cmd %d", i_query);*/
812     switch( i_query )
813     {
814     case DEMUX_GET_POSITION:
815         /* arg is 0.0 - 1.0 percent of overall file position */
816         if( ( i64 = p_sys->i_stream_size ) > 0 )
817         {
818             pf = (double*) va_arg( args, double* );
819             *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
820             return VLC_SUCCESS;
821         }
822         return VLC_EGENERIC;
823
824     case DEMUX_SET_POSITION:
825         /* arg is 0.0 - 1.0 percent of overall file position */
826         f = (double) va_arg( args, double );
827         //msg_Dbg(p_demux, "Control - set position to %2.3f", f);
828         if ((i64 = p_sys->i_stream_size) > 0)
829             return ty_stream_seek(p_demux, f);
830         return VLC_EGENERIC;
831     case DEMUX_GET_TIME:
832         /* return latest PTS - start PTS */
833         p_i64 = (int64_t *) va_arg(args, int64_t *);
834         *p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
835         return VLC_SUCCESS;
836     case DEMUX_SET_TIME:      /* arg is time in microsecs */
837     case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
838     case DEMUX_GET_FPS:
839     default:
840         return VLC_EGENERIC;
841     }
842 }
843
844
845 /* =========================================================================== */
846 static void TyClose( vlc_object_t *p_this )
847 {
848     demux_sys_t *p_sys = ((demux_t *) p_this)->p_sys;
849
850     free(p_sys->rec_hdrs);
851     free(p_sys);
852 }
853
854
855 /* =========================================================================== */
856 static int get_chunk_header(demux_t *p_demux)
857 {
858     int i_readSize, i_num_recs, i;
859     uint8_t packet_header[4];
860     uint8_t record_header[16];
861     ty_rec_hdr_t *p_rec_hdr;
862     demux_sys_t *p_sys = p_demux->p_sys;
863     int i_payload_size = 0;         /* sum of all records */
864
865     msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_chunk_count );
866
867     /* if we have left-over filler space from the last chunk, get that */
868     if (p_sys->i_stuff_cnt > 0)
869         stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
870
871     /* read the TY packet header */
872     i_readSize = stream_Read( p_demux->s, packet_header, 4 );
873     p_sys->i_chunk_count++;
874   
875     if ( i_readSize < 4 )
876     {
877         /* EOF */
878         p_sys->eof = 1;
879         return 0;
880     }
881   
882     /* if it's a PART Header, then try again. */
883     if( U32_AT( &packet_header[ 0 ] ) == TIVO_PES_FILEID )
884     {
885         msg_Dbg( p_demux, "skipping TY PART Header" );
886         /* TODO: if stream is seekable, should we seek() instead of read() ?? */
887         stream_Read( p_demux->s, NULL, CHUNK_SIZE - 4 );
888         return get_chunk_header(p_demux);
889     }
890
891     /* number of records in chunk (8- or 16-bit number) */
892     if (packet_header[3] & 0x80)
893     {
894         /* 16 bit rec cnt */
895         p_sys->i_num_recs = i_num_recs = (packet_header[1] << 8) + packet_header[0];
896         p_sys->i_seq_rec = (packet_header[3] << 8) + packet_header[2];
897         if (p_sys->i_seq_rec != 0xffff)
898         {
899             p_sys->i_seq_rec &= ~0x8000;
900         }
901     }
902     else
903     {
904         /* 8 bit reclen - tivo 1.3 format */
905         p_sys->i_num_recs = i_num_recs = packet_header[0];
906         p_sys->i_seq_rec = packet_header[1];
907     }
908     p_sys->i_cur_rec = 0;
909     p_sys->b_first_chunk = VLC_FALSE;
910   
911     /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
912
913     /* parse headers into array */
914     if (p_sys->rec_hdrs)
915         free(p_sys->rec_hdrs);
916     p_sys->rec_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
917     for (i = 0; i < i_num_recs; i++)
918     {
919         i_readSize = stream_Read( p_demux->s, record_header, 16 );
920         if (i_readSize < 16)
921         {
922             /* EOF */
923             p_sys->eof = VLC_TRUE;
924             return 0;
925         }
926         p_rec_hdr = &p_sys->rec_hdrs[i];     /* for brevity */
927         p_rec_hdr->rec_type = record_header[3];
928         p_rec_hdr->subrec_type = record_header[2] & 0x0f;
929         if ((record_header[ 0 ] & 0x80) == 0x80)
930         {
931             unsigned char b1, b2;
932             /* marker bit 2 set, so read extended data */
933             b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) | 
934                    ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
935             b1 &= 0x7f;
936             b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) | 
937                    ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
938             b2 &= 0x7f;
939
940             p_rec_hdr->ex1 = b1;
941             p_rec_hdr->ex2 = b2;
942             p_rec_hdr->l_rec_size = 0;
943             p_rec_hdr->b_ext = VLC_TRUE;
944         }
945         else
946         {
947             p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
948                 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
949             i_payload_size += p_rec_hdr->l_rec_size;
950             p_rec_hdr->b_ext = VLC_FALSE;
951         }
952     } /* end of record-header loop */
953     p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
954         (p_sys->i_num_recs * 16) - i_payload_size;
955     if (p_sys->i_stuff_cnt > 0)
956         msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
957                  p_sys->i_stuff_cnt );
958     return 1;
959 }