1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: sub.c,v 1.16 2003/06/26 18:14:56 sam Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /* define USE_FREETYPE to use freetype for subtitles */
26 /*****************************************************************************
28 *****************************************************************************/
32 #include <sys/types.h>
35 #include <vlc/input.h>
36 #if defined(USE_FREETYPE)
39 #include "vlc_video.h"
44 static int Open ( vlc_object_t *p_this );
46 static int sub_open ( subtitle_demux_t *p_sub,
47 input_thread_t *p_input,
49 mtime_t i_microsecperframe );
50 static int sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate );
51 static int sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date );
52 static void sub_close( subtitle_demux_t *p_sub );
54 static void sub_fix( subtitle_demux_t *p_sub );
56 static char *ppsz_sub_type[] = { "microdvd", "subrip", "ssa1", "ssa2-4", "vplayer", "sami", NULL };
59 /*****************************************************************************
61 *****************************************************************************/
62 #define SUB_DELAY_LONGTEXT \
63 "Delay subtitles (in 1/10s)"
64 #define SUB_FPS_LONGTEXT \
65 "Override frames per second. " \
66 "It will only work with MicroDVD subtitles."
67 #define SUB_TYPE_LONGTEXT \
68 "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" " \
69 "\"sami\" (nothing for autodetection, it should always work)."
72 set_description( _("Text subtitles demux") );
73 set_capability( "subtitle demux", 12 );
74 add_category_hint( "Subtitles", NULL, VLC_TRUE );
75 add_file( "sub-file", NULL, NULL,
76 "Subtitles file name", "Subtitles file name", VLC_TRUE );
77 add_float( "sub-fps", 0.0, NULL,
79 SUB_FPS_LONGTEXT, VLC_TRUE );
80 add_integer( "sub-delay", 0, NULL,
81 "Delay subtitles (in 1/10s)",
82 SUB_DELAY_LONGTEXT, VLC_TRUE );
83 add_string_from_list( "sub-type", NULL, ppsz_sub_type, NULL,
85 SUB_TYPE_LONGTEXT, VLC_TRUE );
86 set_callbacks( Open, NULL );
89 /*****************************************************************************
91 *****************************************************************************/
92 static int Open ( vlc_object_t *p_this )
94 subtitle_demux_t *p_sub = (subtitle_demux_t*)p_this;
96 p_sub->pf_open = sub_open;
97 p_sub->pf_demux = sub_demux;
98 p_sub->pf_seek = sub_seek;
99 p_sub->pf_close = sub_close;
104 #define MAX_LINE 2048
106 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
115 static int text_load( text_t *txt, char *psz_name )
122 txt->i_line_count = 0;
124 txt->line = calloc( i_line_max, sizeof( char * ) );
127 if( !( f = fopen( psz_name, "rb" ) ) )
132 /* load the complete file */
138 if( fgets( buffer, 8096, f ) <= 0)
142 while( ( p = strchr( buffer, '\r' ) ) )
146 while( ( p = strchr( buffer, '\n' ) ) )
151 txt->line[txt->i_line_count++] = strdup( buffer );
153 if( txt->i_line_count >= i_line_max )
156 txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
162 if( txt->i_line_count <= 0 )
165 return( VLC_EGENERIC );
168 return( VLC_SUCCESS );
170 static void text_unload( text_t *txt )
174 for( i = 0; i < txt->i_line_count; i++ )
176 FREE( txt->line[i] );
180 txt->i_line_count = 0;
183 static char *text_get_line( text_t *txt )
185 if( txt->i_line >= txt->i_line_count )
190 return( txt->line[txt->i_line++] );
192 static void text_previous_line( text_t *txt )
194 if( txt->i_line > 0 )
199 static void text_rewind( text_t *txt )
204 static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
205 static int sub_SubRipRead ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
206 static int sub_SSA1Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
207 static int sub_SSA2_4Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
208 static int sub_Vplayer ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
209 static int sub_Sami ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
216 int (*pf_read_subtitle) ( text_t *, subtitle_t*, mtime_t );
217 } sub_read_subtitle_function [] =
219 { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", sub_MicroDvdRead },
220 { "subrip", SUB_TYPE_SUBRIP, "SubRIP", sub_SubRipRead },
221 { "ssa1", SUB_TYPE_SSA1, "SSA-1", sub_SSA1Read },
222 { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4",sub_SSA2_4Read },
223 { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", sub_Vplayer },
224 { "sami", SUB_TYPE_SAMI, "SAMI", sub_Sami },
225 { NULL, SUB_TYPE_UNKNOWN, "Unknow", NULL }
228 /*****************************************************************************
229 * sub_open: Open a subtitle file and add subtitle ES
230 *****************************************************************************/
231 static int sub_open ( subtitle_demux_t *p_sub,
232 input_thread_t *p_input,
234 mtime_t i_microsecperframe )
242 int (*pf_read_subtitle)( text_t *, subtitle_t *, mtime_t ) = NULL;
244 p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
246 p_sub->i_subtitles = 0;
247 p_sub->subtitle = NULL;
248 p_sub->p_input = p_input;
250 if( !psz_name || !*psz_name)
252 psz_name = config_GetPsz( p_sub, "sub-file" );
253 if( !psz_name || !*psz_name )
260 psz_name = strdup( psz_name );
263 /* *** load the file *** */
264 if( text_load( &txt, psz_name ) )
266 msg_Err( p_sub, "cannot open `%s' subtitle file", psz_name );
270 msg_Dbg( p_sub, "opened `%s'", psz_name );
274 if( config_GetFloat( p_sub, "sub-fps" ) >= 1.0 )
276 i_microsecperframe = (mtime_t)( (float)1000000 /
277 config_GetFloat( p_sub, "sub-fps" ) );
279 else if( i_microsecperframe <= 0 )
281 i_microsecperframe = 40000; /* default: 25fps */
284 psz_file_type = config_GetPsz( p_sub, "sub-type" );
285 if( psz_file_type && *psz_file_type)
291 if( sub_read_subtitle_function[i].psz_type_name == NULL )
293 i_sub_type = SUB_TYPE_UNKNOWN;
296 if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
299 i_sub_type = sub_read_subtitle_function[i].i_type;
306 i_sub_type = SUB_TYPE_UNKNOWN;
308 FREE( psz_file_type );
310 /* *** Now try to autodetect subtitle format *** */
311 if( i_sub_type == SUB_TYPE_UNKNOWN )
316 msg_Dbg( p_input, "trying to autodetect file format" );
317 for( i_try = 0; i_try < MAX_TRY; i_try++ )
321 if( ( s = text_get_line( &txt ) ) == NULL )
326 if( strstr( s, "<SAMI>" ) )
328 i_sub_type = SUB_TYPE_SAMI;
331 else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
332 sscanf( s, "{%d}{}", &i_dummy ) == 1)
334 i_sub_type = SUB_TYPE_MICRODVD;
338 "%d:%d:%d,%d --> %d:%d:%d,%d",
339 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
340 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
342 i_sub_type = SUB_TYPE_SUBRIP;
346 "!: This is a Sub Station Alpha v%d.x script.",
351 i_sub_type = SUB_TYPE_SSA1;
355 i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
359 else if( strstr( s, "This is a Sub Station Alpha v4 script" ) )
361 i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
363 else if( !strncmp( s, "Dialogue: Marked", 16 ) )
365 i_sub_type = SUB_TYPE_SSA2_4; // could be wrong
368 else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
369 sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
371 i_sub_type = SUB_TYPE_VPLAYER;
379 /* *** Load this file in memory *** */
382 if( sub_read_subtitle_function[i].i_type == SUB_TYPE_UNKNOWN )
384 msg_Dbg( p_input, "unknown subtitile file" );
389 if( sub_read_subtitle_function[i].i_type == i_sub_type )
392 "detected %s format",
393 sub_read_subtitle_function[i].psz_name );
394 pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
401 if( p_sub->i_subtitles >= i_max )
404 if( p_sub->subtitle )
406 p_sub->subtitle = realloc( p_sub->subtitle,
407 sizeof( subtitle_t ) * i_max );
411 p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max );
414 if( pf_read_subtitle( &txt,
415 p_sub->subtitle + p_sub->i_subtitles,
416 i_microsecperframe ) < 0 )
420 p_sub->i_subtitles++;
422 msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );
424 /* *** Close the file *** */
427 /* *** fix subtitle (order and time) *** */
428 p_sub->i_subtitle = 0; // will be modified by sub_fix
431 /* *** add subtitle ES *** */
432 vlc_mutex_lock( &p_input->stream.stream_lock );
433 p_sub->p_es = input_AddES( p_input, p_input->stream.p_selected_program,
436 vlc_mutex_unlock( &p_input->stream.stream_lock );
438 p_sub->p_es->i_stream_id = 0xff; // FIXME
439 p_sub->p_es->i_fourcc = VLC_FOURCC( 's','u','b','t' );
441 p_sub->i_previously_selected = 0;
445 /*****************************************************************************
446 * sub_demux: Send subtitle to decoder until i_maxdate
447 *****************************************************************************/
448 static int sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
450 #if defined(USE_FREETYPE)
451 vlc_object_t *p_vout = NULL;
453 if( p_sub->p_es->p_decoder_fifo && !p_sub->i_previously_selected )
455 p_sub->i_previously_selected = 1;
456 p_sub->pf_seek( p_sub, i_maxdate );
459 else if( !p_sub->p_es->p_decoder_fifo && p_sub->i_previously_selected )
461 p_sub->i_previously_selected = 0;
465 while( p_sub->i_subtitle < p_sub->i_subtitles &&
466 p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
469 data_packet_t *p_data;
472 #if defined(USE_FREETYPE)
473 p_vout = vlc_object_find( p_sub, VLC_OBJECT_VOUT, FIND_ANYWHERE );
476 i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;
484 if( !( p_pes = input_NewPES( p_sub->p_input->p_method_data ) ) )
490 if( !( p_data = input_NewPacket( p_sub->p_input->p_method_data,
493 input_DeletePES( p_sub->p_input->p_method_data, p_pes );
497 p_data->p_payload_end = p_data->p_payload_start + i_len;
500 input_ClockGetTS( p_sub->p_input,
501 p_sub->p_input->stream.p_selected_program,
502 p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
503 if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
506 * i_dts means end of display...
509 input_ClockGetTS( p_sub->p_input,
510 p_sub->p_input->stream.p_selected_program,
511 p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
517 #if defined(USE_FREETYPE)
520 vlc_value_t val, lockval;
521 if( var_Get( p_vout, "lock", &lockval ) == VLC_SUCCESS )
524 vlc_mutex_lock( lockval.p_address );
525 msg_Dbg( p_sub, "pts "I64Fd" dts " I64Fd, p_pes->i_pts,
527 i_tmp = p_pes->i_dts - p_pes->i_pts;
528 val.i_int = OSD_ALIGN_LEFT|OSD_ALIGN_BOTTOM;
529 var_Set( p_vout, "flags", val );
530 val.time.i_low = (int)(p_pes->i_pts+p_sub->p_input->i_pts_delay);
531 val.time.i_high = (int)( p_pes->i_pts >> 32 );
532 var_Set( p_vout, "start-date", val );
533 val.time.i_low = (int)(p_pes->i_dts + p_sub->p_input->i_pts_delay);
534 val.time.i_high = (int)( p_pes->i_dts >> 32 );
535 var_Set( p_vout, "stop-date", val );
537 var_Set( p_vout, "x-margin", val );
539 var_Set( p_vout, "y-margin", val );
540 val.psz_string = p_sub->subtitle[p_sub->i_subtitle].psz_text;
541 var_Set( p_vout, "string", val );
542 vlc_mutex_unlock( lockval.p_address );
546 p_pes->i_nb_data = 1;
548 p_pes->p_last = p_data;
549 p_pes->i_pes_size = i_len;
551 memcpy( p_data->p_payload_start,
552 p_sub->subtitle[p_sub->i_subtitle].psz_text,
554 if( p_sub->p_es->p_decoder_fifo && p_pes->i_pts > 0 )
557 input_DecodePES( p_sub->p_es->p_decoder_fifo, p_pes );
561 input_DeletePES( p_sub->p_input->p_method_data, p_pes );
566 #if defined(USE_FREETYPE)
569 vlc_object_release( p_vout );
575 /*****************************************************************************
576 * sub_seek: Seek to i_date
577 *****************************************************************************/
578 static int sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
580 /* should be fast enough... */
581 p_sub->i_subtitle = 0;
582 while( p_sub->i_subtitle < p_sub->i_subtitles &&
583 p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
591 /*****************************************************************************
592 * sub_close: Close subtitle demux
593 *****************************************************************************/
594 static void sub_close( subtitle_demux_t *p_sub )
596 if( p_sub->subtitle )
599 for( i = 0; i < p_sub->i_subtitles; i++ )
601 if( p_sub->subtitle[i].psz_text )
603 free( p_sub->subtitle[i].psz_text );
606 free( p_sub->subtitle );
609 /*****************************************************************************
611 * sub_fix: fix time stamp and order of subtitle
612 *****************************************************************************/
613 static void sub_fix( subtitle_demux_t *p_sub )
620 /* *** fix order (to be sure...) *** */
621 /* We suppose that there are near in order and this durty bubble sort
622 * wont take too much time
627 for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
629 if( p_sub->subtitle[i_index].i_start <
630 p_sub->subtitle[i_index - 1].i_start )
634 p_sub->subtitle + i_index - 1,
635 sizeof( subtitle_t ) );
636 memcpy( p_sub->subtitle + i_index - 1,
637 p_sub->subtitle + i_index,
638 sizeof( subtitle_t ) );
639 memcpy( p_sub->subtitle + i_index,
641 sizeof( subtitle_t ) );
647 /* *** and at the end add delay *** */
648 i_delay = (mtime_t)config_GetInt( p_sub, "sub-delay" ) * 100000;
651 for( i = 0; i < p_sub->i_subtitles; i++ )
653 p_sub->subtitle[i].i_start += i_delay;
654 p_sub->subtitle[i].i_stop += i_delay;
655 if( p_sub->subtitle[i].i_start < 0 )
657 p_sub->i_subtitle = i + 1;
665 /*****************************************************************************
666 * Specific Subtitle function
667 *****************************************************************************/
668 static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
672 * {n1}{n2}Line1|Line2|Line3....
673 * where n1 and n2 are the video frame number...
678 char buffer_text[MAX_LINE + 1];
685 if( ( s = text_get_line( txt ) ) == NULL )
687 return( VLC_EGENERIC );
692 memset( buffer_text, '\0', MAX_LINE );
693 if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
694 sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
699 /* replace | by \n */
700 for( i = 0; i < strlen( buffer_text ); i++ )
702 if( buffer_text[i] == '|' )
704 buffer_text[i] = '\n';
707 p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
708 p_subtitle->i_stop = (mtime_t)i_stop * (mtime_t)i_microsecperframe;
709 p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
713 static int sub_SubRipRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
717 * h1:m1:s1,d1 --> h2:m2:s2,d2
725 char buffer_text[ 10 * MAX_LINE];
732 int h1, m1, s1, d1, h2, m2, s2, d2;
733 if( ( s = text_get_line( txt ) ) == NULL )
735 return( VLC_EGENERIC );
738 "%d:%d:%d,%d --> %d:%d:%d,%d",
740 &h2, &m2, &s2, &d2 ) == 8 )
742 i_start = ( (mtime_t)h1 * 3600*1000 +
743 (mtime_t)m1 * 60*1000 +
745 (mtime_t)d1 ) * 1000;
747 i_stop = ( (mtime_t)h2 * 3600*1000 +
748 (mtime_t)m2 * 60*1000 +
750 (mtime_t)d2 ) * 1000;
752 /* Now read text until an empty line */
753 for( i_buffer_text = 0;; )
756 if( ( s = text_get_line( txt ) ) == NULL )
758 return( VLC_EGENERIC );
764 // empty line -> end of this subtitle
765 buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
766 p_subtitle->i_start = i_start;
767 p_subtitle->i_stop = i_stop;
768 p_subtitle->psz_text = strdup( buffer_text );
773 if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
775 memcpy( buffer_text + i_buffer_text,
778 i_buffer_text += i_len;
780 buffer_text[i_buffer_text] = '\n';
790 static int sub_SSARead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe, int i_comma_count )
792 char buffer_text[ 10 * MAX_LINE];
802 int h1, m1, s1, c1, h2, m2, s2, c2;
805 if( ( s = text_get_line( txt ) ) == NULL )
807 return( VLC_EGENERIC );
810 "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
814 buffer_text ) == 10 )
816 i_start = ( (mtime_t)h1 * 3600*1000 +
817 (mtime_t)m1 * 60*1000 +
819 (mtime_t)c1 * 10 ) * 1000;
821 i_stop = ( (mtime_t)h2 * 3600*1000 +
822 (mtime_t)m2 * 60*1000 +
824 (mtime_t)c2 * 10 ) * 1000;
826 p_buffer_text = buffer_text;
828 while( i_comma < i_comma_count &&
829 *p_buffer_text != '\0' )
831 if( *p_buffer_text == ',' )
837 p_subtitle->psz_text = malloc( strlen( p_buffer_text ) + 1);
839 while( *p_buffer_text )
841 if( *p_buffer_text == '\\' && ( *p_buffer_text =='n' || *p_buffer_text =='N' ) )
843 p_subtitle->psz_text[i_text] = '\n';
847 else if( *p_buffer_text == '{' && *p_buffer_text == '\\')
849 while( *p_buffer_text && *p_buffer_text != '}' )
856 p_subtitle->psz_text[i_text] = *p_buffer_text;
861 p_subtitle->psz_text[i_text] = '\0';
862 p_subtitle->i_start = i_start;
863 p_subtitle->i_stop = i_stop;
869 static int sub_SSA1Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
871 return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 8 ) );
873 static int sub_SSA2_4Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
875 return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 9 ) );
878 static int sub_Vplayer( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
882 * h:m:s:Line1|Line2|Line3....
884 * h:m:s Line1|Line2|Line3....
885 * where n1 and n2 are the video frame number...
889 char buffer_text[MAX_LINE + 1];
898 if( ( p = text_get_line( txt ) ) == NULL )
900 return( VLC_EGENERIC );
905 memset( buffer_text, '\0', MAX_LINE );
906 if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
908 i_start = ( (mtime_t)h * 3600*1000 +
909 (mtime_t)m * 60*1000 +
910 (mtime_t)s * 1000 ) * 1000;
915 /* replace | by \n */
916 for( i = 0; i < strlen( buffer_text ); i++ )
918 if( buffer_text[i] == '|' )
920 buffer_text[i] = '\n';
923 p_subtitle->i_start = i_start;
925 p_subtitle->i_stop = 0;
926 p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
930 static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
934 if( strstr( psz_start, psz_str ) )
936 char *s = strstr( psz_start, psz_str );
938 s += strlen( psz_str );
946 if( ( p = text_get_line( txt ) ) == NULL )
950 if( strstr( p, psz_str ) )
952 char *s = strstr( p, psz_str );
954 s += strlen( psz_str );
961 static int sub_Sami( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
967 char buffer_text[10*MAX_LINE + 1];
969 if( i_text < 10*MAX_LINE ) \
971 buffer_text[i_text++] = c; \
972 buffer_text[i_text] = '\0'; \
975 /* search "Start=" */
976 if( !( p = sub_SamiSearch( txt, NULL, "Start=" ) ) )
981 /* get start value */
982 i_start = strtol( p, &p, 0 );
985 if( !( p = sub_SamiSearch( txt, p, "<P" ) ) )
990 if( !( p = sub_SamiSearch( txt, p, ">" ) ) )
996 buffer_text[0] = '\0';
997 /* now get all txt until a "Start=" line */
1004 if( !strncmp( p, "<br", 3 ) || !strncmp( p, "<BR", 3 ) )
1008 else if( strstr( p, "Start=" ) )
1010 text_previous_line( txt );
1013 p = sub_SamiSearch( txt, p, ">" );
1015 else if( !strncmp( p, " ", 6 ) )
1020 else if( *p == '\t' )
1033 p = text_get_line( txt );
1042 p_subtitle->i_start = i_start * 1000;
1043 p_subtitle->i_stop = 0;
1044 p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1046 return( VLC_SUCCESS );