demux: mp4: use static mapping table per layout
[vlc.git] / modules / demux / subtitle.c
blob8fb3e2c451c99d05c15be30056880bca2b577f33
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_TYPE_LONGTEXT \
52 N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
53 #define SUB_DESCRIPTION_LONGTEXT \
54 N_("Override the default track description.")
56 static const char *const ppsz_sub_type[] =
58 "auto", "microdvd", "subrip", "subviewer", "ssa1",
59 "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
60 "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
61 "subviewer1", "sbv"
64 vlc_module_begin ()
65 set_shortname( N_("Subtitles"))
66 set_description( N_("Text subtitle parser") )
67 set_capability( "demux", 0 )
68 set_category( CAT_INPUT )
69 set_subcategory( SUBCAT_INPUT_DEMUX )
70 add_string( "sub-type", "auto", N_("Subtitle format"),
71 SUB_TYPE_LONGTEXT, true )
72 change_string_list( ppsz_sub_type, ppsz_sub_type )
73 add_string( "sub-description", NULL, N_("Subtitle description"),
74 SUB_DESCRIPTION_LONGTEXT, true )
75 set_callbacks( Open, Close )
77 add_shortcut( "subtitle" )
78 vlc_module_end ()
80 /*****************************************************************************
81 * Prototypes:
82 *****************************************************************************/
83 enum subtitle_type_e
85 SUB_TYPE_UNKNOWN = -1,
86 SUB_TYPE_MICRODVD,
87 SUB_TYPE_SUBRIP,
88 SUB_TYPE_SSA1,
89 SUB_TYPE_SSA2_4,
90 SUB_TYPE_ASS,
91 SUB_TYPE_VPLAYER,
92 SUB_TYPE_SAMI,
93 SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
94 SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
95 SUB_TYPE_MPL2,
96 SUB_TYPE_AQT,
97 SUB_TYPE_PJS,
98 SUB_TYPE_MPSUB,
99 SUB_TYPE_JACOSUB,
100 SUB_TYPE_PSB,
101 SUB_TYPE_RT,
102 SUB_TYPE_DKS,
103 SUB_TYPE_SUBVIEW1, /* SUBVIEWER 1 - mplayer calls it subrip09,
104 and Gnome subtitles SubViewer 1.0 */
105 SUB_TYPE_SBV,
106 SUB_TYPE_SCC, /* Scenarist Closed Caption */
109 typedef struct
111 size_t i_line_count;
112 size_t i_line;
113 char **line;
114 } text_t;
116 static int TextLoad( text_t *, stream_t *s );
117 static void TextUnload( text_t * );
119 typedef struct
121 vlc_tick_t i_start;
122 vlc_tick_t i_stop;
124 char *psz_text;
125 } subtitle_t;
127 typedef struct
129 enum subtitle_type_e i_type;
130 vlc_tick_t i_microsecperframe;
132 char *psz_header; /* SSA */
134 struct
136 bool b_inited;
138 int i_comment;
139 int i_time_resolution;
140 int i_time_shift;
141 } jss;
143 struct
145 bool b_inited;
147 float f_total;
148 int i_factor;
149 } mpsub;
151 struct
153 const char *psz_start;
154 } sami;
156 } subs_properties_t;
158 typedef struct
160 es_out_id_t *es;
161 bool b_slave;
162 bool b_first_time;
164 double f_rate;
165 vlc_tick_t i_next_demux_date;
167 struct
169 subtitle_t *p_array;
170 size_t i_count;
171 size_t i_current;
172 } subtitles;
174 vlc_tick_t i_length;
176 /* */
177 subs_properties_t props;
179 block_t * (*pf_convert)( const subtitle_t * );
180 } demux_sys_t;
182 static int ParseMicroDvd ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
183 static int ParseSubRip ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
184 static int ParseSubViewer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
185 static int ParseSSA ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
186 static int ParseVplayer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
187 static int ParseSami ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
188 static int ParseDVDSubtitle( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
189 static int ParseMPL2 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
190 static int ParseAQT ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
191 static int ParsePJS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
192 static int ParseMPSub ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
193 static int ParseJSS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
194 static int ParsePSB ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
195 static int ParseRealText ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
196 static int ParseDKS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
197 static int ParseSubViewer1 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
198 static int ParseCommonSBV ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
199 static int ParseSCC ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
201 static const struct
203 const char *psz_type_name;
204 int i_type;
205 const char *psz_name;
206 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
207 } sub_read_subtitle_function [] =
209 { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", ParseMicroDvd },
210 { "subrip", SUB_TYPE_SUBRIP, "SubRIP", ParseSubRip },
211 { "subviewer", SUB_TYPE_SUBVIEWER, "SubViewer", ParseSubViewer },
212 { "ssa1", SUB_TYPE_SSA1, "SSA-1", ParseSSA },
213 { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4", ParseSSA },
214 { "ass", SUB_TYPE_ASS, "SSA/ASS", ParseSSA },
215 { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", ParseVplayer },
216 { "sami", SUB_TYPE_SAMI, "SAMI", ParseSami },
217 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle },
218 { "mpl2", SUB_TYPE_MPL2, "MPL2", ParseMPL2 },
219 { "aqt", SUB_TYPE_AQT, "AQTitle", ParseAQT },
220 { "pjs", SUB_TYPE_PJS, "PhoenixSub", ParsePJS },
221 { "mpsub", SUB_TYPE_MPSUB, "MPSub", ParseMPSub },
222 { "jacosub", SUB_TYPE_JACOSUB, "JacoSub", ParseJSS },
223 { "psb", SUB_TYPE_PSB, "PowerDivx", ParsePSB },
224 { "realtext", SUB_TYPE_RT, "RealText", ParseRealText },
225 { "dks", SUB_TYPE_DKS, "DKS", ParseDKS },
226 { "subviewer1", SUB_TYPE_SUBVIEW1, "Subviewer 1", ParseSubViewer1 },
227 { "sbv", SUB_TYPE_SBV, "SBV", ParseCommonSBV },
228 { "scc", SUB_TYPE_SCC, "SCC", ParseSCC },
229 { NULL, SUB_TYPE_UNKNOWN, "Unknown", NULL }
231 /* When adding support for more formats, be sure to add their file extension
232 * to src/input/subtitles.c to enable auto-detection.
235 static int Demux( demux_t * );
236 static int Control( demux_t *, int, va_list );
238 static void Fix( demux_t * );
239 static char * get_language_from_filename( const char * );
241 /*****************************************************************************
242 * Decoder format output function
243 *****************************************************************************/
245 static block_t *ToTextBlock( const subtitle_t *p_subtitle )
247 block_t *p_block;
248 size_t i_len = strlen( p_subtitle->psz_text ) + 1;
250 if( i_len <= 1 || !(p_block = block_Alloc( i_len )) )
251 return NULL;
253 memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );
255 return p_block;
258 static block_t *ToEIA608Block( const subtitle_t *p_subtitle )
260 block_t *p_block;
261 const size_t i_len = strlen( p_subtitle->psz_text );
262 const size_t i_block = (1 + i_len / 5) * 3;
264 if( i_len < 4 || !(p_block = block_Alloc( i_block )) )
265 return NULL;
267 p_block->i_buffer = 0;
269 char *saveptr = NULL;
270 char *psz_tok = strtok_r( p_subtitle->psz_text, " ", &saveptr );
271 unsigned a, b;
272 while( psz_tok &&
273 sscanf( psz_tok, "%2x%2x", &a, &b ) == 2 &&
274 i_block - p_block->i_buffer >= 3 )
276 uint8_t *p_data = &p_block->p_buffer[p_block->i_buffer];
277 p_data[0] = 0xFC;
278 p_data[1] = a;
279 p_data[2] = b;
280 p_block->i_buffer += 3;
281 psz_tok = strtok_r( NULL, " ", &saveptr );
284 return p_block;
287 /*****************************************************************************
288 * Module initializer
289 *****************************************************************************/
290 static int Open ( vlc_object_t *p_this )
292 demux_t *p_demux = (demux_t*)p_this;
293 demux_sys_t *p_sys;
294 es_format_t fmt;
295 float f_fps;
296 char *psz_type;
297 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
299 if( !p_demux->obj.force )
301 msg_Dbg( p_demux, "subtitle demux discarded" );
302 return VLC_EGENERIC;
305 p_demux->pf_demux = Demux;
306 p_demux->pf_control = Control;
307 p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
308 if( p_sys == NULL )
309 return VLC_ENOMEM;
311 p_sys->b_slave = false;
312 p_sys->b_first_time = true;
313 p_sys->i_next_demux_date = 0;
314 p_sys->f_rate = 1.0;
316 p_sys->pf_convert = ToTextBlock;
318 p_sys->subtitles.i_current= 0;
319 p_sys->subtitles.i_count = 0;
320 p_sys->subtitles.p_array = NULL;
322 p_sys->props.psz_header = NULL;
323 p_sys->props.i_microsecperframe = VLC_TICK_FROM_MS(40);
324 p_sys->props.jss.b_inited = false;
325 p_sys->props.mpsub.b_inited = false;
326 p_sys->props.sami.psz_start = NULL;
328 /* Get the FPS */
329 f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
330 if( f_fps >= 1.f )
332 p_sys->props.i_microsecperframe = llroundf( (float)CLOCK_FREQ / f_fps );
333 msg_Dbg( p_demux, "Override subtitle fps %f", (double) f_fps );
336 /* Get or probe the type */
337 p_sys->props.i_type = SUB_TYPE_UNKNOWN;
338 psz_type = var_CreateGetString( p_demux, "sub-type" );
339 if( psz_type && *psz_type )
341 for( int i = 0; ; i++ )
343 if( sub_read_subtitle_function[i].psz_type_name == NULL )
344 break;
346 if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
347 psz_type ) )
349 p_sys->props.i_type = sub_read_subtitle_function[i].i_type;
350 break;
354 free( psz_type );
356 #ifndef NDEBUG
357 const uint64_t i_start_pos = vlc_stream_Tell( p_demux->s );
358 #endif
360 size_t i_peek;
361 const uint8_t *p_peek;
362 if( vlc_stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
364 free( p_sys );
365 return VLC_EGENERIC;
368 enum
370 UTF8BOM,
371 UTF16LE,
372 UTF16BE,
373 NOBOM,
374 } e_bom = NOBOM;
375 const char *psz_bom = NULL;
377 i_peek = 4096;
378 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
379 if( !memcmp( p_peek, "\xEF\xBB\xBF", 3 ) )
381 e_bom = UTF8BOM;
382 psz_bom = "UTF-8";
384 else if( !memcmp( p_peek, "\xFF\xFE", 2 ) )
386 e_bom = UTF16LE;
387 psz_bom = "UTF-16LE";
388 i_peek *= 2;
390 else if( !memcmp( p_peek, "\xFE\xFF", 2 ) )
392 e_bom = UTF16BE;
393 psz_bom = "UTF-16BE";
394 i_peek *= 2;
397 if( e_bom != NOBOM )
398 msg_Dbg( p_demux, "detected %s Byte Order Mark", psz_bom );
400 i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_peek );
401 if( unlikely(i_peek < 16) )
403 free( p_sys );
404 return VLC_EGENERIC;
407 stream_t *p_probestream = NULL;
408 if( e_bom != UTF8BOM && e_bom != NOBOM )
410 if( i_peek > 16 )
412 char *p_outbuf = FromCharset( psz_bom, p_peek, i_peek );
413 if( p_outbuf != NULL )
414 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *)p_outbuf,
415 strlen( p_outbuf ),
416 false ); /* free p_outbuf on release */
419 else
421 const size_t i_skip = (e_bom == UTF8BOM) ? 3 : 0;
422 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *) &p_peek[i_skip],
423 i_peek - i_skip, true );
426 if( p_probestream == NULL )
428 free( p_sys );
429 return VLC_EGENERIC;
432 /* Probe if unknown type */
433 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
435 int i_try;
436 char *s = NULL;
438 msg_Dbg( p_demux, "autodetecting subtitle format" );
439 for( i_try = 0; i_try < 256; i_try++ )
441 int i_dummy;
442 char p_dummy;
444 if( (s = vlc_stream_ReadLine( p_probestream ) ) == NULL )
445 break;
447 if( strcasestr( s, "<SAMI>" ) )
449 p_sys->props.i_type = SUB_TYPE_SAMI;
450 break;
452 else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
453 sscanf( s, "{%d}{}", &i_dummy ) == 1)
455 p_sys->props.i_type = SUB_TYPE_MICRODVD;
456 break;
458 else if( sscanf( s, "%d:%d:%d,%d --> %d:%d:%d,%d",
459 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
460 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
461 sscanf( s, "%d:%d:%d --> %d:%d:%d,%d",
462 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
463 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
464 sscanf( s, "%d:%d:%d,%d --> %d:%d:%d",
465 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
466 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
467 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d.%d",
468 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
469 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
470 sscanf( s, "%d:%d:%d --> %d:%d:%d.%d",
471 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
472 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
473 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d",
474 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
475 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
476 sscanf( s, "%d:%d:%d --> %d:%d:%d",
477 &i_dummy,&i_dummy,&i_dummy,
478 &i_dummy,&i_dummy,&i_dummy ) == 6 )
480 p_sys->props.i_type = SUB_TYPE_SUBRIP;
481 break;
483 else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
485 p_sys->props.i_type = SUB_TYPE_SSA1;
486 break;
488 else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
490 p_sys->props.i_type = SUB_TYPE_ASS;
491 break;
493 else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
495 p_sys->props.i_type = SUB_TYPE_SSA2_4;
496 break;
498 else if( !strncasecmp( s, "Dialogue: Marked", 16 ) )
500 p_sys->props.i_type = SUB_TYPE_SSA2_4;
501 break;
503 else if( !strncasecmp( s, "Dialogue:", 9 ) )
505 p_sys->props.i_type = SUB_TYPE_ASS;
506 break;
508 else if( strcasestr( s, "[INFORMATION]" ) )
510 p_sys->props.i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
511 break;
513 else if( sscanf( s, "%d:%d:%d.%d %d:%d:%d",
514 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
515 &i_dummy, &i_dummy, &i_dummy ) == 7 ||
516 sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2)
518 p_sys->props.i_type = SUB_TYPE_JACOSUB;
519 break;
521 else if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
522 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
523 &i_dummy, &i_dummy, &i_dummy, &i_dummy ) == 8 )
525 p_sys->props.i_type = SUB_TYPE_SBV;
526 break;
528 else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
529 sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
531 p_sys->props.i_type = SUB_TYPE_VPLAYER;
532 break;
534 else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy,
535 &i_dummy, &i_dummy ) == 4 )
537 p_sys->props.i_type = SUB_TYPE_DVDSUBTITLE;
538 break;
540 else if( sscanf( s, "[%d:%d:%d]%c",
541 &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 )
543 p_sys->props.i_type = SUB_TYPE_DKS;
544 break;
546 else if( strstr( s, "*** START SCRIPT" ) )
548 p_sys->props.i_type = SUB_TYPE_SUBVIEW1;
549 break;
551 else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 ||
552 sscanf( s, "[%d][]", &i_dummy ) == 1)
554 p_sys->props.i_type = SUB_TYPE_MPL2;
555 break;
557 else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 ||
558 ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1
559 && p_dummy =='E' ) )
561 p_sys->props.i_type = SUB_TYPE_MPSUB;
562 break;
564 else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
566 p_sys->props.i_type = SUB_TYPE_AQT;
567 break;
569 else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
571 p_sys->props.i_type = SUB_TYPE_PJS;
572 break;
574 else if( sscanf( s, "{%d:%d:%d}",
575 &i_dummy, &i_dummy, &i_dummy ) == 3 )
577 p_sys->props.i_type = SUB_TYPE_PSB;
578 break;
580 else if( strcasestr( s, "<time" ) )
582 p_sys->props.i_type = SUB_TYPE_RT;
583 break;
585 else if( !strncasecmp( s, "WEBVTT",6 ) )
587 /* FAIL */
588 break;
590 else if( !strncasecmp( s, "Scenarist_SCC V1.0", 18 ) )
592 p_sys->props.i_type = SUB_TYPE_SCC;
593 p_sys->pf_convert = ToEIA608Block;
594 break;
597 free( s );
598 s = NULL;
601 free( s );
604 vlc_stream_Delete( p_probestream );
606 /* Quit on unknown subtitles */
607 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
609 #ifndef NDEBUG
610 /* Ensure it will work with non seekable streams */
611 assert( i_start_pos == vlc_stream_Tell( p_demux->s ) );
612 #endif
613 msg_Warn( p_demux, "failed to recognize subtitle type" );
614 free( p_sys );
615 return VLC_EGENERIC;
618 for( int i = 0; ; i++ )
620 if( sub_read_subtitle_function[i].i_type == p_sys->props.i_type )
622 msg_Dbg( p_demux, "detected %s format",
623 sub_read_subtitle_function[i].psz_name );
624 pf_read = sub_read_subtitle_function[i].pf_read;
625 break;
629 msg_Dbg( p_demux, "loading all subtitles..." );
631 if( e_bom == UTF8BOM && /* skip BOM */
632 vlc_stream_Read( p_demux->s, NULL, 3 ) != 3 )
634 Close( p_this );
635 return VLC_EGENERIC;
638 /* Load the whole file */
639 text_t txtlines;
640 TextLoad( &txtlines, p_demux->s );
642 /* Parse it */
643 for( size_t i_max = 0; i_max < SIZE_MAX - 500 * sizeof(subtitle_t); )
645 if( p_sys->subtitles.i_count >= i_max )
647 i_max += 500;
648 subtitle_t *p_realloc = realloc( p_sys->subtitles.p_array, sizeof(subtitle_t) * i_max );
649 if( p_realloc == NULL )
651 TextUnload( &txtlines );
652 Close( p_this );
653 return VLC_ENOMEM;
655 p_sys->subtitles.p_array = p_realloc;
658 if( pf_read( VLC_OBJECT(p_demux), &p_sys->props, &txtlines,
659 &p_sys->subtitles.p_array[p_sys->subtitles.i_count],
660 p_sys->subtitles.i_count ) )
661 break;
663 p_sys->subtitles.i_count++;
665 /* Unload */
666 TextUnload( &txtlines );
668 msg_Dbg(p_demux, "loaded %zu subtitles", p_sys->subtitles.i_count );
670 /* *** add subtitle ES *** */
671 if( p_sys->props.i_type == SUB_TYPE_SSA1 ||
672 p_sys->props.i_type == SUB_TYPE_SSA2_4 ||
673 p_sys->props.i_type == SUB_TYPE_ASS )
675 Fix( p_demux );
676 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
678 else if( p_sys->props.i_type == SUB_TYPE_SCC )
680 es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
681 fmt.subs.cc.i_reorder_depth = -1;
683 else
684 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
686 p_sys->subtitles.i_current = 0;
687 p_sys->i_length = 0;
688 if( p_sys->subtitles.i_count > 0 )
689 p_sys->i_length = p_sys->subtitles.p_array[p_sys->subtitles.i_count-1].i_stop;
691 /* Stupid language detection in the filename */
692 char * psz_language = get_language_from_filename( p_demux->psz_filepath );
694 if( psz_language )
696 fmt.psz_language = psz_language;
697 msg_Dbg( p_demux, "detected language %s of subtitle: %s", psz_language,
698 p_demux->psz_location );
701 char *psz_description = var_InheritString( p_demux, "sub-description" );
702 if( psz_description && *psz_description )
703 fmt.psz_description = psz_description;
704 else
705 free( psz_description );
706 if( p_sys->props.psz_header != NULL &&
707 (fmt.p_extra = strdup( p_sys->props.psz_header )) )
709 fmt.i_extra = strlen( p_sys->props.psz_header ) + 1;
712 p_sys->es = es_out_Add( p_demux->out, &fmt );
713 es_format_Clean( &fmt );
714 if( p_sys->es == NULL )
716 Close( p_this );
717 return VLC_EGENERIC;
720 return VLC_SUCCESS;
723 /*****************************************************************************
724 * Close: Close subtitle demux
725 *****************************************************************************/
726 static void Close( vlc_object_t *p_this )
728 demux_t *p_demux = (demux_t*)p_this;
729 demux_sys_t *p_sys = p_demux->p_sys;
731 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
732 free( p_sys->subtitles.p_array[i].psz_text );
733 free( p_sys->subtitles.p_array );
734 free( p_sys->props.psz_header );
736 free( p_sys );
739 static void
740 ResetCurrentIndex( demux_t *p_demux )
742 demux_sys_t *p_sys = p_demux->p_sys;
743 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
745 if( p_sys->subtitles.p_array[i].i_start * p_sys->f_rate >
746 p_sys->i_next_demux_date && i > 0 )
747 break;
748 p_sys->subtitles.i_current = i;
752 /*****************************************************************************
753 * Control:
754 *****************************************************************************/
755 static int Control( demux_t *p_demux, int i_query, va_list args )
757 demux_sys_t *p_sys = p_demux->p_sys;
758 double *pf, f;
760 switch( i_query )
762 case DEMUX_CAN_SEEK:
763 *va_arg( args, bool * ) = true;
764 return VLC_SUCCESS;
766 case DEMUX_GET_LENGTH:
767 *va_arg( args, vlc_tick_t * ) = p_sys->i_length;
768 return VLC_SUCCESS;
770 case DEMUX_GET_TIME:
771 *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_date;
772 return VLC_SUCCESS;
774 case DEMUX_SET_TIME:
776 p_sys->b_first_time = true;
777 p_sys->i_next_demux_date = va_arg( args, vlc_tick_t );
778 ResetCurrentIndex( p_demux );
779 return VLC_SUCCESS;
782 case DEMUX_GET_POSITION:
783 pf = va_arg( args, double * );
784 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
786 *pf = 1.0;
788 else if( p_sys->subtitles.i_count > 0 && p_sys->i_length )
790 *pf = p_sys->i_next_demux_date;
791 *pf /= p_sys->i_length;
793 else
795 *pf = 0.0;
797 return VLC_SUCCESS;
799 case DEMUX_SET_POSITION:
800 f = va_arg( args, double );
801 if( p_sys->subtitles.i_count && p_sys->i_length )
803 vlc_tick_t i64 = VLC_TICK_0 + f * p_sys->i_length;
804 return demux_Control( p_demux, DEMUX_SET_TIME, i64 );
806 break;
808 case DEMUX_CAN_CONTROL_RATE:
809 *va_arg( args, bool * ) = true;
810 return VLC_SUCCESS;
811 case DEMUX_SET_RATE:
812 p_sys->f_rate = (double)INPUT_RATE_DEFAULT / *va_arg( args, int * );
813 ResetCurrentIndex( p_demux );
814 return VLC_SUCCESS;
815 case DEMUX_SET_NEXT_DEMUX_TIME:
816 p_sys->b_slave = true;
817 p_sys->i_next_demux_date = va_arg( args, vlc_tick_t ) - VLC_TICK_0;
818 return VLC_SUCCESS;
820 case DEMUX_CAN_PAUSE:
821 case DEMUX_SET_PAUSE_STATE:
822 case DEMUX_CAN_CONTROL_PACE:
823 return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
825 case DEMUX_GET_PTS_DELAY:
826 case DEMUX_GET_FPS:
827 case DEMUX_GET_META:
828 case DEMUX_GET_ATTACHMENTS:
829 case DEMUX_GET_TITLE_INFO:
830 case DEMUX_HAS_UNSUPPORTED_META:
831 case DEMUX_CAN_RECORD:
832 default:
833 break;
836 return VLC_EGENERIC;
839 /*****************************************************************************
840 * Demux: Send subtitle to decoder
841 *****************************************************************************/
842 static int Demux( demux_t *p_demux )
844 demux_sys_t *p_sys = p_demux->p_sys;
846 vlc_tick_t i_barrier = p_sys->i_next_demux_date;
848 while( p_sys->subtitles.i_current < p_sys->subtitles.i_count &&
849 ( p_sys->subtitles.p_array[p_sys->subtitles.i_current].i_start *
850 p_sys->f_rate ) <= i_barrier )
852 const subtitle_t *p_subtitle = &p_sys->subtitles.p_array[p_sys->subtitles.i_current];
854 if ( !p_sys->b_slave && p_sys->b_first_time )
856 es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
857 p_sys->b_first_time = false;
860 if( p_subtitle->i_start >= 0 )
862 block_t *p_block = p_sys->pf_convert( p_subtitle );
863 if( p_block )
865 p_block->i_dts =
866 p_block->i_pts = VLC_TICK_0 + p_subtitle->i_start * p_sys->f_rate;
867 if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
868 p_block->i_length = (p_subtitle->i_stop - p_subtitle->i_start) * p_sys->f_rate;
870 es_out_Send( p_demux->out, p_sys->es, p_block );
874 p_sys->subtitles.i_current++;
877 if ( !p_sys->b_slave )
879 es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
880 p_sys->i_next_demux_date += VLC_TICK_FROM_MS(125);
883 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
884 return VLC_DEMUXER_EOF;
886 return VLC_DEMUXER_SUCCESS;
890 static int subtitle_cmp( const void *first, const void *second )
892 vlc_tick_t result = ((subtitle_t *)(first))->i_start - ((subtitle_t *)(second))->i_start;
893 /* Return -1, 0 ,1, and not directly subtraction
894 * as result can be > INT_MAX */
895 return result == 0 ? 0 : result > 0 ? 1 : -1;
897 /*****************************************************************************
898 * Fix: fix time stamp and order of subtitle
899 *****************************************************************************/
900 static void Fix( demux_t *p_demux )
902 demux_sys_t *p_sys = p_demux->p_sys;
904 /* *** fix order (to be sure...) *** */
905 qsort( p_sys->subtitles.p_array, p_sys->subtitles.i_count, sizeof( p_sys->subtitles.p_array[0] ), subtitle_cmp);
908 static int TextLoad( text_t *txt, stream_t *s )
910 size_t i_line_max;
912 /* init txt */
913 i_line_max = 500;
914 txt->i_line_count = 0;
915 txt->i_line = 0;
916 txt->line = calloc( i_line_max, sizeof( char * ) );
917 if( !txt->line )
918 return VLC_ENOMEM;
920 /* load the complete file */
921 for( ;; )
923 char *psz = vlc_stream_ReadLine( s );
925 if( psz == NULL )
926 break;
928 txt->line[txt->i_line_count] = psz;
929 if( txt->i_line_count + 1 >= i_line_max )
931 i_line_max += 100;
932 char **p_realloc = realloc( txt->line, i_line_max * sizeof( char * ) );
933 if( p_realloc == NULL )
934 return VLC_ENOMEM;
935 txt->line = p_realloc;
937 txt->i_line_count++;
940 if( txt->i_line_count == 0 )
942 free( txt->line );
943 return VLC_EGENERIC;
946 return VLC_SUCCESS;
948 static void TextUnload( text_t *txt )
950 if( txt->i_line_count )
952 for( size_t i = 0; i < txt->i_line_count; i++ )
953 free( txt->line[i] );
954 free( txt->line );
956 txt->i_line = 0;
957 txt->i_line_count = 0;
960 static char *TextGetLine( text_t *txt )
962 if( txt->i_line >= txt->i_line_count )
963 return( NULL );
965 return txt->line[txt->i_line++];
967 static void TextPreviousLine( text_t *txt )
969 if( txt->i_line > 0 )
970 txt->i_line--;
973 /*****************************************************************************
974 * Specific Subtitle function
975 *****************************************************************************/
976 /* ParseMicroDvd:
977 * Format:
978 * {n1}{n2}Line1|Line2|Line3....
979 * where n1 and n2 are the video frame number (n2 can be empty)
981 static int ParseMicroDvd( vlc_object_t *p_obj, subs_properties_t *p_props,
982 text_t *txt, subtitle_t *p_subtitle,
983 size_t i_idx )
985 VLC_UNUSED( i_idx );
986 char *psz_text;
987 int i_start;
988 int i_stop;
989 int i;
991 for( ;; )
993 const char *s = TextGetLine( txt );
994 if( !s )
995 return VLC_EGENERIC;
997 psz_text = malloc( strlen(s) + 1 );
998 if( !psz_text )
999 return VLC_ENOMEM;
1001 i_start = 0;
1002 i_stop = -1;
1003 if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
1004 sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1006 if( i_start != 1 || i_stop != 1 )
1007 break;
1009 /* We found a possible setting of the framerate "{1}{1}23.976" */
1010 /* Check if it's usable, and if the sub-original-fps is not set */
1011 float f_fps = us_strtof( psz_text, NULL );
1012 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-original-fps" ) <= 0.f )
1013 p_props->i_microsecperframe = llroundf((float)CLOCK_FREQ / f_fps);
1015 free( psz_text );
1018 /* replace | by \n */
1019 for( i = 0; psz_text[i] != '\0'; i++ )
1021 if( psz_text[i] == '|' )
1022 psz_text[i] = '\n';
1025 /* */
1026 p_subtitle->i_start = i_start * p_props->i_microsecperframe;
1027 p_subtitle->i_stop = i_stop >= 0 ? (i_stop * p_props->i_microsecperframe) : -1;
1028 p_subtitle->psz_text = psz_text;
1029 return VLC_SUCCESS;
1032 /* ParseSubRipSubViewer
1033 * Format SubRip
1035 * h1:m1:s1,d1 --> h2:m2:s2,d2
1036 * Line1
1037 * Line2
1038 * ....
1039 * [Empty line]
1040 * Format SubViewer v1/v2
1041 * h1:m1:s1.d1,h2:m2:s2.d2
1042 * Line1[br]Line2
1043 * Line3
1044 * ...
1045 * [empty line]
1046 * We ignore line number for SubRip
1048 static int ParseSubRipSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1049 text_t *txt, subtitle_t *p_subtitle,
1050 int (* pf_parse_timing)(subtitle_t *, const char *),
1051 bool b_replace_br )
1053 VLC_UNUSED(p_obj);
1054 VLC_UNUSED(p_props);
1055 char *psz_text;
1057 for( ;; )
1059 const char *s = TextGetLine( txt );
1061 if( !s )
1062 return VLC_EGENERIC;
1064 if( pf_parse_timing( p_subtitle, s) == VLC_SUCCESS &&
1065 p_subtitle->i_start < p_subtitle->i_stop )
1067 break;
1071 /* Now read text until an empty line */
1072 psz_text = strdup("");
1073 if( !psz_text )
1074 return VLC_ENOMEM;
1076 for( ;; )
1078 const char *s = TextGetLine( txt );
1079 size_t i_len;
1080 size_t i_old;
1082 i_len = s ? strlen( s ) : 0;
1083 if( i_len <= 0 )
1085 p_subtitle->psz_text = psz_text;
1086 return VLC_SUCCESS;
1089 i_old = strlen( psz_text );
1090 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1091 if( !psz_text )
1093 return VLC_ENOMEM;
1095 strcat( psz_text, s );
1096 strcat( psz_text, "\n" );
1098 /* replace [br] by \n */
1099 if( b_replace_br )
1101 char *p;
1103 while( ( p = strstr( psz_text, "[br]" ) ) )
1105 *p++ = '\n';
1106 memmove( p, &p[3], strlen(&p[3])+1 );
1112 /* subtitle_ParseSubRipTimingValue
1113 * Parses SubRip timing value.
1115 static int subtitle_ParseSubRipTimingValue(vlc_tick_t *timing_value,
1116 const char *s)
1118 int h1, m1, s1, d1 = 0;
1120 if ( sscanf( s, "%d:%d:%d,%d",
1121 &h1, &m1, &s1, &d1 ) == 4 ||
1122 sscanf( s, "%d:%d:%d.%d",
1123 &h1, &m1, &s1, &d1 ) == 4 ||
1124 sscanf( s, "%d:%d:%d",
1125 &h1, &m1, &s1) == 3 )
1127 (*timing_value) = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
1128 VLC_TICK_FROM_MS( d1 );
1130 return VLC_SUCCESS;
1133 return VLC_EGENERIC;
1136 /* subtitle_ParseSubRipTiming
1137 * Parses SubRip timing.
1139 static int subtitle_ParseSubRipTiming( subtitle_t *p_subtitle,
1140 const char *s )
1142 int i_result = VLC_EGENERIC;
1143 char *psz_start, *psz_stop;
1144 psz_start = malloc( strlen(s) + 1 );
1145 psz_stop = malloc( strlen(s) + 1 );
1147 if( sscanf( s, "%s --> %s", psz_start, psz_stop) == 2 &&
1148 subtitle_ParseSubRipTimingValue( &p_subtitle->i_start, psz_start ) == VLC_SUCCESS &&
1149 subtitle_ParseSubRipTimingValue( &p_subtitle->i_stop, psz_stop ) == VLC_SUCCESS )
1151 i_result = VLC_SUCCESS;
1154 free(psz_start);
1155 free(psz_stop);
1157 return i_result;
1159 /* ParseSubRip
1161 static int ParseSubRip( vlc_object_t *p_obj, subs_properties_t *p_props,
1162 text_t *txt, subtitle_t *p_subtitle,
1163 size_t i_idx )
1165 VLC_UNUSED( i_idx );
1166 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1167 &subtitle_ParseSubRipTiming,
1168 false );
1171 /* subtitle_ParseSubViewerTiming
1172 * Parses SubViewer timing.
1174 static int subtitle_ParseSubViewerTiming( subtitle_t *p_subtitle,
1175 const char *s )
1177 int h1, m1, s1, d1, h2, m2, s2, d2;
1179 if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
1180 &h1, &m1, &s1, &d1, &h2, &m2, &s2, &d2) == 8 )
1182 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
1183 VLC_TICK_FROM_MS( d1 );
1185 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
1186 VLC_TICK_FROM_MS( d2 );
1187 return VLC_SUCCESS;
1189 return VLC_EGENERIC;
1192 /* ParseSubViewer
1194 static int ParseSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1195 text_t *txt, subtitle_t *p_subtitle,
1196 size_t i_idx )
1198 VLC_UNUSED( i_idx );
1200 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1201 &subtitle_ParseSubViewerTiming,
1202 true );
1205 /* ParseSSA
1207 static int ParseSSA( vlc_object_t *p_obj, subs_properties_t *p_props,
1208 text_t *txt, subtitle_t *p_subtitle,
1209 size_t i_idx )
1211 VLC_UNUSED(p_obj);
1212 size_t header_len = 0;
1214 for( ;; )
1216 const char *s = TextGetLine( txt );
1217 int h1, m1, s1, c1, h2, m2, s2, c2;
1218 char *psz_text, *psz_temp;
1219 char temp[16];
1221 if( !s )
1222 return VLC_EGENERIC;
1224 /* We expect (SSA2-4):
1225 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1226 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1228 * 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.
1231 /* For ASS:
1232 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1233 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1236 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1237 psz_text = malloc( strlen(s) );
1238 if( !psz_text )
1239 return VLC_ENOMEM;
1241 if( sscanf( s,
1242 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1243 temp,
1244 &h1, &m1, &s1, &c1,
1245 &h2, &m2, &s2, &c2,
1246 psz_text ) == 10 )
1248 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1249 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1250 if( p_props->i_type == SUB_TYPE_SSA1 )
1252 /* SSA1 has only 8 commas before the text starts, not 9 */
1253 memmove( &psz_text[1], psz_text, strlen(psz_text)+1 );
1254 psz_text[0] = ',';
1256 else
1258 int i_layer = ( p_props->i_type == SUB_TYPE_ASS ) ? atoi( temp ) : 0;
1260 /* ReadOrder, Layer, %s(rest of fields) */
1261 if( asprintf( &psz_temp, "%zu,%d,%s", i_idx, i_layer, psz_text ) == -1 )
1263 free( psz_text );
1264 return VLC_ENOMEM;
1267 free( psz_text );
1268 psz_text = psz_temp;
1271 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
1272 VLC_TICK_FROM_MS( c1 * 10 );
1273 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
1274 VLC_TICK_FROM_MS( c2 * 10 );
1275 p_subtitle->psz_text = psz_text;
1276 return VLC_SUCCESS;
1278 free( psz_text );
1280 /* All the other stuff we add to the header field */
1281 if( header_len == 0 && p_props->psz_header )
1282 header_len = strlen( p_props->psz_header );
1284 size_t s_len = strlen( s );
1285 p_props->psz_header = realloc_or_free( p_props->psz_header, header_len + s_len + 2 );
1286 if( !p_props->psz_header )
1287 return VLC_ENOMEM;
1288 snprintf( p_props->psz_header + header_len, s_len + 2, "%s\n", s );
1289 header_len += s_len + 1;
1293 /* ParseVplayer
1294 * Format
1295 * h:m:s:Line1|Line2|Line3....
1296 * or
1297 * h:m:s Line1|Line2|Line3....
1299 static int ParseVplayer( vlc_object_t *p_obj, subs_properties_t *p_props,
1300 text_t *txt, subtitle_t *p_subtitle,
1301 size_t i_idx )
1303 VLC_UNUSED(p_obj);
1304 VLC_UNUSED(p_props);
1305 VLC_UNUSED( i_idx );
1306 char *psz_text;
1308 for( ;; )
1310 const char *s = TextGetLine( txt );
1311 int h1, m1, s1;
1313 if( !s )
1314 return VLC_EGENERIC;
1316 psz_text = malloc( strlen( s ) + 1 );
1317 if( !psz_text )
1318 return VLC_ENOMEM;
1320 if( sscanf( s, "%d:%d:%d%*c%[^\r\n]",
1321 &h1, &m1, &s1, psz_text ) == 4 )
1323 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
1324 p_subtitle->i_stop = -1;
1325 break;
1327 free( psz_text );
1330 /* replace | by \n */
1331 for( size_t i = 0; psz_text[i] != '\0'; i++ )
1333 if( psz_text[i] == '|' )
1334 psz_text[i] = '\n';
1336 p_subtitle->psz_text = psz_text;
1337 return VLC_SUCCESS;
1340 /* ParseSami
1342 static const char *ParseSamiSearch( text_t *txt,
1343 const char *psz_start, const char *psz_str )
1345 if( psz_start && strcasestr( psz_start, psz_str ) )
1347 const char *s = strcasestr( psz_start, psz_str );
1348 return &s[strlen( psz_str )];
1351 for( ;; )
1353 const char *p = TextGetLine( txt );
1354 if( !p )
1355 return NULL;
1357 const char *s = strcasestr( p, psz_str );
1358 if( s != NULL )
1359 return &s[strlen( psz_str )];
1362 static int ParseSami( vlc_object_t *p_obj, subs_properties_t *p_props,
1363 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1365 VLC_UNUSED(p_obj);
1366 VLC_UNUSED(p_props);
1367 VLC_UNUSED( i_idx );
1368 const char *s;
1369 int64_t i_start;
1371 unsigned int i_text;
1372 char text[8192]; /* Arbitrary but should be long enough */
1374 /* search "Start=" */
1375 s = ParseSamiSearch( txt, p_props->sami.psz_start, "Start=" );
1376 p_props->sami.psz_start = NULL;
1377 if( !s )
1378 return VLC_EGENERIC;
1380 /* get start value */
1381 char *psz_end;
1382 i_start = strtol( s, &psz_end, 0 );
1383 s = psz_end;
1385 /* search <P */
1386 if( !( s = ParseSamiSearch( txt, s, "<P" ) ) )
1387 return VLC_EGENERIC;
1389 /* search > */
1390 if( !( s = ParseSamiSearch( txt, s, ">" ) ) )
1391 return VLC_EGENERIC;
1393 i_text = 0;
1394 text[0] = '\0';
1395 /* now get all txt until a "Start=" line */
1396 for( ;; )
1398 char c = '\0';
1399 /* Search non empty line */
1400 while( s && *s == '\0' )
1401 s = TextGetLine( txt );
1402 if( !s )
1403 break;
1405 if( *s == '<' )
1407 if( !strncasecmp( s, "<br", 3 ) )
1409 c = '\n';
1411 else if( strcasestr( s, "Start=" ) )
1413 p_props->sami.psz_start = s;
1414 break;
1416 s = ParseSamiSearch( txt, s, ">" );
1418 else if( !strncmp( s, "&nbsp;", 6 ) )
1420 c = ' ';
1421 s += 6;
1423 else if( *s == '\t' )
1425 c = ' ';
1426 s++;
1428 else
1430 c = *s;
1431 s++;
1433 if( c != '\0' && i_text+1 < sizeof(text) )
1435 text[i_text++] = c;
1436 text[i_text] = '\0';
1440 p_subtitle->i_start = VLC_TICK_FROM_MS(i_start);
1441 p_subtitle->i_stop = -1;
1442 p_subtitle->psz_text = strdup( text );
1444 return VLC_SUCCESS;
1447 /* ParseDVDSubtitle
1448 * Format
1449 * {T h1:m1:s1:c1
1450 * Line1
1451 * Line2
1452 * ...
1454 * TODO it can have a header
1455 * { HEAD
1456 * ...
1457 * CODEPAGE=...
1458 * FORMAT=...
1459 * LANG=English
1461 * LANG support would be cool
1462 * CODEPAGE is probably mandatory FIXME
1464 static int ParseDVDSubtitle(vlc_object_t *p_obj, subs_properties_t *p_props,
1465 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1467 VLC_UNUSED(p_obj);
1468 VLC_UNUSED(p_props);
1469 VLC_UNUSED( i_idx );
1470 char *psz_text;
1472 for( ;; )
1474 const char *s = TextGetLine( txt );
1475 int h1, m1, s1, c1;
1477 if( !s )
1478 return VLC_EGENERIC;
1480 if( sscanf( s,
1481 "{T %d:%d:%d:%d",
1482 &h1, &m1, &s1, &c1 ) == 4 )
1484 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
1485 VLC_TICK_FROM_MS( c1 * 10 );
1486 p_subtitle->i_stop = -1;
1487 break;
1491 /* Now read text until a line containing "}" */
1492 psz_text = strdup("");
1493 if( !psz_text )
1494 return VLC_ENOMEM;
1495 for( ;; )
1497 const char *s = TextGetLine( txt );
1498 int i_len;
1499 int i_old;
1501 if( !s )
1503 free( psz_text );
1504 return VLC_EGENERIC;
1507 i_len = strlen( s );
1508 if( i_len == 1 && s[0] == '}')
1510 p_subtitle->psz_text = psz_text;
1511 return VLC_SUCCESS;
1514 i_old = strlen( psz_text );
1515 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1516 if( !psz_text )
1517 return VLC_ENOMEM;
1518 strcat( psz_text, s );
1519 strcat( psz_text, "\n" );
1523 /* ParseMPL2
1524 * Format
1525 * [n1][n2]Line1|Line2|Line3...
1526 * where n1 and n2 are the video frame number (n2 can be empty)
1528 static int ParseMPL2(vlc_object_t *p_obj, subs_properties_t *p_props,
1529 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1531 VLC_UNUSED(p_obj);
1532 VLC_UNUSED(p_props);
1533 VLC_UNUSED( i_idx );
1534 char *psz_text;
1535 int i;
1537 for( ;; )
1539 const char *s = TextGetLine( txt );
1540 int i_start;
1541 int i_stop;
1543 if( !s )
1544 return VLC_EGENERIC;
1546 psz_text = malloc( strlen(s) + 1 );
1547 if( !psz_text )
1548 return VLC_ENOMEM;
1550 i_start = 0;
1551 i_stop = -1;
1552 if( sscanf( s, "[%d][] %[^\r\n]", &i_start, psz_text ) == 2 ||
1553 sscanf( s, "[%d][%d] %[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1555 p_subtitle->i_start = VLC_TICK_FROM_MS(i_start * 100);
1556 p_subtitle->i_stop = i_stop >= 0 ? VLC_TICK_FROM_MS(i_stop * 100) : -1;
1557 break;
1559 free( psz_text );
1562 for( i = 0; psz_text[i] != '\0'; )
1564 /* replace | by \n */
1565 if( psz_text[i] == '|' )
1566 psz_text[i] = '\n';
1568 /* Remove italic */
1569 if( psz_text[i] == '/' && ( i == 0 || psz_text[i-1] == '\n' ) )
1570 memmove( &psz_text[i], &psz_text[i+1], strlen(&psz_text[i+1])+1 );
1571 else
1572 i++;
1574 p_subtitle->psz_text = psz_text;
1575 return VLC_SUCCESS;
1578 static int ParseAQT(vlc_object_t *p_obj, subs_properties_t *p_props, text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1580 VLC_UNUSED(p_obj);
1581 VLC_UNUSED(p_props);
1582 VLC_UNUSED( i_idx );
1584 char *psz_text = strdup( "" );
1585 int i_old = 0;
1586 int i_firstline = 1;
1588 for( ;; )
1590 int t; /* Time */
1592 const char *s = TextGetLine( txt );
1594 if( !s )
1596 free( psz_text );
1597 return VLC_EGENERIC;
1600 /* Data Lines */
1601 if( sscanf (s, "-->> %d", &t) == 1)
1603 p_subtitle->i_start = (int64_t)t; /* * FPS*/
1604 p_subtitle->i_stop = -1;
1606 /* Starting of a subtitle */
1607 if( i_firstline )
1609 i_firstline = 0;
1611 /* We have been too far: end of the subtitle, begin of next */
1612 else
1614 TextPreviousLine( txt );
1615 break;
1618 /* Text Lines */
1619 else
1621 i_old = strlen( psz_text ) + 1;
1622 psz_text = realloc_or_free( psz_text, i_old + strlen( s ) + 1 );
1623 if( !psz_text )
1624 return VLC_ENOMEM;
1625 strcat( psz_text, s );
1626 strcat( psz_text, "\n" );
1627 if( txt->i_line == txt->i_line_count )
1628 break;
1631 p_subtitle->psz_text = psz_text;
1632 return VLC_SUCCESS;
1635 static int ParsePJS(vlc_object_t *p_obj, subs_properties_t *p_props,
1636 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1638 VLC_UNUSED(p_obj);
1639 VLC_UNUSED(p_props);
1640 VLC_UNUSED( i_idx );
1642 char *psz_text;
1643 int i;
1645 for( ;; )
1647 const char *s = TextGetLine( txt );
1648 int t1, t2;
1650 if( !s )
1651 return VLC_EGENERIC;
1653 psz_text = malloc( strlen(s) + 1 );
1654 if( !psz_text )
1655 return VLC_ENOMEM;
1657 /* Data Lines */
1658 if( sscanf (s, "%d,%d,\"%[^\n\r]", &t1, &t2, psz_text ) == 3 )
1660 /* 1/10th of second ? Frame based ? FIXME */
1661 p_subtitle->i_start = 10 * t1;
1662 p_subtitle->i_stop = 10 * t2;
1663 /* Remove latest " */
1664 psz_text[ strlen(psz_text) - 1 ] = '\0';
1666 break;
1668 free( psz_text );
1671 /* replace | by \n */
1672 for( i = 0; psz_text[i] != '\0'; i++ )
1674 if( psz_text[i] == '|' )
1675 psz_text[i] = '\n';
1678 p_subtitle->psz_text = psz_text;
1679 msg_Dbg( p_obj, "%s", psz_text );
1680 return VLC_SUCCESS;
1683 static int ParseMPSub( vlc_object_t *p_obj, subs_properties_t *p_props,
1684 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1686 VLC_UNUSED( i_idx );
1688 char *psz_text = strdup( "" );
1690 if( !p_props->mpsub.b_inited )
1692 p_props->mpsub.f_total = 0.0;
1693 p_props->mpsub.i_factor = 0;
1695 p_props->mpsub.b_inited = true;
1698 for( ;; )
1700 char p_dummy;
1701 char *psz_temp;
1703 const char *s = TextGetLine( txt );
1704 if( !s )
1706 free( psz_text );
1707 return VLC_EGENERIC;
1710 if( strstr( s, "FORMAT" ) )
1712 if( sscanf (s, "FORMAT=TIM%c", &p_dummy ) == 1 && p_dummy == 'E')
1714 p_props->mpsub.i_factor = 100;
1715 break;
1718 psz_temp = malloc( strlen(s) );
1719 if( !psz_temp )
1721 free( psz_text );
1722 return VLC_ENOMEM;
1725 if( sscanf( s, "FORMAT=%[^\r\n]", psz_temp ) )
1727 float f_fps = us_strtof( psz_temp, NULL );
1729 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-original-fps" ) <= 0.f )
1730 var_SetFloat( p_obj, "sub-original-fps", f_fps );
1732 p_props->mpsub.i_factor = 1;
1733 free( psz_temp );
1734 break;
1736 free( psz_temp );
1739 /* Data Lines */
1740 float f1 = us_strtof( s, &psz_temp );
1741 if( *psz_temp )
1743 float f2 = us_strtof( psz_temp, NULL );
1744 p_props->mpsub.f_total += f1 * p_props->mpsub.i_factor;
1745 p_subtitle->i_start = llroundf(10000.f * p_props->mpsub.f_total);
1746 p_props->mpsub.f_total += f2 * p_props->mpsub.i_factor;
1747 p_subtitle->i_stop = llroundf(10000.f * p_props->mpsub.f_total);
1748 break;
1752 for( ;; )
1754 const char *s = TextGetLine( txt );
1756 if( !s )
1758 free( psz_text );
1759 return VLC_EGENERIC;
1762 size_t i_len = strlen( s );
1763 if( i_len == 0 )
1764 break;
1766 size_t i_old = strlen( psz_text );
1768 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1769 if( !psz_text )
1770 return VLC_ENOMEM;
1772 strcat( psz_text, s );
1773 strcat( psz_text, "\n" );
1776 p_subtitle->psz_text = psz_text;
1777 return VLC_SUCCESS;
1780 static int ParseJSS( vlc_object_t *p_obj, subs_properties_t *p_props,
1781 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1783 VLC_UNUSED( i_idx );
1784 char *psz_text, *psz_orig;
1785 char *psz_text2, *psz_orig2;
1787 if( !p_props->jss.b_inited )
1789 p_props->jss.i_comment = 0;
1790 p_props->jss.i_time_resolution = 30;
1791 p_props->jss.i_time_shift = 0;
1793 p_props->jss.b_inited = true;
1796 /* Parse the main lines */
1797 for( ;; )
1799 const char *s = TextGetLine( txt );
1800 if( !s )
1801 return VLC_EGENERIC;
1803 size_t line_length = strlen( s );
1804 psz_orig = malloc( line_length + 1 );
1805 if( !psz_orig )
1806 return VLC_ENOMEM;
1807 psz_text = psz_orig;
1809 /* Complete time lines */
1810 int h1, h2, m1, m2, s1, s2, f1, f2;
1811 if( sscanf( s, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1812 &h1, &m1, &s1, &f1, &h2, &m2, &s2, &f2, psz_text ) == 9 )
1814 p_subtitle->i_start = vlc_tick_from_sec( ( h1 *3600 + m1 * 60 + s1 ) +
1815 (int64_t)( ( f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) );
1816 p_subtitle->i_stop = vlc_tick_from_sec( ( h2 *3600 + m2 * 60 + s2 ) +
1817 (int64_t)( ( f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) );
1818 break;
1820 /* Short time lines */
1821 else if( sscanf( s, "@%d @%d %[^\n\r]", &f1, &f2, psz_text ) == 3 )
1823 p_subtitle->i_start =
1824 vlc_tick_from_sec( (f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution );
1825 p_subtitle->i_stop =
1826 vlc_tick_from_sec( (f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution );
1827 break;
1829 /* General Directive lines */
1830 /* Only TIME and SHIFT are supported so far */
1831 else if( s[0] == '#' )
1833 int h = 0, m =0, sec = 1, f = 1;
1834 unsigned shift = 1;
1835 int inv = 1;
1837 strcpy( psz_text, s );
1839 switch( toupper( (unsigned char)psz_text[1] ) )
1841 case 'S':
1842 shift = isalpha( (unsigned char)psz_text[2] ) ? 6 : 2 ;
1843 if ( shift > line_length )
1844 break;
1846 if( sscanf( &psz_text[shift], "%d", &h ) )
1848 /* Negative shifting */
1849 if( h < 0 )
1851 h *= -1;
1852 inv = -1;
1855 if( sscanf( &psz_text[shift], "%*d:%d", &m ) )
1857 if( sscanf( &psz_text[shift], "%*d:%*d:%d", &sec ) )
1859 sscanf( &psz_text[shift], "%*d:%*d:%*d.%d", &f );
1861 else
1863 h = 0;
1864 sscanf( &psz_text[shift], "%d:%d.%d",
1865 &m, &sec, &f );
1866 m *= inv;
1869 else
1871 h = m = 0;
1872 sscanf( &psz_text[shift], "%d.%d", &sec, &f);
1873 sec *= inv;
1875 p_props->jss.i_time_shift = ( ( h * 3600 + m * 60 + sec )
1876 * p_props->jss.i_time_resolution + f ) * inv;
1878 break;
1880 case 'T':
1881 shift = isalpha( (unsigned char)psz_text[2] ) ? 8 : 2 ;
1882 if ( shift > line_length )
1883 break;
1885 sscanf( &psz_text[shift], "%d", &p_props->jss.i_time_resolution );
1886 if( !p_props->jss.i_time_resolution )
1887 p_props->jss.i_time_resolution = 30;
1888 break;
1890 free( psz_orig );
1891 continue;
1893 else
1894 /* Unkown type line, probably a comment */
1896 free( psz_orig );
1897 continue;
1901 while( psz_text[ strlen( psz_text ) - 1 ] == '\\' )
1903 const char *s2 = TextGetLine( txt );
1905 if( !s2 )
1907 free( psz_orig );
1908 return VLC_EGENERIC;
1911 size_t i_len = strlen( s2 );
1912 if( i_len == 0 )
1913 break;
1915 size_t i_old = strlen( psz_text );
1917 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 );
1918 if( !psz_text )
1919 return VLC_ENOMEM;
1921 psz_orig = psz_text;
1922 strcat( psz_text, s2 );
1925 /* Skip the blanks */
1926 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1928 /* Parse the directives */
1929 if( isalpha( (unsigned char)*psz_text ) || *psz_text == '[' )
1931 while( *psz_text && *psz_text != ' ' )
1932 ++psz_text;
1934 /* Directives are NOT parsed yet */
1935 /* This has probably a better place in a decoder ? */
1936 /* directive = malloc( strlen( psz_text ) + 1 );
1937 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1940 /* Skip the blanks after directives */
1941 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1943 /* Clean all the lines from inline comments and other stuffs */
1944 psz_orig2 = calloc( strlen( psz_text) + 1, 1 );
1945 psz_text2 = psz_orig2;
1947 for( ; *psz_text != '\0' && *psz_text != '\n' && *psz_text != '\r'; )
1949 switch( *psz_text )
1951 case '{':
1952 p_props->jss.i_comment++;
1953 break;
1954 case '}':
1955 if( p_props->jss.i_comment )
1957 p_props->jss.i_comment = 0;
1958 if( (*(psz_text + 1 ) ) == ' ' ) psz_text++;
1960 break;
1961 case '~':
1962 if( !p_props->jss.i_comment )
1964 *psz_text2 = ' ';
1965 psz_text2++;
1967 break;
1968 case ' ':
1969 case '\t':
1970 if( (*(psz_text + 1 ) ) == ' ' || (*(psz_text + 1 ) ) == '\t' )
1971 break;
1972 if( !p_props->jss.i_comment )
1974 *psz_text2 = ' ';
1975 psz_text2++;
1977 break;
1978 case '\\':
1979 if( (*(psz_text + 1 ) ) == 'n' )
1981 *psz_text2 = '\n';
1982 psz_text++;
1983 psz_text2++;
1984 break;
1986 if( ( toupper((unsigned char)*(psz_text + 1 ) ) == 'C' ) ||
1987 ( toupper((unsigned char)*(psz_text + 1 ) ) == 'F' ) )
1989 psz_text++;
1990 break;
1992 if( (*(psz_text + 1 ) ) == 'B' || (*(psz_text + 1 ) ) == 'b' ||
1993 (*(psz_text + 1 ) ) == 'I' || (*(psz_text + 1 ) ) == 'i' ||
1994 (*(psz_text + 1 ) ) == 'U' || (*(psz_text + 1 ) ) == 'u' ||
1995 (*(psz_text + 1 ) ) == 'D' || (*(psz_text + 1 ) ) == 'N' )
1997 psz_text++;
1998 break;
2000 if( (*(psz_text + 1 ) ) == '~' || (*(psz_text + 1 ) ) == '{' ||
2001 (*(psz_text + 1 ) ) == '\\' )
2002 psz_text++;
2003 else if( ( *(psz_text + 1 ) == '\r' || *(psz_text + 1 ) == '\n' ) &&
2004 *(psz_text + 1 ) != '\0' )
2006 psz_text++;
2008 break;
2009 default:
2010 if( !p_props->jss.i_comment )
2012 *psz_text2 = *psz_text;
2013 psz_text2++;
2016 psz_text++;
2019 p_subtitle->psz_text = psz_orig2;
2020 msg_Dbg( p_obj, "%s", p_subtitle->psz_text );
2021 free( psz_orig );
2022 return VLC_SUCCESS;
2025 static int ParsePSB( vlc_object_t *p_obj, subs_properties_t *p_props,
2026 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2028 VLC_UNUSED(p_obj);
2029 VLC_UNUSED(p_props);
2030 VLC_UNUSED( i_idx );
2032 char *psz_text;
2033 int i;
2035 for( ;; )
2037 int h1, m1, s1;
2038 int h2, m2, s2;
2039 const char *s = TextGetLine( txt );
2041 if( !s )
2042 return VLC_EGENERIC;
2044 psz_text = malloc( strlen( s ) + 1 );
2045 if( !psz_text )
2046 return VLC_ENOMEM;
2048 if( sscanf( s, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2049 &h1, &m1, &s1, &h2, &m2, &s2, psz_text ) == 7 )
2051 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2052 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 );
2053 break;
2055 free( psz_text );
2058 /* replace | by \n */
2059 for( i = 0; psz_text[i] != '\0'; i++ )
2061 if( psz_text[i] == '|' )
2062 psz_text[i] = '\n';
2064 p_subtitle->psz_text = psz_text;
2065 return VLC_SUCCESS;
2068 static int64_t ParseRealTime( char *psz, int *h, int *m, int *s, int *f )
2070 if( *psz == '\0' ) return 0;
2071 if( sscanf( psz, "%d:%d:%d.%d", h, m, s, f ) == 4 ||
2072 sscanf( psz, "%d:%d.%d", m, s, f ) == 3 ||
2073 sscanf( psz, "%d.%d", s, f ) == 2 ||
2074 sscanf( psz, "%d:%d", m, s ) == 2 ||
2075 sscanf( psz, "%d", s ) == 1 )
2077 return vlc_tick_from_sec((( *h * 60 + *m ) * 60 ) + *s )
2078 + VLC_TICK_FROM_MS(*f * 10);
2080 else return VLC_EGENERIC;
2083 static int ParseRealText( vlc_object_t *p_obj, subs_properties_t *p_props,
2084 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2086 VLC_UNUSED(p_obj);
2087 VLC_UNUSED(p_props);
2088 VLC_UNUSED( i_idx );
2089 char *psz_text = NULL;
2091 for( ;; )
2093 int h1 = 0, m1 = 0, s1 = 0, f1 = 0;
2094 int h2 = 0, m2 = 0, s2 = 0, f2 = 0;
2095 const char *s = TextGetLine( txt );
2096 free( psz_text );
2098 if( !s )
2099 return VLC_EGENERIC;
2101 psz_text = malloc( strlen( s ) + 1 );
2102 if( !psz_text )
2103 return VLC_ENOMEM;
2105 /* Find the good begining. This removes extra spaces at the beginning
2106 of the line.*/
2107 char *psz_temp = strcasestr( s, "<time");
2108 if( psz_temp != NULL )
2110 char psz_end[12], psz_begin[12];
2111 /* Line has begin and end */
2112 if( ( sscanf( psz_temp,
2113 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2114 psz_begin, psz_end, psz_text) != 3 ) &&
2115 /* Line has begin and no end */
2116 ( sscanf( psz_temp,
2117 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2118 psz_begin, psz_text ) != 2) )
2119 /* Line is not recognized */
2121 continue;
2124 /* Get the times */
2125 int64_t i_time = ParseRealTime( psz_begin, &h1, &m1, &s1, &f1 );
2126 p_subtitle->i_start = i_time >= 0 ? i_time : 0;
2128 i_time = ParseRealTime( psz_end, &h2, &m2, &s2, &f2 );
2129 p_subtitle->i_stop = i_time >= 0 ? i_time : -1;
2130 break;
2134 /* Get the following Lines */
2135 for( ;; )
2137 const char *s = TextGetLine( txt );
2139 if( !s )
2141 free( psz_text );
2142 return VLC_EGENERIC;
2145 size_t i_len = strlen( s );
2146 if( i_len == 0 ) break;
2148 if( strcasestr( s, "<time" ) ||
2149 strcasestr( s, "<clear/") )
2151 TextPreviousLine( txt );
2152 break;
2155 size_t i_old = strlen( psz_text );
2157 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2158 if( !psz_text )
2159 return VLC_ENOMEM;
2161 strcat( psz_text, s );
2162 strcat( psz_text, "\n" );
2165 /* Remove the starting ">" that remained after the sscanf */
2166 memmove( &psz_text[0], &psz_text[1], strlen( psz_text ) );
2168 p_subtitle->psz_text = psz_text;
2170 return VLC_SUCCESS;
2173 static int ParseDKS( vlc_object_t *p_obj, subs_properties_t *p_props,
2174 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2176 VLC_UNUSED(p_obj);
2177 VLC_UNUSED(p_props);
2178 VLC_UNUSED( i_idx );
2180 char *psz_text;
2182 for( ;; )
2184 int h1, m1, s1;
2185 int h2, m2, s2;
2186 char *s = TextGetLine( txt );
2188 if( !s )
2189 return VLC_EGENERIC;
2191 psz_text = malloc( strlen( s ) + 1 );
2192 if( !psz_text )
2193 return VLC_ENOMEM;
2195 if( sscanf( s, "[%d:%d:%d]%[^\r\n]",
2196 &h1, &m1, &s1, psz_text ) == 4 )
2198 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2200 s = TextGetLine( txt );
2201 if( !s )
2203 free( psz_text );
2204 return VLC_EGENERIC;
2207 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2208 p_subtitle->i_stop = vlc_tick_from_sec(h2 * 3600 + m2 * 60 + s2 );
2209 else
2210 p_subtitle->i_stop = -1;
2211 break;
2213 free( psz_text );
2216 /* replace [br] by \n */
2217 char *p;
2218 while( ( p = strstr( psz_text, "[br]" ) ) )
2220 *p++ = '\n';
2221 memmove( p, &p[3], strlen(&p[3])+1 );
2224 p_subtitle->psz_text = psz_text;
2225 return VLC_SUCCESS;
2228 static int ParseSubViewer1( vlc_object_t *p_obj, subs_properties_t *p_props,
2229 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2231 VLC_UNUSED(p_obj);
2232 VLC_UNUSED(p_props);
2233 VLC_UNUSED( i_idx );
2234 char *psz_text;
2236 for( ;; )
2238 int h1, m1, s1;
2239 int h2, m2, s2;
2240 char *s = TextGetLine( txt );
2242 if( !s )
2243 return VLC_EGENERIC;
2245 if( sscanf( s, "[%d:%d:%d]", &h1, &m1, &s1 ) == 3 )
2247 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2249 s = TextGetLine( txt );
2250 if( !s )
2251 return VLC_EGENERIC;
2253 psz_text = strdup( s );
2254 if( !psz_text )
2255 return VLC_ENOMEM;
2257 s = TextGetLine( txt );
2258 if( !s )
2260 free( psz_text );
2261 return VLC_EGENERIC;
2264 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2265 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 );
2266 else
2267 p_subtitle->i_stop = -1;
2269 break;
2273 p_subtitle->psz_text = psz_text;
2275 return VLC_SUCCESS;
2278 static int ParseCommonSBV( vlc_object_t *p_obj, subs_properties_t *p_props,
2279 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2281 VLC_UNUSED(p_obj);
2282 VLC_UNUSED( i_idx );
2283 VLC_UNUSED( p_props );
2284 char *psz_text;
2286 for( ;; )
2288 const char *s = TextGetLine( txt );
2289 int h1 = 0, m1 = 0, s1 = 0, d1 = 0;
2290 int h2 = 0, m2 = 0, s2 = 0, d2 = 0;
2292 if( !s )
2293 return VLC_EGENERIC;
2295 if( sscanf( s,"%d:%d:%d.%d,%d:%d:%d.%d",
2296 &h1, &m1, &s1, &d1,
2297 &h2, &m2, &s2, &d2 ) == 8 )
2299 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
2300 VLC_TICK_FROM_MS( d1 );
2302 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
2303 VLC_TICK_FROM_MS( d2 );
2304 if( p_subtitle->i_start < p_subtitle->i_stop )
2305 break;
2309 /* Now read text until an empty line */
2310 psz_text = strdup("");
2311 if( !psz_text )
2312 return VLC_ENOMEM;
2314 for( ;; )
2316 const char *s = TextGetLine( txt );
2317 size_t i_len;
2318 size_t i_old;
2320 i_len = s ? strlen( s ) : 0;
2321 if( i_len <= 0 )
2323 p_subtitle->psz_text = psz_text;
2324 return VLC_SUCCESS;
2327 i_old = strlen( psz_text );
2328 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2329 if( !psz_text )
2330 return VLC_ENOMEM;
2332 strcat( psz_text, s );
2333 strcat( psz_text, "\n" );
2337 static int ParseSCC( vlc_object_t *p_obj, subs_properties_t *p_props,
2338 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2340 VLC_UNUSED(p_obj);
2341 VLC_UNUSED( i_idx );
2342 VLC_UNUSED( p_props );
2344 static const struct rates
2346 unsigned val;
2347 vlc_rational_t rate;
2348 bool b_drop_allowed;
2349 } framerates[] = {
2350 { 2398, { 24000, 1001 }, false },
2351 { 2400, { 24, 1 }, false },
2352 { 2500, { 25, 1 }, false },
2353 { 2997, { 30000, 1001 }, true }, /* encoding rate */
2354 { 3000, { 30, 1 }, false },
2355 { 5000, { 50, 1 }, false },
2356 { 5994, { 60000, 1001 }, true },
2357 { 6000, { 60, 1 }, false },
2359 const struct rates *p_rate = &framerates[3];
2360 float f_fps = var_GetFloat( p_obj, "sub-original-fps" );
2361 if( f_fps > 1.0 )
2363 for( size_t i=0; i<ARRAY_SIZE(framerates); i++ )
2365 if( (unsigned)(f_fps * 100) == framerates[i].val )
2367 p_rate = &framerates[i];
2368 break;
2373 for( ;; )
2375 const char *psz_line = TextGetLine( txt );
2376 if( !psz_line )
2377 return VLC_EGENERIC;
2379 unsigned h, m, s, f;
2380 char c;
2381 if( sscanf( psz_line, "%u:%u:%u%c%u ", &h, &m, &s, &c, &f ) != 5 ||
2382 ( c != ':' && c != ';' ) )
2383 continue;
2385 /* convert everything to seconds */
2386 uint64_t i_frames = h * 3600 + m * 60 + s;
2388 if( c == ';' && p_rate->b_drop_allowed ) /* dropframe */
2390 /* convert to frame # to be accurate between inter drop drift
2391 * of 18 frames see http://andrewduncan.net/timecodes/ */
2392 const unsigned i_mins = h * 60 + m;
2393 i_frames = i_frames * p_rate[+1].rate.num + f
2394 - (p_rate[+1].rate.den * 2 * (i_mins - i_mins % 10));
2396 else
2398 /* convert to frame # at 29.97 */
2399 i_frames = i_frames * framerates[3].rate.num / framerates[3].rate.den + f;
2401 p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec(i_frames)*
2402 p_rate->rate.den / p_rate->rate.num;
2403 p_subtitle->i_stop = -1;
2405 const char *psz_text = strchr( psz_line, '\t' );
2406 if( !psz_text && !(psz_text = strchr( psz_line, ' ' )) )
2407 continue;
2409 if ( psz_text[1] == '\0' )
2410 continue;
2412 p_subtitle->psz_text = strdup( psz_text + 1 );
2413 if( !p_subtitle->psz_text )
2414 return VLC_ENOMEM;
2416 break;
2419 return VLC_SUCCESS;
2422 /* Matches filename.xx.srt */
2423 static char * get_language_from_filename( const char * psz_sub_file )
2425 char *psz_ret = NULL;
2426 char *psz_tmp, *psz_language_begin;
2428 if( !psz_sub_file ) return NULL;
2429 char *psz_work = strdup( psz_sub_file );
2431 /* Removing extension, but leaving the dot */
2432 psz_tmp = strrchr( psz_work, '.' );
2433 if( psz_tmp )
2435 psz_tmp[0] = '\0';
2436 psz_language_begin = strrchr( psz_work, '.' );
2437 if( psz_language_begin )
2438 psz_ret = strdup(++psz_language_begin);
2439 psz_tmp[0] = '.';
2442 free( psz_work );
2443 return psz_ret;