Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

ty.c

00001 /*****************************************************************************
00002  * ty.c - TiVo ty stream video demuxer for VLC
00003  *****************************************************************************
00004  * Copyright (C) 2005 the VideoLAN team
00005  * Copyright (C) 2005 by Neal Symms ([email protected]) - February 2005
00006  * based on code by Christopher Wingert for tivo-mplayer
00007  * tivo(at)wingert.org, February 2003
00008  *
00009  * $Id: ty.c 11664 2005-07-09 06:17:09Z courmisch $
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00024  *
00025  * CODE CHANGES:
00026  * v1.0.0 - 24-Feb-2005 - Initial release - Series 1 support ONLY!
00027  * v1.0.1 - 25-Feb-2005 - Added fix for bad GOP headers - Neal
00028  * v1.0.2 - 26-Feb-2005 - No longer require "seekable" input stream - Neal
00029  *****************************************************************************/
00030 
00031 /*****************************************************************************
00032  * Preamble
00033  *****************************************************************************/
00034 
00035 #include <stdlib.h>
00036 #include <vlc/vlc.h>
00037 #include <vlc/input.h>
00038 #include "vlc_codec.h"
00039 
00040 #define SERIES1_PES_LENGTH  (11)
00041 #define SERIES2_PES_LENGTH  (16)
00042 #define AC3_PES_LENGTH      (14)
00043 #define DTIVO_PTS_OFFSET    (6)
00044 #define SA_PTS_OFFSET       (9)
00045 #define AC3_PTS_OFFSET      (9)
00046 static const unsigned char ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
00047 static const unsigned char ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
00048 static const unsigned char ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
00049 
00050 /*****************************************************************************
00051  * Local prototypes
00052  *****************************************************************************/
00053 static int get_chunk_header(demux_t *);
00054 static void setup_audio_streams(char, demux_t *);
00055 static mtime_t get_pts( unsigned char *buf );
00056 static int find_es_header( unsigned const char *header,
00057    unsigned char *buffer, int bufferSize, int *esOffset1 );
00058 static int ty_stream_seek(demux_t *p_demux, double seek_pct);
00059 
00060 static int TyOpen (vlc_object_t *);
00061 static void TyClose(vlc_object_t *);
00062 static int TyDemux(demux_t *);
00063 static int Control(demux_t *, int, va_list);
00064 
00065 /*****************************************************************************
00066  * Module descriptor
00067  *****************************************************************************/
00068 vlc_module_begin();
00069     set_shortname( "TY" );
00070     set_description(_("TY Stream audio/video demux"));
00071     set_category( CAT_INPUT );
00072     set_subcategory( SUBCAT_INPUT_DEMUX );
00073     set_capability("demux2", 8);
00074     /* FIXME: there seems to be a segfault when using PVR access
00075      * and TY demux has a bigger priority than PS
00076      * Something must be wrong.
00077      */
00078     set_callbacks(TyOpen, TyClose);
00079     add_shortcut("ty");
00080     add_shortcut("tivo");
00081 vlc_module_end();
00082 
00083 /* packet types for reference:
00084  2/c0: audio data continued
00085  3/c0: audio packet header (PES header)
00086  4/c0: audio data (S/A only?)
00087  9/c0: audio packet header, AC-3 audio
00088  2/e0: video data continued
00089  6/e0: video packet header (PES header)
00090  7/e0: video sequence header start
00091  8/e0: video I-frame header start
00092  a/e0: video P-frame header start
00093  b/e0: video B-frame header start
00094  c/e0: video GOP header start
00095  e/01: closed-caption data
00096  e/02: Extended data services data 
00097  e/03: ipreview data ("thumbs up to record" signal)
00098 */
00099 
00100 #define TIVO_PES_FILEID   ( 0xf5467abd )
00101 #define TIVO_PART_LENGTH  ( 0x20000000 )    /* 536,870,912 bytes */
00102 #define CHUNK_SIZE        ( 128 * 1024 )
00103 
00104 typedef struct
00105 {
00106   long l_rec_size;
00107   unsigned char ex1, ex2;
00108   unsigned char rec_type;
00109   unsigned char subrec_type;
00110   char b_ext;
00111 } ty_rec_hdr_t;
00112 
00113 struct demux_sys_t
00114 {
00115   es_out_id_t *p_video;               /* ptr to video codec */
00116   es_out_id_t *p_audio;               /* holds either ac3 or mpeg codec ptr */
00117 
00118   int             i_chunk_count;
00119   int             i_stuff_cnt;
00120   size_t          i_stream_size;      /* size of input stream (if known) */
00121   vlc_bool_t      b_seekable;         /* is this stream seekable? */
00122   int             tivoType;           /* 1 = SA, 2 = DTiVo */
00123   vlc_bool_t      b_mpeg_audio;       /* true if we're using MPEG audio */
00124   uint8_t         pes_buffer[20];     /* holds incomplete pes headers */
00125   int             i_pes_buf_cnt;      /* how many bytes in our buffer */
00126 
00127   mtime_t         firstAudioPTS;
00128   mtime_t         lastAudioPTS;
00129   mtime_t         lastVideoPTS;
00130 
00131   ty_rec_hdr_t    *rec_hdrs;          /* record headers array */
00132   int             i_cur_rec;          /* current record in this chunk */
00133   int             i_num_recs;         /* number of recs in this chunk */
00134   int             i_seq_rec;          /* record number where seq start is */
00135   vlc_bool_t      eof;
00136   vlc_bool_t      b_first_chunk;
00137 };
00138 
00139 
00140 /*
00141  * TyOpen: check file and initialize demux structures
00142  *
00143  * here's what we do:
00144  * 1. peek at the first 12 bytes of the stream for the
00145  *    magic TiVo PART header & stream type & chunk size
00146  * 2. if it's not there, error with VLC_EGENERIC
00147  * 3. set up video (mpgv) codec
00148  * 4. return VLC_SUCCESS
00149  */
00150 static int TyOpen(vlc_object_t *p_this)
00151 {
00152     demux_t *p_demux = (demux_t *)p_this;
00153     demux_sys_t *p_sys;
00154     es_format_t fmt;
00155     uint8_t *p_peek;
00156 
00157     /* peek at the first 12 bytes. */
00158     /* for TY streams, they're always the same */
00159     if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
00160         return VLC_EGENERIC;
00161 
00162     if ( U32_AT(p_peek) != TIVO_PES_FILEID ||
00163          U32_AT(&p_peek[4]) != 0x02 ||
00164          U32_AT(&p_peek[8]) != CHUNK_SIZE )
00165     {
00166         /* doesn't look like a TY file... */
00167         char *psz_ext = strrchr(p_demux->psz_path, '.');
00168 
00169         if( !p_demux->b_force &&
00170             (!psz_ext || strcasecmp(psz_ext, ".ty")) ) return VLC_EGENERIC;
00171         msg_Warn( p_demux, "this does not look like a TY file, "
00172                   "continuing anyway..." );
00173     }
00174 
00175     /* at this point, we assume we have a valid TY stream */  
00176     msg_Dbg( p_demux, "valid TY stream detected" );
00177 
00178     /* Set exported functions */
00179     p_demux->pf_demux = TyDemux;
00180     p_demux->pf_control = Control;
00181 
00182     /* create our structure that will hold all data */
00183     p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t));
00184     memset(p_sys, 0, sizeof(demux_sys_t));
00185 
00186     /* set up our struct (most were zero'd out with the memset above) */
00187     p_sys->b_first_chunk = VLC_TRUE;
00188     p_sys->firstAudioPTS = -1;
00189     p_sys->i_stream_size = stream_Size(p_demux->s);
00190     p_sys->b_mpeg_audio = VLC_FALSE;
00191 
00192     /* see if this stream is seekable */
00193     stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
00194 
00195     /* TODO: read first chunk & parse first audio PTS, then (if seekable)
00196      *       seek to last chunk & last record; read its PTS and compute
00197      *       overall program time.  Also determine Tivo type.   */
00198 
00199     /* NOTE: we wait to create the audio ES until we know what
00200      * audio type we have.   */
00201     p_sys->p_audio = NULL;
00202 
00203     /* register the video stream */
00204     es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
00205     p_sys->p_video = es_out_Add( p_demux->out, &fmt );
00206 
00207 #if 0
00208     /* register the CC decoder */
00209     es_format_Init( &fmt, SPU_ES, VLC_FOURCC('s', 'u', 'b', 't'));
00210     p_sys->p_subt_es = es_out_Add(p_demux->out, &fmt);
00211 #endif
00212 
00213     return VLC_SUCCESS;
00214 }
00215 
00216 
00217 /* set up audio codec.
00218  * this will be called once we determine audio type */
00219 static void setup_audio_streams(char stream_type, demux_t *p_demux)
00220 {
00221     demux_sys_t *p_sys = p_demux->p_sys;
00222     es_format_t  fmt;
00223 
00224     if (stream_type == 'A') {
00225         /* AC3 audio detected */
00226         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
00227         p_sys->tivoType = 2;      /* AC3 is only on dtivo */
00228     } else {
00229         /* assume MPEG */
00230         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
00231         p_sys->b_mpeg_audio = VLC_TRUE;
00232     }
00233     /* register the chosen audio output codec */
00234     p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
00235 }
00236 
00237 
00238 /* =========================================================================== */
00239 /* Compute Presentation Time Stamp (PTS)
00240  * Assume buf points to beginning of PTS */
00241 static mtime_t get_pts( unsigned char *buf )
00242 {
00243     mtime_t i_pts;
00244 
00245     i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)|
00246              (mtime_t)(buf[1] << 22)|
00247             ((mtime_t)(buf[2]&0xfe) << 14)|
00248              (mtime_t)(buf[3] << 7)|
00249              (mtime_t)(buf[4] >> 1);
00250     i_pts *= 100 / 9;   /* convert PTS (90Khz clock) to microseconds */
00251     return i_pts;
00252 }
00253 
00254 
00255 /* =========================================================================== */
00256 static int find_es_header( unsigned const char *header,
00257    unsigned char *buffer, int bufferSize, int *esOffset1 )
00258 {
00259     int count;
00260 
00261     *esOffset1 = -1;
00262     for( count = 0 ; count < bufferSize ; count++ )
00263     {
00264         if ( ( buffer[ count + 0 ] == header[ 0 ] ) &&
00265              ( buffer[ count + 1 ] == header[ 1 ] ) &&
00266              ( buffer[ count + 2 ] == header[ 2 ] ) &&
00267              ( buffer[ count + 3 ] == header[ 3 ] ) )
00268         {
00269             *esOffset1 = count;
00270             return 1;
00271         }
00272     }
00273     return( -1 );
00274 }
00275 
00276 
00277 /* =========================================================================== */
00278 /* check if we have a full PES header, if not, then save what we have.
00279  * this is called when audio-start packets are encountered.
00280  * Returns:
00281  *     1 partial PES hdr found, some audio data found (buffer adjusted),
00282  *    -1 partial PES hdr found, no audio data found
00283  *     0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) */
00284 /* TODO: fix it so it works with S2 / SA / DTivo / HD etc... */
00285 static int check_sync_pes( demux_t *p_demux, block_t *p_block,
00286                            int32_t offset, int32_t rec_len )
00287 {
00288     demux_sys_t *p_sys = p_demux->p_sys;
00289     int pts_offset;
00290     int pes_length = p_sys->b_mpeg_audio?SERIES1_PES_LENGTH:AC3_PES_LENGTH;
00291 
00292     if( p_sys->tivoType == 1 )
00293     {
00294         /* SA tivo */
00295         pts_offset = SA_PTS_OFFSET;
00296     }
00297     else
00298     {
00299         /* DTivo */
00300         pts_offset = p_sys->b_mpeg_audio?DTIVO_PTS_OFFSET:AC3_PTS_OFFSET;
00301     }
00302     if ( offset < 0 || offset + pes_length > rec_len )
00303     {
00304         /* entire PES header not present */
00305         msg_Dbg( p_demux, "PES header at %d not complete in record. storing.",
00306                  offset );
00307         /* save the partial pes header */
00308         if( offset < 0 )
00309         {
00310             /* no header found, fake some 00's (this works, believe me) */
00311             memset( p_sys->pes_buffer, 4, 0 );
00312             p_sys->i_pes_buf_cnt = 4;
00313             if( rec_len > 4 )
00314                 msg_Err( p_demux, "PES header not found in record of %d bytes!",
00315                          rec_len );
00316             return -1;
00317         }
00318         /* copy the partial pes header we found */
00319         memcpy( p_sys->pes_buffer, p_block->p_buffer + offset,
00320                 rec_len - offset );
00321         p_sys->i_pes_buf_cnt = rec_len - offset;
00322 
00323         if( offset > 0 )
00324         {
00325             /* PES Header was found, but not complete, so trim the end of this record */
00326             p_block->i_buffer -= rec_len - offset;
00327             return 1;
00328         }
00329         return -1;    /* partial PES, no audio data */
00330     }
00331     /* full PES header present, extract PTS */
00332     p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset + pts_offset ] );
00333     if (p_sys->firstAudioPTS < 0)
00334         p_sys->firstAudioPTS = p_sys->lastAudioPTS;
00335     p_block->i_pts = p_sys->lastAudioPTS;
00336     /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
00337     /* adjust audio record to remove PES header */
00338     memmove(p_block->p_buffer + offset, p_block->p_buffer + offset + pes_length,
00339             rec_len - pes_length);
00340     p_block->i_buffer -= pes_length;
00341     return 0;
00342 }
00343 
00344 
00345 /* =========================================================================== */
00346 /* TyDemux: Read & Demux one record from the chunk
00347  *
00348  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
00349  *
00350  * NOTE: I think we can return the number of packets sent instead of just 1.
00351  * that means we can demux an entire chunk and shoot it back (may be more efficient)
00352  * -- should try that some day :) --
00353  */
00354 int TyDemux(demux_t *p_demux)
00355 {
00356     int              invalidType = 0;
00357     int              recordsDecoded = 0;
00358 
00359     int              rec_type;
00360     long             l_rec_size;
00361     int              i_cur_rec;
00362     int              subrec_type;
00363     ty_rec_hdr_t     *rec_hdr;
00364 
00365     block_t          *p_block_in = NULL;
00366     int              esOffset1;
00367 
00368     unsigned char    lastCC[ 16 ];
00369     unsigned char    lastXDS[ 16 ];
00370 
00371     demux_sys_t      *p_sys = p_demux->p_sys;
00372 
00373     /*msg_Dbg(p_demux, "ty demux processing" );*/
00374    
00375     /* did we hit EOF earlier? */
00376     if (p_sys->eof) return 0;
00377 
00378     /*
00379      * what we do (1 record now.. maybe more later):
00380     * - use stream_Read() to read the chunk header & record headers
00381     * - discard entire chunk if it is a PART header chunk
00382     * - parse all the headers into record header array
00383     * - keep a pointer of which record we're on
00384     * - use stream_Block() to fetch each record
00385     * - parse out PTS from PES headers
00386     * - set PTS for data packets
00387     * - pass the data on to the proper codec via es_out_Send()
00388 
00389     * if this is the first time or  
00390     * if we're at the end of this chunk, start a new one
00391     */
00392     /* parse the next chunk's record headers */
00393     if (p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs)
00394         if (get_chunk_header(p_demux) == 0)
00395             return 0;
00396 
00397     /*======================================================================
00398      * parse & send one record of the chunk
00399      *====================================================================== */
00400     i_cur_rec = p_sys->i_cur_rec;
00401     recordsDecoded++;
00402     rec_hdr = &p_sys->rec_hdrs[ i_cur_rec ];
00403     subrec_type = rec_hdr->subrec_type;
00404     rec_type = rec_hdr->rec_type;
00405     l_rec_size = rec_hdr->l_rec_size;
00406 
00407     if (!rec_hdr->b_ext)
00408     {
00409         /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
00410                     subrec_type, rec_type, l_rec_size );*/
00411   
00412         /* some normal records are 0 length, so check for that... */
00413         if (l_rec_size > 0)
00414         {
00415             /* read in this record's payload */
00416             if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
00417             {
00418                 /* EOF */
00419                 p_sys->eof = 1;
00420                 return 0;
00421             }
00422             /* set these as 'unknown' for now */
00423             p_block_in->i_pts = p_block_in->i_dts = 0;
00424         }
00425         else
00426         {
00427             /* no data in payload; we're done */
00428             p_sys->i_cur_rec++;
00429             return 1;
00430         }
00431     }
00432     /*else
00433     {
00434         -- don't read any data from the stream, data was in the record header --
00435         msg_Dbg(p_demux,
00436                "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
00437                 rec_type, rec_hdr->ex1, rec_hdr->ex2);
00438     }*/
00439 
00440     /*================================================================*
00441      * Video Parsing
00442      *================================================================*/
00443     if ( rec_type == 0xe0 )
00444     {
00445         if( subrec_type == 0x06 )
00446         {
00447             /* get the PTS from this packet.
00448              * Do NOT Pass this packet (a PES Header) on to the MPEG2 codec */
00449             find_es_header( ty_VideoPacket, p_block_in->p_buffer,
00450                             l_rec_size, &esOffset1 );
00451             if ( esOffset1 != -1 )
00452             {
00453                 /* msg_Dbg(p_demux, "Video PES hdr at offset %d", esOffset1); */
00454                 p_sys->lastVideoPTS = get_pts( &p_block_in->p_buffer[ esOffset1 + 9 ] );
00455                 /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
00456                             p_sys->lastVideoPTS );*/
00457             }
00458             block_Release(p_block_in);
00459         }
00460         else
00461         {
00462 #if 0
00463             msg_Dbg(p_demux, "packet buffer has "
00464                     "%02x %02x %02x %02x %02x %02x %02x %02x "
00465                     "%02x %02x %02x %02x %02x %02x %02x %02x",
00466                     p_block_in->p_buffer[0], p_block_in->p_buffer[1],
00467                     p_block_in->p_buffer[2], p_block_in->p_buffer[3],
00468                     p_block_in->p_buffer[4], p_block_in->p_buffer[5],
00469                     p_block_in->p_buffer[6], p_block_in->p_buffer[7],
00470                     p_block_in->p_buffer[8], p_block_in->p_buffer[9],
00471                     p_block_in->p_buffer[10], p_block_in->p_buffer[11],
00472                     p_block_in->p_buffer[12], p_block_in->p_buffer[13],
00473                     p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
00474 #endif
00475             /* if it's not a continue blk, then set PTS */
00476             if (subrec_type != 0x02)
00477             {
00478                 /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
00479                            subrec_type);*/
00480                 /* if it's a GOP header, make sure it's legal
00481                  * (if we have enough data) */
00482                 /* Some ty files don't have this bit set
00483                  * and it causes problems */
00484                 if (subrec_type == 0x0c && l_rec_size >= 6)
00485                     p_block_in->p_buffer[5] |= 0x08;
00486                 /* set PTS for this block before we send */
00487                 if (p_sys->lastVideoPTS > 0)
00488                 {
00489                     p_block_in->i_pts = p_sys->lastVideoPTS;
00490                     /* PTS gets used ONCE. 
00491                      * Any subsequent frames we get BEFORE next PES
00492                      * header will have their PTS computed in the codec */
00493                     p_sys->lastVideoPTS = 0;
00494                 }
00495             } 
00496             es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
00497         }
00498     } /* end if video rec type */
00499     /* ================================================================
00500      * Audio Parsing
00501      * ================================================================
00502      * parse PES headers and send the rest to the codec
00503      */
00504     else if ( rec_type == 0xc0 )
00505     {
00506 #if 0
00507         int i;
00508         printf( "Audio Packet Header " );
00509         for( i = 0 ; i < 24 ; i++ )
00510             printf( "%2.2x ", p_block_in->p_buffer[i] );
00511         printf( "\n" );
00512 #endif
00513         /* load a codec if we haven't yet */
00514         if ( p_sys->p_audio == NULL )
00515         {
00516             if ( subrec_type == 0x09 )
00517             {
00518                 /* set up for AC-3 audio */
00519                 msg_Dbg(p_demux, "detected AC-3 Audio" );
00520                         setup_audio_streams('A', p_demux);
00521             }
00522             else
00523             {
00524                 /* set up for MPEG audio */
00525                 msg_Dbg(p_demux, "detected MPEG Audio" );
00526                 setup_audio_streams('M', p_demux);
00527             }
00528         }
00529 
00530         /* SA or DTiVo Audio Data, no PES (continued block)
00531          * ================================================
00532          */
00533         if ( subrec_type == 2 )
00534         {
00535             /* continue PES if previous was incomplete */
00536             /* TODO: Make this work for all series & types of tivos */
00537             if (p_sys->i_pes_buf_cnt > 0)
00538             {
00539                 int i_need = SERIES1_PES_LENGTH - p_sys->i_pes_buf_cnt;
00540 
00541                 msg_Dbg(p_demux, "continuing PES header");
00542                 /* do we have enough data to complete? */
00543                 if (i_need < l_rec_size)
00544                 {
00545                     /* we have enough; reconstruct this p_frame with the new hdr */
00546                     memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
00547                            p_block_in->p_buffer, i_need);
00548                     /* advance the block past the PES header (don't want to send it) */
00549                     p_block_in->p_buffer += i_need;
00550                     p_block_in->i_buffer -= i_need;
00551                     /* get the PTS out of this PES header (MPEG or AC3) */
00552                     if (p_sys->b_mpeg_audio)
00553                         find_es_header(ty_MPEGAudioPacket, p_sys->pes_buffer,
00554                                         10, &esOffset1);
00555                     else
00556                         find_es_header(ty_AC3AudioPacket, p_sys->pes_buffer,
00557                                         10, &esOffset1);
00558                     if (esOffset1 < 0)
00559                     {
00560                         /* god help us; something's really wrong */
00561                         msg_Err(p_demux, "can't find audio PES header in packet");
00562                     }
00563                     else
00564                     {
00565                         p_sys->lastAudioPTS = get_pts( 
00566                             &p_sys->pes_buffer[ esOffset1 + DTIVO_PTS_OFFSET ] );
00567                         p_block_in->i_pts = p_sys->lastAudioPTS;
00568                     }
00569                     p_sys->i_pes_buf_cnt = 0;
00570                 }
00571                 else
00572                 {
00573                     /* don't have complete PES hdr; save what we have and return */
00574                     memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
00575                             p_block_in->p_buffer, l_rec_size);
00576                     p_sys->i_pes_buf_cnt += l_rec_size;
00577                     p_sys->i_cur_rec++;
00578                     block_Release(p_block_in);
00579                     return 1;
00580                 }
00581             }
00582             /* set PCR before we send */
00583             /*es_out_Control( p_demux->out, ES_OUT_SET_PCR,
00584                               p_block_in->i_pts );*/
00585             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
00586         } /* subrec == 2 */
00587 
00588         /* MPEG Audio with PES Header, either SA or DTiVo   */
00589         /* ================================================ */
00590         if ( subrec_type == 0x03 )
00591         {
00592             find_es_header( ty_MPEGAudioPacket, p_block_in->p_buffer,
00593             l_rec_size, &esOffset1 );
00594 
00595             /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
00596                p_block_in->p_buffer[0], p_block_in->p_buffer[1],
00597                p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
00598             msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
00599 
00600             /* SA PES Header, No Audio Data                     */
00601             /* ================================================ */
00602             if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
00603             {
00604                 p_sys->tivoType = 1;
00605                 p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
00606                             SA_PTS_OFFSET ] );
00607                 if (p_sys->firstAudioPTS < 0)
00608                     p_sys->firstAudioPTS = p_sys->lastAudioPTS;
00609                 block_Release(p_block_in);
00610                 /*msg_Dbg(p_demux, "SA Audio PTS %lld",
00611                            p_sys->lastAudioPTS );*/
00612             }
00613             else
00614             /* DTiVo Audio with PES Header                      */
00615             /* ================================================ */
00616             {
00617                 p_sys->tivoType = 2;
00618 
00619                 /* Check for complete PES
00620                  * (TODO: handle proper size for tivo version) */
00621                 if (check_sync_pes(p_demux, p_block_in, esOffset1,
00622                                     l_rec_size) == -1)
00623                 {
00624                     /* partial PES header found, nothing else. 
00625                      * we're done. */
00626                     p_sys->i_cur_rec++;
00627                     block_Release(p_block_in);
00628                     return 1;
00629                 }
00630 #if 0
00631                 msg_Dbg(p_demux, "packet buffer has "
00632                          "%02x %02x %02x %02x %02x %02x %02x %02x "
00633                          "%02x %02x %02x %02x %02x %02x %02x %02x",
00634                          p_block_in->p_buffer[0], p_block_in->p_buffer[1],
00635                          p_block_in->p_buffer[2], p_block_in->p_buffer[3],
00636                          p_block_in->p_buffer[4], p_block_in->p_buffer[5],
00637                          p_block_in->p_buffer[6], p_block_in->p_buffer[7],
00638                          p_block_in->p_buffer[8], p_block_in->p_buffer[9],
00639                          p_block_in->p_buffer[10], p_block_in->p_buffer[11],
00640                          p_block_in->p_buffer[12], p_block_in->p_buffer[13],
00641                          p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
00642 #endif
00643                 /* set PCR before we send */
00644                 if( p_block_in->i_pts > 0 )
00645                     es_out_Control( p_demux->out, ES_OUT_SET_PCR,
00646                                     p_block_in->i_pts );
00647                 es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
00648             }   /* if DTiVo */
00649         }   /* if subrec == 0x03 */
00650 
00651         /* SA Audio with no PES Header                      */
00652         /* ================================================ */
00653         if ( subrec_type == 0x04 )
00654         {
00655             /*msg_Dbg(p_demux,
00656                     "Adding SA Audio Packet Size %ld", l_rec_size ); */
00657 
00658             /* set PCR before we send */
00659             if (p_sys->lastAudioPTS > 0)
00660             {
00661                 p_block_in->i_pts = p_sys->lastAudioPTS;
00662                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
00663                                 p_block_in->i_pts );
00664             }
00665             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
00666         }
00667 
00668         /* DTiVo AC3 Audio Data with PES Header             */
00669         /* ================================================ */
00670         if ( subrec_type == 0x09 )
00671         {
00672             find_es_header( ty_AC3AudioPacket, p_block_in->p_buffer,
00673                             l_rec_size, &esOffset1 );
00674 
00675             /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
00676                        p_block_in->p_buffer[0], p_block_in->p_buffer[1],
00677                        p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
00678             msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);*/
00679 
00680             /* Check for complete PES */
00681             if (check_sync_pes(p_demux, p_block_in, esOffset1,
00682                                 l_rec_size) == -1)
00683             {
00684                 /* partial PES header found, nothing else.  we're done. */
00685                 p_sys->i_cur_rec++;
00686                 return 1;
00687             }
00688             /* set PCR before we send (if PTS found) */
00689             if( p_block_in->i_pts > 0 )
00690             {
00691                 es_out_Control( p_demux->out, ES_OUT_SET_PCR,
00692                                 p_block_in->i_pts );
00693             }
00694             es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
00695         }
00696     } /* end "if audio" */
00697     /* ================================================================ */
00698     /* Closed Caption                                                   */
00699     /* ================================================================ */
00700     else if ( rec_type == 0x01 )
00701     {
00702         /*msg_Dbg(p_demux, "CC1 %02x %02x [%c%c]", rec_hdr->ex1,
00703                    rec_hdr->ex2, rec_hdr->ex1, rec_hdr->ex2 );*/
00704 
00705         /* construct a 'user-data' MPEG packet */
00706         lastCC[ 0x00 ] = 0x00;
00707         lastCC[ 0x01 ] = 0x00;
00708         lastCC[ 0x02 ] = 0x01;
00709         lastCC[ 0x03 ] = 0xb2;
00710         lastCC[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
00711         lastCC[ 0x05 ] = 'Y';    /* (no other notes there) */
00712         lastCC[ 0x06 ] = 0x01;
00713         lastCC[ 0x07 ] = rec_hdr->ex1;
00714         lastCC[ 0x08 ] = rec_hdr->ex2;
00715         /* not sure what to send, because VLC doesn't yet support
00716          * teletext type of subtitles (only supports the full-sentence type) */
00717         /*p_block_in = block_NewEmpty(); ????
00718         es_out_Send( p_demux->out, p_sys->p_subt_es, p_block_in );*/
00719     }
00720     else if ( rec_type == 0x02 )
00721     {
00722         /*msg_Dbg(p_demux, "CC2 %02x %02x", rec_hdr->ex1, rec_hdr->ex2 );*/
00723 
00724         /* construct a 'user-data' MPEG packet */
00725         lastXDS[ 0x00 ] = 0x00;
00726         lastXDS[ 0x01 ] = 0x00;
00727         lastXDS[ 0x02 ] = 0x01;
00728         lastXDS[ 0x03 ] = 0xb2;
00729         lastXDS[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
00730         lastXDS[ 0x05 ] = 'Y';    /* (no other notes there) */
00731         lastXDS[ 0x06 ] = 0x02;
00732         lastXDS[ 0x07 ] = rec_hdr->ex1;
00733         lastXDS[ 0x08 ] = rec_hdr->ex2;
00734         /* not sure what to send, because VLC doesn't support this?? */
00735         /*p_block_in = block_NewEmpty(); ????
00736         es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );*/
00737     }
00738     /* ================================================================ */
00739     /* Tivo data services (e.g. "thumbs-up to record!")  useless for us */
00740     /* ================================================================ */
00741     else if ( rec_type == 0x03 )
00742     {
00743     }
00744     /* ================================================================ */
00745     /* Unknown, but seen regularly */
00746     /* ================================================================ */
00747     else if ( rec_type == 0x05 )
00748     {
00749     }
00750     else
00751     {
00752         msg_Dbg(p_demux, "Invalid record type 0x%02x", rec_type );
00753         if (p_block_in) block_Release(p_block_in);
00754             invalidType++;
00755     }
00756     p_sys->i_cur_rec++;
00757     return 1;
00758 }
00759 
00760 
00761 /* seek to a position within the stream, if possible */
00762 static int ty_stream_seek(demux_t *p_demux, double seek_pct)
00763 {
00764     demux_sys_t *p_sys = p_demux->p_sys;
00765     int64_t seek_pos = p_sys->i_stream_size * seek_pct;
00766     int i;
00767     long l_skip_amt;
00768 
00769     /* if we're not seekable, there's nothing to do */
00770     if (!p_sys->b_seekable)
00771         return VLC_EGENERIC;
00772 
00773     /* figure out which chunk we want & go there */
00774     p_sys->i_chunk_count = seek_pos / CHUNK_SIZE;
00775 
00776     if ( stream_Seek( p_demux->s, p_sys->i_chunk_count * CHUNK_SIZE))
00777     {
00778         /* can't seek stream */
00779         return VLC_EGENERIC;
00780     }
00781     /* load the chunk */
00782     get_chunk_header(p_demux);
00783   
00784     /* seek within the chunk to get roughly to where we want */
00785     p_sys->i_cur_rec = (int)
00786       ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
00787     msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
00788     msg_Dbg(p_demux, " (chunk %d, record %d)",
00789              p_sys->i_chunk_count - 1, p_sys->i_cur_rec);
00790 
00791     /* seek to the start of this record's data.
00792      * to do that, we have to skip past all prior records */
00793     l_skip_amt = 0;
00794     for (i=0; i<p_sys->i_cur_rec; i++)
00795         l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
00796     stream_Seek(p_demux->s, ((p_sys->i_chunk_count-1) * CHUNK_SIZE) +
00797                  (p_sys->i_num_recs * 16) + l_skip_amt + 4);
00798 
00799     /* to hell with syncing any audio or video, just start reading records... :) */
00800     /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
00801     es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
00802     return VLC_SUCCESS;
00803 }
00804 
00805 
00806 static int Control(demux_t *p_demux, int i_query, va_list args)
00807 {
00808     demux_sys_t *p_sys = p_demux->p_sys;
00809     double f, *pf;
00810     int64_t i64, *p_i64;
00811   
00812     /*msg_Info(p_demux, "control cmd %d", i_query);*/
00813     switch( i_query )
00814     {
00815     case DEMUX_GET_POSITION:
00816         /* arg is 0.0 - 1.0 percent of overall file position */
00817         if( ( i64 = p_sys->i_stream_size ) > 0 )
00818         {
00819             pf = (double*) va_arg( args, double* );
00820             *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
00821             return VLC_SUCCESS;
00822         }
00823         return VLC_EGENERIC;
00824 
00825     case DEMUX_SET_POSITION:
00826         /* arg is 0.0 - 1.0 percent of overall file position */
00827         f = (double) va_arg( args, double );
00828         //msg_Dbg(p_demux, "Control - set position to %2.3f", f);
00829         if ((i64 = p_sys->i_stream_size) > 0)
00830             return ty_stream_seek(p_demux, f);
00831         return VLC_EGENERIC;
00832     case DEMUX_GET_TIME:
00833         /* return latest PTS - start PTS */
00834         p_i64 = (int64_t *) va_arg(args, int64_t *);
00835         *p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
00836         return VLC_SUCCESS;
00837     case DEMUX_SET_TIME:      /* arg is time in microsecs */
00838     case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
00839     case DEMUX_GET_FPS:
00840     default:
00841         return VLC_EGENERIC;
00842     }
00843 }
00844 
00845 
00846 /* =========================================================================== */
00847 static void TyClose( vlc_object_t *p_this )
00848 {
00849     demux_sys_t *p_sys = ((demux_t *) p_this)->p_sys;
00850 
00851     free(p_sys->rec_hdrs);
00852     free(p_sys);
00853 }
00854 
00855 
00856 /* =========================================================================== */
00857 static int get_chunk_header(demux_t *p_demux)
00858 {
00859     int i_readSize, i_num_recs, i;
00860     uint8_t packet_header[4];
00861     uint8_t record_header[16];
00862     ty_rec_hdr_t *p_rec_hdr;
00863     demux_sys_t *p_sys = p_demux->p_sys;
00864     int i_payload_size = 0;         /* sum of all records */
00865 
00866     msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys->i_chunk_count );
00867 
00868     /* if we have left-over filler space from the last chunk, get that */
00869     if (p_sys->i_stuff_cnt > 0)
00870         stream_Read( p_demux->s, NULL, p_sys->i_stuff_cnt);
00871 
00872     /* read the TY packet header */
00873     i_readSize = stream_Read( p_demux->s, packet_header, 4 );
00874     p_sys->i_chunk_count++;
00875   
00876     if ( i_readSize < 4 )
00877     {
00878         /* EOF */
00879         p_sys->eof = 1;
00880         return 0;
00881     }
00882   
00883     /* if it's a PART Header, then try again. */
00884     if( U32_AT( &packet_header[ 0 ] ) == TIVO_PES_FILEID )
00885     {
00886         msg_Dbg( p_demux, "skipping TY PART Header" );
00887         /* TODO: if stream is seekable, should we seek() instead of read() ?? */
00888         stream_Read( p_demux->s, NULL, CHUNK_SIZE - 4 );
00889         return get_chunk_header(p_demux);
00890     }
00891 
00892     /* number of records in chunk (8- or 16-bit number) */
00893     if (packet_header[3] & 0x80)
00894     {
00895         /* 16 bit rec cnt */
00896         p_sys->i_num_recs = i_num_recs = (packet_header[1] << 8) + packet_header[0];
00897         p_sys->i_seq_rec = (packet_header[3] << 8) + packet_header[2];
00898         if (p_sys->i_seq_rec != 0xffff)
00899         {
00900             p_sys->i_seq_rec &= ~0x8000;
00901         }
00902     }
00903     else
00904     {
00905         /* 8 bit reclen - tivo 1.3 format */
00906         p_sys->i_num_recs = i_num_recs = packet_header[0];
00907         p_sys->i_seq_rec = packet_header[1];
00908     }
00909     p_sys->i_cur_rec = 0;
00910     p_sys->b_first_chunk = VLC_FALSE;
00911   
00912     /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
00913 
00914     /* parse headers into array */
00915     if (p_sys->rec_hdrs)
00916         free(p_sys->rec_hdrs);
00917     p_sys->rec_hdrs = malloc(i_num_recs * sizeof(ty_rec_hdr_t));
00918     for (i = 0; i < i_num_recs; i++)
00919     {
00920         i_readSize = stream_Read( p_demux->s, record_header, 16 );
00921         if (i_readSize < 16)
00922         {
00923             /* EOF */
00924             p_sys->eof = VLC_TRUE;
00925             return 0;
00926         }
00927         p_rec_hdr = &p_sys->rec_hdrs[i];     /* for brevity */
00928         p_rec_hdr->rec_type = record_header[3];
00929         p_rec_hdr->subrec_type = record_header[2] & 0x0f;
00930         if ((record_header[ 0 ] & 0x80) == 0x80)
00931         {
00932             unsigned char b1, b2;
00933             /* marker bit 2 set, so read extended data */
00934             b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) | 
00935                    ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
00936             b1 &= 0x7f;
00937             b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) | 
00938                    ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
00939             b2 &= 0x7f;
00940 
00941             p_rec_hdr->ex1 = b1;
00942             p_rec_hdr->ex2 = b2;
00943             p_rec_hdr->l_rec_size = 0;
00944             p_rec_hdr->b_ext = VLC_TRUE;
00945         }
00946         else
00947         {
00948             p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
00949                 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
00950             i_payload_size += p_rec_hdr->l_rec_size;
00951             p_rec_hdr->b_ext = VLC_FALSE;
00952         }
00953     } /* end of record-header loop */
00954     p_sys->i_stuff_cnt = CHUNK_SIZE - 4 -
00955         (p_sys->i_num_recs * 16) - i_payload_size;
00956     if (p_sys->i_stuff_cnt > 0)
00957         msg_Dbg( p_demux, "chunk has %d stuff bytes at end",
00958                  p_sys->i_stuff_cnt );
00959     return 1;
00960 }

Generated on Tue Dec 20 10:14:36 2005 for vlc-0.8.4a by  doxygen 1.4.2