]> git.sesse.net Git - vlc/blob - modules/demux/ty.c
* modules/demux/ty.c: fixed segfault while probing.
[vlc] / modules / demux / ty.c
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
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., 59 Temple Place - Suite 330, Boston, MA  02111, 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 <stdlib.h>
36 #include <vlc/vlc.h>
37 #include <vlc/input.h>
38 #include "vlc_codec.h"
39
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 };
49
50 /*****************************************************************************
51  * Local prototypes
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);
59
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);
64
65 /*****************************************************************************
66  * Module descriptor
67  *****************************************************************************/
68 vlc_module_begin();
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.
77      */
78     set_callbacks(TyOpen, TyClose);
79     add_shortcut("ty");
80     add_shortcut("tivo");
81 vlc_module_end();
82
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)
98 */
99
100 #define TIVO_PES_FILEID   ( 0xf5467abd )
101 #define TIVO_PART_LENGTH  ( 0x20000000 )    /* 536,870,912 bytes */
102 #define CHUNK_SIZE        ( 128 * 1024 )
103
104 typedef struct
105 {
106   long l_rec_size;
107   unsigned char ex1, ex2;
108   unsigned char rec_type;
109   unsigned char subrec_type;
110   char b_ext;
111 } ty_rec_hdr_t;
112
113 struct demux_sys_t
114 {
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 */
117
118   int             i_chunk_count;
119   int             i_stuff_cnt;
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 */
126
127   mtime_t         firstAudioPTS;
128   mtime_t         lastAudioPTS;
129   mtime_t         lastVideoPTS;
130
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 */
135   vlc_bool_t      eof;
136   vlc_bool_t      b_first_chunk;
137 };
138
139
140 /*
141  * TyOpen: check file and initialize demux structures
142  *
143  * here's what we do:
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
149  */
150 static int TyOpen(vlc_object_t *p_this)
151 {
152     demux_t *p_demux = (demux_t *)p_this;
153     demux_sys_t *p_sys;
154     vlc_bool_t b_seekable;
155     es_format_t fmt;
156     uint8_t *p_peek;
157
158     /* see if this stream is seekable */
159     stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable );
160   
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 )
164         return VLC_EGENERIC;
165
166     if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
167          U32_AT(&p_peek[4]) != 0x02 ||
168          U32_AT(&p_peek[8]) != CHUNK_SIZE )
169     {
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"))
175             return VLC_EGENERIC;
176         msg_Warn(p_demux, "this does not look like a TY file, continuing anyway...");
177     }
178
179     /* at this point, we assume we have a valid TY stream */  
180     msg_Dbg( p_demux, "valid TY stream detected" );
181
182     /* Set exported functions */
183     p_demux->pf_demux = TyDemux;
184     p_demux->pf_control = Control;
185
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));
189
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;
196   
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.   */
200
201     /* NOTE: we wait to create the audio ES until we know what
202      * audio type we have.   */
203     p_sys->p_audio = NULL;
204
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 );
208
209 #if 0
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);
213 #endif
214
215     return VLC_SUCCESS;
216 }
217
218
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)
222 {
223     demux_sys_t *p_sys = p_demux->p_sys;
224     es_format_t  fmt;
225
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 */
230     } else {
231         /* assume MPEG */
232         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
233         p_sys->b_mpeg_audio = VLC_TRUE;
234     }
235     /* register the chosen audio output codec */
236     p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
237 }
238
239
240 /* =========================================================================== */
241 /* Compute Presentation Time Stamp (PTS)
242  * Assume buf points to beginning of PTS */
243 static mtime_t get_pts( unsigned char *buf )
244 {
245     mtime_t i_pts;
246
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 */
253     return i_pts;
254 }
255
256
257 /* =========================================================================== */
258 static int find_es_header( unsigned const char *header,
259    unsigned char *buffer, int bufferSize, int *esOffset1 )
260 {
261     int count;
262
263     *esOffset1 = -1;
264     for( count = 0 ; count < bufferSize ; count++ )
265     {
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 ] ) )
270         {
271             *esOffset1 = count;
272             return 1;
273         }
274     }
275     return( -1 );
276 }
277
278
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.
282  * Returns:
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 )
289 {
290     demux_sys_t *p_sys = p_demux->p_sys;
291     int pts_offset;
292     int pes_length = p_sys->b_mpeg_audio?SERIES1_PES_LENGTH:AC3_PES_LENGTH;
293
294     if( p_sys->tivoType == 1 )
295     {
296         /* SA tivo */
297         pts_offset = SA_PTS_OFFSET;
298     }
299     else
300     {
301         /* DTivo */
302         pts_offset = p_sys->b_mpeg_audio?DTIVO_PTS_OFFSET:AC3_PTS_OFFSET;
303     }
304     if ( offset < 0 || offset + pes_length > rec_len )
305     {
306         /* entire PES header not present */
307         msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
308                  offset );
309         /* save the partial pes header */
310         if( offset < 0 )
311         {
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;
315             if( rec_len > 4 )
316                 msg_Err( p_demux, "PES header not found in record of %d bytes!",
317                          rec_len );
318             return -1;
319         }
320         /* copy the partial pes header we found */
321         memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
322                 rec_len - offset );
323         p_sys->i_pes_buf_cnt = rec_len - offset;
324
325         if( offset > 0 )
326         {
327             /* PES Header was found, but not complete, so trim the end of this record */
328             p_block->i_buffer -= rec_len - offset;
329             return 1;
330         }
331         return -1;    /* partial PES, no audio data */
332     }
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;
343     return 0;
344 }
345
346
347 /* =========================================================================== */
348 /* TyDemux: Read & Demux one record from the chunk
349  *
350  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
351  *
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 :) --
355  */
356 int TyDemux(demux_t *p_demux)
357 {
358     int              invalidType = 0;
359     int              recordsDecoded = 0;
360
361     int              rec_type;
362     long             l_rec_size;
363     int              i_cur_rec;
364     int              subrec_type;
365     ty_rec_hdr_t     *rec_hdr;
366
367     block_t          *p_block_in = NULL;
368     int              esOffset1;
369
370     unsigned char    lastCC[ 16 ];
371     unsigned char    lastXDS[ 16 ];
372
373     demux_sys_t      *p_sys = p_demux->p_sys;
374
375     /*msg_Dbg(p_demux, "ty demux processing" );*/
376    
377     /* did we hit EOF earlier? */
378     if (p_sys->eof) return 0;
379
380     /*
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()
390
391     * if this is the first time or  
392     * if we're at the end of this chunk, start a new one
393     */
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)
397             return 0;
398
399     /*======================================================================
400      * parse & send one record of the chunk
401      *====================================================================== */
402     i_cur_rec = p_sys->i_cur_rec;
403     recordsDecoded++;
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;
408
409     if (!rec_hdr->b_ext)
410     {
411         /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
412                     subrec_type, rec_type, l_rec_size );*/
413   
414         /* some normal records are 0 length, so check for that... */
415         if (l_rec_size > 0)
416         {
417             /* read in this record's payload */
418             if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
419             {
420                 /* EOF */
421                 p_sys->eof = 1;
422                 return 0;
423             }
424             /* set these as 'unknown' for now */
425             p_block_in->i_pts = p_block_in->i_dts = 0;
426         }
427         else
428         {
429             /* no data in payload; we're done */
430             p_sys->i_cur_rec++;
431             return 1;
432         }
433     }
434     /*else
435     {
436         -- don't read any data from the stream, data was in the record header --
437         msg_Dbg(p_demux,
438                "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
439                 rec_type, rec_hdr->ex1, rec_hdr->ex2);
440     }*/
441
442     /*================================================================*
443      * Video Parsing
444      *================================================================*/
445     if ( rec_type == 0xe0 )
446     {
447         if( subrec_type == 0x06 )
448         {
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 )
454             {
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 );*/
459             }
460             block_Release(p_block_in);
461         }
462         else
463         {
464 #if 0
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]);
476 #endif
477             /* if it's not a continue blk, then set PTS */
478             if (subrec_type != 0x02)
479             {
480                 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
481                            subrec_type);*/
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)
490                 {
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;
496                 }
497             } 
498             es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
499         }
500     } /* end if video rec type */
501     /* ================================================================
502      * Audio Parsing
503      * ================================================================
504      * parse PES headers and send the rest to the codec
505      */
506     else if ( rec_type == 0xc0 )
507     {
508 #if 0
509         int i;
510         printf( "Audio Packet Header " );
511         for( i = 0 ; i < 24 ; i++ )
512             printf( "%2.2x ", p_block_in->p_buffer[i] );
513         printf( "\n" );
514 #endif
515         /* load a codec if we haven't yet */
516         if ( p_sys->p_audio == NULL )
517         {
518             if ( subrec_type == 0x09 )
519             {
520                 /* set up for AC-3 audio */
521                 msg_Dbg(p_demux, "detected AC-3 Audio" );
522                         setup_audio_streams('A', p_demux);
523             }
524             else
525             {
526                 /* set up for MPEG audio */
527                 msg_Dbg(p_demux, "detected MPEG Audio" );
528                 setup_audio_streams('M', p_demux);
529             }
530         }
531
532         /* SA or DTiVo Audio Data, no PES (continued block)
533          * ================================================
534          */
535         if ( subrec_type == 2 )
536         {
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)
540             {
541                 int i_need = SERIES1_PES_LENGTH - p_sys->i_pes_buf_cnt;
542
543                 msg_Dbg(p_demux, "continuing PES header");
544                 /* do we have enough data to complete? */
545                 if (i_need < l_rec_size)
546                 {
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,
556                                         10, &esOffset1);
557                     else
558                         find_es_header(ty_AC3AudioPacket, p_sys->pes_buffer,
559                                         10, &esOffset1);
560                     if (esOffset1 < 0)
561                     {
562                         /* god help us; something's really wrong */
563                         msg_Err(p_demux, "can't find audio PES header in packet");
564                     }
565                     else
566                     {
567                         p_sys->lastAudioPTS = get_pts( 
568                             &p_sys->pes_buffer[ esOffset1 + DTIVO_PTS_OFFSET ] );
569                         p_block_in->i_pts = p_sys->lastAudioPTS;
570                     }
571                     p_sys->i_pes_buf_cnt = 0;
572                 }
573                 else
574                 {
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;
579                     p_sys->i_cur_rec++;
580                     block_Release(p_block_in);
581                     return 1;
582                 }
583             }
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 );
588         } /* subrec == 2 */
589
590         /* MPEG Audio with PES Header, either SA or DTiVo   */
591         /* ================================================ */
592         if ( subrec_type == 0x03 )
593         {
594             find_es_header( ty_MPEGAudioPacket, p_block_in->p_buffer,
595             l_rec_size, &esOffset1 );
596
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);*/
601
602             /* SA PES Header, No Audio Data                     */
603             /* ================================================ */
604             if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
605             {
606                 p_sys->tivoType = 1;
607                 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
608                             SA_PTS_OFFSET ] );
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 );*/
614             }
615             else
616             /* DTiVo Audio with PES Header                      */
617             /* ================================================ */
618             {
619                 p_sys->tivoType = 2;
620
621                 /* Check for complete PES
622                  * (TODO: handle proper size for tivo version) */
623                 if (check_sync_pes(p_demux, p_block_in, esOffset1,
624                                     l_rec_size) == -1)
625                 {
626                     /* partial PES header found, nothing else. 
627                      * we're done. */
628                     p_sys->i_cur_rec++;
629                     block_Release(p_block_in);
630                     return 1;
631                 }
632 #if 0
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]);
644 #endif
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,
648                                     p_block_in->i_pts );
649                 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
650             }   /* if DTiVo */
651         }   /* if subrec == 0x03 */
652
653         /* SA Audio with no PES Header                      */
654         /* ================================================ */
655         if ( subrec_type == 0x04 )
656         {
657             /*msg_Dbg(p_demux,
658                     "Adding SA Audio Packet Size %ld", l_rec_size ); */
659
660             /* set PCR before we send */
661             if (p_sys->lastAudioPTS > 0)
662             {
663                 p_block_in->i_pts = p_sys->lastAudioPTS;
664                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
665                                 p_block_in->i_pts );
666             }
667             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
668         }
669
670         /* DTiVo AC3 Audio Data with PES Header             */
671         /* ================================================ */
672         if ( subrec_type == 0x09 )
673         {
674             find_es_header( ty_AC3AudioPacket, p_block_in->p_buffer,
675                             l_rec_size, &esOffset1 );
676
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);*/
681
682             /* Check for complete PES */
683             if (check_sync_pes(p_demux, p_block_in, esOffset1,
684                                 l_rec_size) == -1)
685             {
686                 /* partial PES header found, nothing else.  we're done. */
687                 p_sys->i_cur_rec++;
688                 return 1;
689             }
690             /* set PCR before we send (if PTS found) */
691             if( p_block_in->i_pts > 0 )
692             {
693                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
694                                 p_block_in->i_pts );
695             }
696             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
697         }
698     } /* end "if audio" */
699     /* ================================================================ */
700     /* Closed Caption                                                   */
701     /* ================================================================ */
702     else if ( rec_type == 0x01 )
703     {
704         /*msg_Dbg(p_demux, "CC1 %02x %02x [%c%c]", rec_hdr->ex1,
705                    rec_hdr->ex2, rec_hdr->ex1, rec_hdr->ex2 );*/
706
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 );*/
721     }
722     else if ( rec_type == 0x02 )
723     {
724         /*msg_Dbg(p_demux, "CC2 %02x %02x", rec_hdr->ex1, rec_hdr->ex2 );*/
725
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 );*/
739     }
740     /* ================================================================ */
741     /* Tivo data services (e.g. "thumbs-up to record!")  useless for us */
742     /* ================================================================ */
743     else if ( rec_type == 0x03 )
744     {
745     }
746     /* ================================================================ */
747     /* Unknown, but seen regularly */
748     /* ================================================================ */
749     else if ( rec_type == 0x05 )
750     {
751     }
752     else
753     {
754         msg_Dbg(p_demux, "Invalid record type 0x%02x", rec_type );
755         if (p_block_in) block_Release(p_block_in);
756             invalidType++;
757     }
758     p_sys->i_cur_rec++;
759     return 1;
760 }
761
762
763 /* seek to a position within the stream, if possible */
764 static int ty_stream_seek(demux_t *p_demux, double seek_pct)
765 {
766     demux_sys_t *p_sys = p_demux->p_sys;
767     int64_t seek_pos = p_sys->i_stream_size * seek_pct;
768     int i;
769     long l_skip_amt;
770
771     /* if we're not seekable, there's nothing to do */
772     if (!p_sys->b_seekable)
773         return VLC_EGENERIC;
774
775     /* figure out which chunk we want & go there */
776     p_sys->i_chunk_count = seek_pos / CHUNK_SIZE;
777
778     if ( stream_Seek( p_demux->s, p_sys->i_chunk_count * CHUNK_SIZE))
779     {
780         /* can't seek stream */
781         return VLC_EGENERIC;
782     }
783     /* load the chunk */
784     get_chunk_header(p_demux);
785   
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);
792
793     /* seek to the start of this record's data.
794      * to do that, we have to skip past all prior records */
795     l_skip_amt = 0;
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);
800
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 );
804     return VLC_SUCCESS;
805 }
806
807
808 static int Control(demux_t *p_demux, int i_query, va_list args)
809 {
810     demux_sys_t *p_sys = p_demux->p_sys;
811     double f, *pf;
812     int64_t i64, *p_i64;
813   
814     /*msg_Info(p_demux, "control cmd %d", i_query);*/
815     switch( i_query )
816     {
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 )
820         {
821             pf = (double*) va_arg( args, double* );
822             *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
823             return VLC_SUCCESS;
824         }
825         return VLC_EGENERIC;
826
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);
833         return VLC_EGENERIC;
834     case DEMUX_GET_TIME:
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;
838         return VLC_SUCCESS;
839     case DEMUX_SET_TIME:      /* arg is time in microsecs */
840     case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
841     case DEMUX_GET_FPS:
842     default:
843         return VLC_EGENERIC;
844     }
845 }
846
847
848 /* =========================================================================== */
849 static void TyClose( vlc_object_t *p_this )
850 {
851     demux_sys_t *p_sys = ((demux_t *) p_this)->p_sys;
852
853     free(p_sys->rec_hdrs);
854     free(p_sys);
855 }
856
857
858 /* =========================================================================== */
859 static int get_chunk_header(demux_t *p_demux)
860 {
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 */
867
868     msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_chunk_count );
869
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);
873
874     /* read the TY packet header */
875     i_readSize = stream_Read( p_demux->s, packet_header, 4 );
876     p_sys->i_chunk_count++;
877   
878     if ( i_readSize < 4 )
879     {
880         /* EOF */
881         p_sys->eof = 1;
882         return 0;
883     }
884   
885     /* if it's a PART Header, then try again. */
886     if( U32_AT( &packet_header[ 0 ] ) == TIVO_PES_FILEID )
887     {
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);
892     }
893
894     /* number of records in chunk (8- or 16-bit number) */
895     if (packet_header[3] & 0x80)
896     {
897         /* 16 bit rec cnt */
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)
901         {
902             p_sys->i_seq_rec &= ~0x8000;
903         }
904     }
905     else
906     {
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];
910     }
911     p_sys->i_cur_rec = 0;
912     p_sys->b_first_chunk = VLC_FALSE;
913   
914     /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
915
916     /* parse headers into array */
917     if (p_sys->rec_hdrs)
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++)
921     {
922         i_readSize = stream_Read( p_demux->s, record_header, 16 );
923         if (i_readSize < 16)
924         {
925             /* EOF */
926             p_sys->eof = VLC_TRUE;
927             return 0;
928         }
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)
933         {
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 ) );
938             b1 &= 0x7f;
939             b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) | 
940                    ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
941             b2 &= 0x7f;
942
943             p_rec_hdr->ex1 = b1;
944             p_rec_hdr->ex2 = b2;
945             p_rec_hdr->l_rec_size = 0;
946             p_rec_hdr->b_ext = VLC_TRUE;
947         }
948         else
949         {
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;
954         }
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 );
961     return 1;
962 }