1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1999-2001 VideoLAN
5 * $Id: sub.c,v 1.14 2003/05/11 14:33:32 sigmunau 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)
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 *****************************************************************************/
63 #define SUB_FPS_LONGTEXT \
64 "Override frames per second. " \
65 "It will work only with MicroDVD"
66 #define SUB_TYPE_LONGTEXT \
67 "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"vplayer\" \"sami\"" \
68 "(nothing for autodetection, it should always work)"
71 set_description( _("text subtitle demux") );
72 set_capability( "subtitle demux", 12 );
73 add_category_hint( "subtitle", NULL, VLC_TRUE );
74 add_file( "sub-file", NULL, NULL,
75 "subtitle file name", "subtitle file name", VLC_TRUE );
76 add_float( "sub-fps", 0.0, NULL,
77 "override frames per second",
78 SUB_FPS_LONGTEXT, VLC_TRUE );
79 add_integer( "sub-delay", 0, NULL,
80 "delay subtitles (in 1/10s)",
81 "delay subtitles (in 1/10s)", VLC_TRUE );
82 add_string_from_list( "sub-type", NULL, ppsz_sub_type, NULL,
84 SUB_TYPE_LONGTEXT, VLC_TRUE );
85 set_callbacks( Open, NULL );
88 /*****************************************************************************
90 *****************************************************************************/
91 static int Open ( vlc_object_t *p_this )
93 subtitle_demux_t *p_sub = (subtitle_demux_t*)p_this;
95 p_sub->pf_open = sub_open;
96 p_sub->pf_demux = sub_demux;
97 p_sub->pf_seek = sub_seek;
98 p_sub->pf_close = sub_close;
103 #define MAX_LINE 2048
105 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
114 static int text_load( text_t *txt, char *psz_name )
121 txt->i_line_count = 0;
123 txt->line = calloc( i_line_max, sizeof( char * ) );
126 if( !( f = fopen( psz_name, "rb" ) ) )
131 /* load the complete file */
137 if( fgets( buffer, 8096, f ) <= 0)
141 while( ( p = strchr( buffer, '\r' ) ) )
145 while( ( p = strchr( buffer, '\n' ) ) )
150 txt->line[txt->i_line_count++] = strdup( buffer );
152 if( txt->i_line_count >= i_line_max )
155 txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
161 if( txt->i_line_count <= 0 )
164 return( VLC_EGENERIC );
167 return( VLC_SUCCESS );
169 static void text_unload( text_t *txt )
173 for( i = 0; i < txt->i_line_count; i++ )
175 FREE( txt->line[i] );
179 txt->i_line_count = 0;
182 static char *text_get_line( text_t *txt )
184 if( txt->i_line >= txt->i_line_count )
189 return( txt->line[txt->i_line++] );
191 static void text_previous_line( text_t *txt )
193 if( txt->i_line > 0 )
198 static void text_rewind( text_t *txt )
203 static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
204 static int sub_SubRipRead ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
205 static int sub_SSA1Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
206 static int sub_SSA2_4Read ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
207 static int sub_Vplayer ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
208 static int sub_Sami ( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe );
215 int (*pf_read_subtitle) ( text_t *, subtitle_t*, mtime_t );
216 } sub_read_subtitle_function [] =
218 { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", sub_MicroDvdRead },
219 { "subrip", SUB_TYPE_SUBRIP, "SubRIP", sub_SubRipRead },
220 { "ssa1", SUB_TYPE_SSA1, "SSA-1", sub_SSA1Read },
221 { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4",sub_SSA2_4Read },
222 { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", sub_Vplayer },
223 { "sami", SUB_TYPE_SAMI, "SAMI", sub_Sami },
224 { NULL, SUB_TYPE_UNKNOWN, "Unknow", NULL }
227 /*****************************************************************************
228 * sub_open: Open a subtitle file and add subtitle ES
229 *****************************************************************************/
230 static int sub_open ( subtitle_demux_t *p_sub,
231 input_thread_t *p_input,
233 mtime_t i_microsecperframe )
241 int (*pf_read_subtitle)( text_t *, subtitle_t *, mtime_t ) = NULL;
243 p_sub->i_sub_type = SUB_TYPE_UNKNOWN;
245 p_sub->i_subtitles = 0;
246 p_sub->subtitle = NULL;
247 p_sub->p_input = p_input;
249 if( !psz_name || !*psz_name)
251 psz_name = config_GetPsz( p_sub, "sub-file" );
252 if( !psz_name || !*psz_name )
259 psz_name = strdup( psz_name );
262 /* *** load the file *** */
263 if( text_load( &txt, psz_name ) )
265 msg_Err( p_sub, "cannot open `%s' subtitle file", psz_name );
269 msg_Dbg( p_sub, "opened `%s'", psz_name );
273 if( config_GetFloat( p_sub, "sub-fps" ) >= 1.0 )
275 i_microsecperframe = (mtime_t)( (float)1000000 /
276 config_GetFloat( p_sub, "sub-fps" ) );
278 else if( i_microsecperframe <= 0 )
280 i_microsecperframe = 40000; /* default: 25fps */
283 psz_file_type = config_GetPsz( p_sub, "sub-type" );
284 if( psz_file_type && *psz_file_type)
290 if( sub_read_subtitle_function[i].psz_type_name == NULL )
292 i_sub_type = SUB_TYPE_UNKNOWN;
295 if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
298 i_sub_type = sub_read_subtitle_function[i].i_type;
305 i_sub_type = SUB_TYPE_UNKNOWN;
307 FREE( psz_file_type );
309 /* *** Now try to autodetect subtitle format *** */
310 if( i_sub_type == SUB_TYPE_UNKNOWN )
315 msg_Dbg( p_input, "trying to autodetect file format" );
316 for( i_try = 0; i_try < MAX_TRY; i_try++ )
320 if( ( s = text_get_line( &txt ) ) == NULL )
325 if( strstr( s, "<SAMI>" ) )
327 i_sub_type = SUB_TYPE_SAMI;
330 else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
331 sscanf( s, "{%d}{}", &i_dummy ) == 1)
333 i_sub_type = SUB_TYPE_MICRODVD;
337 "%d:%d:%d,%d --> %d:%d:%d,%d",
338 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
339 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 )
341 i_sub_type = SUB_TYPE_SUBRIP;
345 "!: This is a Sub Station Alpha v%d.x script.",
350 i_sub_type = SUB_TYPE_SSA1;
354 i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
358 else if( strstr( s, "This is a Sub Station Alpha v4 script" ) )
360 i_sub_type = SUB_TYPE_SSA2_4; // I hop this will work
362 else if( !strncmp( s, "Dialogue: Marked", 16 ) )
364 i_sub_type = SUB_TYPE_SSA2_4; // could be wrong
367 else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
368 sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
370 i_sub_type = SUB_TYPE_VPLAYER;
378 /* *** Load this file in memory *** */
381 if( sub_read_subtitle_function[i].i_type == SUB_TYPE_UNKNOWN )
383 msg_Dbg( p_input, "unknown subtitile file" );
388 if( sub_read_subtitle_function[i].i_type == i_sub_type )
391 "detected %s format",
392 sub_read_subtitle_function[i].psz_name );
393 pf_read_subtitle = sub_read_subtitle_function[i].pf_read_subtitle;
400 if( p_sub->i_subtitles >= i_max )
403 if( p_sub->subtitle )
405 p_sub->subtitle = realloc( p_sub->subtitle,
406 sizeof( subtitle_t ) * i_max );
410 p_sub->subtitle = malloc( sizeof( subtitle_t ) * i_max );
413 if( pf_read_subtitle( &txt,
414 p_sub->subtitle + p_sub->i_subtitles,
415 i_microsecperframe ) < 0 )
419 p_sub->i_subtitles++;
421 msg_Dbg( p_sub, "loaded %d subtitles", p_sub->i_subtitles );
423 /* *** Close the file *** */
426 /* *** fix subtitle (order and time) *** */
427 p_sub->i_subtitle = 0; // will be modified by sub_fix
430 /* *** add subtitle ES *** */
431 vlc_mutex_lock( &p_input->stream.stream_lock );
432 p_sub->p_es = input_AddES( p_input, p_input->stream.p_selected_program,
435 vlc_mutex_unlock( &p_input->stream.stream_lock );
437 p_sub->p_es->i_stream_id = 0xff; // FIXME
438 p_sub->p_es->i_fourcc = VLC_FOURCC( 's','u','b','t' );
440 p_sub->i_previously_selected = 0;
444 /*****************************************************************************
445 * sub_demux: Send subtitle to decoder until i_maxdate
446 *****************************************************************************/
447 static int sub_demux( subtitle_demux_t *p_sub, mtime_t i_maxdate )
449 #if defined(USE_FREETYPE)
450 vlc_object_t *p_vout = NULL;
452 if( p_sub->p_es->p_decoder_fifo && !p_sub->i_previously_selected )
454 p_sub->i_previously_selected = 1;
455 p_sub->pf_seek( p_sub, i_maxdate );
458 else if( !p_sub->p_es->p_decoder_fifo && p_sub->i_previously_selected )
460 p_sub->i_previously_selected = 0;
464 while( p_sub->i_subtitle < p_sub->i_subtitles &&
465 p_sub->subtitle[p_sub->i_subtitle].i_start < i_maxdate )
468 data_packet_t *p_data;
471 #if defined(USE_FREETYPE)
472 p_vout = vlc_object_find( p_sub, VLC_OBJECT_VOUT, FIND_ANYWHERE );
475 i_len = strlen( p_sub->subtitle[p_sub->i_subtitle].psz_text ) + 1;
483 if( !( p_pes = input_NewPES( p_sub->p_input->p_method_data ) ) )
489 if( !( p_data = input_NewPacket( p_sub->p_input->p_method_data,
492 input_DeletePES( p_sub->p_input->p_method_data, p_pes );
496 p_data->p_payload_end = p_data->p_payload_start + i_len;
499 input_ClockGetTS( p_sub->p_input,
500 p_sub->p_input->stream.p_selected_program,
501 p_sub->subtitle[p_sub->i_subtitle].i_start*9/100);
502 if( p_sub->subtitle[p_sub->i_subtitle].i_stop > 0 )
505 * i_dts means end of display...
508 input_ClockGetTS( p_sub->p_input,
509 p_sub->p_input->stream.p_selected_program,
510 p_sub->subtitle[p_sub->i_subtitle].i_stop *9/100);
516 #if defined(USE_FREETYPE)
519 vlc_value_t val, lockval;
520 if( var_Get( p_vout, "lock", &lockval ) == VLC_SUCCESS )
523 vlc_mutex_lock( lockval.p_address );
524 msg_Dbg( p_sub, "pts "I64Fd" dts " I64Fd, p_pes->i_pts,
526 i_tmp = p_pes->i_dts - p_pes->i_pts;
527 val.i_int = OSD_ALIGN_LEFT|OSD_ALIGN_BOTTOM;
528 var_Set( p_vout, "flags", val );
529 val.time.i_low = (int)(p_pes->i_pts+p_sub->p_input->i_pts_delay);
530 val.time.i_high = (int)( p_pes->i_pts >> 32 );
531 var_Set( p_vout, "start-date", val );
532 val.time.i_low = (int)(p_pes->i_dts + p_sub->p_input->i_pts_delay);
533 val.time.i_high = (int)( p_pes->i_dts >> 32 );
534 var_Set( p_vout, "stop-date", val );
536 var_Set( p_vout, "x-margin", val );
538 var_Set( p_vout, "y-margin", val );
539 val.psz_string = p_sub->subtitle[p_sub->i_subtitle].psz_text;
540 var_Set( p_vout, "string", val );
541 vlc_mutex_unlock( lockval.p_address );
545 p_pes->i_nb_data = 1;
547 p_pes->p_last = p_data;
548 p_pes->i_pes_size = i_len;
550 memcpy( p_data->p_payload_start,
551 p_sub->subtitle[p_sub->i_subtitle].psz_text,
553 if( p_sub->p_es->p_decoder_fifo && p_pes->i_pts > 0 )
556 input_DecodePES( p_sub->p_es->p_decoder_fifo, p_pes );
560 input_DeletePES( p_sub->p_input->p_method_data, p_pes );
565 #if defined(USE_FREETYPE)
568 vlc_object_release( p_vout );
574 /*****************************************************************************
575 * sub_seek: Seek to i_date
576 *****************************************************************************/
577 static int sub_seek ( subtitle_demux_t *p_sub, mtime_t i_date )
579 /* should be fast enough... */
580 p_sub->i_subtitle = 0;
581 while( p_sub->i_subtitle < p_sub->i_subtitles &&
582 p_sub->subtitle[p_sub->i_subtitle].i_start < i_date )
590 /*****************************************************************************
591 * sub_close: Close subtitle demux
592 *****************************************************************************/
593 static void sub_close( subtitle_demux_t *p_sub )
595 if( p_sub->subtitle )
598 for( i = 0; i < p_sub->i_subtitles; i++ )
600 if( p_sub->subtitle[i].psz_text )
602 free( p_sub->subtitle[i].psz_text );
605 free( p_sub->subtitle );
608 /*****************************************************************************
610 * sub_fix: fix time stamp and order of subtitle
611 *****************************************************************************/
612 static void sub_fix( subtitle_demux_t *p_sub )
619 /* *** fix order (to be sure...) *** */
620 /* We suppose that there are near in order and this durty bubble sort
621 * wont take too much time
626 for( i_index = 1; i_index < p_sub->i_subtitles; i_index++ )
628 if( p_sub->subtitle[i_index].i_start <
629 p_sub->subtitle[i_index - 1].i_start )
633 p_sub->subtitle + i_index - 1,
634 sizeof( subtitle_t ) );
635 memcpy( p_sub->subtitle + i_index - 1,
636 p_sub->subtitle + i_index,
637 sizeof( subtitle_t ) );
638 memcpy( p_sub->subtitle + i_index,
640 sizeof( subtitle_t ) );
646 /* *** and at the end add delay *** */
647 i_delay = (mtime_t)config_GetInt( p_sub, "sub-delay" ) * 100000;
650 for( i = 0; i < p_sub->i_subtitles; i++ )
652 p_sub->subtitle[i].i_start += i_delay;
653 p_sub->subtitle[i].i_stop += i_delay;
654 if( p_sub->subtitle[i].i_start < 0 )
656 p_sub->i_subtitle = i + 1;
664 /*****************************************************************************
665 * Specific Subtitle function
666 *****************************************************************************/
667 static int sub_MicroDvdRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
671 * {n1}{n2}Line1|Line2|Line3....
672 * where n1 and n2 are the video frame number...
677 char buffer_text[MAX_LINE + 1];
684 if( ( s = text_get_line( txt ) ) == NULL )
686 return( VLC_EGENERIC );
691 memset( buffer_text, '\0', MAX_LINE );
692 if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, buffer_text ) == 2 ||
693 sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, buffer_text ) == 3)
698 /* replace | by \n */
699 for( i = 0; i < strlen( buffer_text ); i++ )
701 if( buffer_text[i] == '|' )
703 buffer_text[i] = '\n';
706 p_subtitle->i_start = (mtime_t)i_start * (mtime_t)i_microsecperframe;
707 p_subtitle->i_stop = (mtime_t)i_stop * (mtime_t)i_microsecperframe;
708 p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
712 static int sub_SubRipRead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
716 * h1:m1:s1,d1 --> h2:m2:s2,d2
724 char buffer_text[ 10 * MAX_LINE];
731 int h1, m1, s1, d1, h2, m2, s2, d2;
732 if( ( s = text_get_line( txt ) ) == NULL )
734 return( VLC_EGENERIC );
737 "%d:%d:%d,%d --> %d:%d:%d,%d",
739 &h2, &m2, &s2, &d2 ) == 8 )
741 i_start = ( (mtime_t)h1 * 3600*1000 +
742 (mtime_t)m1 * 60*1000 +
744 (mtime_t)d1 ) * 1000;
746 i_stop = ( (mtime_t)h2 * 3600*1000 +
747 (mtime_t)m2 * 60*1000 +
749 (mtime_t)d2 ) * 1000;
751 /* Now read text until an empty line */
752 for( i_buffer_text = 0;; )
755 if( ( s = text_get_line( txt ) ) == NULL )
757 return( VLC_EGENERIC );
763 // empty line -> end of this subtitle
764 buffer_text[__MAX( i_buffer_text - 1, 0 )] = '\0';
765 p_subtitle->i_start = i_start;
766 p_subtitle->i_stop = i_stop;
767 p_subtitle->psz_text = strdup( buffer_text );
772 if( i_buffer_text + i_len + 1 < 10 * MAX_LINE )
774 memcpy( buffer_text + i_buffer_text,
777 i_buffer_text += i_len;
779 buffer_text[i_buffer_text] = '\n';
789 static int sub_SSARead( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe, int i_comma_count )
791 char buffer_text[ 10 * MAX_LINE];
801 int h1, m1, s1, c1, h2, m2, s2, c2;
804 if( ( s = text_get_line( txt ) ) == NULL )
806 return( VLC_EGENERIC );
809 "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
813 buffer_text ) == 10 )
815 i_start = ( (mtime_t)h1 * 3600*1000 +
816 (mtime_t)m1 * 60*1000 +
818 (mtime_t)c1 * 10 ) * 1000;
820 i_stop = ( (mtime_t)h2 * 3600*1000 +
821 (mtime_t)m2 * 60*1000 +
823 (mtime_t)c2 * 10 ) * 1000;
825 p_buffer_text = buffer_text;
827 while( i_comma < i_comma_count &&
828 *p_buffer_text != '\0' )
830 if( *p_buffer_text == ',' )
836 p_subtitle->psz_text = malloc( strlen( p_buffer_text ) + 1);
838 while( *p_buffer_text )
840 if( *p_buffer_text == '\\' && ( *p_buffer_text =='n' || *p_buffer_text =='N' ) )
842 p_subtitle->psz_text[i_text] = '\n';
846 else if( *p_buffer_text == '{' && *p_buffer_text == '\\')
848 while( *p_buffer_text && *p_buffer_text != '}' )
855 p_subtitle->psz_text[i_text] = *p_buffer_text;
860 p_subtitle->psz_text[i_text] = '\0';
861 p_subtitle->i_start = i_start;
862 p_subtitle->i_stop = i_stop;
868 static int sub_SSA1Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
870 return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 8 ) );
872 static int sub_SSA2_4Read( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
874 return( sub_SSARead( txt, p_subtitle, i_microsecperframe, 9 ) );
877 static int sub_Vplayer( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe)
881 * h:m:s:Line1|Line2|Line3....
883 * h:m:s Line1|Line2|Line3....
884 * where n1 and n2 are the video frame number...
888 char buffer_text[MAX_LINE + 1];
897 if( ( p = text_get_line( txt ) ) == NULL )
899 return( VLC_EGENERIC );
904 memset( buffer_text, '\0', MAX_LINE );
905 if( sscanf( p, "%d:%d:%d%[ :]%[^\r\n]", &h, &m, &s, &c, buffer_text ) == 5 )
907 i_start = ( (mtime_t)h * 3600*1000 +
908 (mtime_t)m * 60*1000 +
909 (mtime_t)s * 1000 ) * 1000;
914 /* replace | by \n */
915 for( i = 0; i < strlen( buffer_text ); i++ )
917 if( buffer_text[i] == '|' )
919 buffer_text[i] = '\n';
922 p_subtitle->i_start = i_start;
924 p_subtitle->i_stop = 0;
925 p_subtitle->psz_text = strndup( buffer_text, MAX_LINE );
929 static char *sub_SamiSearch( text_t *txt, char *psz_start, char *psz_str )
933 if( strstr( psz_start, psz_str ) )
935 char *s = strstr( psz_start, psz_str );
937 s += strlen( psz_str );
945 if( ( p = text_get_line( txt ) ) == NULL )
949 if( strstr( p, psz_str ) )
951 char *s = strstr( p, psz_str );
953 s += strlen( psz_str );
960 static int sub_Sami( text_t *txt, subtitle_t *p_subtitle, mtime_t i_microsecperframe )
966 char buffer_text[10*MAX_LINE + 1];
968 if( i_text < 10*MAX_LINE ) \
970 buffer_text[i_text++] = c; \
971 buffer_text[i_text] = '\0'; \
974 /* search "Start=" */
975 if( !( p = sub_SamiSearch( txt, NULL, "Start=" ) ) )
980 /* get start value */
981 i_start = strtol( p, &p, 0 );
984 if( !( p = sub_SamiSearch( txt, p, "<P" ) ) )
989 if( !( p = sub_SamiSearch( txt, p, ">" ) ) )
995 buffer_text[0] = '\0';
996 /* now get all txt until a "Start=" line */
1003 if( !strncmp( p, "<br", 3 ) || !strncmp( p, "<BR", 3 ) )
1007 else if( strstr( p, "Start=" ) )
1009 text_previous_line( txt );
1012 p = sub_SamiSearch( txt, p, ">" );
1014 else if( !strncmp( p, " ", 6 ) )
1019 else if( *p == '\t' )
1032 p = text_get_line( txt );
1041 p_subtitle->i_start = i_start * 1000;
1042 p_subtitle->i_stop = 0;
1043 p_subtitle->psz_text = strndup( buffer_text, 10*MAX_LINE );
1045 return( VLC_SUCCESS );