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

subtitle.c

00001 /*****************************************************************************
00002  * subtitle.c: Demux for subtitle text files.
00003  *****************************************************************************
00004  * Copyright (C) 1999-2004 the VideoLAN team
00005  * $Id: subtitle.c 12548 2005-09-14 00:36:41Z hartman $
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 #ifdef HAVE_SYS_TYPES_H
00032 #   include <sys/types.h>
00033 #endif
00034 #include <ctype.h>
00035 
00036 #include <vlc/vlc.h>
00037 #include <vlc/input.h>
00038 #include "vlc_video.h"
00039 
00040 /*****************************************************************************
00041  * Module descriptor
00042  *****************************************************************************/
00043 static int  Open ( vlc_object_t *p_this );
00044 static void Close( vlc_object_t *p_this );
00045 
00046 #define SUB_DELAY_LONGTEXT \
00047     "Delay subtitles (in 1/10s)"
00048 #define SUB_FPS_LONGTEXT \
00049     "Override frames per second. " \
00050     "It will only work with MicroDVD subtitles."
00051 #define SUB_TYPE_LONGTEXT \
00052     "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"ass\", \"vplayer\" " \
00053     "\"sami\" (auto for autodetection, it should always work)."
00054 static char *ppsz_sub_type[] =
00055 {
00056     "auto", "microdvd", "subrip", "subviewer", "ssa1",
00057     "ssa2-4", "ass", "vplayer", "sami"
00058 };
00059 
00060 vlc_module_begin();
00061     set_shortname( _("Subtitles"));
00062     set_description( _("Text subtitles demux") );
00063     set_capability( "demux2", 0 );
00064     set_category( CAT_INPUT );
00065     set_subcategory( SUBCAT_INPUT_DEMUX );
00066     add_float( "sub-fps", 0.0, NULL,
00067                N_("Frames per second"),
00068                SUB_FPS_LONGTEXT, VLC_TRUE );
00069     add_integer( "sub-delay", 0, NULL,
00070                N_("Subtitles delay"),
00071                SUB_DELAY_LONGTEXT, VLC_TRUE );
00072     add_string( "sub-type", "auto", NULL, "Subtitles fileformat",
00073                 SUB_TYPE_LONGTEXT, VLC_TRUE );
00074         change_string_list( ppsz_sub_type, 0, 0 );
00075     set_callbacks( Open, Close );
00076 
00077     add_shortcut( "subtitle" );
00078 vlc_module_end();
00079 
00080 /*****************************************************************************
00081  * Prototypes:
00082  *****************************************************************************/
00083 enum
00084 {
00085     SUB_TYPE_UNKNOWN = -1,
00086     SUB_TYPE_MICRODVD,
00087     SUB_TYPE_SUBRIP,
00088     SUB_TYPE_SSA1,
00089     SUB_TYPE_SSA2_4,
00090     SUB_TYPE_ASS,
00091     SUB_TYPE_VPLAYER,
00092     SUB_TYPE_SAMI,
00093     SUB_TYPE_SUBVIEWER,
00094 };
00095 
00096 typedef struct
00097 {
00098     int     i_line_count;
00099     int     i_line;
00100     char    **line;
00101 } text_t;
00102 static int  TextLoad( text_t *, stream_t *s );
00103 static void TextUnload( text_t * );
00104 
00105 typedef struct
00106 {
00107     int64_t i_start;
00108     int64_t i_stop;
00109 
00110     char    *psz_text;
00111 } subtitle_t;
00112 
00113 
00114 struct demux_sys_t
00115 {
00116     int         i_type;
00117     text_t      txt;
00118     es_out_id_t *es;
00119 
00120     int64_t     i_next_demux_date;
00121 
00122     int64_t     i_microsecperframe;
00123     int64_t     i_original_mspf;
00124 
00125     char        *psz_header;
00126     int         i_subtitle;
00127     int         i_subtitles;
00128     subtitle_t  *subtitle;
00129 
00130     int64_t     i_length;
00131 };
00132 
00133 static int  ParseMicroDvd ( demux_t *, subtitle_t * );
00134 static int  ParseSubRip   ( demux_t *, subtitle_t * );
00135 static int  ParseSubViewer( demux_t *, subtitle_t * );
00136 static int  ParseSSA      ( demux_t *, subtitle_t * );
00137 static int  ParseVplayer  ( demux_t *, subtitle_t * );
00138 static int  ParseSami     ( demux_t *, subtitle_t * );
00139 
00140 static struct
00141 {
00142     char *psz_type_name;
00143     int  i_type;
00144     char *psz_name;
00145     int  (*pf_read)( demux_t *, subtitle_t* );
00146 } sub_read_subtitle_function [] =
00147 {
00148     { "microdvd",   SUB_TYPE_MICRODVD,  "MicroDVD", ParseMicroDvd },
00149     { "subrip",     SUB_TYPE_SUBRIP,    "SubRIP",   ParseSubRip },
00150     { "subviewer",  SUB_TYPE_SUBVIEWER, "SubViewer",ParseSubViewer },
00151     { "ssa1",       SUB_TYPE_SSA1,      "SSA-1",    ParseSSA },
00152     { "ssa2-4",     SUB_TYPE_SSA2_4,    "SSA-2/3/4",ParseSSA },
00153     { "ass",        SUB_TYPE_ASS,       "SSA/ASS",  ParseSSA },
00154     { "vplayer",    SUB_TYPE_VPLAYER,   "VPlayer",  ParseVplayer },
00155     { "sami",       SUB_TYPE_SAMI,      "SAMI",     ParseSami },
00156     { NULL,         SUB_TYPE_UNKNOWN,   "Unknown",  NULL }
00157 };
00158 
00159 static int Demux( demux_t * );
00160 static int Control( demux_t *, int, va_list );
00161 
00162 /*static void Fix( demux_t * );*/
00163 
00164 /*****************************************************************************
00165  * Module initializer
00166  *****************************************************************************/
00167 static int Open ( vlc_object_t *p_this )
00168 {
00169     demux_t     *p_demux = (demux_t*)p_this;
00170     demux_sys_t *p_sys;
00171     es_format_t fmt;
00172     float f_fps;
00173     char *psz_type;
00174     int  (*pf_read)( demux_t *, subtitle_t* );
00175     int i, i_max;
00176 
00177     if( strcmp( p_demux->psz_demux, "subtitle" ) )
00178     {
00179         msg_Dbg( p_demux, "subtitle demux discarded" );
00180         return VLC_EGENERIC;
00181     }
00182 
00183     p_demux->pf_demux = Demux;
00184     p_demux->pf_control = Control;
00185     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
00186     p_sys->psz_header = NULL;
00187     p_sys->i_subtitle = 0;
00188     p_sys->i_subtitles= 0;
00189     p_sys->subtitle   = NULL;
00190 
00191 
00192     /* Get the FPS */
00193     f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
00194     if( f_fps >= 1.0 )
00195     {
00196         p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps );
00197     }
00198     else
00199     {
00200         p_sys->i_microsecperframe = 0;
00201     }
00202 
00203     f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
00204     if( f_fps >= 1.0 )
00205     {
00206         p_sys->i_original_mspf = (int64_t)( (float)1000000 / f_fps );
00207     }
00208     else
00209     {
00210         p_sys->i_original_mspf = 0;
00211     }
00212 
00213     /* Get or probe the type */
00214     p_sys->i_type = SUB_TYPE_UNKNOWN;
00215     psz_type = var_CreateGetString( p_demux, "sub-type" );
00216     if( *psz_type )
00217     {
00218         int i;
00219 
00220         for( i = 0; ; i++ )
00221         {
00222             if( sub_read_subtitle_function[i].psz_type_name == NULL )
00223                 break;
00224 
00225             if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
00226                          psz_type ) )
00227             {
00228                 p_sys->i_type = sub_read_subtitle_function[i].i_type;
00229                 break;
00230             }
00231         }
00232     }
00233     free( psz_type );
00234 
00235     /* Probe if unknown type */
00236     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
00237     {
00238         int     i_try;
00239         char    *s = NULL;
00240 
00241         msg_Dbg( p_demux, "autodetecting subtitle format" );
00242         for( i_try = 0; i_try < 256; i_try++ )
00243         {
00244             int i_dummy;
00245 
00246             if( ( s = stream_ReadLine( p_demux->s ) ) == NULL )
00247                 break;
00248 
00249             if( strcasestr( s, "<SAMI>" ) )
00250             {
00251                 p_sys->i_type = SUB_TYPE_SAMI;
00252                 break;
00253             }
00254             else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
00255                      sscanf( s, "{%d}{}", &i_dummy ) == 1)
00256             {
00257                 p_sys->i_type = SUB_TYPE_MICRODVD;
00258                 break;
00259             }
00260             else if( sscanf( s,
00261                              "%d:%d:%d,%d --> %d:%d:%d,%d",
00262                              &i_dummy,&i_dummy,&i_dummy,&i_dummy,
00263                              &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
00264             {
00265                 p_sys->i_type = SUB_TYPE_SUBRIP;
00266                 break;
00267             }
00268             else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
00269             {
00270                 p_sys->i_type = SUB_TYPE_SSA1;
00271                 break;
00272             }
00273             else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
00274             {
00275                 p_sys->i_type = SUB_TYPE_ASS;
00276                 break;
00277             }
00278             else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
00279             {
00280                 p_sys->i_type = SUB_TYPE_SSA2_4;
00281                 break;
00282             }
00283             else if( !strncasecmp( s, "Dialogue: Marked", 16  ) )
00284             {
00285                 p_sys->i_type = SUB_TYPE_SSA2_4;
00286                 break;
00287             }
00288             else if( !strncasecmp( s, "Dialogue:", 9  ) )
00289             {
00290                 p_sys->i_type = SUB_TYPE_ASS;
00291                 break;
00292             }
00293             else if( strcasestr( s, "[INFORMATION]" ) )
00294             {
00295                 p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
00296                 break;
00297             }
00298             else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
00299                      sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
00300             {
00301                 p_sys->i_type = SUB_TYPE_VPLAYER;
00302                 break;
00303             }
00304 
00305             free( s );
00306             s = NULL;
00307         }
00308 
00309         if( s ) free( s );
00310 
00311         /* It will nearly always work even for non seekable stream thanks the
00312          * caching system, and if it fails we loose just a few sub */
00313         if( stream_Seek( p_demux->s, 0 ) )
00314         {
00315             msg_Warn( p_demux, "failed to rewind" );
00316         }
00317     }
00318     if( p_sys->i_type == SUB_TYPE_UNKNOWN )
00319     {
00320         msg_Err( p_demux, "failed to recognize subtitle type" );
00321         free( p_sys );
00322         return VLC_EGENERIC;
00323     }
00324 
00325     for( i = 0; ; i++ )
00326     {
00327         if( sub_read_subtitle_function[i].i_type == p_sys->i_type )
00328         {
00329             msg_Dbg( p_demux, "detected %s format",
00330                      sub_read_subtitle_function[i].psz_name );
00331             pf_read = sub_read_subtitle_function[i].pf_read;
00332             break;
00333         }
00334     }
00335 
00336     msg_Dbg( p_demux, "loading all subtitles..." );
00337 
00338     /* Load the whole file */
00339     TextLoad( &p_sys->txt, p_demux->s );
00340 
00341     /* Parse it */
00342     for( i_max = 0;; )
00343     {
00344         if( p_sys->i_subtitles >= i_max )
00345         {
00346             i_max += 500;
00347             if( !( p_sys->subtitle = realloc( p_sys->subtitle,
00348                                               sizeof(subtitle_t) * i_max ) ) )
00349             {
00350                 msg_Err( p_demux, "out of memory");
00351                 if( p_sys->subtitle != NULL )
00352                     free( p_sys->subtitle );
00353                 TextUnload( &p_sys->txt );
00354                 free( p_sys );
00355                 return VLC_ENOMEM;
00356             }
00357         }
00358 
00359         if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) )
00360             break;
00361 
00362         p_sys->i_subtitles++;
00363     }
00364     /* Unload */
00365     TextUnload( &p_sys->txt );
00366 
00367     msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
00368 
00369     /* Fix subtitle (order and time) *** */
00370     p_sys->i_subtitle = 0;
00371     p_sys->i_length = 0;
00372     if( p_sys->i_subtitles > 0 )
00373     {
00374         p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
00375         /* +1 to avoid 0 */
00376         if( p_sys->i_length <= 0 )
00377             p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
00378     }
00379 
00380     /* *** add subtitle ES *** */
00381     if( p_sys->i_type == SUB_TYPE_SSA1 ||
00382              p_sys->i_type == SUB_TYPE_SSA2_4 ||
00383              p_sys->i_type == SUB_TYPE_ASS )
00384     {
00385         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) );
00386     }
00387     else
00388     {
00389         es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) );
00390     }
00391     if( p_sys->psz_header != NULL )
00392     {
00393         fmt.i_extra = strlen( p_sys->psz_header ) + 1;
00394         fmt.p_extra = strdup( p_sys->psz_header );
00395     }
00396     p_sys->es = es_out_Add( p_demux->out, &fmt );
00397 
00398     return VLC_SUCCESS;
00399 }
00400 
00401 /*****************************************************************************
00402  * Close: Close subtitle demux
00403  *****************************************************************************/
00404 static void Close( vlc_object_t *p_this )
00405 {
00406     demux_t *p_demux = (demux_t*)p_this;
00407     demux_sys_t *p_sys = p_demux->p_sys;
00408     int i;
00409 
00410     for( i = 0; i < p_sys->i_subtitles; i++ )
00411     {
00412         if( p_sys->subtitle[i].psz_text )
00413             free( p_sys->subtitle[i].psz_text );
00414     }
00415     if( p_sys->subtitle )
00416         free( p_sys->subtitle );
00417 
00418     free( p_sys );
00419 }
00420 
00421 /*****************************************************************************
00422  * Control:
00423  *****************************************************************************/
00424 static int Control( demux_t *p_demux, int i_query, va_list args )
00425 {
00426     demux_sys_t *p_sys = p_demux->p_sys;
00427     int64_t *pi64, i64;
00428     double *pf, f;
00429 
00430     switch( i_query )
00431     {
00432         case DEMUX_GET_LENGTH:
00433             pi64 = (int64_t*)va_arg( args, int64_t * );
00434             *pi64 = p_sys->i_length;
00435             return VLC_SUCCESS;
00436 
00437         case DEMUX_GET_TIME:
00438             pi64 = (int64_t*)va_arg( args, int64_t * );
00439             if( p_sys->i_subtitle < p_sys->i_subtitles )
00440             {
00441                 *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
00442                 return VLC_SUCCESS;
00443             }
00444             return VLC_EGENERIC;
00445 
00446         case DEMUX_SET_TIME:
00447             i64 = (int64_t)va_arg( args, int64_t );
00448             p_sys->i_subtitle = 0;
00449             while( p_sys->i_subtitle < p_sys->i_subtitles &&
00450                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
00451             {
00452                 p_sys->i_subtitle++;
00453             }
00454 
00455             if( p_sys->i_subtitle >= p_sys->i_subtitles )
00456                 return VLC_EGENERIC;
00457             return VLC_SUCCESS;
00458 
00459         case DEMUX_GET_POSITION:
00460             pf = (double*)va_arg( args, double * );
00461             if( p_sys->i_subtitle >= p_sys->i_subtitles )
00462             {
00463                 *pf = 1.0;
00464             }
00465             else if( p_sys->i_subtitles > 0 )
00466             {
00467                 *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
00468                       (double)p_sys->i_length;
00469             }
00470             else
00471             {
00472                 *pf = 0.0;
00473             }
00474             return VLC_SUCCESS;
00475 
00476         case DEMUX_SET_POSITION:
00477             f = (double)va_arg( args, double );
00478             i64 = f * p_sys->i_length;
00479 
00480             p_sys->i_subtitle = 0;
00481             while( p_sys->i_subtitle < p_sys->i_subtitles &&
00482                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
00483             {
00484                 p_sys->i_subtitle++;
00485             }
00486             if( p_sys->i_subtitle >= p_sys->i_subtitles )
00487                 return VLC_EGENERIC;
00488             return VLC_SUCCESS;
00489 
00490         case DEMUX_SET_NEXT_DEMUX_TIME:
00491             p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
00492             return VLC_SUCCESS;
00493 
00494         case DEMUX_GET_FPS:
00495         case DEMUX_GET_META:
00496         case DEMUX_GET_TITLE_INFO:
00497             return VLC_EGENERIC;
00498 
00499         default:
00500             msg_Err( p_demux, "unknown query in subtitle control" );
00501             return VLC_EGENERIC;
00502     }
00503 }
00504 
00505 /*****************************************************************************
00506  * Demux: Send subtitle to decoder
00507  *****************************************************************************/
00508 static int Demux( demux_t *p_demux )
00509 {
00510     demux_sys_t *p_sys = p_demux->p_sys;
00511     int64_t i_maxdate;
00512 
00513     if( p_sys->i_subtitle >= p_sys->i_subtitles )
00514         return 0;
00515 
00516     i_maxdate = p_sys->i_next_demux_date - var_GetTime( p_demux->p_parent, "spu-delay" );;
00517     if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
00518     {
00519         /* Should not happen */
00520         i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
00521     }
00522 
00523     while( p_sys->i_subtitle < p_sys->i_subtitles &&
00524            p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
00525     {
00526         block_t *p_block;
00527         int i_len = strlen( p_sys->subtitle[p_sys->i_subtitle].psz_text ) + 1;
00528 
00529         if( i_len <= 1 )
00530         {
00531             /* empty subtitle */
00532             p_sys->i_subtitle++;
00533             continue;
00534         }
00535 
00536         if( ( p_block = block_New( p_demux, i_len ) ) == NULL )
00537         {
00538             p_sys->i_subtitle++;
00539             continue;
00540         }
00541 
00542         if( p_sys->subtitle[p_sys->i_subtitle].i_start < 0 )
00543         {
00544             p_sys->i_subtitle++;
00545             continue;
00546         }
00547 
00548         p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
00549         p_block->i_dts = p_block->i_pts;
00550         if( p_sys->subtitle[p_sys->i_subtitle].i_stop > 0 )
00551         {
00552             p_block->i_length =
00553                 p_sys->subtitle[p_sys->i_subtitle].i_stop - p_block->i_pts;
00554         }
00555 
00556         memcpy( p_block->p_buffer,
00557                 p_sys->subtitle[p_sys->i_subtitle].psz_text, i_len );
00558         if( p_block->i_pts > 0 )
00559         {
00560             es_out_Send( p_demux->out, p_sys->es, p_block );
00561         }
00562         else
00563         {
00564             block_Release( p_block );
00565         }
00566         p_sys->i_subtitle++;
00567     }
00568 
00569     /* */
00570     p_sys->i_next_demux_date = 0;
00571 
00572     return 1;
00573 }
00574 
00575 /*****************************************************************************
00576  * Fix: fix time stamp and order of subtitle
00577  *****************************************************************************/
00578 #ifdef USE_THIS_UNUSED_PIECE_OF_CODE
00579 static void Fix( demux_t *p_demux )
00580 {
00581     demux_sys_t *p_sys = p_demux->p_sys;
00582     vlc_bool_t b_done;
00583     int     i_index;
00584 
00585     /* *** fix order (to be sure...) *** */
00586     /* We suppose that there are near in order and this durty bubble sort
00587      * wont take too much time
00588      */
00589     do
00590     {
00591         b_done = VLC_TRUE;
00592         for( i_index = 1; i_index < p_sys->i_subtitles; i_index++ )
00593         {
00594             if( p_sys->subtitle[i_index].i_start <
00595                     p_sys->subtitle[i_index - 1].i_start )
00596             {
00597                 subtitle_t sub_xch;
00598                 memcpy( &sub_xch,
00599                         p_sys->subtitle + i_index - 1,
00600                         sizeof( subtitle_t ) );
00601                 memcpy( p_sys->subtitle + i_index - 1,
00602                         p_sys->subtitle + i_index,
00603                         sizeof( subtitle_t ) );
00604                 memcpy( p_sys->subtitle + i_index,
00605                         &sub_xch,
00606                         sizeof( subtitle_t ) );
00607                 b_done = VLC_FALSE;
00608             }
00609         }
00610     } while( !b_done );
00611 }
00612 #endif
00613 
00614 static int TextLoad( text_t *txt, stream_t *s )
00615 {
00616     int   i_line_max;
00617 
00618     /* init txt */
00619     i_line_max          = 500;
00620     txt->i_line_count   = 0;
00621     txt->i_line         = 0;
00622     txt->line           = calloc( i_line_max, sizeof( char * ) );
00623 
00624     /* load the complete file */
00625     for( ;; )
00626     {
00627         char *psz = stream_ReadLine( s );
00628 
00629         if( psz == NULL )
00630             break;
00631 
00632         txt->line[txt->i_line_count++] = psz;
00633         if( txt->i_line_count >= i_line_max )
00634         {
00635             i_line_max += 100;
00636             txt->line = realloc( txt->line, i_line_max * sizeof( char * ) );
00637         }
00638     }
00639 
00640     if( txt->i_line_count <= 0 )
00641     {
00642         free( txt->line );
00643         return VLC_EGENERIC;
00644     }
00645 
00646     return VLC_SUCCESS;
00647 }
00648 static void TextUnload( text_t *txt )
00649 {
00650     int i;
00651 
00652     for( i = 0; i < txt->i_line_count; i++ )
00653     {
00654         free( txt->line[i] );
00655     }
00656     free( txt->line );
00657     txt->i_line       = 0;
00658     txt->i_line_count = 0;
00659 }
00660 
00661 static char *TextGetLine( text_t *txt )
00662 {
00663     if( txt->i_line >= txt->i_line_count )
00664         return( NULL );
00665 
00666     return txt->line[txt->i_line++];
00667 }
00668 static void TextPreviousLine( text_t *txt )
00669 {
00670     if( txt->i_line > 0 )
00671         txt->i_line--;
00672 }
00673 
00674 /*****************************************************************************
00675  * Specific Subtitle function
00676  *****************************************************************************/
00677 #define MAX_LINE 8192
00678 static int ParseMicroDvd( demux_t *p_demux, subtitle_t *p_subtitle )
00679 {
00680     demux_sys_t *p_sys = p_demux->p_sys;
00681     text_t      *txt = &p_sys->txt;
00682     /*
00683      * each line:
00684      *  {n1}{n2}Line1|Line2|Line3....
00685      * where n1 and n2 are the video frame number...
00686      * {n2} can also be {}
00687      */
00688     char *s;
00689 
00690     char buffer_text[MAX_LINE + 1];
00691     int    i_start;
00692     int    i_stop;
00693     unsigned int i;
00694 
00695     int i_microsecperframe = 40000; /* default to 25 fps */
00696     if( p_sys->i_microsecperframe > 0 ) 
00697         i_microsecperframe = p_sys->i_microsecperframe;
00698     
00699     p_subtitle->i_start = 0;
00700     p_subtitle->i_stop  = 0;
00701     p_subtitle->psz_text = NULL;
00702 
00703     for( ;; )
00704     {
00705         if( ( s = TextGetLine( txt ) ) == NULL )
00706         {
00707             return( VLC_EGENERIC );
00708         }
00709         i_start = 0;
00710         i_stop  = 0;
00711 
00712         memset( buffer_text, '\0', MAX_LINE );
00713         if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
00714             sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
00715         {
00716             break;
00717         }
00718     }
00719     /* replace | by \n */
00720     for( i = 0; i < strlen( buffer_text ); i++ )
00721     {
00722         if( buffer_text[i] == '|' )
00723         {
00724             buffer_text[i] = '\n';
00725         }
00726     }
00727 
00728     p_subtitle->i_start = (int64_t)i_start * i_microsecperframe;
00729     p_subtitle->i_stop  = (int64_t)i_stop  * i_microsecperframe;
00730     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
00731     return( 0 );
00732 }
00733 
00734 static int  ParseSubRip( demux_t *p_demux, subtitle_t *p_subtitle )
00735 {
00736     demux_sys_t *p_sys = p_demux->p_sys;
00737     text_t      *txt = &p_sys->txt;
00738 
00739     /*
00740      * n
00741      * h1:m1:s1,d1 --> h2:m2:s2,d2
00742      * Line1
00743      * Line2
00744      * ...
00745      * [empty line]
00746      *
00747      */
00748     char *s;
00749     char buffer_text[ 10 * MAX_LINE];
00750     int  i_buffer_text;
00751     int64_t     i_start;
00752     int64_t     i_stop;
00753 
00754     p_subtitle->i_start = 0;
00755     p_subtitle->i_stop  = 0;
00756     p_subtitle->psz_text = NULL;
00757 
00758     for( ;; )
00759     {
00760         int h1, m1, s1, d1, h2, m2, s2, d2;
00761         if( ( s = TextGetLine( txt ) ) == NULL )
00762         {
00763             return( VLC_EGENERIC );
00764         }
00765         if( sscanf( s,
00766                     "%d:%d:%d,%d --> %d:%d:%d,%d",
00767                     &h1, &m1, &s1, &d1,
00768                     &h2, &m2, &s2, &d2 ) == 8 )
00769         {
00770             i_start = ( (int64_t)h1 * 3600*1000 +
00771                         (int64_t)m1 * 60*1000 +
00772                         (int64_t)s1 * 1000 +
00773                         (int64_t)d1 ) * 1000;
00774 
00775             i_stop  = ( (int64_t)h2 * 3600*1000 +
00776                         (int64_t)m2 * 60*1000 +
00777                         (int64_t)s2 * 1000 +
00778                         (int64_t)d2 ) * 1000;
00779 
00780             /* Now read text until an empty line */
00781             for( i_buffer_text = 0;; )
00782             {
00783                 int i_len;
00784                 if( ( s = TextGetLine( txt ) ) == NULL )
00785                 {
00786                     return( VLC_EGENERIC );
00787                 }
00788 
00789                 i_len = strlen( s );
00790                 if( i_len <= 0 )
00791                 {
00792                     /* empty line -> end of this subtitle */
00793                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
00794                     p_subtitle->i_start = i_start;
00795                     p_subtitle->i_stop = i_stop;
00796                     p_subtitle->psz_text = strdup( buffer_text );
00797                     /* If framerate is available, use sub-fps */
00798                     if( p_sys->i_microsecperframe != 0 &&
00799                         p_sys->i_original_mspf != 0)
00800                     {
00801                         p_subtitle->i_start = (int64_t)i_start *
00802                                               p_sys->i_microsecperframe/
00803                                               p_sys->i_original_mspf;
00804                         p_subtitle->i_stop  = (int64_t)i_stop  *
00805                                               p_sys->i_microsecperframe /
00806                                               p_sys->i_original_mspf;
00807                     }
00808                     return 0;
00809                 }
00810                 else
00811                 {
00812                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
00813                     {
00814                         memcpy( buffer_text + i_buffer_text,
00815                                 s,
00816                                 i_len );
00817                         i_buffer_text += i_len;
00818 
00819                         buffer_text[i_buffer_text] = '\n';
00820                         i_buffer_text++;
00821                     }
00822                 }
00823             }
00824         }
00825     }
00826 }
00827 
00828 static int  ParseSubViewer( demux_t *p_demux, subtitle_t *p_subtitle )
00829 {
00830     demux_sys_t *p_sys = p_demux->p_sys;
00831     text_t      *txt = &p_sys->txt;
00832 
00833     /*
00834      * h1:m1:s1.d1,h2:m2:s2.d2
00835      * Line1[br]Line2
00836      * Line3
00837      * ...
00838      * [empty line]
00839      * ( works with subviewer and subviewer v2 )
00840      */
00841     char *s;
00842     char buffer_text[ 10 * MAX_LINE];
00843     int  i_buffer_text;
00844     int64_t     i_start;
00845     int64_t     i_stop;
00846 
00847     p_subtitle->i_start = 0;
00848     p_subtitle->i_stop  = 0;
00849     p_subtitle->psz_text = NULL;
00850 
00851     for( ;; )
00852     {
00853         int h1, m1, s1, d1, h2, m2, s2, d2;
00854         if( ( s = TextGetLine( txt ) ) == NULL )
00855         {
00856             return( VLC_EGENERIC );
00857         }
00858         if( sscanf( s,
00859                     "%d:%d:%d.%d,%d:%d:%d.%d",
00860                     &h1, &m1, &s1, &d1,
00861                     &h2, &m2, &s2, &d2 ) == 8 )
00862         {
00863             i_start = ( (int64_t)h1 * 3600*1000 +
00864                         (int64_t)m1 * 60*1000 +
00865                         (int64_t)s1 * 1000 +
00866                         (int64_t)d1 ) * 1000;
00867 
00868             i_stop  = ( (int64_t)h2 * 3600*1000 +
00869                         (int64_t)m2 * 60*1000 +
00870                         (int64_t)s2 * 1000 +
00871                         (int64_t)d2 ) * 1000;
00872 
00873             /* Now read text until an empty line */
00874             for( i_buffer_text = 0;; )
00875             {
00876                 int i_len, i;
00877                 if( ( s = TextGetLine( txt ) ) == NULL )
00878                 {
00879                     return( VLC_EGENERIC );
00880                 }
00881 
00882                 i_len = strlen( s );
00883                 if( i_len <= 0 )
00884                 {
00885                     /* empty line -> end of this subtitle */
00886                     buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
00887                     p_subtitle->i_start = i_start;
00888                     p_subtitle->i_stop = i_stop;
00889 
00890                     /* replace [br] by \n */
00891                     for( i = 0; i < i_buffer_text - 3; i++ )
00892                     {
00893                         if( buffer_text[i] == '[' && buffer_text[i+1] == 'b' &&
00894                             buffer_text[i+2] == 'r' && buffer_text[i+3] == ']' )
00895                         {
00896                             char *temp = buffer_text + i + 1;
00897                             buffer_text[i] = '\n';
00898                             memmove( temp, temp+3, strlen( temp ) -3 );
00899                             temp[strlen( temp )-3] = '\0';
00900                         }
00901                     }
00902                     p_subtitle->psz_text = strdup( buffer_text );
00903                     return( 0 );
00904                 }
00905                 else
00906                 {
00907                     if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
00908                     {
00909                         memcpy( buffer_text + i_buffer_text,
00910                                 s,
00911                                 i_len );
00912                         i_buffer_text += i_len;
00913 
00914                         buffer_text[i_buffer_text] = '\n';
00915                         i_buffer_text++;
00916                     }
00917                 }
00918             }
00919         }
00920     }
00921 }
00922 
00923 
00924 static int  ParseSSA( demux_t *p_demux, subtitle_t *p_subtitle )
00925 {
00926     demux_sys_t *p_sys = p_demux->p_sys;
00927     text_t      *txt = &p_sys->txt;
00928 
00929     char buffer_text[ 10 * MAX_LINE];
00930     char buffer_text2[ 10 * MAX_LINE];
00931     char *s;
00932     int64_t     i_start;
00933     int64_t     i_stop;
00934 
00935     p_subtitle->i_start = 0;
00936     p_subtitle->i_stop  = 0;
00937     p_subtitle->psz_text = NULL;
00938 
00939     for( ;; )
00940     {
00941         int h1, m1, s1, c1, h2, m2, s2, c2;
00942 
00943         if( ( s = TextGetLine( txt ) ) == NULL )
00944         {
00945             return( VLC_EGENERIC );
00946         }
00947         p_subtitle->psz_text = malloc( strlen( s ) );
00948 
00949         /* We expect (SSA2-4):
00950          * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
00951          * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
00952          *
00953          * SSA-1 is similar but only has 8 commas up untill the subtitle text. Probably the Effect field is no present, but not 100 % sure.
00954          */
00955 
00956         /* For ASS:
00957          * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
00958          * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
00959          */
00960         if( sscanf( s,
00961                     "Dialogue: %[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
00962                     buffer_text2,
00963                     &h1, &m1, &s1, &c1,
00964                     &h2, &m2, &s2, &c2,
00965                     buffer_text ) == 10 )
00966         {
00967             i_start = ( (int64_t)h1 * 3600*1000 +
00968                         (int64_t)m1 * 60*1000 +
00969                         (int64_t)s1 * 1000 +
00970                         (int64_t)c1 * 10 ) * 1000;
00971 
00972             i_stop  = ( (int64_t)h2 * 3600*1000 +
00973                         (int64_t)m2 * 60*1000 +
00974                         (int64_t)s2 * 1000 +
00975                         (int64_t)c2 * 10 ) * 1000;
00976 
00977             /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
00978             /* (Layer comes from ASS specs ... it's empty for SSA.) */
00979             if( p_sys->i_type == SUB_TYPE_SSA1 )
00980             {
00981                 sprintf( p_subtitle->psz_text,
00982                          ",%s", strdup( buffer_text) ); /* SSA1 has only 8 commas before the text starts, not 9 */
00983             }
00984             else
00985             {
00986                 sprintf( p_subtitle->psz_text,
00987                          ",,%s", strdup( buffer_text) ); /* ReadOrder, Layer, %s(rest of fields) */
00988             }
00989             p_subtitle->i_start = i_start;
00990             p_subtitle->i_stop = i_stop;
00991             return 0;
00992         }
00993         else
00994         {
00995             /* All the other stuff we add to the header field */
00996             if( p_sys->psz_header != NULL )
00997             {
00998                 if( !( p_sys->psz_header = realloc( p_sys->psz_header,
00999                           strlen( p_sys->psz_header ) + 1 + strlen( s ) + 2 ) ) )
01000                 {
01001                     msg_Err( p_demux, "out of memory");
01002                     return VLC_ENOMEM;
01003                 }
01004                 p_sys->psz_header = strcat( p_sys->psz_header,  s );
01005                 p_sys->psz_header = strcat( p_sys->psz_header, "\n" );
01006             }
01007             else
01008             {
01009                 if( !( p_sys->psz_header = malloc( strlen( s ) + 2 ) ) )
01010                 {
01011                     msg_Err( p_demux, "out of memory");
01012                     return VLC_ENOMEM;
01013                 }
01014                 p_sys->psz_header = s;
01015                 p_sys->psz_header = strcat( p_sys->psz_header, "\n" );
01016             }
01017         }
01018     }
01019 }
01020 
01021 static int  ParseVplayer( demux_t *p_demux, subtitle_t *p_subtitle )
01022 {
01023     demux_sys_t *p_sys = p_demux->p_sys;
01024     text_t      *txt = &p_sys->txt;
01025 
01026     /*
01027      * each line:
01028      *  h:m:s:Line1|Line2|Line3....
01029      *  or
01030      *  h:m:s Line1|Line2|Line3....
01031      *
01032      */
01033     char *p;
01034     char buffer_text[MAX_LINE + 1];
01035     int64_t    i_start;
01036     unsigned int i;
01037 
01038     p_subtitle->i_start = 0;
01039     p_subtitle->i_stop  = 0;
01040     p_subtitle->psz_text = NULL;
01041 
01042     for( ;; )
01043     {
01044         int h, m, s;
01045         char c;
01046 
01047         if( ( p = TextGetLine( txt ) ) == NULL )
01048         {
01049             return( VLC_EGENERIC );
01050         }
01051 
01052         i_start = 0;
01053 
01054         memset( buffer_text, '\0', MAX_LINE );
01055         if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
01056         {
01057             i_start = ( (int64_t)h * 3600*1000 +
01058                         (int64_t)m * 60*1000 +
01059                         (int64_t)s * 1000 ) * 1000;
01060             break;
01061         }
01062     }
01063 
01064     /* replace | by \n */
01065     for( i = 0; i < strlen( buffer_text ); i++ )
01066     {
01067         if( buffer_text[i] == '|' )
01068         {
01069             buffer_text[i] = '\n';
01070         }
01071     }
01072     p_subtitle->i_start = i_start;
01073 
01074     p_subtitle->i_stop  = 0;
01075     p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
01076     return( 0 );
01077 }
01078 
01079 static char *ParseSamiSearch( text_t *txt, char *psz_start, char *psz_str )
01080 {
01081     if( psz_start )
01082     {
01083         if( strcasestr( psz_start, psz_str ) )
01084         {
01085             char *s = strcasestr( psz_start, psz_str );
01086 
01087             s += strlen( psz_str );
01088 
01089             return( s );
01090         }
01091     }
01092     for( ;; )
01093     {
01094         char *p;
01095         if( ( p = TextGetLine( txt ) ) == NULL )
01096         {
01097             return NULL;
01098         }
01099         if( strcasestr( p, psz_str ) )
01100         {
01101             char *s = strcasestr( p, psz_str );
01102 
01103             s += strlen( psz_str );
01104 
01105             return(  s);
01106         }
01107     }
01108 }
01109 
01110 static int  ParseSami( demux_t *p_demux, subtitle_t *p_subtitle )
01111 {
01112     demux_sys_t *p_sys = p_demux->p_sys;
01113     text_t      *txt = &p_sys->txt;
01114 
01115     char *p;
01116     int64_t i_start;
01117 
01118     int  i_text;
01119     char buffer_text[10*MAX_LINE + 1];
01120 
01121     p_subtitle->i_start = 0;
01122     p_subtitle->i_stop  = 0;
01123     p_subtitle->psz_text = NULL;
01124 
01125 #define ADDC( c ) \
01126     if( i_text < 10*MAX_LINE )      \
01127     {                               \
01128         buffer_text[i_text++] = c;  \
01129         buffer_text[i_text] = '\0'; \
01130     }
01131 
01132     /* search "Start=" */
01133     if( !( p = ParseSamiSearch( txt, NULL, "Start=" ) ) )
01134     {
01135         return VLC_EGENERIC;
01136     }
01137 
01138     /* get start value */
01139     i_start = strtol( p, &p, 0 );
01140 
01141     /* search <P */
01142     if( !( p = ParseSamiSearch( txt, p, "<P" ) ) )
01143     {
01144         return VLC_EGENERIC;
01145     }
01146     /* search > */
01147     if( !( p = ParseSamiSearch( txt, p, ">" ) ) )
01148     {
01149         return VLC_EGENERIC;
01150     }
01151 
01152     i_text = 0;
01153     buffer_text[0] = '\0';
01154     /* now get all txt until  a "Start=" line */
01155     for( ;; )
01156     {
01157         if( *p )
01158         {
01159             if( *p == '<' )
01160             {
01161                 if( !strncasecmp( p, "<br", 3 ) )
01162                 {
01163                     ADDC( '\n' );
01164                 }
01165                 else if( strcasestr( p, "Start=" ) )
01166                 {
01167                     TextPreviousLine( txt );
01168                     break;
01169                 }
01170                 p = ParseSamiSearch( txt, p, ">" );
01171             }
01172             else if( !strncmp( p, "&nbsp;", 6 ) )
01173             {
01174                 ADDC( ' ' );
01175                 p += 6;
01176             }
01177             else if( *p == '\t' )
01178             {
01179                 ADDC( ' ' );
01180                 p++;
01181             }
01182             else
01183             {
01184                 ADDC( *p );
01185                 p++;
01186             }
01187         }
01188         else
01189         {
01190             p = TextGetLine( txt );
01191         }
01192 
01193         if( p == NULL )
01194         {
01195             break;
01196         }
01197     }
01198 
01199     p_subtitle->i_start = i_start * 1000;
01200     p_subtitle->i_stop  = 0;
01201     p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
01202 
01203     return( VLC_SUCCESS );
01204 #undef ADDC
01205 }

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