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

vobsub.c

00001 /*****************************************************************************
00002  * subtitle.c: Demux vobsub files.
00003  *****************************************************************************
00004  * Copyright (C) 1999-2004 the VideoLAN team
00005  * $Id: vobsub.c 11664 2005-07-09 06:17:09Z courmisch $
00006  *
00007  * Authors: Laurent Aimar <[email protected]>
00008  *          Derk-Jan Hartman <hartman at videolan dot org>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 /*****************************************************************************
00026  * Preamble
00027  *****************************************************************************/
00028 #include <stdlib.h>
00029 
00030 #include <errno.h>
00031 #include <sys/types.h>
00032 
00033 #include <vlc/vlc.h>
00034 #include <vlc/input.h>
00035 #include "vlc_video.h"
00036 
00037 #include "ps.h"
00038 
00039 #define MAX_LINE 8192
00040 
00041 /*****************************************************************************
00042  * Module descriptor
00043  *****************************************************************************/
00044 static int  Open ( vlc_object_t *p_this );
00045 static void Close( vlc_object_t *p_this );
00046 
00047 vlc_module_begin();
00048     set_description( _("Vobsub subtitles demux") );
00049     set_category( CAT_INPUT );
00050     set_subcategory( SUBCAT_INPUT_DEMUX );
00051     set_capability( "demux2", 1 );
00052     
00053     set_callbacks( Open, Close );
00054 
00055     add_shortcut( "vobsub" );
00056     add_shortcut( "subtitle" );
00057 vlc_module_end();
00058 
00059 /*****************************************************************************
00060  * Prototypes:
00061  *****************************************************************************/
00062 
00063 typedef struct
00064 {
00065     int     i_line_count;
00066     int     i_line;
00067     char    **line;
00068 } text_t;
00069 static int  TextLoad( text_t *, stream_t *s );
00070 static void TextUnload( text_t * );
00071 
00072 typedef struct
00073 {
00074     int64_t i_start;
00075     int     i_vobsub_location;
00076 } subtitle_t;
00077 
00078 typedef struct
00079 {
00080     es_format_t fmt;
00081     es_out_id_t *p_es;
00082     int         i_track_id;
00083     
00084     int         i_current_subtitle;
00085     int         i_subtitles;
00086     subtitle_t  *p_subtitles;
00087 } vobsub_track_t;
00088 
00089 struct demux_sys_t
00090 {
00091     int64_t     i_next_demux_date;
00092     int64_t     i_length;
00093 
00094     text_t      txt;
00095     FILE        *p_vobsub_file;
00096     
00097     /* all tracks */
00098     int            i_tracks;
00099     vobsub_track_t *track;
00100     
00101     int         i_original_frame_width;
00102     int         i_original_frame_height;
00103 };
00104 
00105 static int Demux( demux_t * );
00106 static int Control( demux_t *, int, va_list );
00107 
00108 static int ParseVobSubIDX( demux_t * );
00109 static int DemuxVobSub( demux_t *, block_t *);
00110 
00111 /*****************************************************************************
00112  * Module initializer
00113  *****************************************************************************/
00114 static int Open ( vlc_object_t *p_this )
00115 {
00116     demux_t     *p_demux = (demux_t*)p_this;
00117     demux_sys_t *p_sys;
00118     char *psz_vobname, *s;
00119     int i_len;
00120 
00121     if( ( s = stream_ReadLine( p_demux->s ) ) != NULL )
00122     {
00123         if( !strcasestr( s, "# VobSub index file" ) )
00124         {
00125             msg_Dbg( p_demux, "this doesn't seem to be a vobsub file" );
00126             free( s );
00127             if( stream_Seek( p_demux->s, 0 ) )
00128             {
00129                 msg_Warn( p_demux, "failed to rewind" );
00130             }
00131             return VLC_EGENERIC;
00132         }
00133         free( s );
00134 
00135     }
00136     else
00137     {
00138         msg_Dbg( p_demux, "could not read vobsub IDX file" );
00139         return VLC_EGENERIC;
00140     }
00141 
00142     p_demux->pf_demux = Demux;
00143     p_demux->pf_control = Control;
00144     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
00145     p_sys->i_length = 0;
00146     p_sys->p_vobsub_file = NULL;
00147     p_sys->i_tracks = 0;
00148     p_sys->track = (vobsub_track_t *)malloc( sizeof( vobsub_track_t ) );
00149     p_sys->i_original_frame_width = -1;
00150     p_sys->i_original_frame_height = -1;
00151 
00152     /* Load the whole file */
00153     TextLoad( &p_sys->txt, p_demux->s );
00154 
00155     /* Parse it */
00156     ParseVobSubIDX( p_demux );
00157 
00158     /* Unload */
00159     TextUnload( &p_sys->txt );
00160 
00161     /* Find the total length of the vobsubs */
00162     if( p_sys->i_tracks > 0 )
00163     {
00164         int i;
00165         for( i = 0; i < p_sys->i_tracks; i++ )
00166         {
00167             if( p_sys->track[i].i_subtitles > 1 )
00168             {
00169                 if( p_sys->track[i].p_subtitles[p_sys->track[i].i_subtitles-1].i_start > p_sys->i_length )
00170                     p_sys->i_length = (int64_t) p_sys->track[i].p_subtitles[p_sys->track[i].i_subtitles-1].i_start + ( 1 *1000 *1000 );
00171             }
00172         }
00173     }
00174 
00175     i_len = strlen( p_demux->psz_path );
00176     psz_vobname = strdup( p_demux->psz_path );
00177 
00178     strcpy( psz_vobname + i_len - 4, ".sub" );
00179 
00180     /* open file */
00181     if( !( p_sys->p_vobsub_file = fopen( psz_vobname, "rb" ) ) )
00182     {
00183         msg_Err( p_demux, "couldn't open .sub Vobsub file: %s",
00184                  psz_vobname );
00185         free( p_sys );
00186         free( psz_vobname );
00187         return VLC_EGENERIC;
00188     }
00189     free( psz_vobname );
00190 
00191     return VLC_SUCCESS;
00192 }
00193 
00194 /*****************************************************************************
00195  * Close: Close subtitle demux
00196  *****************************************************************************/
00197 static void Close( vlc_object_t *p_this )
00198 {
00199     demux_t *p_demux = (demux_t*)p_this;
00200     demux_sys_t *p_sys = p_demux->p_sys;
00201 
00202     /* Clean all subs from all tracks
00203     if( p_sys->subtitle )
00204         free( p_sys->subtitle );
00205 */
00206     if( p_sys->p_vobsub_file )
00207         fclose( p_sys->p_vobsub_file );
00208 
00209     free( p_sys );
00210 }
00211 
00212 /*****************************************************************************
00213  * Control:
00214  *****************************************************************************/
00215 static int Control( demux_t *p_demux, int i_query, va_list args )
00216 {
00217     demux_sys_t *p_sys = p_demux->p_sys;
00218     int64_t *pi64, i64;
00219     int i;
00220     double *pf, f;
00221 
00222     switch( i_query )
00223     {
00224         case DEMUX_GET_LENGTH:
00225             pi64 = (int64_t*)va_arg( args, int64_t * );
00226             *pi64 = (int64_t) p_sys->i_length;
00227             return VLC_SUCCESS;
00228 
00229         case DEMUX_GET_TIME:
00230             pi64 = (int64_t*)va_arg( args, int64_t * );
00231             for( i = 0; i < p_sys->i_tracks; i++ )
00232             {
00233                 vlc_bool_t b_selected;
00234                 /* Check the ES is selected */
00235                 es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE,
00236                                 p_sys->track[i].p_es, &b_selected );
00237                 if( b_selected ) break;
00238             }
00239             if( i < p_sys->i_tracks && p_sys->track[i].i_current_subtitle < p_sys->track[i].i_subtitles )
00240             {
00241                 *pi64 = p_sys->track[i].p_subtitles[p_sys->track[i].i_current_subtitle].i_start;
00242                 return VLC_SUCCESS;
00243             }
00244             return VLC_EGENERIC;
00245 
00246         case DEMUX_SET_TIME:
00247             i64 = (int64_t)va_arg( args, int64_t );
00248             for( i = 0; i < p_sys->i_tracks; i++ )
00249             {
00250                 p_sys->track[i].i_current_subtitle = 0;
00251                 while( p_sys->track[i].i_current_subtitle < p_sys->track[i].i_subtitles &&
00252                        p_sys->track[i].p_subtitles[p_sys->track[i].i_current_subtitle].i_start < i64 )
00253                 {
00254                     p_sys->track[i].i_current_subtitle++;
00255                 }
00256 
00257                 if( p_sys->track[i].i_current_subtitle >= p_sys->track[i].i_subtitles )
00258                     return VLC_EGENERIC;
00259             }
00260             return VLC_SUCCESS;
00261 
00262         case DEMUX_GET_POSITION:
00263             pf = (double*)va_arg( args, double * );
00264             for( i = 0; i < p_sys->i_tracks; i++ )
00265             {
00266                 vlc_bool_t b_selected;
00267                 /* Check the ES is selected */
00268                 es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE,
00269                                 p_sys->track[i].p_es, &b_selected );
00270                 if( b_selected ) break;
00271             }
00272             if( p_sys->track[i].i_current_subtitle >= p_sys->track[i].i_subtitles )
00273             {
00274                 *pf = 1.0;
00275             }
00276             else if( p_sys->track[i].i_subtitles > 0 )
00277             {
00278                 *pf = (double)p_sys->track[i].p_subtitles[p_sys->track[i].i_current_subtitle].i_start /
00279                       (double)p_sys->i_length;
00280             }
00281             else
00282             {
00283                 *pf = 0.0;
00284             }
00285             return VLC_SUCCESS;
00286 
00287         case DEMUX_SET_POSITION:
00288             f = (double)va_arg( args, double );
00289             i64 = (int64_t) f * p_sys->i_length;
00290 
00291             for( i = 0; i < p_sys->i_tracks; i++ )
00292             {
00293                 p_sys->track[i].i_current_subtitle = 0;
00294                 while( p_sys->track[i].i_current_subtitle < p_sys->track[i].i_subtitles &&
00295                        p_sys->track[i].p_subtitles[p_sys->track[i].i_current_subtitle].i_start < i64 )
00296                 {
00297                     p_sys->track[i].i_current_subtitle++;
00298                 }
00299                 if( p_sys->track[i].i_current_subtitle >= p_sys->track[i].i_subtitles )
00300                     return VLC_EGENERIC;
00301             }
00302             return VLC_SUCCESS;
00303 
00304         case DEMUX_SET_NEXT_DEMUX_TIME:
00305             p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
00306             return VLC_SUCCESS;
00307 
00308         case DEMUX_GET_FPS:
00309         case DEMUX_GET_META:
00310         case DEMUX_GET_TITLE_INFO:
00311             return VLC_EGENERIC;
00312 
00313         default:
00314             msg_Err( p_demux, "unknown query in subtitle control" );
00315             return VLC_EGENERIC;
00316     }
00317 }
00318 
00319 /*****************************************************************************
00320  * Demux: Send subtitle to decoder
00321  *****************************************************************************/
00322 static int Demux( demux_t *p_demux )
00323 {
00324     demux_sys_t *p_sys = p_demux->p_sys;
00325     int64_t i_maxdate;
00326     int i;
00327 
00328     for( i = 0; i < p_sys->i_tracks; i++ )
00329     {
00330 #define tk p_sys->track[i]
00331         if( tk.i_current_subtitle >= tk.i_subtitles )
00332             continue;
00333 
00334         i_maxdate = (int64_t) p_sys->i_next_demux_date;
00335         if( i_maxdate <= 0 && tk.i_current_subtitle < tk.i_subtitles )
00336         {
00337             /* Should not happen */
00338             i_maxdate = (int64_t) tk.p_subtitles[tk.i_current_subtitle].i_start + 1;
00339         }
00340 
00341         while( tk.i_current_subtitle < tk.i_subtitles &&
00342                tk.p_subtitles[tk.i_current_subtitle].i_start < i_maxdate )
00343         {
00344             int i_pos = tk.p_subtitles[tk.i_current_subtitle].i_vobsub_location;
00345             block_t *p_block;
00346             int i_size = 0;
00347 
00348             /* first compute SPU size */
00349             if( tk.i_current_subtitle + 1 < tk.i_subtitles )
00350             {
00351                 i_size = tk.p_subtitles[tk.i_current_subtitle+1].i_vobsub_location - i_pos;
00352             }
00353             if( i_size <= 0 ) i_size = 65535;   /* Invalid or EOF */
00354 
00355             /* Seek at the right place */
00356             if( fseek( p_sys->p_vobsub_file, i_pos, SEEK_SET ) )
00357             {
00358                 msg_Warn( p_demux,
00359                           "cannot seek at right vobsub location %d", i_pos );
00360                 tk.i_current_subtitle++;
00361                 continue;
00362             }
00363 
00364             /* allocate a packet */
00365             if( ( p_block = block_New( p_demux, i_size ) ) == NULL )
00366             {
00367                 tk.i_current_subtitle++;
00368                 continue;
00369             }
00370 
00371             /* read data */
00372             p_block->i_buffer = fread( p_block->p_buffer, 1, i_size,
00373                                        p_sys->p_vobsub_file );
00374             if( p_block->i_buffer <= 6 )
00375             {
00376                 block_Release( p_block );
00377                 tk.i_current_subtitle++;
00378                 continue;
00379             }
00380 
00381             /* pts */
00382             p_block->i_pts = tk.p_subtitles[tk.i_current_subtitle].i_start;
00383 
00384             /* demux this block */
00385             DemuxVobSub( p_demux, p_block );
00386 
00387             tk.i_current_subtitle++;
00388         }
00389 #undef tk
00390     }
00391 
00392     /* */
00393     p_sys->i_next_demux_date = 0;
00394 
00395     return 1;
00396 }
00397 
00398 static int TextLoad( text_t *txt, stream_t *s )
00399 {
00400     int   i_line_max;
00401 
00402     /* init txt */
00403     i_line_max          = 500;
00404     txt->i_line_count   = 0;
00405     txt->i_line         = 0;
00406     txt->line           = calloc( i_line_max, sizeof( char * ) );
00407 
00408     /* load the complete file */
00409     for( ;; )
00410     {
00411         char *psz = stream_ReadLine( s );
00412 
00413         if( psz == NULL )
00414             break;
00415 
00416         txt->line[txt->i_line_count++] = psz;
00417         if( txt->i_line_count >= i_line_max )
00418         {
00419             i_line_max += 100;
00420             txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
00421         }
00422     }
00423 
00424     if( txt->i_line_count <= 0 )
00425     {
00426         free( txt->line );
00427         return VLC_EGENERIC;
00428     }
00429 
00430     return VLC_SUCCESS;
00431 }
00432 static void TextUnload( text_t *txt )
00433 {
00434     int i;
00435 
00436     for( i = 0; i < txt->i_line_count; i++ )
00437     {
00438         free( txt->line[i] );
00439     }
00440     free( txt->line );
00441     txt->i_line       = 0;
00442     txt->i_line_count = 0;
00443 }
00444 
00445 static char *TextGetLine( text_t *txt )
00446 {
00447     if( txt->i_line >= txt->i_line_count )
00448         return( NULL );
00449 
00450     return txt->line[txt->i_line++];
00451 }
00452 
00453 static int ParseVobSubIDX( demux_t *p_demux )
00454 {
00455     demux_sys_t *p_sys = p_demux->p_sys;
00456     text_t      *txt = &p_sys->txt;
00457     char        *line;
00458     vobsub_track_t *current_tk;
00459 
00460     for( ;; )
00461     {
00462         if( ( line = TextGetLine( txt ) ) == NULL )
00463         {
00464             return( VLC_EGENERIC );
00465         }
00466         
00467         if( *line == 0 || *line == '\r' || *line == '\n' || *line == '#' ) 
00468             continue;
00469         else if( !strncmp( "size:", line, 5 ) )
00470         {
00471             /* Store the original size of the video */
00472             if( sscanf( line, "size: %dx%d",
00473                         &p_sys->i_original_frame_width, &p_sys->i_original_frame_height ) == 2 )
00474             {
00475                 msg_Dbg( p_demux, "original frame size: %dx%d", p_sys->i_original_frame_width, p_sys->i_original_frame_height );
00476             }
00477             else
00478             {
00479                 msg_Warn( p_demux, "reading original frame size failed" );
00480             }
00481         }
00482         else if( !strncmp( "id:", line, 3 ) )
00483         {
00484             char language[20];
00485             int i_track_id;
00486             es_format_t fmt;
00487 
00488             /* Lets start a new track */
00489             if( sscanf( line, "id: %2s, index: %d",
00490                         language, &i_track_id ) == 2 )
00491             {
00492                 p_sys->i_tracks++;
00493                 p_sys->track = (vobsub_track_t*)realloc( p_sys->track, sizeof( vobsub_track_t ) * (p_sys->i_tracks + 1 ) );
00494 
00495                 /* Init the track */
00496                 current_tk = &p_sys->track[p_sys->i_tracks - 1];
00497                 memset( current_tk, 0, sizeof( vobsub_track_t ) );
00498                 current_tk->i_current_subtitle = 0;
00499                 current_tk->i_subtitles = 0;
00500                 current_tk->p_subtitles = (subtitle_t*)malloc( sizeof( subtitle_t ) );;
00501                 current_tk->i_track_id = i_track_id;
00502 
00503                 es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
00504                 fmt.subs.spu.i_original_frame_width = p_sys->i_original_frame_width;
00505                 fmt.subs.spu.i_original_frame_height = p_sys->i_original_frame_height;
00506                 fmt.psz_language = strdup( language );
00507                 current_tk->p_es = es_out_Add( p_demux->out, &fmt );
00508 
00509                 msg_Dbg( p_demux, "new vobsub track detected" );
00510             }
00511             else
00512             {
00513                 msg_Warn( p_demux, "reading new track failed" );
00514             }
00515         }
00516         else if( !strncmp( line, "timestamp:", 10 ) )
00517         {
00518             /*
00519              * timestamp: hh:mm:ss:mss, filepos: loc
00520              * loc is the hex location of the spu in the .sub file
00521              *
00522              */
00523             int h, m, s, ms, loc;
00524             int64_t i_start, i_location = 0;
00525             
00526             vobsub_track_t *current_tk = &p_sys->track[p_sys->i_tracks - 1];
00527 
00528             if( sscanf( line, "timestamp: %d:%d:%d:%d, filepos: %x",
00529                         &h, &m, &s, &ms, &loc ) == 5 )
00530             {
00531                 subtitle_t *current_sub;
00532                 
00533                 i_start = (int64_t) ( h * 3600*1000 +
00534                             m * 60*1000 +
00535                             s * 1000 +
00536                             ms ) * 1000;
00537                 i_location = loc;
00538                 
00539                 current_tk->i_subtitles++;
00540                 current_tk->p_subtitles = (subtitle_t*)realloc( current_tk->p_subtitles, sizeof( subtitle_t ) * (current_tk->i_subtitles + 1 ) );
00541                 current_sub = &current_tk->p_subtitles[current_tk->i_subtitles - 1];
00542                 
00543                 current_sub->i_start = (int64_t) i_start;
00544                 current_sub->i_vobsub_location = i_location;
00545             }
00546         }
00547     }
00548     return( 0 );
00549 }
00550 
00551 static int DemuxVobSub( demux_t *p_demux, block_t *p_bk )
00552 {
00553     demux_sys_t *p_sys = p_demux->p_sys;
00554     uint8_t     *p = p_bk->p_buffer;
00555     uint8_t     *p_end = &p_bk->p_buffer[p_bk->i_buffer];
00556     int i;
00557 
00558     while( p < p_end )
00559     {
00560         int i_size = ps_pkt_size( p, p_end - p );
00561         block_t *p_pkt;
00562         int      i_id;
00563         int      i_spu;
00564 
00565         if( i_size <= 0 )
00566         {
00567             break;
00568         }
00569         if( p[0] != 0 || p[1] != 0 || p[2] != 0x01 )
00570         {
00571             msg_Warn( p_demux, "invalid PES" );
00572             break;
00573         }
00574 
00575         if( p[3] != 0xbd )
00576         {
00577             /* msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] ); */
00578             p += i_size;
00579             continue;
00580         }
00581 
00582         /* Create a block */
00583         p_pkt = block_New( p_demux, i_size );
00584         memcpy( p_pkt->p_buffer, p, i_size);
00585         p += i_size;
00586 
00587         i_id = ps_pkt_id( p_pkt );
00588         if( (i_id&0xffe0) != 0xbd20 ||
00589             ps_pkt_parse_pes( p_pkt, 1 ) )
00590         {
00591             block_Release( p_pkt );
00592             continue;
00593         }
00594         i_spu = i_id&0x1f;
00595         /* msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size ); */
00596 
00597         for( i = 0; i < p_sys->i_tracks; i++ )
00598         {
00599 #define tk p_sys->track[i]
00600             p_pkt->i_dts = p_pkt->i_pts = p_bk->i_pts;
00601             p_pkt->i_length = 0;
00602             
00603             if( tk.p_es && tk.i_track_id == i_spu )
00604             {
00605                 es_out_Send( p_demux->out, tk.p_es, p_pkt );
00606                 p_bk->i_pts = 0;     /*only first packet has a pts */
00607                 break;
00608             }
00609             else if( i == p_sys->i_tracks - 1 )
00610             {
00611                 block_Release( p_pkt );
00612             }
00613 #undef tk
00614         }
00615     }
00616 
00617     return VLC_SUCCESS;
00618 }

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