subtitle: fix var inheritance
[vlc.git] / modules / demux / subtitle.c
blob35f6e81ec27729cceb180b9544a8bcbddf283ea4
1 /*****************************************************************************
2 * subtitle.c: Demux for subtitle text files.
3 *****************************************************************************
4 * Copyright (C) 1999-2007 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Derk-Jan Hartman <hartman at videolan dot org>
9 * Jean-Baptiste Kempf <jb@videolan.org>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
38 #include <ctype.h>
39 #include <math.h>
40 #include <assert.h>
42 #include <vlc_demux.h>
43 #include <vlc_charset.h>
45 /*****************************************************************************
46 * Module descriptor
47 *****************************************************************************/
48 static int Open ( vlc_object_t *p_this );
49 static void Close( vlc_object_t *p_this );
51 #define SUB_DELAY_LONGTEXT \
52 N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")
53 #define SUB_FPS_LONGTEXT \
54 N_("Override the normal frames per second settings. " \
55 "This will only work with MicroDVD and SubRIP (SRT) subtitles.")
56 #define SUB_TYPE_LONGTEXT \
57 N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
58 #define SUB_DESCRIPTION_LONGTEXT \
59 N_("Override the default track description.")
61 static const char *const ppsz_sub_type[] =
63 "auto", "microdvd", "subrip", "subviewer", "ssa1",
64 "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
65 "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
66 "subviewer1", "sbv"
69 vlc_module_begin ()
70 set_shortname( N_("Subtitles"))
71 set_description( N_("Text subtitle parser") )
72 set_capability( "demux", 0 )
73 set_category( CAT_INPUT )
74 set_subcategory( SUBCAT_INPUT_DEMUX )
75 add_float( "sub-fps", 0.0,
76 N_("Frames per Second"),
77 SUB_FPS_LONGTEXT, true )
78 add_integer( "sub-delay", 0,
79 N_("Subtitle delay"),
80 SUB_DELAY_LONGTEXT, true )
81 add_string( "sub-type", "auto", N_("Subtitle format"),
82 SUB_TYPE_LONGTEXT, true )
83 change_string_list( ppsz_sub_type, ppsz_sub_type )
84 add_string( "sub-description", NULL, N_("Subtitle description"),
85 SUB_DESCRIPTION_LONGTEXT, true )
86 set_callbacks( Open, Close )
88 add_shortcut( "subtitle" )
89 vlc_module_end ()
91 /*****************************************************************************
92 * Prototypes:
93 *****************************************************************************/
94 enum subtitle_type_e
96 SUB_TYPE_UNKNOWN = -1,
97 SUB_TYPE_MICRODVD,
98 SUB_TYPE_SUBRIP,
99 SUB_TYPE_SSA1,
100 SUB_TYPE_SSA2_4,
101 SUB_TYPE_ASS,
102 SUB_TYPE_VPLAYER,
103 SUB_TYPE_SAMI,
104 SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
105 SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
106 SUB_TYPE_MPL2,
107 SUB_TYPE_AQT,
108 SUB_TYPE_PJS,
109 SUB_TYPE_MPSUB,
110 SUB_TYPE_JACOSUB,
111 SUB_TYPE_PSB,
112 SUB_TYPE_RT,
113 SUB_TYPE_DKS,
114 SUB_TYPE_SUBVIEW1, /* SUBVIEWER 1 - mplayer calls it subrip09,
115 and Gnome subtitles SubViewer 1.0 */
116 SUB_TYPE_SBV,
117 SUB_TYPE_SCC, /* Scenarist Closed Caption */
120 typedef struct
122 size_t i_line_count;
123 size_t i_line;
124 char **line;
125 } text_t;
127 static int TextLoad( text_t *, stream_t *s );
128 static void TextUnload( text_t * );
130 typedef struct
132 int64_t i_start;
133 int64_t i_stop;
135 char *psz_text;
136 } subtitle_t;
138 typedef struct
140 enum subtitle_type_e i_type;
141 int64_t i_microsecperframe;
143 char *psz_header; /* SSA */
145 struct
147 bool b_inited;
149 int i_comment;
150 int i_time_resolution;
151 int i_time_shift;
152 } jss;
154 struct
156 bool b_inited;
158 float f_total;
159 float f_factor;
160 } mpsub;
162 struct
164 const char *psz_start;
165 } sami;
167 } subs_properties_t;
169 struct demux_sys_t
171 es_out_id_t *es;
172 bool b_slave;
173 bool b_first_time;
175 int64_t i_next_demux_date;
177 struct
179 subtitle_t *p_array;
180 size_t i_count;
181 size_t i_current;
182 } subtitles;
184 int64_t i_length;
186 /* */
187 subs_properties_t props;
189 block_t * (*pf_convert)( const subtitle_t * );
192 static int ParseMicroDvd ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
193 static int ParseSubRip ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
194 static int ParseSubViewer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
195 static int ParseSSA ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
196 static int ParseVplayer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
197 static int ParseSami ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
198 static int ParseDVDSubtitle( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
199 static int ParseMPL2 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
200 static int ParseAQT ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
201 static int ParsePJS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
202 static int ParseMPSub ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
203 static int ParseJSS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
204 static int ParsePSB ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
205 static int ParseRealText ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
206 static int ParseDKS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
207 static int ParseSubViewer1 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
208 static int ParseCommonSBV ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
209 static int ParseSCC ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
211 static const struct
213 const char *psz_type_name;
214 int i_type;
215 const char *psz_name;
216 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
217 } sub_read_subtitle_function [] =
219 { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", ParseMicroDvd },
220 { "subrip", SUB_TYPE_SUBRIP, "SubRIP", ParseSubRip },
221 { "subviewer", SUB_TYPE_SUBVIEWER, "SubViewer", ParseSubViewer },
222 { "ssa1", SUB_TYPE_SSA1, "SSA-1", ParseSSA },
223 { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4", ParseSSA },
224 { "ass", SUB_TYPE_ASS, "SSA/ASS", ParseSSA },
225 { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", ParseVplayer },
226 { "sami", SUB_TYPE_SAMI, "SAMI", ParseSami },
227 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle },
228 { "mpl2", SUB_TYPE_MPL2, "MPL2", ParseMPL2 },
229 { "aqt", SUB_TYPE_AQT, "AQTitle", ParseAQT },
230 { "pjs", SUB_TYPE_PJS, "PhoenixSub", ParsePJS },
231 { "mpsub", SUB_TYPE_MPSUB, "MPSub", ParseMPSub },
232 { "jacosub", SUB_TYPE_JACOSUB, "JacoSub", ParseJSS },
233 { "psb", SUB_TYPE_PSB, "PowerDivx", ParsePSB },
234 { "realtext", SUB_TYPE_RT, "RealText", ParseRealText },
235 { "dks", SUB_TYPE_DKS, "DKS", ParseDKS },
236 { "subviewer1", SUB_TYPE_SUBVIEW1, "Subviewer 1", ParseSubViewer1 },
237 { "sbv", SUB_TYPE_SBV, "SBV", ParseCommonSBV },
238 { "scc", SUB_TYPE_SCC, "SCC", ParseSCC },
239 { NULL, SUB_TYPE_UNKNOWN, "Unknown", NULL }
241 /* When adding support for more formats, be sure to add their file extension
242 * to src/input/subtitles.c to enable auto-detection.
245 static int Demux( demux_t * );
246 static int Control( demux_t *, int, va_list );
248 static void Fix( demux_t * );
249 static char * get_language_from_filename( const char * );
251 /*****************************************************************************
252 * Decoder format output function
253 *****************************************************************************/
255 static block_t *ToTextBlock( const subtitle_t *p_subtitle )
257 block_t *p_block;
258 size_t i_len = strlen( p_subtitle->psz_text ) + 1;
260 if( i_len <= 1 || !(p_block = block_Alloc( i_len )) )
261 return NULL;
263 memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );
265 return p_block;
268 static block_t *ToEIA608Block( const subtitle_t *p_subtitle )
270 block_t *p_block;
271 const size_t i_len = strlen( p_subtitle->psz_text );
272 const size_t i_block = (1 + i_len / 5) * 3;
274 if( i_len < 4 || !(p_block = block_Alloc( i_block )) )
275 return NULL;
277 p_block->i_buffer = 0;
279 char *saveptr = NULL;
280 char *psz_tok = strtok_r( p_subtitle->psz_text, " ", &saveptr );
281 unsigned a, b;
282 while( psz_tok &&
283 sscanf( psz_tok, "%2x%2x", &a, &b ) == 2 &&
284 i_block - p_block->i_buffer >= 3 )
286 uint8_t *p_data = &p_block->p_buffer[p_block->i_buffer];
287 p_data[0] = 0xFC;
288 p_data[1] = a;
289 p_data[2] = b;
290 p_block->i_buffer += 3;
291 psz_tok = strtok_r( NULL, " ", &saveptr );
294 return p_block;
297 /*****************************************************************************
298 * Module initializer
299 *****************************************************************************/
300 static int Open ( vlc_object_t *p_this )
302 demux_t *p_demux = (demux_t*)p_this;
303 demux_sys_t *p_sys;
304 es_format_t fmt;
305 float f_fps;
306 char *psz_type;
307 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
309 if( !p_demux->obj.force )
311 msg_Dbg( p_demux, "subtitle demux discarded" );
312 return VLC_EGENERIC;
315 p_demux->pf_demux = Demux;
316 p_demux->pf_control = Control;
317 p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
318 if( p_sys == NULL )
319 return VLC_ENOMEM;
321 p_sys->b_slave = false;
322 p_sys->b_first_time = true;
323 p_sys->i_next_demux_date = 0;
325 p_sys->pf_convert = ToTextBlock;
327 p_sys->subtitles.i_current= 0;
328 p_sys->subtitles.i_count = 0;
329 p_sys->subtitles.p_array = NULL;
331 p_sys->props.psz_header = NULL;
332 p_sys->props.i_microsecperframe = 40000;
333 p_sys->props.jss.b_inited = false;
334 p_sys->props.mpsub.b_inited = false;
335 p_sys->props.sami.psz_start = NULL;
337 /* Get the FPS */
338 f_fps = p_demux->p_input ? var_GetFloat( p_demux->p_input, "sub-original-fps" ) : 0.0f;
339 if( f_fps >= 1.f )
340 p_sys->props.i_microsecperframe = llroundf( 1000000.f / f_fps );
342 msg_Dbg( p_demux, "Movie fps: %f", (double) f_fps );
344 /* Check for override of the fps */
345 f_fps = var_CreateGetFloat( p_demux, "sub-fps" );
346 if( f_fps >= 1.f )
348 p_sys->props.i_microsecperframe = llroundf( 1000000.f / f_fps );
349 msg_Dbg( p_demux, "Override subtitle fps %f", (double) f_fps );
352 /* Get or probe the type */
353 p_sys->props.i_type = SUB_TYPE_UNKNOWN;
354 psz_type = var_CreateGetString( p_demux, "sub-type" );
355 if( psz_type && *psz_type )
357 for( int i = 0; ; i++ )
359 if( sub_read_subtitle_function[i].psz_type_name == NULL )
360 break;
362 if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
363 psz_type ) )
365 p_sys->props.i_type = sub_read_subtitle_function[i].i_type;
366 break;
370 free( psz_type );
372 #ifndef NDEBUG
373 const uint64_t i_start_pos = vlc_stream_Tell( p_demux->s );
374 #endif
376 size_t i_peek;
377 const uint8_t *p_peek;
378 if( vlc_stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
380 free( p_sys );
381 return VLC_EGENERIC;
384 enum
386 UTF8BOM,
387 UTF16LE,
388 UTF16BE,
389 NOBOM,
390 } e_bom = NOBOM;
391 const char *psz_bom = NULL;
393 i_peek = 4096;
394 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
395 if( !memcmp( p_peek, "\xEF\xBB\xBF", 3 ) )
397 e_bom = UTF8BOM;
398 psz_bom = "UTF-8";
400 else if( !memcmp( p_peek, "\xFF\xFE", 2 ) )
402 e_bom = UTF16LE;
403 psz_bom = "UTF-16LE";
404 i_peek *= 2;
406 else if( !memcmp( p_peek, "\xFE\xFF", 2 ) )
408 e_bom = UTF16BE;
409 psz_bom = "UTF-16BE";
410 i_peek *= 2;
413 if( e_bom != NOBOM )
414 msg_Dbg( p_demux, "detected %s Byte Order Mark", psz_bom );
416 i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_peek );
417 if( unlikely(i_peek < 16) )
419 free( p_sys );
420 return VLC_EGENERIC;
423 stream_t *p_probestream = NULL;
424 if( e_bom != UTF8BOM && e_bom != NOBOM )
426 if( i_peek > 16 )
428 char *p_outbuf = FromCharset( psz_bom, p_peek, i_peek );
429 if( p_outbuf != NULL )
430 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *)p_outbuf,
431 strlen( p_outbuf ),
432 false ); /* free p_outbuf on release */
435 else
437 const size_t i_skip = (e_bom == UTF8BOM) ? 3 : 0;
438 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *) &p_peek[i_skip],
439 i_peek - i_skip, true );
442 if( p_probestream == NULL )
444 free( p_sys );
445 return VLC_EGENERIC;
448 /* Probe if unknown type */
449 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
451 int i_try;
452 char *s = NULL;
454 msg_Dbg( p_demux, "autodetecting subtitle format" );
455 for( i_try = 0; i_try < 256; i_try++ )
457 int i_dummy;
458 char p_dummy;
460 if( (s = vlc_stream_ReadLine( p_probestream ) ) == NULL )
461 break;
463 if( strcasestr( s, "<SAMI>" ) )
465 p_sys->props.i_type = SUB_TYPE_SAMI;
466 break;
468 else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
469 sscanf( s, "{%d}{}", &i_dummy ) == 1)
471 p_sys->props.i_type = SUB_TYPE_MICRODVD;
472 break;
474 else if( sscanf( s, "%d:%d:%d,%d --> %d:%d:%d,%d",
475 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
476 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
477 sscanf( s, "%d:%d:%d --> %d:%d:%d,%d",
478 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
479 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
480 sscanf( s, "%d:%d:%d,%d --> %d:%d:%d",
481 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
482 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
483 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d.%d",
484 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
485 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
486 sscanf( s, "%d:%d:%d --> %d:%d:%d.%d",
487 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
488 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
489 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d",
490 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
491 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
492 sscanf( s, "%d:%d:%d --> %d:%d:%d",
493 &i_dummy,&i_dummy,&i_dummy,
494 &i_dummy,&i_dummy,&i_dummy ) == 6 )
496 p_sys->props.i_type = SUB_TYPE_SUBRIP;
497 break;
499 else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
501 p_sys->props.i_type = SUB_TYPE_SSA1;
502 break;
504 else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
506 p_sys->props.i_type = SUB_TYPE_ASS;
507 break;
509 else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
511 p_sys->props.i_type = SUB_TYPE_SSA2_4;
512 break;
514 else if( !strncasecmp( s, "Dialogue: Marked", 16 ) )
516 p_sys->props.i_type = SUB_TYPE_SSA2_4;
517 break;
519 else if( !strncasecmp( s, "Dialogue:", 9 ) )
521 p_sys->props.i_type = SUB_TYPE_ASS;
522 break;
524 else if( strcasestr( s, "[INFORMATION]" ) )
526 p_sys->props.i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
527 break;
529 else if( sscanf( s, "%d:%d:%d.%d %d:%d:%d",
530 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
531 &i_dummy, &i_dummy, &i_dummy ) == 7 ||
532 sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2)
534 p_sys->props.i_type = SUB_TYPE_JACOSUB;
535 break;
537 else if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
538 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
539 &i_dummy, &i_dummy, &i_dummy, &i_dummy ) == 8 )
541 p_sys->props.i_type = SUB_TYPE_SBV;
542 break;
544 else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
545 sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
547 p_sys->props.i_type = SUB_TYPE_VPLAYER;
548 break;
550 else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy,
551 &i_dummy, &i_dummy ) == 4 )
553 p_sys->props.i_type = SUB_TYPE_DVDSUBTITLE;
554 break;
556 else if( sscanf( s, "[%d:%d:%d]%c",
557 &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 )
559 p_sys->props.i_type = SUB_TYPE_DKS;
560 break;
562 else if( strstr( s, "*** START SCRIPT" ) )
564 p_sys->props.i_type = SUB_TYPE_SUBVIEW1;
565 break;
567 else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 ||
568 sscanf( s, "[%d][]", &i_dummy ) == 1)
570 p_sys->props.i_type = SUB_TYPE_MPL2;
571 break;
573 else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 ||
574 ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1
575 && p_dummy =='E' ) )
577 p_sys->props.i_type = SUB_TYPE_MPSUB;
578 break;
580 else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
582 p_sys->props.i_type = SUB_TYPE_AQT;
583 break;
585 else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
587 p_sys->props.i_type = SUB_TYPE_PJS;
588 break;
590 else if( sscanf( s, "{%d:%d:%d}",
591 &i_dummy, &i_dummy, &i_dummy ) == 3 )
593 p_sys->props.i_type = SUB_TYPE_PSB;
594 break;
596 else if( strcasestr( s, "<time" ) )
598 p_sys->props.i_type = SUB_TYPE_RT;
599 break;
601 else if( !strncasecmp( s, "WEBVTT",6 ) )
603 /* FAIL */
604 break;
606 else if( !strncasecmp( s, "Scenarist_SCC V1.0", 18 ) )
608 p_sys->props.i_type = SUB_TYPE_SCC;
609 p_sys->pf_convert = ToEIA608Block;
610 break;
613 free( s );
614 s = NULL;
617 free( s );
620 vlc_stream_Delete( p_probestream );
622 /* Quit on unknown subtitles */
623 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
625 #ifndef NDEBUG
626 /* Ensure it will work with non seekable streams */
627 assert( i_start_pos == vlc_stream_Tell( p_demux->s ) );
628 #endif
629 msg_Warn( p_demux, "failed to recognize subtitle type" );
630 free( p_sys );
631 return VLC_EGENERIC;
634 for( int i = 0; ; i++ )
636 if( sub_read_subtitle_function[i].i_type == p_sys->props.i_type )
638 msg_Dbg( p_demux, "detected %s format",
639 sub_read_subtitle_function[i].psz_name );
640 pf_read = sub_read_subtitle_function[i].pf_read;
641 break;
645 msg_Dbg( p_demux, "loading all subtitles..." );
647 if( e_bom == UTF8BOM && /* skip BOM */
648 vlc_stream_Read( p_demux->s, NULL, 3 ) != 3 )
650 Close( p_this );
651 return VLC_EGENERIC;
654 /* Load the whole file */
655 text_t txtlines;
656 TextLoad( &txtlines, p_demux->s );
658 /* Parse it */
659 for( size_t i_max = 0; i_max < SIZE_MAX - 500 * sizeof(subtitle_t); )
661 if( p_sys->subtitles.i_count >= i_max )
663 i_max += 500;
664 subtitle_t *p_realloc = realloc( p_sys->subtitles.p_array, sizeof(subtitle_t) * i_max );
665 if( p_realloc == NULL )
667 TextUnload( &txtlines );
668 Close( p_this );
669 return VLC_ENOMEM;
671 p_sys->subtitles.p_array = p_realloc;
674 if( pf_read( VLC_OBJECT(p_demux), &p_sys->props, &txtlines,
675 &p_sys->subtitles.p_array[p_sys->subtitles.i_count],
676 p_sys->subtitles.i_count ) )
677 break;
679 p_sys->subtitles.i_count++;
681 /* Unload */
682 TextUnload( &txtlines );
684 msg_Dbg(p_demux, "loaded %zu subtitles", p_sys->subtitles.i_count );
686 /* Fix subtitle (order and time) *** */
687 p_sys->subtitles.i_current = 0;
688 p_sys->i_length = 0;
689 if( p_sys->subtitles.i_count > 0 )
690 p_sys->i_length = p_sys->subtitles.p_array[p_sys->subtitles.i_count-1].i_stop;
692 /* *** add subtitle ES *** */
693 if( p_sys->props.i_type == SUB_TYPE_SSA1 ||
694 p_sys->props.i_type == SUB_TYPE_SSA2_4 ||
695 p_sys->props.i_type == SUB_TYPE_ASS )
697 Fix( p_demux );
698 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
700 else if( p_sys->props.i_type == SUB_TYPE_SCC )
702 es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
703 fmt.subs.cc.i_reorder_depth = -1;
705 else
706 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
708 /* Stupid language detection in the filename */
709 char * psz_language = get_language_from_filename( p_demux->psz_filepath );
711 if( psz_language )
713 fmt.psz_language = psz_language;
714 msg_Dbg( p_demux, "detected language %s of subtitle: %s", psz_language,
715 p_demux->psz_location );
718 if( psz_bom )
719 fmt.subs.psz_encoding = strdup( psz_bom );
720 char *psz_description = var_InheritString( p_demux, "sub-description" );
721 if( psz_description && *psz_description )
722 fmt.psz_description = psz_description;
723 else
724 free( psz_description );
725 if( p_sys->props.psz_header != NULL &&
726 (fmt.p_extra = strdup( p_sys->props.psz_header )) )
728 fmt.i_extra = strlen( p_sys->props.psz_header ) + 1;
731 p_sys->es = es_out_Add( p_demux->out, &fmt );
732 es_format_Clean( &fmt );
733 if( p_sys->es == NULL )
735 Close( p_this );
736 return VLC_EGENERIC;
739 return VLC_SUCCESS;
742 /*****************************************************************************
743 * Close: Close subtitle demux
744 *****************************************************************************/
745 static void Close( vlc_object_t *p_this )
747 demux_t *p_demux = (demux_t*)p_this;
748 demux_sys_t *p_sys = p_demux->p_sys;
750 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
751 free( p_sys->subtitles.p_array[i].psz_text );
752 free( p_sys->subtitles.p_array );
753 free( p_sys->props.psz_header );
755 free( p_sys );
758 /*****************************************************************************
759 * Control:
760 *****************************************************************************/
761 static int Control( demux_t *p_demux, int i_query, va_list args )
763 demux_sys_t *p_sys = p_demux->p_sys;
764 int64_t *pi64, i64;
765 double *pf, f;
767 switch( i_query )
769 case DEMUX_CAN_SEEK:
770 *va_arg( args, bool * ) = true;
771 return VLC_SUCCESS;
773 case DEMUX_GET_LENGTH:
774 pi64 = va_arg( args, int64_t * );
775 *pi64 = p_sys->i_length;
776 return VLC_SUCCESS;
778 case DEMUX_GET_TIME:
779 pi64 = va_arg( args, int64_t * );
780 *pi64 = p_sys->i_next_demux_date - var_GetInteger( p_demux->obj.parent, "spu-delay" );
781 if( *pi64 < 0 )
782 *pi64 = p_sys->i_next_demux_date;
783 return VLC_SUCCESS;
785 case DEMUX_SET_TIME:
786 i64 = va_arg( args, int64_t );
787 p_sys->b_first_time = true;
788 p_sys->i_next_demux_date = i64;
789 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
791 if( p_sys->subtitles.p_array[i].i_start > i64 && i > 0 )
792 break;
793 p_sys->subtitles.i_current = i;
795 return VLC_SUCCESS;
797 case DEMUX_GET_POSITION:
798 pf = va_arg( args, double * );
799 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
801 *pf = 1.0;
803 else if( p_sys->subtitles.i_count > 0 && p_sys->i_length )
805 *pf = p_sys->i_next_demux_date - var_GetInteger( p_demux->obj.parent, "spu-delay" );
806 if( *pf < 0 )
807 *pf = p_sys->i_next_demux_date;
808 *pf /= p_sys->i_length;
810 else
812 *pf = 0.0;
814 return VLC_SUCCESS;
816 case DEMUX_SET_POSITION:
817 f = va_arg( args, double );
818 if( p_sys->subtitles.i_count && p_sys->i_length )
820 i64 = VLC_TS_0 + f * p_sys->i_length;
821 return demux_Control( p_demux, DEMUX_SET_TIME, i64 );
823 break;
825 case DEMUX_SET_NEXT_DEMUX_TIME:
826 p_sys->b_slave = true;
827 p_sys->i_next_demux_date = va_arg( args, int64_t ) - VLC_TS_0;
828 return VLC_SUCCESS;
830 case DEMUX_CAN_PAUSE:
831 case DEMUX_SET_PAUSE_STATE:
832 case DEMUX_CAN_CONTROL_PACE:
833 return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
835 case DEMUX_GET_PTS_DELAY:
836 case DEMUX_GET_FPS:
837 case DEMUX_GET_META:
838 case DEMUX_GET_ATTACHMENTS:
839 case DEMUX_GET_TITLE_INFO:
840 case DEMUX_HAS_UNSUPPORTED_META:
841 case DEMUX_CAN_RECORD:
842 default:
843 break;
846 return VLC_EGENERIC;
849 /*****************************************************************************
850 * Demux: Send subtitle to decoder
851 *****************************************************************************/
852 static int Demux( demux_t *p_demux )
854 demux_sys_t *p_sys = p_demux->p_sys;
856 int64_t i_barrier = p_sys->i_next_demux_date - var_GetInteger( p_demux->obj.parent, "spu-delay" );
857 if( i_barrier < 0 )
858 i_barrier = p_sys->i_next_demux_date;
860 while( p_sys->subtitles.i_current < p_sys->subtitles.i_count &&
861 p_sys->subtitles.p_array[p_sys->subtitles.i_current].i_start <= i_barrier )
863 const subtitle_t *p_subtitle = &p_sys->subtitles.p_array[p_sys->subtitles.i_current];
865 if ( !p_sys->b_slave && p_sys->b_first_time )
867 es_out_SetPCR( p_demux->out, VLC_TS_0 + i_barrier );
868 p_sys->b_first_time = false;
871 if( p_subtitle->i_start >= 0 )
873 block_t *p_block = p_sys->pf_convert( p_subtitle );
874 if( p_block )
876 p_block->i_dts =
877 p_block->i_pts = VLC_TS_0 + p_subtitle->i_start;
878 if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
879 p_block->i_length = p_subtitle->i_stop - p_subtitle->i_start;
881 es_out_Send( p_demux->out, p_sys->es, p_block );
885 p_sys->subtitles.i_current++;
888 if ( !p_sys->b_slave )
890 es_out_SetPCR( p_demux->out, VLC_TS_0 + i_barrier );
891 p_sys->i_next_demux_date += CLOCK_FREQ / 8;
894 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
895 return VLC_DEMUXER_EOF;
897 return VLC_DEMUXER_SUCCESS;
901 static int subtitle_cmp( const void *first, const void *second )
903 int64_t result = ((subtitle_t *)(first))->i_start - ((subtitle_t *)(second))->i_start;
904 /* Return -1, 0 ,1, and not directly subtraction
905 * as result can be > INT_MAX */
906 return result == 0 ? 0 : result > 0 ? 1 : -1;
908 /*****************************************************************************
909 * Fix: fix time stamp and order of subtitle
910 *****************************************************************************/
911 static void Fix( demux_t *p_demux )
913 demux_sys_t *p_sys = p_demux->p_sys;
915 /* *** fix order (to be sure...) *** */
916 qsort( p_sys->subtitles.p_array, p_sys->subtitles.i_count, sizeof( p_sys->subtitles.p_array[0] ), subtitle_cmp);
919 static int TextLoad( text_t *txt, stream_t *s )
921 size_t i_line_max;
923 /* init txt */
924 i_line_max = 500;
925 txt->i_line_count = 0;
926 txt->i_line = 0;
927 txt->line = calloc( i_line_max, sizeof( char * ) );
928 if( !txt->line )
929 return VLC_ENOMEM;
931 /* load the complete file */
932 for( ;; )
934 char *psz = vlc_stream_ReadLine( s );
936 if( psz == NULL )
937 break;
939 txt->line[txt->i_line_count] = psz;
940 if( txt->i_line_count + 1 >= i_line_max )
942 i_line_max += 100;
943 char **p_realloc = realloc( txt->line, i_line_max * sizeof( char * ) );
944 if( p_realloc == NULL )
945 return VLC_ENOMEM;
946 txt->line = p_realloc;
948 txt->i_line_count++;
951 if( txt->i_line_count == 0 )
953 free( txt->line );
954 return VLC_EGENERIC;
957 return VLC_SUCCESS;
959 static void TextUnload( text_t *txt )
961 if( txt->i_line_count )
963 for( size_t i = 0; i < txt->i_line_count; i++ )
964 free( txt->line[i] );
965 free( txt->line );
967 txt->i_line = 0;
968 txt->i_line_count = 0;
971 static char *TextGetLine( text_t *txt )
973 if( txt->i_line >= txt->i_line_count )
974 return( NULL );
976 return txt->line[txt->i_line++];
978 static void TextPreviousLine( text_t *txt )
980 if( txt->i_line > 0 )
981 txt->i_line--;
984 /*****************************************************************************
985 * Specific Subtitle function
986 *****************************************************************************/
987 /* ParseMicroDvd:
988 * Format:
989 * {n1}{n2}Line1|Line2|Line3....
990 * where n1 and n2 are the video frame number (n2 can be empty)
992 static int ParseMicroDvd( vlc_object_t *p_obj, subs_properties_t *p_props,
993 text_t *txt, subtitle_t *p_subtitle,
994 size_t i_idx )
996 VLC_UNUSED( i_idx );
997 char *psz_text;
998 int i_start;
999 int i_stop;
1000 int i;
1002 for( ;; )
1004 const char *s = TextGetLine( txt );
1005 if( !s )
1006 return VLC_EGENERIC;
1008 psz_text = malloc( strlen(s) + 1 );
1009 if( !psz_text )
1010 return VLC_ENOMEM;
1012 i_start = 0;
1013 i_stop = -1;
1014 if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
1015 sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1017 if( i_start != 1 || i_stop != 1 )
1018 break;
1020 /* We found a possible setting of the framerate "{1}{1}23.976" */
1021 /* Check if it's usable, and if the sub-fps is not set */
1022 float f_fps = us_strtof( psz_text, NULL );
1023 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-fps" ) <= 0.f )
1024 p_props->i_microsecperframe = llroundf(1000000.f / f_fps);
1026 free( psz_text );
1029 /* replace | by \n */
1030 for( i = 0; psz_text[i] != '\0'; i++ )
1032 if( psz_text[i] == '|' )
1033 psz_text[i] = '\n';
1036 /* */
1037 p_subtitle->i_start = i_start * p_props->i_microsecperframe;
1038 p_subtitle->i_stop = i_stop >= 0 ? (i_stop * p_props->i_microsecperframe) : -1;
1039 p_subtitle->psz_text = psz_text;
1040 return VLC_SUCCESS;
1043 /* ParseSubRipSubViewer
1044 * Format SubRip
1046 * h1:m1:s1,d1 --> h2:m2:s2,d2
1047 * Line1
1048 * Line2
1049 * ....
1050 * [Empty line]
1051 * Format SubViewer v1/v2
1052 * h1:m1:s1.d1,h2:m2:s2.d2
1053 * Line1[br]Line2
1054 * Line3
1055 * ...
1056 * [empty line]
1057 * We ignore line number for SubRip
1059 static int ParseSubRipSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1060 text_t *txt, subtitle_t *p_subtitle,
1061 int (* pf_parse_timing)(subtitle_t *, const char *),
1062 bool b_replace_br )
1064 VLC_UNUSED(p_obj);
1065 VLC_UNUSED(p_props);
1066 char *psz_text;
1068 for( ;; )
1070 const char *s = TextGetLine( txt );
1072 if( !s )
1073 return VLC_EGENERIC;
1075 if( pf_parse_timing( p_subtitle, s) == VLC_SUCCESS &&
1076 p_subtitle->i_start < p_subtitle->i_stop )
1078 break;
1082 /* Now read text until an empty line */
1083 psz_text = strdup("");
1084 if( !psz_text )
1085 return VLC_ENOMEM;
1087 for( ;; )
1089 const char *s = TextGetLine( txt );
1090 size_t i_len;
1091 size_t i_old;
1093 i_len = s ? strlen( s ) : 0;
1094 if( i_len <= 0 )
1096 p_subtitle->psz_text = psz_text;
1097 return VLC_SUCCESS;
1100 i_old = strlen( psz_text );
1101 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1102 if( !psz_text )
1104 return VLC_ENOMEM;
1106 strcat( psz_text, s );
1107 strcat( psz_text, "\n" );
1109 /* replace [br] by \n */
1110 if( b_replace_br )
1112 char *p;
1114 while( ( p = strstr( psz_text, "[br]" ) ) )
1116 *p++ = '\n';
1117 memmove( p, &p[3], strlen(&p[3])+1 );
1123 /* subtitle_ParseSubRipTimingValue
1124 * Parses SubRip timing value.
1126 static int subtitle_ParseSubRipTimingValue(int64_t *timing_value,
1127 const char *s)
1129 int h1, m1, s1, d1 = 0;
1131 if ( sscanf( s, "%d:%d:%d,%d",
1132 &h1, &m1, &s1, &d1 ) == 4 ||
1133 sscanf( s, "%d:%d:%d.%d",
1134 &h1, &m1, &s1, &d1 ) == 4 ||
1135 sscanf( s, "%d:%d:%d",
1136 &h1, &m1, &s1) == 3 )
1138 (*timing_value) = ( (int64_t)h1 * 3600 * 1000 +
1139 (int64_t)m1 * 60 * 1000 +
1140 (int64_t)s1 * 1000 +
1141 (int64_t)d1 ) * 1000;
1143 return VLC_SUCCESS;
1146 return VLC_EGENERIC;
1149 /* subtitle_ParseSubRipTiming
1150 * Parses SubRip timing.
1152 static int subtitle_ParseSubRipTiming( subtitle_t *p_subtitle,
1153 const char *s )
1155 int i_result = VLC_EGENERIC;
1156 char *psz_start, *psz_stop;
1157 psz_start = malloc( strlen(s) + 1 );
1158 psz_stop = malloc( strlen(s) + 1 );
1160 if( sscanf( s, "%s --> %s", psz_start, psz_stop) == 2 &&
1161 subtitle_ParseSubRipTimingValue( &p_subtitle->i_start, psz_start ) == VLC_SUCCESS &&
1162 subtitle_ParseSubRipTimingValue( &p_subtitle->i_stop, psz_stop ) == VLC_SUCCESS )
1164 i_result = VLC_SUCCESS;
1167 free(psz_start);
1168 free(psz_stop);
1170 return i_result;
1172 /* ParseSubRip
1174 static int ParseSubRip( vlc_object_t *p_obj, subs_properties_t *p_props,
1175 text_t *txt, subtitle_t *p_subtitle,
1176 size_t i_idx )
1178 VLC_UNUSED( i_idx );
1179 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1180 &subtitle_ParseSubRipTiming,
1181 false );
1184 /* subtitle_ParseSubViewerTiming
1185 * Parses SubViewer timing.
1187 static int subtitle_ParseSubViewerTiming( subtitle_t *p_subtitle,
1188 const char *s )
1190 int h1, m1, s1, d1, h2, m2, s2, d2;
1192 if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
1193 &h1, &m1, &s1, &d1, &h2, &m2, &s2, &d2) == 8 )
1195 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1196 (int64_t)m1 * 60*1000 +
1197 (int64_t)s1 * 1000 +
1198 (int64_t)d1 ) * 1000;
1200 p_subtitle->i_stop = ( (int64_t)h2 * 3600*1000 +
1201 (int64_t)m2 * 60*1000 +
1202 (int64_t)s2 * 1000 +
1203 (int64_t)d2 ) * 1000;
1204 return VLC_SUCCESS;
1206 return VLC_EGENERIC;
1209 /* ParseSubViewer
1211 static int ParseSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1212 text_t *txt, subtitle_t *p_subtitle,
1213 size_t i_idx )
1215 VLC_UNUSED( i_idx );
1217 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1218 &subtitle_ParseSubViewerTiming,
1219 true );
1222 /* ParseSSA
1224 static int ParseSSA( vlc_object_t *p_obj, subs_properties_t *p_props,
1225 text_t *txt, subtitle_t *p_subtitle,
1226 size_t i_idx )
1228 VLC_UNUSED(p_obj);
1229 size_t header_len = 0;
1231 for( ;; )
1233 const char *s = TextGetLine( txt );
1234 int h1, m1, s1, c1, h2, m2, s2, c2;
1235 char *psz_text, *psz_temp;
1236 char temp[16];
1238 if( !s )
1239 return VLC_EGENERIC;
1241 /* We expect (SSA2-4):
1242 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1243 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1245 * 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.
1248 /* For ASS:
1249 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1250 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1253 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1254 psz_text = malloc( strlen(s) );
1255 if( !psz_text )
1256 return VLC_ENOMEM;
1258 if( sscanf( s,
1259 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1260 temp,
1261 &h1, &m1, &s1, &c1,
1262 &h2, &m2, &s2, &c2,
1263 psz_text ) == 10 )
1265 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1266 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1267 if( p_props->i_type == SUB_TYPE_SSA1 )
1269 /* SSA1 has only 8 commas before the text starts, not 9 */
1270 memmove( &psz_text[1], psz_text, strlen(psz_text)+1 );
1271 psz_text[0] = ',';
1273 else
1275 int i_layer = ( p_props->i_type == SUB_TYPE_ASS ) ? atoi( temp ) : 0;
1277 /* ReadOrder, Layer, %s(rest of fields) */
1278 if( asprintf( &psz_temp, "%zu,%d,%s", i_idx, i_layer, psz_text ) == -1 )
1280 free( psz_text );
1281 return VLC_ENOMEM;
1284 free( psz_text );
1285 psz_text = psz_temp;
1288 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1289 (int64_t)m1 * 60*1000 +
1290 (int64_t)s1 * 1000 +
1291 (int64_t)c1 * 10 ) * 1000;
1292 p_subtitle->i_stop = ( (int64_t)h2 * 3600*1000 +
1293 (int64_t)m2 * 60*1000 +
1294 (int64_t)s2 * 1000 +
1295 (int64_t)c2 * 10 ) * 1000;
1296 p_subtitle->psz_text = psz_text;
1297 return VLC_SUCCESS;
1299 free( psz_text );
1301 /* All the other stuff we add to the header field */
1302 if( header_len == 0 && p_props->psz_header )
1303 header_len = strlen( p_props->psz_header );
1305 size_t s_len = strlen( s );
1306 p_props->psz_header = realloc_or_free( p_props->psz_header, header_len + s_len + 2 );
1307 if( !p_props->psz_header )
1308 return VLC_ENOMEM;
1309 snprintf( p_props->psz_header + header_len, s_len + 2, "%s\n", s );
1310 header_len += s_len + 1;
1314 /* ParseVplayer
1315 * Format
1316 * h:m:s:Line1|Line2|Line3....
1317 * or
1318 * h:m:s Line1|Line2|Line3....
1320 static int ParseVplayer( vlc_object_t *p_obj, subs_properties_t *p_props,
1321 text_t *txt, subtitle_t *p_subtitle,
1322 size_t i_idx )
1324 VLC_UNUSED(p_obj);
1325 VLC_UNUSED(p_props);
1326 VLC_UNUSED( i_idx );
1327 char *psz_text;
1329 for( ;; )
1331 const char *s = TextGetLine( txt );
1332 int h1, m1, s1;
1334 if( !s )
1335 return VLC_EGENERIC;
1337 psz_text = malloc( strlen( s ) + 1 );
1338 if( !psz_text )
1339 return VLC_ENOMEM;
1341 if( sscanf( s, "%d:%d:%d%*c%[^\r\n]",
1342 &h1, &m1, &s1, psz_text ) == 4 )
1344 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1345 (int64_t)m1 * 60*1000 +
1346 (int64_t)s1 * 1000 ) * 1000;
1347 p_subtitle->i_stop = -1;
1348 break;
1350 free( psz_text );
1353 /* replace | by \n */
1354 for( size_t i = 0; psz_text[i] != '\0'; i++ )
1356 if( psz_text[i] == '|' )
1357 psz_text[i] = '\n';
1359 p_subtitle->psz_text = psz_text;
1360 return VLC_SUCCESS;
1363 /* ParseSami
1365 static const char *ParseSamiSearch( text_t *txt,
1366 const char *psz_start, const char *psz_str )
1368 if( psz_start && strcasestr( psz_start, psz_str ) )
1370 const char *s = strcasestr( psz_start, psz_str );
1371 return &s[strlen( psz_str )];
1374 for( ;; )
1376 const char *p = TextGetLine( txt );
1377 if( !p )
1378 return NULL;
1380 const char *s = strcasestr( p, psz_str );
1381 if( s != NULL )
1382 return &s[strlen( psz_str )];
1385 static int ParseSami( vlc_object_t *p_obj, subs_properties_t *p_props,
1386 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1388 VLC_UNUSED(p_obj);
1389 VLC_UNUSED(p_props);
1390 VLC_UNUSED( i_idx );
1391 const char *s;
1392 int64_t i_start;
1394 unsigned int i_text;
1395 char text[8192]; /* Arbitrary but should be long enough */
1397 /* search "Start=" */
1398 s = ParseSamiSearch( txt, p_props->sami.psz_start, "Start=" );
1399 p_props->sami.psz_start = NULL;
1400 if( !s )
1401 return VLC_EGENERIC;
1403 /* get start value */
1404 char *psz_end;
1405 i_start = strtol( s, &psz_end, 0 );
1406 s = psz_end;
1408 /* search <P */
1409 if( !( s = ParseSamiSearch( txt, s, "<P" ) ) )
1410 return VLC_EGENERIC;
1412 /* search > */
1413 if( !( s = ParseSamiSearch( txt, s, ">" ) ) )
1414 return VLC_EGENERIC;
1416 i_text = 0;
1417 text[0] = '\0';
1418 /* now get all txt until a "Start=" line */
1419 for( ;; )
1421 char c = '\0';
1422 /* Search non empty line */
1423 while( s && *s == '\0' )
1424 s = TextGetLine( txt );
1425 if( !s )
1426 break;
1428 if( *s == '<' )
1430 if( !strncasecmp( s, "<br", 3 ) )
1432 c = '\n';
1434 else if( strcasestr( s, "Start=" ) )
1436 p_props->sami.psz_start = s;
1437 break;
1439 s = ParseSamiSearch( txt, s, ">" );
1441 else if( !strncmp( s, "&nbsp;", 6 ) )
1443 c = ' ';
1444 s += 6;
1446 else if( *s == '\t' )
1448 c = ' ';
1449 s++;
1451 else
1453 c = *s;
1454 s++;
1456 if( c != '\0' && i_text+1 < sizeof(text) )
1458 text[i_text++] = c;
1459 text[i_text] = '\0';
1463 p_subtitle->i_start = i_start * 1000;
1464 p_subtitle->i_stop = -1;
1465 p_subtitle->psz_text = strdup( text );
1467 return VLC_SUCCESS;
1470 /* ParseDVDSubtitle
1471 * Format
1472 * {T h1:m1:s1:c1
1473 * Line1
1474 * Line2
1475 * ...
1477 * TODO it can have a header
1478 * { HEAD
1479 * ...
1480 * CODEPAGE=...
1481 * FORMAT=...
1482 * LANG=English
1484 * LANG support would be cool
1485 * CODEPAGE is probably mandatory FIXME
1487 static int ParseDVDSubtitle(vlc_object_t *p_obj, subs_properties_t *p_props,
1488 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1490 VLC_UNUSED(p_obj);
1491 VLC_UNUSED(p_props);
1492 VLC_UNUSED( i_idx );
1493 char *psz_text;
1495 for( ;; )
1497 const char *s = TextGetLine( txt );
1498 int h1, m1, s1, c1;
1500 if( !s )
1501 return VLC_EGENERIC;
1503 if( sscanf( s,
1504 "{T %d:%d:%d:%d",
1505 &h1, &m1, &s1, &c1 ) == 4 )
1507 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
1508 (int64_t)m1 * 60*1000 +
1509 (int64_t)s1 * 1000 +
1510 (int64_t)c1 * 10) * 1000;
1511 p_subtitle->i_stop = -1;
1512 break;
1516 /* Now read text until a line containing "}" */
1517 psz_text = strdup("");
1518 if( !psz_text )
1519 return VLC_ENOMEM;
1520 for( ;; )
1522 const char *s = TextGetLine( txt );
1523 int i_len;
1524 int i_old;
1526 if( !s )
1528 free( psz_text );
1529 return VLC_EGENERIC;
1532 i_len = strlen( s );
1533 if( i_len == 1 && s[0] == '}')
1535 p_subtitle->psz_text = psz_text;
1536 return VLC_SUCCESS;
1539 i_old = strlen( psz_text );
1540 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1541 if( !psz_text )
1542 return VLC_ENOMEM;
1543 strcat( psz_text, s );
1544 strcat( psz_text, "\n" );
1548 /* ParseMPL2
1549 * Format
1550 * [n1][n2]Line1|Line2|Line3...
1551 * where n1 and n2 are the video frame number (n2 can be empty)
1553 static int ParseMPL2(vlc_object_t *p_obj, subs_properties_t *p_props,
1554 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1556 VLC_UNUSED(p_obj);
1557 VLC_UNUSED(p_props);
1558 VLC_UNUSED( i_idx );
1559 char *psz_text;
1560 int i;
1562 for( ;; )
1564 const char *s = TextGetLine( txt );
1565 int i_start;
1566 int i_stop;
1568 if( !s )
1569 return VLC_EGENERIC;
1571 psz_text = malloc( strlen(s) + 1 );
1572 if( !psz_text )
1573 return VLC_ENOMEM;
1575 i_start = 0;
1576 i_stop = -1;
1577 if( sscanf( s, "[%d][] %[^\r\n]", &i_start, psz_text ) == 2 ||
1578 sscanf( s, "[%d][%d] %[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1580 p_subtitle->i_start = (int64_t)i_start * 100000;
1581 p_subtitle->i_stop = i_stop >= 0 ? ((int64_t)i_stop * 100000) : -1;
1582 break;
1584 free( psz_text );
1587 for( i = 0; psz_text[i] != '\0'; )
1589 /* replace | by \n */
1590 if( psz_text[i] == '|' )
1591 psz_text[i] = '\n';
1593 /* Remove italic */
1594 if( psz_text[i] == '/' && ( i == 0 || psz_text[i-1] == '\n' ) )
1595 memmove( &psz_text[i], &psz_text[i+1], strlen(&psz_text[i+1])+1 );
1596 else
1597 i++;
1599 p_subtitle->psz_text = psz_text;
1600 return VLC_SUCCESS;
1603 static int ParseAQT(vlc_object_t *p_obj, subs_properties_t *p_props, text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1605 VLC_UNUSED(p_obj);
1606 VLC_UNUSED(p_props);
1607 VLC_UNUSED( i_idx );
1609 char *psz_text = strdup( "" );
1610 int i_old = 0;
1611 int i_firstline = 1;
1613 for( ;; )
1615 int t; /* Time */
1617 const char *s = TextGetLine( txt );
1619 if( !s )
1621 free( psz_text );
1622 return VLC_EGENERIC;
1625 /* Data Lines */
1626 if( sscanf (s, "-->> %d", &t) == 1)
1628 p_subtitle->i_start = (int64_t)t; /* * FPS*/
1629 p_subtitle->i_stop = -1;
1631 /* Starting of a subtitle */
1632 if( i_firstline )
1634 i_firstline = 0;
1636 /* We have been too far: end of the subtitle, begin of next */
1637 else
1639 TextPreviousLine( txt );
1640 break;
1643 /* Text Lines */
1644 else
1646 i_old = strlen( psz_text ) + 1;
1647 psz_text = realloc_or_free( psz_text, i_old + strlen( s ) + 1 );
1648 if( !psz_text )
1649 return VLC_ENOMEM;
1650 strcat( psz_text, s );
1651 strcat( psz_text, "\n" );
1652 if( txt->i_line == txt->i_line_count )
1653 break;
1656 p_subtitle->psz_text = psz_text;
1657 return VLC_SUCCESS;
1660 static int ParsePJS(vlc_object_t *p_obj, subs_properties_t *p_props,
1661 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1663 VLC_UNUSED(p_obj);
1664 VLC_UNUSED(p_props);
1665 VLC_UNUSED( i_idx );
1667 char *psz_text;
1668 int i;
1670 for( ;; )
1672 const char *s = TextGetLine( txt );
1673 int t1, t2;
1675 if( !s )
1676 return VLC_EGENERIC;
1678 psz_text = malloc( strlen(s) + 1 );
1679 if( !psz_text )
1680 return VLC_ENOMEM;
1682 /* Data Lines */
1683 if( sscanf (s, "%d,%d,\"%[^\n\r]", &t1, &t2, psz_text ) == 3 )
1685 /* 1/10th of second ? Frame based ? FIXME */
1686 p_subtitle->i_start = 10 * t1;
1687 p_subtitle->i_stop = 10 * t2;
1688 /* Remove latest " */
1689 psz_text[ strlen(psz_text) - 1 ] = '\0';
1691 break;
1693 free( psz_text );
1696 /* replace | by \n */
1697 for( i = 0; psz_text[i] != '\0'; i++ )
1699 if( psz_text[i] == '|' )
1700 psz_text[i] = '\n';
1703 p_subtitle->psz_text = psz_text;
1704 msg_Dbg( p_obj, "%s", psz_text );
1705 return VLC_SUCCESS;
1708 static int ParseMPSub( vlc_object_t *p_obj, subs_properties_t *p_props,
1709 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1711 VLC_UNUSED( i_idx );
1713 char *psz_text = strdup( "" );
1715 if( !p_props->mpsub.b_inited )
1717 p_props->mpsub.f_total = 0.0;
1718 p_props->mpsub.f_factor = 0.0;
1720 p_props->mpsub.b_inited = true;
1723 for( ;; )
1725 char p_dummy;
1726 char *psz_temp;
1728 const char *s = TextGetLine( txt );
1729 if( !s )
1731 free( psz_text );
1732 return VLC_EGENERIC;
1735 if( strstr( s, "FORMAT" ) )
1737 if( sscanf (s, "FORMAT=TIM%c", &p_dummy ) == 1 && p_dummy == 'E')
1739 p_props->mpsub.f_factor = 100.0;
1740 break;
1743 psz_temp = malloc( strlen(s) );
1744 if( !psz_temp )
1746 free( psz_text );
1747 return VLC_ENOMEM;
1750 if( sscanf( s, "FORMAT=%[^\r\n]", psz_temp ) )
1752 float f_fps = us_strtof( psz_temp, NULL );
1754 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-fps" ) <= 0.f )
1755 var_SetFloat( p_obj, "sub-fps", f_fps );
1757 p_props->mpsub.f_factor = 1.f;
1758 free( psz_temp );
1759 break;
1761 free( psz_temp );
1764 /* Data Lines */
1765 float f1 = us_strtof( s, &psz_temp );
1766 if( *psz_temp )
1768 float f2 = us_strtof( psz_temp, NULL );
1769 p_props->mpsub.f_total += f1 * p_props->mpsub.f_factor;
1770 p_subtitle->i_start = llroundf(10000.f * p_props->mpsub.f_total);
1771 p_props->mpsub.f_total += f2 * p_props->mpsub.f_factor;
1772 p_subtitle->i_stop = llroundf(10000.f * p_props->mpsub.f_total);
1773 break;
1777 for( ;; )
1779 const char *s = TextGetLine( txt );
1781 if( !s )
1783 free( psz_text );
1784 return VLC_EGENERIC;
1787 size_t i_len = strlen( s );
1788 if( i_len == 0 )
1789 break;
1791 size_t i_old = strlen( psz_text );
1793 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1794 if( !psz_text )
1795 return VLC_ENOMEM;
1797 strcat( psz_text, s );
1798 strcat( psz_text, "\n" );
1801 p_subtitle->psz_text = psz_text;
1802 return VLC_SUCCESS;
1805 static int ParseJSS( vlc_object_t *p_obj, subs_properties_t *p_props,
1806 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1808 VLC_UNUSED( i_idx );
1809 char *psz_text, *psz_orig;
1810 char *psz_text2, *psz_orig2;
1812 if( !p_props->jss.b_inited )
1814 p_props->jss.i_comment = 0;
1815 p_props->jss.i_time_resolution = 30;
1816 p_props->jss.i_time_shift = 0;
1818 p_props->jss.b_inited = true;
1821 /* Parse the main lines */
1822 for( ;; )
1824 const char *s = TextGetLine( txt );
1825 if( !s )
1826 return VLC_EGENERIC;
1828 size_t line_length = strlen( s );
1829 psz_orig = malloc( line_length + 1 );
1830 if( !psz_orig )
1831 return VLC_ENOMEM;
1832 psz_text = psz_orig;
1834 /* Complete time lines */
1835 int h1, h2, m1, m2, s1, s2, f1, f2;
1836 if( sscanf( s, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1837 &h1, &m1, &s1, &f1, &h2, &m2, &s2, &f2, psz_text ) == 9 )
1839 p_subtitle->i_start = ( ( (int64_t) h1 *3600 + m1 * 60 + s1 ) +
1840 (int64_t)( ( f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) )
1841 * 1000000;
1842 p_subtitle->i_stop = ( ( (int64_t) h2 *3600 + m2 * 60 + s2 ) +
1843 (int64_t)( ( f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) )
1844 * 1000000;
1845 break;
1847 /* Short time lines */
1848 else if( sscanf( s, "@%d @%d %[^\n\r]", &f1, &f2, psz_text ) == 3 )
1850 p_subtitle->i_start = ((int64_t)
1851 ( f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution * 1000000.0 );
1852 p_subtitle->i_stop = ((int64_t)
1853 ( f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution * 1000000.0 );
1854 break;
1856 /* General Directive lines */
1857 /* Only TIME and SHIFT are supported so far */
1858 else if( s[0] == '#' )
1860 int h = 0, m =0, sec = 1, f = 1;
1861 unsigned shift = 1;
1862 int inv = 1;
1864 strcpy( psz_text, s );
1866 switch( toupper( (unsigned char)psz_text[1] ) )
1868 case 'S':
1869 shift = isalpha( (unsigned char)psz_text[2] ) ? 6 : 2 ;
1870 if ( shift > line_length )
1871 break;
1873 if( sscanf( &psz_text[shift], "%d", &h ) )
1875 /* Negative shifting */
1876 if( h < 0 )
1878 h *= -1;
1879 inv = -1;
1882 if( sscanf( &psz_text[shift], "%*d:%d", &m ) )
1884 if( sscanf( &psz_text[shift], "%*d:%*d:%d", &sec ) )
1886 sscanf( &psz_text[shift], "%*d:%*d:%*d.%d", &f );
1888 else
1890 h = 0;
1891 sscanf( &psz_text[shift], "%d:%d.%d",
1892 &m, &sec, &f );
1893 m *= inv;
1896 else
1898 h = m = 0;
1899 sscanf( &psz_text[shift], "%d.%d", &sec, &f);
1900 sec *= inv;
1902 p_props->jss.i_time_shift = ( ( h * 3600 + m * 60 + sec )
1903 * p_props->jss.i_time_resolution + f ) * inv;
1905 break;
1907 case 'T':
1908 shift = isalpha( (unsigned char)psz_text[2] ) ? 8 : 2 ;
1909 if ( shift > line_length )
1910 break;
1912 sscanf( &psz_text[shift], "%d", &p_props->jss.i_time_resolution );
1913 if( !p_props->jss.i_time_resolution )
1914 p_props->jss.i_time_resolution = 30;
1915 break;
1917 free( psz_orig );
1918 continue;
1920 else
1921 /* Unkown type line, probably a comment */
1923 free( psz_orig );
1924 continue;
1928 while( psz_text[ strlen( psz_text ) - 1 ] == '\\' )
1930 const char *s2 = TextGetLine( txt );
1932 if( !s2 )
1934 free( psz_orig );
1935 return VLC_EGENERIC;
1938 size_t i_len = strlen( s2 );
1939 if( i_len == 0 )
1940 break;
1942 size_t i_old = strlen( psz_text );
1944 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 );
1945 if( !psz_text )
1946 return VLC_ENOMEM;
1948 psz_orig = psz_text;
1949 strcat( psz_text, s2 );
1952 /* Skip the blanks */
1953 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1955 /* Parse the directives */
1956 if( isalpha( (unsigned char)*psz_text ) || *psz_text == '[' )
1958 while( *psz_text && *psz_text != ' ' )
1959 ++psz_text;
1961 /* Directives are NOT parsed yet */
1962 /* This has probably a better place in a decoder ? */
1963 /* directive = malloc( strlen( psz_text ) + 1 );
1964 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1967 /* Skip the blanks after directives */
1968 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1970 /* Clean all the lines from inline comments and other stuffs */
1971 psz_orig2 = calloc( strlen( psz_text) + 1, 1 );
1972 psz_text2 = psz_orig2;
1974 for( ; *psz_text != '\0' && *psz_text != '\n' && *psz_text != '\r'; )
1976 switch( *psz_text )
1978 case '{':
1979 p_props->jss.i_comment++;
1980 break;
1981 case '}':
1982 if( p_props->jss.i_comment )
1984 p_props->jss.i_comment = 0;
1985 if( (*(psz_text + 1 ) ) == ' ' ) psz_text++;
1987 break;
1988 case '~':
1989 if( !p_props->jss.i_comment )
1991 *psz_text2 = ' ';
1992 psz_text2++;
1994 break;
1995 case ' ':
1996 case '\t':
1997 if( (*(psz_text + 1 ) ) == ' ' || (*(psz_text + 1 ) ) == '\t' )
1998 break;
1999 if( !p_props->jss.i_comment )
2001 *psz_text2 = ' ';
2002 psz_text2++;
2004 break;
2005 case '\\':
2006 if( (*(psz_text + 1 ) ) == 'n' )
2008 *psz_text2 = '\n';
2009 psz_text++;
2010 psz_text2++;
2011 break;
2013 if( ( toupper((unsigned char)*(psz_text + 1 ) ) == 'C' ) ||
2014 ( toupper((unsigned char)*(psz_text + 1 ) ) == 'F' ) )
2016 psz_text++;
2017 break;
2019 if( (*(psz_text + 1 ) ) == 'B' || (*(psz_text + 1 ) ) == 'b' ||
2020 (*(psz_text + 1 ) ) == 'I' || (*(psz_text + 1 ) ) == 'i' ||
2021 (*(psz_text + 1 ) ) == 'U' || (*(psz_text + 1 ) ) == 'u' ||
2022 (*(psz_text + 1 ) ) == 'D' || (*(psz_text + 1 ) ) == 'N' )
2024 psz_text++;
2025 break;
2027 if( (*(psz_text + 1 ) ) == '~' || (*(psz_text + 1 ) ) == '{' ||
2028 (*(psz_text + 1 ) ) == '\\' )
2029 psz_text++;
2030 else if( ( *(psz_text + 1 ) == '\r' || *(psz_text + 1 ) == '\n' ) &&
2031 *(psz_text + 1 ) != '\0' )
2033 psz_text++;
2035 break;
2036 default:
2037 if( !p_props->jss.i_comment )
2039 *psz_text2 = *psz_text;
2040 psz_text2++;
2043 psz_text++;
2046 p_subtitle->psz_text = psz_orig2;
2047 msg_Dbg( p_obj, "%s", p_subtitle->psz_text );
2048 free( psz_orig );
2049 return VLC_SUCCESS;
2052 static int ParsePSB( vlc_object_t *p_obj, subs_properties_t *p_props,
2053 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2055 VLC_UNUSED(p_obj);
2056 VLC_UNUSED(p_props);
2057 VLC_UNUSED( i_idx );
2059 char *psz_text;
2060 int i;
2062 for( ;; )
2064 int h1, m1, s1;
2065 int h2, m2, s2;
2066 const char *s = TextGetLine( txt );
2068 if( !s )
2069 return VLC_EGENERIC;
2071 psz_text = malloc( strlen( s ) + 1 );
2072 if( !psz_text )
2073 return VLC_ENOMEM;
2075 if( sscanf( s, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2076 &h1, &m1, &s1, &h2, &m2, &s2, psz_text ) == 7 )
2078 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
2079 (int64_t)m1 * 60*1000 +
2080 (int64_t)s1 * 1000 ) * 1000;
2081 p_subtitle->i_stop = ( (int64_t)h2 * 3600*1000 +
2082 (int64_t)m2 * 60*1000 +
2083 (int64_t)s2 * 1000 ) * 1000;
2084 break;
2086 free( psz_text );
2089 /* replace | by \n */
2090 for( i = 0; psz_text[i] != '\0'; i++ )
2092 if( psz_text[i] == '|' )
2093 psz_text[i] = '\n';
2095 p_subtitle->psz_text = psz_text;
2096 return VLC_SUCCESS;
2099 static int64_t ParseRealTime( char *psz, int *h, int *m, int *s, int *f )
2101 if( *psz == '\0' ) return 0;
2102 if( sscanf( psz, "%d:%d:%d.%d", h, m, s, f ) == 4 ||
2103 sscanf( psz, "%d:%d.%d", m, s, f ) == 3 ||
2104 sscanf( psz, "%d.%d", s, f ) == 2 ||
2105 sscanf( psz, "%d:%d", m, s ) == 2 ||
2106 sscanf( psz, "%d", s ) == 1 )
2108 return (int64_t)((( *h * 60 + *m ) * 60 ) + *s ) * 1000 * 1000
2109 + (int64_t)*f * 10 * 1000;
2111 else return VLC_EGENERIC;
2114 static int ParseRealText( vlc_object_t *p_obj, subs_properties_t *p_props,
2115 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2117 VLC_UNUSED(p_obj);
2118 VLC_UNUSED(p_props);
2119 VLC_UNUSED( i_idx );
2120 char *psz_text = NULL;
2122 for( ;; )
2124 int h1 = 0, m1 = 0, s1 = 0, f1 = 0;
2125 int h2 = 0, m2 = 0, s2 = 0, f2 = 0;
2126 const char *s = TextGetLine( txt );
2127 free( psz_text );
2129 if( !s )
2130 return VLC_EGENERIC;
2132 psz_text = malloc( strlen( s ) + 1 );
2133 if( !psz_text )
2134 return VLC_ENOMEM;
2136 /* Find the good begining. This removes extra spaces at the beginning
2137 of the line.*/
2138 char *psz_temp = strcasestr( s, "<time");
2139 if( psz_temp != NULL )
2141 char psz_end[12], psz_begin[12];
2142 /* Line has begin and end */
2143 if( ( sscanf( psz_temp,
2144 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2145 psz_begin, psz_end, psz_text) != 3 ) &&
2146 /* Line has begin and no end */
2147 ( sscanf( psz_temp,
2148 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2149 psz_begin, psz_text ) != 2) )
2150 /* Line is not recognized */
2152 continue;
2155 /* Get the times */
2156 int64_t i_time = ParseRealTime( psz_begin, &h1, &m1, &s1, &f1 );
2157 p_subtitle->i_start = i_time >= 0 ? i_time : 0;
2159 i_time = ParseRealTime( psz_end, &h2, &m2, &s2, &f2 );
2160 p_subtitle->i_stop = i_time >= 0 ? i_time : -1;
2161 break;
2165 /* Get the following Lines */
2166 for( ;; )
2168 const char *s = TextGetLine( txt );
2170 if( !s )
2172 free( psz_text );
2173 return VLC_EGENERIC;
2176 size_t i_len = strlen( s );
2177 if( i_len == 0 ) break;
2179 if( strcasestr( s, "<time" ) ||
2180 strcasestr( s, "<clear/") )
2182 TextPreviousLine( txt );
2183 break;
2186 size_t i_old = strlen( psz_text );
2188 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2189 if( !psz_text )
2190 return VLC_ENOMEM;
2192 strcat( psz_text, s );
2193 strcat( psz_text, "\n" );
2196 /* Remove the starting ">" that remained after the sscanf */
2197 memmove( &psz_text[0], &psz_text[1], strlen( psz_text ) );
2199 p_subtitle->psz_text = psz_text;
2201 return VLC_SUCCESS;
2204 static int ParseDKS( vlc_object_t *p_obj, subs_properties_t *p_props,
2205 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2207 VLC_UNUSED(p_obj);
2208 VLC_UNUSED(p_props);
2209 VLC_UNUSED( i_idx );
2211 char *psz_text;
2213 for( ;; )
2215 int h1, m1, s1;
2216 int h2, m2, s2;
2217 char *s = TextGetLine( txt );
2219 if( !s )
2220 return VLC_EGENERIC;
2222 psz_text = malloc( strlen( s ) + 1 );
2223 if( !psz_text )
2224 return VLC_ENOMEM;
2226 if( sscanf( s, "[%d:%d:%d]%[^\r\n]",
2227 &h1, &m1, &s1, psz_text ) == 4 )
2229 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
2230 (int64_t)m1 * 60*1000 +
2231 (int64_t)s1 * 1000 ) * 1000;
2233 s = TextGetLine( txt );
2234 if( !s )
2236 free( psz_text );
2237 return VLC_EGENERIC;
2240 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2241 p_subtitle->i_stop = ( (int64_t)h2 * 3600*1000 +
2242 (int64_t)m2 * 60*1000 +
2243 (int64_t)s2 * 1000 ) * 1000;
2244 else
2245 p_subtitle->i_stop = -1;
2246 break;
2248 free( psz_text );
2251 /* replace [br] by \n */
2252 char *p;
2253 while( ( p = strstr( psz_text, "[br]" ) ) )
2255 *p++ = '\n';
2256 memmove( p, &p[3], strlen(&p[3])+1 );
2259 p_subtitle->psz_text = psz_text;
2260 return VLC_SUCCESS;
2263 static int ParseSubViewer1( vlc_object_t *p_obj, subs_properties_t *p_props,
2264 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2266 VLC_UNUSED(p_obj);
2267 VLC_UNUSED(p_props);
2268 VLC_UNUSED( i_idx );
2269 char *psz_text;
2271 for( ;; )
2273 int h1, m1, s1;
2274 int h2, m2, s2;
2275 char *s = TextGetLine( txt );
2277 if( !s )
2278 return VLC_EGENERIC;
2280 if( sscanf( s, "[%d:%d:%d]", &h1, &m1, &s1 ) == 3 )
2282 p_subtitle->i_start = ( (int64_t)h1 * 3600*1000 +
2283 (int64_t)m1 * 60*1000 +
2284 (int64_t)s1 * 1000 ) * 1000;
2286 s = TextGetLine( txt );
2287 if( !s )
2288 return VLC_EGENERIC;
2290 psz_text = strdup( s );
2291 if( !psz_text )
2292 return VLC_ENOMEM;
2294 s = TextGetLine( txt );
2295 if( !s )
2297 free( psz_text );
2298 return VLC_EGENERIC;
2301 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2302 p_subtitle->i_stop = ( (int64_t)h2 * 3600*1000 +
2303 (int64_t)m2 * 60*1000 +
2304 (int64_t)s2 * 1000 ) * 1000;
2305 else
2306 p_subtitle->i_stop = -1;
2308 break;
2312 p_subtitle->psz_text = psz_text;
2314 return VLC_SUCCESS;
2317 static int ParseCommonSBV( vlc_object_t *p_obj, subs_properties_t *p_props,
2318 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2320 VLC_UNUSED(p_obj);
2321 VLC_UNUSED( i_idx );
2322 VLC_UNUSED( p_props );
2323 char *psz_text;
2325 for( ;; )
2327 const char *s = TextGetLine( txt );
2328 int h1 = 0, m1 = 0, s1 = 0, d1 = 0;
2329 int h2 = 0, m2 = 0, s2 = 0, d2 = 0;
2331 if( !s )
2332 return VLC_EGENERIC;
2334 if( sscanf( s,"%d:%d:%d.%d,%d:%d:%d.%d",
2335 &h1, &m1, &s1, &d1,
2336 &h2, &m2, &s2, &d2 ) == 8 )
2338 p_subtitle->i_start = ( (int64_t)h1 * 3600 * 1000 +
2339 (int64_t)m1 * 60 * 1000 +
2340 (int64_t)s1 * 1000 +
2341 (int64_t)d1 ) * 1000;
2343 p_subtitle->i_stop = ( (int64_t)h2 * 3600 * 1000 +
2344 (int64_t)m2 * 60 * 1000 +
2345 (int64_t)s2 * 1000 +
2346 (int64_t)d2 ) * 1000;
2347 if( p_subtitle->i_start < p_subtitle->i_stop )
2348 break;
2352 /* Now read text until an empty line */
2353 psz_text = strdup("");
2354 if( !psz_text )
2355 return VLC_ENOMEM;
2357 for( ;; )
2359 const char *s = TextGetLine( txt );
2360 size_t i_len;
2361 size_t i_old;
2363 i_len = s ? strlen( s ) : 0;
2364 if( i_len <= 0 )
2366 p_subtitle->psz_text = psz_text;
2367 return VLC_SUCCESS;
2370 i_old = strlen( psz_text );
2371 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2372 if( !psz_text )
2373 return VLC_ENOMEM;
2375 strcat( psz_text, s );
2376 strcat( psz_text, "\n" );
2380 static int ParseSCC( vlc_object_t *p_obj, subs_properties_t *p_props,
2381 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2383 VLC_UNUSED(p_obj);
2384 VLC_UNUSED( i_idx );
2385 VLC_UNUSED( p_props );
2387 static const struct rates
2389 unsigned val;
2390 vlc_rational_t rate;
2391 bool b_drop_allowed;
2392 } framerates[] = {
2393 { 2398, { 24000, 1001 }, false },
2394 { 2400, { 24, 1 }, false },
2395 { 2500, { 25, 1 }, false },
2396 { 2997, { 30000, 1001 }, true }, /* encoding rate */
2397 { 3000, { 30, 1 }, false },
2398 { 5000, { 50, 1 }, false },
2399 { 5994, { 60000, 1001 }, true },
2400 { 6000, { 60, 1 }, false },
2402 const struct rates *p_rate = &framerates[3];
2403 float f_fps = var_GetFloat( p_obj, "sub-fps" );
2404 if( f_fps > 1.0 )
2406 for( size_t i=0; i<ARRAY_SIZE(framerates); i++ )
2408 if( (unsigned)(f_fps * 100) == framerates[i].val )
2410 p_rate = &framerates[i];
2411 break;
2416 for( ;; )
2418 const char *psz_line = TextGetLine( txt );
2419 if( !psz_line )
2420 return VLC_EGENERIC;
2422 unsigned h, m, s, f;
2423 char c;
2424 if( sscanf( psz_line, "%u:%u:%u%c%u ", &h, &m, &s, &c, &f ) != 5 ||
2425 ( c != ':' && c != ';' ) )
2426 continue;
2428 /* convert everything to seconds */
2429 mtime_t i_frames = h * 3600 + m * 60 + s;
2431 if( c == ';' && p_rate->b_drop_allowed ) /* dropframe */
2433 /* convert to frame # to be accurate between inter drop drift
2434 * of 18 frames see http://andrewduncan.net/timecodes/ */
2435 const unsigned i_mins = h * 60 + m;
2436 i_frames = i_frames * p_rate[+1].rate.num + f
2437 - (p_rate[+1].rate.den * 2 * (i_mins - i_mins % 10));
2439 else
2441 /* convert to frame # at 29.97 */
2442 i_frames = i_frames * framerates[3].rate.num / framerates[3].rate.den + f;
2444 p_subtitle->i_start = VLC_TS_0 + i_frames * CLOCK_FREQ *
2445 p_rate->rate.den / p_rate->rate.num;
2446 p_subtitle->i_stop = -1;
2448 const char *psz_text = strchr( psz_line, '\t' );
2449 if( !psz_text && !(psz_text = strchr( psz_line, ' ' )) )
2450 continue;
2452 if ( psz_text[1] == '\0' )
2453 continue;
2455 p_subtitle->psz_text = strdup( psz_text + 1 );
2456 if( !p_subtitle->psz_text )
2457 return VLC_ENOMEM;
2459 break;
2462 return VLC_SUCCESS;
2465 /* Matches filename.xx.srt */
2466 static char * get_language_from_filename( const char * psz_sub_file )
2468 char *psz_ret = NULL;
2469 char *psz_tmp, *psz_language_begin;
2471 if( !psz_sub_file ) return NULL;
2472 char *psz_work = strdup( psz_sub_file );
2474 /* Removing extension, but leaving the dot */
2475 psz_tmp = strrchr( psz_work, '.' );
2476 if( psz_tmp )
2478 psz_tmp[0] = '\0';
2479 psz_language_begin = strrchr( psz_work, '.' );
2480 if( psz_language_begin )
2481 psz_ret = strdup(++psz_language_begin);
2482 psz_tmp[0] = '.';
2485 free( psz_work );
2486 return psz_ret;