qt: playlist: use item title if available
[vlc.git] / modules / demux / subtitle.c
blobf117bda2b76e9e72e3e98a70b547df98169bce5d
1 /*****************************************************************************
2 * subtitle.c: Demux for subtitle text files.
3 *****************************************************************************
4 * Copyright (C) 1999-2007 VLC authors and VideoLAN
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7 * Derk-Jan Hartman <hartman at videolan dot org>
8 * Jean-Baptiste Kempf <jb@videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <ctype.h>
37 #include <math.h>
38 #include <assert.h>
40 #include <vlc_demux.h>
41 #include <vlc_charset.h>
43 /*****************************************************************************
44 * Module descriptor
45 *****************************************************************************/
46 static int Open ( vlc_object_t *p_this );
47 static void Close( vlc_object_t *p_this );
49 #define SUB_TYPE_LONGTEXT \
50 N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
51 #define SUB_DESCRIPTION_LONGTEXT \
52 N_("Override the default track description.")
54 static const char *const ppsz_sub_type[] =
56 "auto", "microdvd", "subrip", "subviewer", "ssa1",
57 "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
58 "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
59 "subviewer1", "sbv"
62 vlc_module_begin ()
63 set_shortname( N_("Subtitles"))
64 set_description( N_("Text subtitle parser") )
65 set_capability( "demux", 0 )
66 set_category( CAT_INPUT )
67 set_subcategory( SUBCAT_INPUT_DEMUX )
68 add_string( "sub-type", "auto", N_("Subtitle format"),
69 SUB_TYPE_LONGTEXT, true )
70 change_string_list( ppsz_sub_type, ppsz_sub_type )
71 add_string( "sub-description", NULL, N_("Subtitle description"),
72 SUB_DESCRIPTION_LONGTEXT, true )
73 set_callbacks( Open, Close )
75 add_shortcut( "subtitle" )
76 vlc_module_end ()
78 /*****************************************************************************
79 * Prototypes:
80 *****************************************************************************/
81 enum subtitle_type_e
83 SUB_TYPE_UNKNOWN = -1,
84 SUB_TYPE_MICRODVD,
85 SUB_TYPE_SUBRIP,
86 SUB_TYPE_SSA1,
87 SUB_TYPE_SSA2_4,
88 SUB_TYPE_ASS,
89 SUB_TYPE_VPLAYER,
90 SUB_TYPE_SAMI,
91 SUB_TYPE_SUBVIEWER, /* SUBVIEWER 2 */
92 SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */
93 SUB_TYPE_MPL2,
94 SUB_TYPE_AQT,
95 SUB_TYPE_PJS,
96 SUB_TYPE_MPSUB,
97 SUB_TYPE_JACOSUB,
98 SUB_TYPE_PSB,
99 SUB_TYPE_RT,
100 SUB_TYPE_DKS,
101 SUB_TYPE_SUBVIEW1, /* SUBVIEWER 1 - mplayer calls it subrip09,
102 and Gnome subtitles SubViewer 1.0 */
103 SUB_TYPE_SBV,
104 SUB_TYPE_SCC, /* Scenarist Closed Caption */
107 typedef struct
109 size_t i_line_count;
110 size_t i_line;
111 char **line;
112 } text_t;
114 static int TextLoad( text_t *, stream_t *s );
115 static void TextUnload( text_t * );
117 typedef struct
119 vlc_tick_t i_start;
120 vlc_tick_t i_stop;
122 char *psz_text;
123 } subtitle_t;
125 typedef struct
127 enum subtitle_type_e i_type;
128 vlc_tick_t i_microsecperframe;
130 char *psz_header; /* SSA */
132 struct
134 bool b_inited;
136 int i_comment;
137 int i_time_resolution;
138 int i_time_shift;
139 } jss;
141 struct
143 bool b_inited;
145 float f_total;
146 int i_factor;
147 } mpsub;
149 struct
151 const char *psz_start;
152 } sami;
154 } subs_properties_t;
156 typedef struct
158 es_out_id_t *es;
159 bool b_slave;
160 bool b_first_time;
162 double f_rate;
163 vlc_tick_t i_next_demux_date;
165 struct
167 subtitle_t *p_array;
168 size_t i_count;
169 size_t i_current;
170 } subtitles;
172 vlc_tick_t i_length;
174 /* */
175 subs_properties_t props;
177 block_t * (*pf_convert)( const subtitle_t * );
178 } demux_sys_t;
180 static int ParseMicroDvd ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
181 static int ParseSubRip ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
182 static int ParseSubViewer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
183 static int ParseSSA ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
184 static int ParseVplayer ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
185 static int ParseSami ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
186 static int ParseDVDSubtitle( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
187 static int ParseMPL2 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
188 static int ParseAQT ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
189 static int ParsePJS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
190 static int ParseMPSub ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
191 static int ParseJSS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
192 static int ParsePSB ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
193 static int ParseRealText ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
194 static int ParseDKS ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
195 static int ParseSubViewer1 ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
196 static int ParseCommonSBV ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
197 static int ParseSCC ( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t *, size_t );
199 static const struct
201 const char *psz_type_name;
202 int i_type;
203 const char *psz_name;
204 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
205 } sub_read_subtitle_function [] =
207 { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", ParseMicroDvd },
208 { "subrip", SUB_TYPE_SUBRIP, "SubRIP", ParseSubRip },
209 { "subviewer", SUB_TYPE_SUBVIEWER, "SubViewer", ParseSubViewer },
210 { "ssa1", SUB_TYPE_SSA1, "SSA-1", ParseSSA },
211 { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4", ParseSSA },
212 { "ass", SUB_TYPE_ASS, "SSA/ASS", ParseSSA },
213 { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", ParseVplayer },
214 { "sami", SUB_TYPE_SAMI, "SAMI", ParseSami },
215 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle },
216 { "mpl2", SUB_TYPE_MPL2, "MPL2", ParseMPL2 },
217 { "aqt", SUB_TYPE_AQT, "AQTitle", ParseAQT },
218 { "pjs", SUB_TYPE_PJS, "PhoenixSub", ParsePJS },
219 { "mpsub", SUB_TYPE_MPSUB, "MPSub", ParseMPSub },
220 { "jacosub", SUB_TYPE_JACOSUB, "JacoSub", ParseJSS },
221 { "psb", SUB_TYPE_PSB, "PowerDivx", ParsePSB },
222 { "realtext", SUB_TYPE_RT, "RealText", ParseRealText },
223 { "dks", SUB_TYPE_DKS, "DKS", ParseDKS },
224 { "subviewer1", SUB_TYPE_SUBVIEW1, "Subviewer 1", ParseSubViewer1 },
225 { "sbv", SUB_TYPE_SBV, "SBV", ParseCommonSBV },
226 { "scc", SUB_TYPE_SCC, "SCC", ParseSCC },
227 { NULL, SUB_TYPE_UNKNOWN, "Unknown", NULL }
229 /* When adding support for more formats, be sure to add their file extension
230 * to src/input/subtitles.c to enable auto-detection.
233 static int Demux( demux_t * );
234 static int Control( demux_t *, int, va_list );
236 static void Fix( demux_t * );
237 static char * get_language_from_filename( const char * );
239 /*****************************************************************************
240 * Decoder format output function
241 *****************************************************************************/
243 static block_t *ToTextBlock( const subtitle_t *p_subtitle )
245 block_t *p_block;
246 size_t i_len = strlen( p_subtitle->psz_text ) + 1;
248 if( i_len <= 1 || !(p_block = block_Alloc( i_len )) )
249 return NULL;
251 memcpy( p_block->p_buffer, p_subtitle->psz_text, i_len );
253 return p_block;
256 static block_t *ToEIA608Block( const subtitle_t *p_subtitle )
258 block_t *p_block;
259 const size_t i_len = strlen( p_subtitle->psz_text );
260 const size_t i_block = (1 + i_len / 5) * 3;
262 if( i_len < 4 || !(p_block = block_Alloc( i_block )) )
263 return NULL;
265 p_block->i_buffer = 0;
267 char *saveptr = NULL;
268 char *psz_tok = strtok_r( p_subtitle->psz_text, " ", &saveptr );
269 unsigned a, b;
270 while( psz_tok &&
271 sscanf( psz_tok, "%2x%2x", &a, &b ) == 2 &&
272 i_block - p_block->i_buffer >= 3 )
274 uint8_t *p_data = &p_block->p_buffer[p_block->i_buffer];
275 p_data[0] = 0xFC;
276 p_data[1] = a;
277 p_data[2] = b;
278 p_block->i_buffer += 3;
279 psz_tok = strtok_r( NULL, " ", &saveptr );
282 return p_block;
285 /*****************************************************************************
286 * Module initializer
287 *****************************************************************************/
288 static int Open ( vlc_object_t *p_this )
290 demux_t *p_demux = (demux_t*)p_this;
291 demux_sys_t *p_sys;
292 es_format_t fmt;
293 float f_fps;
294 char *psz_type;
295 int (*pf_read)( vlc_object_t *, subs_properties_t *, text_t *, subtitle_t*, size_t );
297 if( !p_demux->obj.force )
299 msg_Dbg( p_demux, "subtitle demux discarded" );
300 return VLC_EGENERIC;
303 p_demux->pf_demux = Demux;
304 p_demux->pf_control = Control;
305 p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
306 if( p_sys == NULL )
307 return VLC_ENOMEM;
309 p_sys->b_slave = false;
310 p_sys->b_first_time = true;
311 p_sys->i_next_demux_date = 0;
312 p_sys->f_rate = 1.0;
314 p_sys->pf_convert = ToTextBlock;
316 p_sys->subtitles.i_current= 0;
317 p_sys->subtitles.i_count = 0;
318 p_sys->subtitles.p_array = NULL;
320 p_sys->props.psz_header = NULL;
321 p_sys->props.i_microsecperframe = VLC_TICK_FROM_MS(40);
322 p_sys->props.jss.b_inited = false;
323 p_sys->props.mpsub.b_inited = false;
324 p_sys->props.sami.psz_start = NULL;
326 /* Get the FPS */
327 f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" );
328 if( f_fps >= 1.f )
330 p_sys->props.i_microsecperframe = llroundf( (float)CLOCK_FREQ / f_fps );
331 msg_Dbg( p_demux, "Override subtitle fps %f", (double) f_fps );
334 /* Get or probe the type */
335 p_sys->props.i_type = SUB_TYPE_UNKNOWN;
336 psz_type = var_CreateGetString( p_demux, "sub-type" );
337 if( psz_type && *psz_type )
339 for( int i = 0; ; i++ )
341 if( sub_read_subtitle_function[i].psz_type_name == NULL )
342 break;
344 if( !strcmp( sub_read_subtitle_function[i].psz_type_name,
345 psz_type ) )
347 p_sys->props.i_type = sub_read_subtitle_function[i].i_type;
348 break;
352 free( psz_type );
354 #ifndef NDEBUG
355 const uint64_t i_start_pos = vlc_stream_Tell( p_demux->s );
356 #endif
358 size_t i_peek;
359 const uint8_t *p_peek;
360 if( vlc_stream_Peek( p_demux->s, &p_peek, 16 ) < 16 )
362 free( p_sys );
363 return VLC_EGENERIC;
366 enum
368 UTF8BOM,
369 UTF16LE,
370 UTF16BE,
371 NOBOM,
372 } e_bom = NOBOM;
373 const char *psz_bom = NULL;
375 i_peek = 4096;
376 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
377 if( !memcmp( p_peek, "\xEF\xBB\xBF", 3 ) )
379 e_bom = UTF8BOM;
380 psz_bom = "UTF-8";
382 else if( !memcmp( p_peek, "\xFF\xFE", 2 ) )
384 e_bom = UTF16LE;
385 psz_bom = "UTF-16LE";
386 i_peek *= 2;
388 else if( !memcmp( p_peek, "\xFE\xFF", 2 ) )
390 e_bom = UTF16BE;
391 psz_bom = "UTF-16BE";
392 i_peek *= 2;
395 if( e_bom != NOBOM )
396 msg_Dbg( p_demux, "detected %s Byte Order Mark", psz_bom );
398 i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_peek );
399 if( unlikely(i_peek < 16) )
401 free( p_sys );
402 return VLC_EGENERIC;
405 stream_t *p_probestream = NULL;
406 if( e_bom != UTF8BOM && e_bom != NOBOM )
408 if( i_peek > 16 )
410 char *p_outbuf = FromCharset( psz_bom, p_peek, i_peek );
411 if( p_outbuf != NULL )
412 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *)p_outbuf,
413 strlen( p_outbuf ),
414 false ); /* free p_outbuf on release */
417 else
419 const size_t i_skip = (e_bom == UTF8BOM) ? 3 : 0;
420 p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *) &p_peek[i_skip],
421 i_peek - i_skip, true );
424 if( p_probestream == NULL )
426 free( p_sys );
427 return VLC_EGENERIC;
430 /* Probe if unknown type */
431 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
433 int i_try;
434 char *s = NULL;
436 msg_Dbg( p_demux, "autodetecting subtitle format" );
437 for( i_try = 0; i_try < 256; i_try++ )
439 int i_dummy;
440 char p_dummy;
442 if( (s = vlc_stream_ReadLine( p_probestream ) ) == NULL )
443 break;
445 if( strcasestr( s, "<SAMI>" ) )
447 p_sys->props.i_type = SUB_TYPE_SAMI;
448 break;
450 else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 ||
451 sscanf( s, "{%d}{}", &i_dummy ) == 1)
453 p_sys->props.i_type = SUB_TYPE_MICRODVD;
454 break;
456 else if( sscanf( s, "%d:%d:%d,%d --> %d:%d:%d,%d",
457 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
458 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
459 sscanf( s, "%d:%d:%d --> %d:%d:%d,%d",
460 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
461 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
462 sscanf( s, "%d:%d:%d,%d --> %d:%d:%d",
463 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
464 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
465 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d.%d",
466 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
467 &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ||
468 sscanf( s, "%d:%d:%d --> %d:%d:%d.%d",
469 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
470 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
471 sscanf( s, "%d:%d:%d.%d --> %d:%d:%d",
472 &i_dummy,&i_dummy,&i_dummy,&i_dummy,
473 &i_dummy,&i_dummy,&i_dummy ) == 7 ||
474 sscanf( s, "%d:%d:%d --> %d:%d:%d",
475 &i_dummy,&i_dummy,&i_dummy,
476 &i_dummy,&i_dummy,&i_dummy ) == 6 )
478 p_sys->props.i_type = SUB_TYPE_SUBRIP;
479 break;
481 else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) )
483 p_sys->props.i_type = SUB_TYPE_SSA1;
484 break;
486 else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) )
488 p_sys->props.i_type = SUB_TYPE_ASS;
489 break;
491 else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) )
493 p_sys->props.i_type = SUB_TYPE_SSA2_4;
494 break;
496 else if( !strncasecmp( s, "Dialogue: Marked", 16 ) )
498 p_sys->props.i_type = SUB_TYPE_SSA2_4;
499 break;
501 else if( !strncasecmp( s, "Dialogue:", 9 ) )
503 p_sys->props.i_type = SUB_TYPE_ASS;
504 break;
506 else if( strcasestr( s, "[INFORMATION]" ) )
508 p_sys->props.i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */
509 break;
511 else if( sscanf( s, "%d:%d:%d.%d %d:%d:%d",
512 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
513 &i_dummy, &i_dummy, &i_dummy ) == 7 ||
514 sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2)
516 p_sys->props.i_type = SUB_TYPE_JACOSUB;
517 break;
519 else if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
520 &i_dummy, &i_dummy, &i_dummy, &i_dummy,
521 &i_dummy, &i_dummy, &i_dummy, &i_dummy ) == 8 )
523 p_sys->props.i_type = SUB_TYPE_SBV;
524 break;
526 else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 ||
527 sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 )
529 p_sys->props.i_type = SUB_TYPE_VPLAYER;
530 break;
532 else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy,
533 &i_dummy, &i_dummy ) == 4 )
535 p_sys->props.i_type = SUB_TYPE_DVDSUBTITLE;
536 break;
538 else if( sscanf( s, "[%d:%d:%d]%c",
539 &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 )
541 p_sys->props.i_type = SUB_TYPE_DKS;
542 break;
544 else if( strstr( s, "*** START SCRIPT" ) )
546 p_sys->props.i_type = SUB_TYPE_SUBVIEW1;
547 break;
549 else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 ||
550 sscanf( s, "[%d][]", &i_dummy ) == 1)
552 p_sys->props.i_type = SUB_TYPE_MPL2;
553 break;
555 else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 ||
556 ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1
557 && p_dummy =='E' ) )
559 p_sys->props.i_type = SUB_TYPE_MPSUB;
560 break;
562 else if( sscanf( s, "-->> %d", &i_dummy) == 1 )
564 p_sys->props.i_type = SUB_TYPE_AQT;
565 break;
567 else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 )
569 p_sys->props.i_type = SUB_TYPE_PJS;
570 break;
572 else if( sscanf( s, "{%d:%d:%d}",
573 &i_dummy, &i_dummy, &i_dummy ) == 3 )
575 p_sys->props.i_type = SUB_TYPE_PSB;
576 break;
578 else if( strcasestr( s, "<time" ) )
580 p_sys->props.i_type = SUB_TYPE_RT;
581 break;
583 else if( !strncasecmp( s, "WEBVTT",6 ) )
585 /* FAIL */
586 break;
588 else if( !strncasecmp( s, "Scenarist_SCC V1.0", 18 ) )
590 p_sys->props.i_type = SUB_TYPE_SCC;
591 p_sys->pf_convert = ToEIA608Block;
592 break;
595 free( s );
596 s = NULL;
599 free( s );
602 vlc_stream_Delete( p_probestream );
604 /* Quit on unknown subtitles */
605 if( p_sys->props.i_type == SUB_TYPE_UNKNOWN )
607 #ifndef NDEBUG
608 /* Ensure it will work with non seekable streams */
609 assert( i_start_pos == vlc_stream_Tell( p_demux->s ) );
610 #endif
611 msg_Warn( p_demux, "failed to recognize subtitle type" );
612 free( p_sys );
613 return VLC_EGENERIC;
616 for( int i = 0; ; i++ )
618 if( sub_read_subtitle_function[i].i_type == p_sys->props.i_type )
620 msg_Dbg( p_demux, "detected %s format",
621 sub_read_subtitle_function[i].psz_name );
622 pf_read = sub_read_subtitle_function[i].pf_read;
623 break;
627 msg_Dbg( p_demux, "loading all subtitles..." );
629 if( e_bom == UTF8BOM && /* skip BOM */
630 vlc_stream_Read( p_demux->s, NULL, 3 ) != 3 )
632 Close( p_this );
633 return VLC_EGENERIC;
636 /* Load the whole file */
637 text_t txtlines;
638 TextLoad( &txtlines, p_demux->s );
640 /* Parse it */
641 for( size_t i_max = 0; i_max < SIZE_MAX - 500 * sizeof(subtitle_t); )
643 if( p_sys->subtitles.i_count >= i_max )
645 i_max += 500;
646 subtitle_t *p_realloc = realloc( p_sys->subtitles.p_array, sizeof(subtitle_t) * i_max );
647 if( p_realloc == NULL )
649 TextUnload( &txtlines );
650 Close( p_this );
651 return VLC_ENOMEM;
653 p_sys->subtitles.p_array = p_realloc;
656 if( pf_read( VLC_OBJECT(p_demux), &p_sys->props, &txtlines,
657 &p_sys->subtitles.p_array[p_sys->subtitles.i_count],
658 p_sys->subtitles.i_count ) )
659 break;
661 p_sys->subtitles.i_count++;
663 /* Unload */
664 TextUnload( &txtlines );
666 msg_Dbg(p_demux, "loaded %zu subtitles", p_sys->subtitles.i_count );
668 /* *** add subtitle ES *** */
669 if( p_sys->props.i_type == SUB_TYPE_SSA1 ||
670 p_sys->props.i_type == SUB_TYPE_SSA2_4 ||
671 p_sys->props.i_type == SUB_TYPE_ASS )
673 Fix( p_demux );
674 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SSA );
676 else if( p_sys->props.i_type == SUB_TYPE_SCC )
678 es_format_Init( &fmt, SPU_ES, VLC_CODEC_CEA608 );
679 fmt.subs.cc.i_reorder_depth = -1;
681 else
682 es_format_Init( &fmt, SPU_ES, VLC_CODEC_SUBT );
684 p_sys->subtitles.i_current = 0;
685 p_sys->i_length = 0;
686 if( p_sys->subtitles.i_count > 0 )
687 p_sys->i_length = p_sys->subtitles.p_array[p_sys->subtitles.i_count-1].i_stop;
689 /* Stupid language detection in the filename */
690 char * psz_language = get_language_from_filename( p_demux->psz_filepath );
692 if( psz_language )
694 fmt.psz_language = psz_language;
695 msg_Dbg( p_demux, "detected language %s of subtitle: %s", psz_language,
696 p_demux->psz_location );
699 char *psz_description = var_InheritString( p_demux, "sub-description" );
700 if( psz_description && *psz_description )
701 fmt.psz_description = psz_description;
702 else
703 free( psz_description );
704 if( p_sys->props.psz_header != NULL &&
705 (fmt.p_extra = strdup( p_sys->props.psz_header )) )
707 fmt.i_extra = strlen( p_sys->props.psz_header ) + 1;
710 fmt.i_id = 0;
711 p_sys->es = es_out_Add( p_demux->out, &fmt );
712 es_format_Clean( &fmt );
713 if( p_sys->es == NULL )
715 Close( p_this );
716 return VLC_EGENERIC;
719 return VLC_SUCCESS;
722 /*****************************************************************************
723 * Close: Close subtitle demux
724 *****************************************************************************/
725 static void Close( vlc_object_t *p_this )
727 demux_t *p_demux = (demux_t*)p_this;
728 demux_sys_t *p_sys = p_demux->p_sys;
730 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
731 free( p_sys->subtitles.p_array[i].psz_text );
732 free( p_sys->subtitles.p_array );
733 free( p_sys->props.psz_header );
735 free( p_sys );
738 static void
739 ResetCurrentIndex( demux_t *p_demux )
741 demux_sys_t *p_sys = p_demux->p_sys;
742 for( size_t i = 0; i < p_sys->subtitles.i_count; i++ )
744 if( p_sys->subtitles.p_array[i].i_start * p_sys->f_rate >
745 p_sys->i_next_demux_date && i > 0 )
746 break;
747 p_sys->subtitles.i_current = i;
751 /*****************************************************************************
752 * Control:
753 *****************************************************************************/
754 static int Control( demux_t *p_demux, int i_query, va_list args )
756 demux_sys_t *p_sys = p_demux->p_sys;
757 double *pf, f;
759 switch( i_query )
761 case DEMUX_CAN_SEEK:
762 *va_arg( args, bool * ) = true;
763 return VLC_SUCCESS;
765 case DEMUX_GET_LENGTH:
766 *va_arg( args, vlc_tick_t * ) = p_sys->i_length;
767 return VLC_SUCCESS;
769 case DEMUX_GET_TIME:
770 *va_arg( args, vlc_tick_t * ) = p_sys->i_next_demux_date;
771 return VLC_SUCCESS;
773 case DEMUX_SET_TIME:
775 p_sys->b_first_time = true;
776 p_sys->i_next_demux_date = va_arg( args, vlc_tick_t );
777 ResetCurrentIndex( p_demux );
778 return VLC_SUCCESS;
781 case DEMUX_GET_POSITION:
782 pf = va_arg( args, double * );
783 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
785 *pf = 1.0;
787 else if( p_sys->subtitles.i_count > 0 && p_sys->i_length )
789 *pf = p_sys->i_next_demux_date;
790 *pf /= p_sys->i_length;
792 else
794 *pf = 0.0;
796 return VLC_SUCCESS;
798 case DEMUX_SET_POSITION:
799 f = va_arg( args, double );
800 if( p_sys->subtitles.i_count && p_sys->i_length )
802 vlc_tick_t i64 = VLC_TICK_0 + f * p_sys->i_length;
803 return demux_Control( p_demux, DEMUX_SET_TIME, i64 );
805 break;
807 case DEMUX_CAN_CONTROL_RATE:
808 *va_arg( args, bool * ) = true;
809 return VLC_SUCCESS;
810 case DEMUX_SET_RATE:
811 p_sys->f_rate = *va_arg( args, float * );
812 ResetCurrentIndex( p_demux );
813 return VLC_SUCCESS;
814 case DEMUX_SET_NEXT_DEMUX_TIME:
815 p_sys->b_slave = true;
816 p_sys->i_next_demux_date = va_arg( args, vlc_tick_t ) - VLC_TICK_0;
817 return VLC_SUCCESS;
819 case DEMUX_CAN_PAUSE:
820 case DEMUX_SET_PAUSE_STATE:
821 case DEMUX_CAN_CONTROL_PACE:
822 return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
824 case DEMUX_GET_PTS_DELAY:
825 case DEMUX_GET_FPS:
826 case DEMUX_GET_META:
827 case DEMUX_GET_ATTACHMENTS:
828 case DEMUX_GET_TITLE_INFO:
829 case DEMUX_HAS_UNSUPPORTED_META:
830 case DEMUX_CAN_RECORD:
831 default:
832 break;
835 return VLC_EGENERIC;
838 /*****************************************************************************
839 * Demux: Send subtitle to decoder
840 *****************************************************************************/
841 static int Demux( demux_t *p_demux )
843 demux_sys_t *p_sys = p_demux->p_sys;
845 vlc_tick_t i_barrier = p_sys->i_next_demux_date;
847 while( p_sys->subtitles.i_current < p_sys->subtitles.i_count &&
848 ( p_sys->subtitles.p_array[p_sys->subtitles.i_current].i_start *
849 p_sys->f_rate ) <= i_barrier )
851 const subtitle_t *p_subtitle = &p_sys->subtitles.p_array[p_sys->subtitles.i_current];
853 if ( !p_sys->b_slave && p_sys->b_first_time )
855 es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
856 p_sys->b_first_time = false;
859 if( p_subtitle->i_start >= 0 )
861 block_t *p_block = p_sys->pf_convert( p_subtitle );
862 if( p_block )
864 p_block->i_dts =
865 p_block->i_pts = VLC_TICK_0 + p_subtitle->i_start * p_sys->f_rate;
866 if( p_subtitle->i_stop >= 0 && p_subtitle->i_stop >= p_subtitle->i_start )
867 p_block->i_length = (p_subtitle->i_stop - p_subtitle->i_start) * p_sys->f_rate;
869 es_out_Send( p_demux->out, p_sys->es, p_block );
873 p_sys->subtitles.i_current++;
876 if ( !p_sys->b_slave )
878 es_out_SetPCR( p_demux->out, VLC_TICK_0 + i_barrier );
879 p_sys->i_next_demux_date += VLC_TICK_FROM_MS(125);
882 if( p_sys->subtitles.i_current >= p_sys->subtitles.i_count )
883 return VLC_DEMUXER_EOF;
885 return VLC_DEMUXER_SUCCESS;
889 static int subtitle_cmp( const void *first, const void *second )
891 vlc_tick_t result = ((subtitle_t *)(first))->i_start - ((subtitle_t *)(second))->i_start;
892 /* Return -1, 0 ,1, and not directly subtraction
893 * as result can be > INT_MAX */
894 return result == 0 ? 0 : result > 0 ? 1 : -1;
896 /*****************************************************************************
897 * Fix: fix time stamp and order of subtitle
898 *****************************************************************************/
899 static void Fix( demux_t *p_demux )
901 demux_sys_t *p_sys = p_demux->p_sys;
903 /* *** fix order (to be sure...) *** */
904 qsort( p_sys->subtitles.p_array, p_sys->subtitles.i_count, sizeof( p_sys->subtitles.p_array[0] ), subtitle_cmp);
907 static int TextLoad( text_t *txt, stream_t *s )
909 size_t i_line_max;
911 /* init txt */
912 i_line_max = 500;
913 txt->i_line_count = 0;
914 txt->i_line = 0;
915 txt->line = calloc( i_line_max, sizeof( char * ) );
916 if( !txt->line )
917 return VLC_ENOMEM;
919 /* load the complete file */
920 for( ;; )
922 char *psz = vlc_stream_ReadLine( s );
924 if( psz == NULL )
925 break;
927 txt->line[txt->i_line_count] = psz;
928 if( txt->i_line_count + 1 >= i_line_max )
930 i_line_max += 100;
931 char **p_realloc = realloc( txt->line, i_line_max * sizeof( char * ) );
932 if( p_realloc == NULL )
933 return VLC_ENOMEM;
934 txt->line = p_realloc;
936 txt->i_line_count++;
939 if( txt->i_line_count == 0 )
941 free( txt->line );
942 return VLC_EGENERIC;
945 return VLC_SUCCESS;
947 static void TextUnload( text_t *txt )
949 if( txt->i_line_count )
951 for( size_t i = 0; i < txt->i_line_count; i++ )
952 free( txt->line[i] );
953 free( txt->line );
955 txt->i_line = 0;
956 txt->i_line_count = 0;
959 static char *TextGetLine( text_t *txt )
961 if( txt->i_line >= txt->i_line_count )
962 return( NULL );
964 return txt->line[txt->i_line++];
966 static void TextPreviousLine( text_t *txt )
968 if( txt->i_line > 0 )
969 txt->i_line--;
972 /*****************************************************************************
973 * Specific Subtitle function
974 *****************************************************************************/
975 /* ParseMicroDvd:
976 * Format:
977 * {n1}{n2}Line1|Line2|Line3....
978 * where n1 and n2 are the video frame number (n2 can be empty)
980 static int ParseMicroDvd( vlc_object_t *p_obj, subs_properties_t *p_props,
981 text_t *txt, subtitle_t *p_subtitle,
982 size_t i_idx )
984 VLC_UNUSED( i_idx );
985 char *psz_text;
986 int i_start;
987 int i_stop;
988 int i;
990 for( ;; )
992 const char *s = TextGetLine( txt );
993 if( !s )
994 return VLC_EGENERIC;
996 psz_text = malloc( strlen(s) + 1 );
997 if( !psz_text )
998 return VLC_ENOMEM;
1000 i_start = 0;
1001 i_stop = -1;
1002 if( sscanf( s, "{%d}{}%[^\r\n]", &i_start, psz_text ) == 2 ||
1003 sscanf( s, "{%d}{%d}%[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1005 if( i_start != 1 || i_stop != 1 )
1006 break;
1008 /* We found a possible setting of the framerate "{1}{1}23.976" */
1009 /* Check if it's usable, and if the sub-original-fps is not set */
1010 float f_fps = us_strtof( psz_text, NULL );
1011 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-original-fps" ) <= 0.f )
1012 p_props->i_microsecperframe = llroundf((float)CLOCK_FREQ / f_fps);
1014 free( psz_text );
1017 /* replace | by \n */
1018 for( i = 0; psz_text[i] != '\0'; i++ )
1020 if( psz_text[i] == '|' )
1021 psz_text[i] = '\n';
1024 /* */
1025 p_subtitle->i_start = i_start * p_props->i_microsecperframe;
1026 p_subtitle->i_stop = i_stop >= 0 ? (i_stop * p_props->i_microsecperframe) : -1;
1027 p_subtitle->psz_text = psz_text;
1028 return VLC_SUCCESS;
1031 /* ParseSubRipSubViewer
1032 * Format SubRip
1034 * h1:m1:s1,d1 --> h2:m2:s2,d2
1035 * Line1
1036 * Line2
1037 * ....
1038 * [Empty line]
1039 * Format SubViewer v1/v2
1040 * h1:m1:s1.d1,h2:m2:s2.d2
1041 * Line1[br]Line2
1042 * Line3
1043 * ...
1044 * [empty line]
1045 * We ignore line number for SubRip
1047 static int ParseSubRipSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1048 text_t *txt, subtitle_t *p_subtitle,
1049 int (* pf_parse_timing)(subtitle_t *, const char *),
1050 bool b_replace_br )
1052 VLC_UNUSED(p_obj);
1053 VLC_UNUSED(p_props);
1054 char *psz_text;
1056 for( ;; )
1058 const char *s = TextGetLine( txt );
1060 if( !s )
1061 return VLC_EGENERIC;
1063 if( pf_parse_timing( p_subtitle, s) == VLC_SUCCESS &&
1064 p_subtitle->i_start < p_subtitle->i_stop )
1066 break;
1070 /* Now read text until an empty line */
1071 psz_text = strdup("");
1072 if( !psz_text )
1073 return VLC_ENOMEM;
1075 for( ;; )
1077 const char *s = TextGetLine( txt );
1078 size_t i_len;
1079 size_t i_old;
1081 i_len = s ? strlen( s ) : 0;
1082 if( i_len <= 0 )
1084 p_subtitle->psz_text = psz_text;
1085 return VLC_SUCCESS;
1088 i_old = strlen( psz_text );
1089 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1090 if( !psz_text )
1092 return VLC_ENOMEM;
1094 strcat( psz_text, s );
1095 strcat( psz_text, "\n" );
1097 /* replace [br] by \n */
1098 if( b_replace_br )
1100 char *p;
1102 while( ( p = strstr( psz_text, "[br]" ) ) )
1104 *p++ = '\n';
1105 memmove( p, &p[3], strlen(&p[3])+1 );
1111 /* subtitle_ParseSubRipTimingValue
1112 * Parses SubRip timing value.
1114 static int subtitle_ParseSubRipTimingValue(vlc_tick_t *timing_value,
1115 const char *s)
1117 int h1, m1, s1, d1 = 0;
1119 if ( sscanf( s, "%d:%d:%d,%d",
1120 &h1, &m1, &s1, &d1 ) == 4 ||
1121 sscanf( s, "%d:%d:%d.%d",
1122 &h1, &m1, &s1, &d1 ) == 4 ||
1123 sscanf( s, "%d:%d:%d",
1124 &h1, &m1, &s1) == 3 )
1126 (*timing_value) = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
1127 VLC_TICK_FROM_MS( d1 );
1129 return VLC_SUCCESS;
1132 return VLC_EGENERIC;
1135 /* subtitle_ParseSubRipTiming
1136 * Parses SubRip timing.
1138 static int subtitle_ParseSubRipTiming( subtitle_t *p_subtitle,
1139 const char *s )
1141 int i_result = VLC_EGENERIC;
1142 char *psz_start, *psz_stop;
1143 psz_start = malloc( strlen(s) + 1 );
1144 psz_stop = malloc( strlen(s) + 1 );
1146 if( sscanf( s, "%s --> %s", psz_start, psz_stop) == 2 &&
1147 subtitle_ParseSubRipTimingValue( &p_subtitle->i_start, psz_start ) == VLC_SUCCESS &&
1148 subtitle_ParseSubRipTimingValue( &p_subtitle->i_stop, psz_stop ) == VLC_SUCCESS )
1150 i_result = VLC_SUCCESS;
1153 free(psz_start);
1154 free(psz_stop);
1156 return i_result;
1158 /* ParseSubRip
1160 static int ParseSubRip( vlc_object_t *p_obj, subs_properties_t *p_props,
1161 text_t *txt, subtitle_t *p_subtitle,
1162 size_t i_idx )
1164 VLC_UNUSED( i_idx );
1165 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1166 &subtitle_ParseSubRipTiming,
1167 false );
1170 /* subtitle_ParseSubViewerTiming
1171 * Parses SubViewer timing.
1173 static int subtitle_ParseSubViewerTiming( subtitle_t *p_subtitle,
1174 const char *s )
1176 int h1, m1, s1, d1, h2, m2, s2, d2;
1178 if( sscanf( s, "%d:%d:%d.%d,%d:%d:%d.%d",
1179 &h1, &m1, &s1, &d1, &h2, &m2, &s2, &d2) == 8 )
1181 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1) +
1182 VLC_TICK_FROM_MS( d1 );
1184 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
1185 VLC_TICK_FROM_MS( d2 );
1186 return VLC_SUCCESS;
1188 return VLC_EGENERIC;
1191 /* ParseSubViewer
1193 static int ParseSubViewer( vlc_object_t *p_obj, subs_properties_t *p_props,
1194 text_t *txt, subtitle_t *p_subtitle,
1195 size_t i_idx )
1197 VLC_UNUSED( i_idx );
1199 return ParseSubRipSubViewer( p_obj, p_props, txt, p_subtitle,
1200 &subtitle_ParseSubViewerTiming,
1201 true );
1204 /* ParseSSA
1206 static int ParseSSA( vlc_object_t *p_obj, subs_properties_t *p_props,
1207 text_t *txt, subtitle_t *p_subtitle,
1208 size_t i_idx )
1210 VLC_UNUSED(p_obj);
1211 size_t header_len = 0;
1213 for( ;; )
1215 const char *s = TextGetLine( txt );
1216 int h1, m1, s1, c1, h2, m2, s2, c2;
1217 char *psz_text, *psz_temp;
1218 char temp[16];
1220 if( !s )
1221 return VLC_EGENERIC;
1223 /* We expect (SSA2-4):
1224 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1225 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1227 * 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.
1230 /* For ASS:
1231 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1232 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1235 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1236 psz_text = malloc( strlen(s) );
1237 if( !psz_text )
1238 return VLC_ENOMEM;
1240 if( sscanf( s,
1241 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1242 temp,
1243 &h1, &m1, &s1, &c1,
1244 &h2, &m2, &s2, &c2,
1245 psz_text ) == 10 )
1247 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1248 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1249 if( p_props->i_type == SUB_TYPE_SSA1 )
1251 /* SSA1 has only 8 commas before the text starts, not 9 */
1252 memmove( &psz_text[1], psz_text, strlen(psz_text)+1 );
1253 psz_text[0] = ',';
1255 else
1257 int i_layer = ( p_props->i_type == SUB_TYPE_ASS ) ? atoi( temp ) : 0;
1259 /* ReadOrder, Layer, %s(rest of fields) */
1260 if( asprintf( &psz_temp, "%zu,%d,%s", i_idx, i_layer, psz_text ) == -1 )
1262 free( psz_text );
1263 return VLC_ENOMEM;
1266 free( psz_text );
1267 psz_text = psz_temp;
1270 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
1271 VLC_TICK_FROM_MS( c1 * 10 );
1272 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
1273 VLC_TICK_FROM_MS( c2 * 10 );
1274 p_subtitle->psz_text = psz_text;
1275 return VLC_SUCCESS;
1277 free( psz_text );
1279 /* All the other stuff we add to the header field */
1280 if( header_len == 0 && p_props->psz_header )
1281 header_len = strlen( p_props->psz_header );
1283 size_t s_len = strlen( s );
1284 p_props->psz_header = realloc_or_free( p_props->psz_header, header_len + s_len + 2 );
1285 if( !p_props->psz_header )
1286 return VLC_ENOMEM;
1287 snprintf( p_props->psz_header + header_len, s_len + 2, "%s\n", s );
1288 header_len += s_len + 1;
1292 /* ParseVplayer
1293 * Format
1294 * h:m:s:Line1|Line2|Line3....
1295 * or
1296 * h:m:s Line1|Line2|Line3....
1298 static int ParseVplayer( vlc_object_t *p_obj, subs_properties_t *p_props,
1299 text_t *txt, subtitle_t *p_subtitle,
1300 size_t i_idx )
1302 VLC_UNUSED(p_obj);
1303 VLC_UNUSED(p_props);
1304 VLC_UNUSED( i_idx );
1305 char *psz_text;
1307 for( ;; )
1309 const char *s = TextGetLine( txt );
1310 int h1, m1, s1;
1312 if( !s )
1313 return VLC_EGENERIC;
1315 psz_text = malloc( strlen( s ) + 1 );
1316 if( !psz_text )
1317 return VLC_ENOMEM;
1319 if( sscanf( s, "%d:%d:%d%*c%[^\r\n]",
1320 &h1, &m1, &s1, psz_text ) == 4 )
1322 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
1323 p_subtitle->i_stop = -1;
1324 break;
1326 free( psz_text );
1329 /* replace | by \n */
1330 for( size_t i = 0; psz_text[i] != '\0'; i++ )
1332 if( psz_text[i] == '|' )
1333 psz_text[i] = '\n';
1335 p_subtitle->psz_text = psz_text;
1336 return VLC_SUCCESS;
1339 /* ParseSami
1341 static const char *ParseSamiSearch( text_t *txt,
1342 const char *psz_start, const char *psz_str )
1344 if( psz_start && strcasestr( psz_start, psz_str ) )
1346 const char *s = strcasestr( psz_start, psz_str );
1347 return &s[strlen( psz_str )];
1350 for( ;; )
1352 const char *p = TextGetLine( txt );
1353 if( !p )
1354 return NULL;
1356 const char *s = strcasestr( p, psz_str );
1357 if( s != NULL )
1358 return &s[strlen( psz_str )];
1361 static int ParseSami( vlc_object_t *p_obj, subs_properties_t *p_props,
1362 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1364 VLC_UNUSED(p_obj);
1365 VLC_UNUSED(p_props);
1366 VLC_UNUSED( i_idx );
1367 const char *s;
1368 int64_t i_start;
1370 unsigned int i_text;
1371 char text[8192]; /* Arbitrary but should be long enough */
1373 /* search "Start=" */
1374 s = ParseSamiSearch( txt, p_props->sami.psz_start, "Start=" );
1375 p_props->sami.psz_start = NULL;
1376 if( !s )
1377 return VLC_EGENERIC;
1379 /* get start value */
1380 char *psz_end;
1381 i_start = strtol( s, &psz_end, 0 );
1382 s = psz_end;
1384 /* search <P */
1385 if( !( s = ParseSamiSearch( txt, s, "<P" ) ) )
1386 return VLC_EGENERIC;
1388 /* search > */
1389 if( !( s = ParseSamiSearch( txt, s, ">" ) ) )
1390 return VLC_EGENERIC;
1392 i_text = 0;
1393 text[0] = '\0';
1394 /* now get all txt until a "Start=" line */
1395 for( ;; )
1397 char c = '\0';
1398 /* Search non empty line */
1399 while( s && *s == '\0' )
1400 s = TextGetLine( txt );
1401 if( !s )
1402 break;
1404 if( *s == '<' )
1406 if( !strncasecmp( s, "<br", 3 ) )
1408 c = '\n';
1410 else if( strcasestr( s, "Start=" ) )
1412 p_props->sami.psz_start = s;
1413 break;
1415 s = ParseSamiSearch( txt, s, ">" );
1417 else if( !strncmp( s, "&nbsp;", 6 ) )
1419 c = ' ';
1420 s += 6;
1422 else if( *s == '\t' )
1424 c = ' ';
1425 s++;
1427 else
1429 c = *s;
1430 s++;
1432 if( c != '\0' && i_text+1 < sizeof(text) )
1434 text[i_text++] = c;
1435 text[i_text] = '\0';
1439 p_subtitle->i_start = VLC_TICK_FROM_MS(i_start);
1440 p_subtitle->i_stop = -1;
1441 p_subtitle->psz_text = strdup( text );
1443 return VLC_SUCCESS;
1446 /* ParseDVDSubtitle
1447 * Format
1448 * {T h1:m1:s1:c1
1449 * Line1
1450 * Line2
1451 * ...
1453 * TODO it can have a header
1454 * { HEAD
1455 * ...
1456 * CODEPAGE=...
1457 * FORMAT=...
1458 * LANG=English
1460 * LANG support would be cool
1461 * CODEPAGE is probably mandatory FIXME
1463 static int ParseDVDSubtitle(vlc_object_t *p_obj, subs_properties_t *p_props,
1464 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1466 VLC_UNUSED(p_obj);
1467 VLC_UNUSED(p_props);
1468 VLC_UNUSED( i_idx );
1469 char *psz_text;
1471 for( ;; )
1473 const char *s = TextGetLine( txt );
1474 int h1, m1, s1, c1;
1476 if( !s )
1477 return VLC_EGENERIC;
1479 if( sscanf( s,
1480 "{T %d:%d:%d:%d",
1481 &h1, &m1, &s1, &c1 ) == 4 )
1483 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
1484 VLC_TICK_FROM_MS( c1 * 10 );
1485 p_subtitle->i_stop = -1;
1486 break;
1490 /* Now read text until a line containing "}" */
1491 psz_text = strdup("");
1492 if( !psz_text )
1493 return VLC_ENOMEM;
1494 for( ;; )
1496 const char *s = TextGetLine( txt );
1497 int i_len;
1498 int i_old;
1500 if( !s )
1502 free( psz_text );
1503 return VLC_EGENERIC;
1506 i_len = strlen( s );
1507 if( i_len == 1 && s[0] == '}')
1509 p_subtitle->psz_text = psz_text;
1510 return VLC_SUCCESS;
1513 i_old = strlen( psz_text );
1514 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1515 if( !psz_text )
1516 return VLC_ENOMEM;
1517 strcat( psz_text, s );
1518 strcat( psz_text, "\n" );
1522 /* ParseMPL2
1523 * Format
1524 * [n1][n2]Line1|Line2|Line3...
1525 * where n1 and n2 are the video frame number (n2 can be empty)
1527 static int ParseMPL2(vlc_object_t *p_obj, subs_properties_t *p_props,
1528 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1530 VLC_UNUSED(p_obj);
1531 VLC_UNUSED(p_props);
1532 VLC_UNUSED( i_idx );
1533 char *psz_text;
1534 int i;
1536 for( ;; )
1538 const char *s = TextGetLine( txt );
1539 int i_start;
1540 int i_stop;
1542 if( !s )
1543 return VLC_EGENERIC;
1545 psz_text = malloc( strlen(s) + 1 );
1546 if( !psz_text )
1547 return VLC_ENOMEM;
1549 i_start = 0;
1550 i_stop = -1;
1551 if( sscanf( s, "[%d][] %[^\r\n]", &i_start, psz_text ) == 2 ||
1552 sscanf( s, "[%d][%d] %[^\r\n]", &i_start, &i_stop, psz_text ) == 3)
1554 p_subtitle->i_start = VLC_TICK_FROM_MS(i_start * 100);
1555 p_subtitle->i_stop = i_stop >= 0 ? VLC_TICK_FROM_MS(i_stop * 100) : -1;
1556 break;
1558 free( psz_text );
1561 for( i = 0; psz_text[i] != '\0'; )
1563 /* replace | by \n */
1564 if( psz_text[i] == '|' )
1565 psz_text[i] = '\n';
1567 /* Remove italic */
1568 if( psz_text[i] == '/' && ( i == 0 || psz_text[i-1] == '\n' ) )
1569 memmove( &psz_text[i], &psz_text[i+1], strlen(&psz_text[i+1])+1 );
1570 else
1571 i++;
1573 p_subtitle->psz_text = psz_text;
1574 return VLC_SUCCESS;
1577 static int ParseAQT(vlc_object_t *p_obj, subs_properties_t *p_props, text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1579 VLC_UNUSED(p_obj);
1580 VLC_UNUSED(p_props);
1581 VLC_UNUSED( i_idx );
1583 char *psz_text = strdup( "" );
1584 int i_old = 0;
1585 int i_firstline = 1;
1587 for( ;; )
1589 int t; /* Time */
1591 const char *s = TextGetLine( txt );
1593 if( !s )
1595 free( psz_text );
1596 return VLC_EGENERIC;
1599 /* Data Lines */
1600 if( sscanf (s, "-->> %d", &t) == 1)
1602 /* Starting of a subtitle */
1603 if( i_firstline )
1605 p_subtitle->i_start = t * p_props->i_microsecperframe;
1606 i_firstline = 0;
1608 /* We have been too far: end of the subtitle, begin of next */
1609 else
1611 p_subtitle->i_stop = t * p_props->i_microsecperframe;
1612 break;
1615 /* Text Lines */
1616 else
1618 i_old = strlen( psz_text ) + 1;
1619 psz_text = realloc_or_free( psz_text, i_old + strlen( s ) + 1 );
1620 if( !psz_text )
1621 return VLC_ENOMEM;
1622 strcat( psz_text, s );
1623 strcat( psz_text, "\n" );
1624 if( txt->i_line == txt->i_line_count )
1625 break;
1628 p_subtitle->psz_text = psz_text;
1629 return VLC_SUCCESS;
1632 static int ParsePJS(vlc_object_t *p_obj, subs_properties_t *p_props,
1633 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1635 VLC_UNUSED(p_obj);
1636 VLC_UNUSED(p_props);
1637 VLC_UNUSED( i_idx );
1639 char *psz_text;
1640 int i;
1642 for( ;; )
1644 const char *s = TextGetLine( txt );
1645 int t1, t2;
1647 if( !s )
1648 return VLC_EGENERIC;
1650 psz_text = malloc( strlen(s) + 1 );
1651 if( !psz_text )
1652 return VLC_ENOMEM;
1654 /* Data Lines */
1655 if( sscanf (s, "%d,%d,\"%[^\n\r]", &t1, &t2, psz_text ) == 3 )
1657 /* 1/10th of second ? Frame based ? FIXME */
1658 p_subtitle->i_start = 10 * t1;
1659 p_subtitle->i_stop = 10 * t2;
1660 /* Remove latest " */
1661 psz_text[ strlen(psz_text) - 1 ] = '\0';
1663 break;
1665 free( psz_text );
1668 /* replace | by \n */
1669 for( i = 0; psz_text[i] != '\0'; i++ )
1671 if( psz_text[i] == '|' )
1672 psz_text[i] = '\n';
1675 p_subtitle->psz_text = psz_text;
1676 msg_Dbg( p_obj, "%s", psz_text );
1677 return VLC_SUCCESS;
1680 static int ParseMPSub( vlc_object_t *p_obj, subs_properties_t *p_props,
1681 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1683 VLC_UNUSED( i_idx );
1685 char *psz_text = strdup( "" );
1687 if( !p_props->mpsub.b_inited )
1689 p_props->mpsub.f_total = 0.0;
1690 p_props->mpsub.i_factor = 0;
1692 p_props->mpsub.b_inited = true;
1695 for( ;; )
1697 char p_dummy;
1698 char *psz_temp;
1700 const char *s = TextGetLine( txt );
1701 if( !s )
1703 free( psz_text );
1704 return VLC_EGENERIC;
1707 if( strstr( s, "FORMAT" ) )
1709 if( sscanf (s, "FORMAT=TIM%c", &p_dummy ) == 1 && p_dummy == 'E')
1711 p_props->mpsub.i_factor = 100;
1712 break;
1715 psz_temp = malloc( strlen(s) );
1716 if( !psz_temp )
1718 free( psz_text );
1719 return VLC_ENOMEM;
1722 if( sscanf( s, "FORMAT=%[^\r\n]", psz_temp ) )
1724 float f_fps = us_strtof( psz_temp, NULL );
1726 if( f_fps > 0.f && var_GetFloat( p_obj, "sub-original-fps" ) <= 0.f )
1727 var_SetFloat( p_obj, "sub-original-fps", f_fps );
1729 p_props->mpsub.i_factor = 1;
1730 free( psz_temp );
1731 break;
1733 free( psz_temp );
1736 /* Data Lines */
1737 float f1 = us_strtof( s, &psz_temp );
1738 if( *psz_temp )
1740 float f2 = us_strtof( psz_temp, NULL );
1741 p_props->mpsub.f_total += f1 * p_props->mpsub.i_factor;
1742 p_subtitle->i_start = llroundf(10000.f * p_props->mpsub.f_total);
1743 p_props->mpsub.f_total += f2 * p_props->mpsub.i_factor;
1744 p_subtitle->i_stop = llroundf(10000.f * p_props->mpsub.f_total);
1745 break;
1749 for( ;; )
1751 const char *s = TextGetLine( txt );
1753 if( !s )
1755 free( psz_text );
1756 return VLC_EGENERIC;
1759 size_t i_len = strlen( s );
1760 if( i_len == 0 )
1761 break;
1763 size_t i_old = strlen( psz_text );
1765 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
1766 if( !psz_text )
1767 return VLC_ENOMEM;
1769 strcat( psz_text, s );
1770 strcat( psz_text, "\n" );
1773 p_subtitle->psz_text = psz_text;
1774 return VLC_SUCCESS;
1777 static int ParseJSS( vlc_object_t *p_obj, subs_properties_t *p_props,
1778 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
1780 VLC_UNUSED( i_idx );
1781 char *psz_text, *psz_orig;
1782 char *psz_text2, *psz_orig2;
1784 if( !p_props->jss.b_inited )
1786 p_props->jss.i_comment = 0;
1787 p_props->jss.i_time_resolution = 30;
1788 p_props->jss.i_time_shift = 0;
1790 p_props->jss.b_inited = true;
1793 /* Parse the main lines */
1794 for( ;; )
1796 const char *s = TextGetLine( txt );
1797 if( !s )
1798 return VLC_EGENERIC;
1800 size_t line_length = strlen( s );
1801 psz_orig = malloc( line_length + 1 );
1802 if( !psz_orig )
1803 return VLC_ENOMEM;
1804 psz_text = psz_orig;
1806 /* Complete time lines */
1807 int h1, h2, m1, m2, s1, s2, f1, f2;
1808 if( sscanf( s, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1809 &h1, &m1, &s1, &f1, &h2, &m2, &s2, &f2, psz_text ) == 9 )
1811 p_subtitle->i_start = vlc_tick_from_sec( ( h1 *3600 + m1 * 60 + s1 ) +
1812 (int64_t)( ( f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) );
1813 p_subtitle->i_stop = vlc_tick_from_sec( ( h2 *3600 + m2 * 60 + s2 ) +
1814 (int64_t)( ( f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution ) );
1815 break;
1817 /* Short time lines */
1818 else if( sscanf( s, "@%d @%d %[^\n\r]", &f1, &f2, psz_text ) == 3 )
1820 p_subtitle->i_start =
1821 vlc_tick_from_sec( (f1 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution );
1822 p_subtitle->i_stop =
1823 vlc_tick_from_sec( (f2 + p_props->jss.i_time_shift ) / p_props->jss.i_time_resolution );
1824 break;
1826 /* General Directive lines */
1827 /* Only TIME and SHIFT are supported so far */
1828 else if( s[0] == '#' )
1830 int h = 0, m =0, sec = 1, f = 1;
1831 unsigned shift = 1;
1832 int inv = 1;
1834 strcpy( psz_text, s );
1836 switch( toupper( (unsigned char)psz_text[1] ) )
1838 case 'S':
1839 shift = isalpha( (unsigned char)psz_text[2] ) ? 6 : 2 ;
1840 if ( shift > line_length )
1841 break;
1843 if( sscanf( &psz_text[shift], "%d", &h ) )
1845 /* Negative shifting */
1846 if( h < 0 )
1848 h *= -1;
1849 inv = -1;
1852 if( sscanf( &psz_text[shift], "%*d:%d", &m ) )
1854 if( sscanf( &psz_text[shift], "%*d:%*d:%d", &sec ) )
1856 sscanf( &psz_text[shift], "%*d:%*d:%*d.%d", &f );
1858 else
1860 h = 0;
1861 sscanf( &psz_text[shift], "%d:%d.%d",
1862 &m, &sec, &f );
1863 m *= inv;
1866 else
1868 h = m = 0;
1869 sscanf( &psz_text[shift], "%d.%d", &sec, &f);
1870 sec *= inv;
1872 p_props->jss.i_time_shift = ( ( h * 3600 + m * 60 + sec )
1873 * p_props->jss.i_time_resolution + f ) * inv;
1875 break;
1877 case 'T':
1878 shift = isalpha( (unsigned char)psz_text[2] ) ? 8 : 2 ;
1879 if ( shift > line_length )
1880 break;
1882 sscanf( &psz_text[shift], "%d", &p_props->jss.i_time_resolution );
1883 if( !p_props->jss.i_time_resolution )
1884 p_props->jss.i_time_resolution = 30;
1885 break;
1887 free( psz_orig );
1888 continue;
1890 else
1891 /* Unkown type line, probably a comment */
1893 free( psz_orig );
1894 continue;
1898 while( psz_text[ strlen( psz_text ) - 1 ] == '\\' )
1900 const char *s2 = TextGetLine( txt );
1902 if( !s2 )
1904 free( psz_orig );
1905 return VLC_EGENERIC;
1908 size_t i_len = strlen( s2 );
1909 if( i_len == 0 )
1910 break;
1912 size_t i_old = strlen( psz_text );
1914 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 );
1915 if( !psz_text )
1916 return VLC_ENOMEM;
1918 psz_orig = psz_text;
1919 strcat( psz_text, s2 );
1922 /* Skip the blanks */
1923 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1925 /* Parse the directives */
1926 if( isalpha( (unsigned char)*psz_text ) || *psz_text == '[' )
1928 while( *psz_text && *psz_text != ' ' )
1929 ++psz_text;
1931 /* Directives are NOT parsed yet */
1932 /* This has probably a better place in a decoder ? */
1933 /* directive = malloc( strlen( psz_text ) + 1 );
1934 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1937 /* Skip the blanks after directives */
1938 while( *psz_text == ' ' || *psz_text == '\t' ) psz_text++;
1940 /* Clean all the lines from inline comments and other stuffs */
1941 psz_orig2 = calloc( strlen( psz_text) + 1, 1 );
1942 psz_text2 = psz_orig2;
1944 for( ; *psz_text != '\0' && *psz_text != '\n' && *psz_text != '\r'; )
1946 switch( *psz_text )
1948 case '{':
1949 p_props->jss.i_comment++;
1950 break;
1951 case '}':
1952 if( p_props->jss.i_comment )
1954 p_props->jss.i_comment = 0;
1955 if( (*(psz_text + 1 ) ) == ' ' ) psz_text++;
1957 break;
1958 case '~':
1959 if( !p_props->jss.i_comment )
1961 *psz_text2 = ' ';
1962 psz_text2++;
1964 break;
1965 case ' ':
1966 case '\t':
1967 if( (*(psz_text + 1 ) ) == ' ' || (*(psz_text + 1 ) ) == '\t' )
1968 break;
1969 if( !p_props->jss.i_comment )
1971 *psz_text2 = ' ';
1972 psz_text2++;
1974 break;
1975 case '\\':
1976 if( (*(psz_text + 1 ) ) == 'n' )
1978 *psz_text2 = '\n';
1979 psz_text++;
1980 psz_text2++;
1981 break;
1983 if( ( toupper((unsigned char)*(psz_text + 1 ) ) == 'C' ) ||
1984 ( toupper((unsigned char)*(psz_text + 1 ) ) == 'F' ) )
1986 psz_text++;
1987 break;
1989 if( (*(psz_text + 1 ) ) == 'B' || (*(psz_text + 1 ) ) == 'b' ||
1990 (*(psz_text + 1 ) ) == 'I' || (*(psz_text + 1 ) ) == 'i' ||
1991 (*(psz_text + 1 ) ) == 'U' || (*(psz_text + 1 ) ) == 'u' ||
1992 (*(psz_text + 1 ) ) == 'D' || (*(psz_text + 1 ) ) == 'N' )
1994 psz_text++;
1995 break;
1997 if( (*(psz_text + 1 ) ) == '~' || (*(psz_text + 1 ) ) == '{' ||
1998 (*(psz_text + 1 ) ) == '\\' )
1999 psz_text++;
2000 else if( ( *(psz_text + 1 ) == '\r' || *(psz_text + 1 ) == '\n' ) &&
2001 *(psz_text + 1 ) != '\0' )
2003 psz_text++;
2005 break;
2006 default:
2007 if( !p_props->jss.i_comment )
2009 *psz_text2 = *psz_text;
2010 psz_text2++;
2013 psz_text++;
2016 p_subtitle->psz_text = psz_orig2;
2017 msg_Dbg( p_obj, "%s", p_subtitle->psz_text );
2018 free( psz_orig );
2019 return VLC_SUCCESS;
2022 static int ParsePSB( vlc_object_t *p_obj, subs_properties_t *p_props,
2023 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2025 VLC_UNUSED(p_obj);
2026 VLC_UNUSED(p_props);
2027 VLC_UNUSED( i_idx );
2029 char *psz_text;
2030 int i;
2032 for( ;; )
2034 int h1, m1, s1;
2035 int h2, m2, s2;
2036 const char *s = TextGetLine( txt );
2038 if( !s )
2039 return VLC_EGENERIC;
2041 psz_text = malloc( strlen( s ) + 1 );
2042 if( !psz_text )
2043 return VLC_ENOMEM;
2045 if( sscanf( s, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2046 &h1, &m1, &s1, &h2, &m2, &s2, psz_text ) == 7 )
2048 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2049 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 );
2050 break;
2052 free( psz_text );
2055 /* replace | by \n */
2056 for( i = 0; psz_text[i] != '\0'; i++ )
2058 if( psz_text[i] == '|' )
2059 psz_text[i] = '\n';
2061 p_subtitle->psz_text = psz_text;
2062 return VLC_SUCCESS;
2065 static int64_t ParseRealTime( char *psz, int *h, int *m, int *s, int *f )
2067 if( *psz == '\0' ) return 0;
2068 if( sscanf( psz, "%d:%d:%d.%d", h, m, s, f ) == 4 ||
2069 sscanf( psz, "%d:%d.%d", m, s, f ) == 3 ||
2070 sscanf( psz, "%d.%d", s, f ) == 2 ||
2071 sscanf( psz, "%d:%d", m, s ) == 2 ||
2072 sscanf( psz, "%d", s ) == 1 )
2074 return vlc_tick_from_sec((( *h * 60 + *m ) * 60 ) + *s )
2075 + VLC_TICK_FROM_MS(*f * 10);
2077 else return VLC_EGENERIC;
2080 static int ParseRealText( vlc_object_t *p_obj, subs_properties_t *p_props,
2081 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2083 VLC_UNUSED(p_obj);
2084 VLC_UNUSED(p_props);
2085 VLC_UNUSED( i_idx );
2086 char *psz_text = NULL;
2088 for( ;; )
2090 int h1 = 0, m1 = 0, s1 = 0, f1 = 0;
2091 int h2 = 0, m2 = 0, s2 = 0, f2 = 0;
2092 const char *s = TextGetLine( txt );
2093 free( psz_text );
2095 if( !s )
2096 return VLC_EGENERIC;
2098 psz_text = malloc( strlen( s ) + 1 );
2099 if( !psz_text )
2100 return VLC_ENOMEM;
2102 /* Find the good begining. This removes extra spaces at the beginning
2103 of the line.*/
2104 char *psz_temp = strcasestr( s, "<time");
2105 if( psz_temp != NULL )
2107 char psz_end[12], psz_begin[12];
2108 /* Line has begin and end */
2109 if( ( sscanf( psz_temp,
2110 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2111 psz_begin, psz_end, psz_text) != 3 ) &&
2112 /* Line has begin and no end */
2113 ( sscanf( psz_temp,
2114 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2115 psz_begin, psz_text ) != 2) )
2116 /* Line is not recognized */
2118 continue;
2121 /* Get the times */
2122 int64_t i_time = ParseRealTime( psz_begin, &h1, &m1, &s1, &f1 );
2123 p_subtitle->i_start = i_time >= 0 ? i_time : 0;
2125 i_time = ParseRealTime( psz_end, &h2, &m2, &s2, &f2 );
2126 p_subtitle->i_stop = i_time >= 0 ? i_time : -1;
2127 break;
2131 /* Get the following Lines */
2132 for( ;; )
2134 const char *s = TextGetLine( txt );
2136 if( !s )
2138 free( psz_text );
2139 return VLC_EGENERIC;
2142 size_t i_len = strlen( s );
2143 if( i_len == 0 ) break;
2145 if( strcasestr( s, "<time" ) ||
2146 strcasestr( s, "<clear/") )
2148 TextPreviousLine( txt );
2149 break;
2152 size_t i_old = strlen( psz_text );
2154 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2155 if( !psz_text )
2156 return VLC_ENOMEM;
2158 strcat( psz_text, s );
2159 strcat( psz_text, "\n" );
2162 /* Remove the starting ">" that remained after the sscanf */
2163 memmove( &psz_text[0], &psz_text[1], strlen( psz_text ) );
2165 p_subtitle->psz_text = psz_text;
2167 return VLC_SUCCESS;
2170 static int ParseDKS( vlc_object_t *p_obj, subs_properties_t *p_props,
2171 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2173 VLC_UNUSED(p_obj);
2174 VLC_UNUSED(p_props);
2175 VLC_UNUSED( i_idx );
2177 char *psz_text;
2179 for( ;; )
2181 int h1, m1, s1;
2182 int h2, m2, s2;
2183 char *s = TextGetLine( txt );
2185 if( !s )
2186 return VLC_EGENERIC;
2188 psz_text = malloc( strlen( s ) + 1 );
2189 if( !psz_text )
2190 return VLC_ENOMEM;
2192 if( sscanf( s, "[%d:%d:%d]%[^\r\n]",
2193 &h1, &m1, &s1, psz_text ) == 4 )
2195 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2197 s = TextGetLine( txt );
2198 if( !s )
2200 free( psz_text );
2201 return VLC_EGENERIC;
2204 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2205 p_subtitle->i_stop = vlc_tick_from_sec(h2 * 3600 + m2 * 60 + s2 );
2206 else
2207 p_subtitle->i_stop = -1;
2208 break;
2210 free( psz_text );
2213 /* replace [br] by \n */
2214 char *p;
2215 while( ( p = strstr( psz_text, "[br]" ) ) )
2217 *p++ = '\n';
2218 memmove( p, &p[3], strlen(&p[3])+1 );
2221 p_subtitle->psz_text = psz_text;
2222 return VLC_SUCCESS;
2225 static int ParseSubViewer1( vlc_object_t *p_obj, subs_properties_t *p_props,
2226 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2228 VLC_UNUSED(p_obj);
2229 VLC_UNUSED(p_props);
2230 VLC_UNUSED( i_idx );
2231 char *psz_text;
2233 for( ;; )
2235 int h1, m1, s1;
2236 int h2, m2, s2;
2237 char *s = TextGetLine( txt );
2239 if( !s )
2240 return VLC_EGENERIC;
2242 if( sscanf( s, "[%d:%d:%d]", &h1, &m1, &s1 ) == 3 )
2244 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 );
2246 s = TextGetLine( txt );
2247 if( !s )
2248 return VLC_EGENERIC;
2250 psz_text = strdup( s );
2251 if( !psz_text )
2252 return VLC_ENOMEM;
2254 s = TextGetLine( txt );
2255 if( !s )
2257 free( psz_text );
2258 return VLC_EGENERIC;
2261 if( sscanf( s, "[%d:%d:%d]", &h2, &m2, &s2 ) == 3 )
2262 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 );
2263 else
2264 p_subtitle->i_stop = -1;
2266 break;
2270 p_subtitle->psz_text = psz_text;
2272 return VLC_SUCCESS;
2275 static int ParseCommonSBV( vlc_object_t *p_obj, subs_properties_t *p_props,
2276 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2278 VLC_UNUSED(p_obj);
2279 VLC_UNUSED( i_idx );
2280 VLC_UNUSED( p_props );
2281 char *psz_text;
2283 for( ;; )
2285 const char *s = TextGetLine( txt );
2286 int h1 = 0, m1 = 0, s1 = 0, d1 = 0;
2287 int h2 = 0, m2 = 0, s2 = 0, d2 = 0;
2289 if( !s )
2290 return VLC_EGENERIC;
2292 if( sscanf( s,"%d:%d:%d.%d,%d:%d:%d.%d",
2293 &h1, &m1, &s1, &d1,
2294 &h2, &m2, &s2, &d2 ) == 8 )
2296 p_subtitle->i_start = vlc_tick_from_sec( h1 * 3600 + m1 * 60 + s1 ) +
2297 VLC_TICK_FROM_MS( d1 );
2299 p_subtitle->i_stop = vlc_tick_from_sec( h2 * 3600 + m2 * 60 + s2 ) +
2300 VLC_TICK_FROM_MS( d2 );
2301 if( p_subtitle->i_start < p_subtitle->i_stop )
2302 break;
2306 /* Now read text until an empty line */
2307 psz_text = strdup("");
2308 if( !psz_text )
2309 return VLC_ENOMEM;
2311 for( ;; )
2313 const char *s = TextGetLine( txt );
2314 size_t i_len;
2315 size_t i_old;
2317 i_len = s ? strlen( s ) : 0;
2318 if( i_len <= 0 )
2320 p_subtitle->psz_text = psz_text;
2321 return VLC_SUCCESS;
2324 i_old = strlen( psz_text );
2325 psz_text = realloc_or_free( psz_text, i_old + i_len + 1 + 1 );
2326 if( !psz_text )
2327 return VLC_ENOMEM;
2329 strcat( psz_text, s );
2330 strcat( psz_text, "\n" );
2334 static int ParseSCC( vlc_object_t *p_obj, subs_properties_t *p_props,
2335 text_t *txt, subtitle_t *p_subtitle, size_t i_idx )
2337 VLC_UNUSED(p_obj);
2338 VLC_UNUSED( i_idx );
2339 VLC_UNUSED( p_props );
2341 static const struct rates
2343 unsigned val;
2344 vlc_rational_t rate;
2345 bool b_drop_allowed;
2346 } framerates[] = {
2347 { 2398, { 24000, 1001 }, false },
2348 { 2400, { 24, 1 }, false },
2349 { 2500, { 25, 1 }, false },
2350 { 2997, { 30000, 1001 }, true }, /* encoding rate */
2351 { 3000, { 30, 1 }, false },
2352 { 5000, { 50, 1 }, false },
2353 { 5994, { 60000, 1001 }, true },
2354 { 6000, { 60, 1 }, false },
2356 const struct rates *p_rate = &framerates[3];
2357 float f_fps = var_GetFloat( p_obj, "sub-original-fps" );
2358 if( f_fps > 1.0 )
2360 for( size_t i=0; i<ARRAY_SIZE(framerates); i++ )
2362 if( (unsigned)(f_fps * 100) == framerates[i].val )
2364 p_rate = &framerates[i];
2365 break;
2370 for( ;; )
2372 const char *psz_line = TextGetLine( txt );
2373 if( !psz_line )
2374 return VLC_EGENERIC;
2376 unsigned h, m, s, f;
2377 char c;
2378 if( sscanf( psz_line, "%u:%u:%u%c%u ", &h, &m, &s, &c, &f ) != 5 ||
2379 ( c != ':' && c != ';' ) )
2380 continue;
2382 /* convert everything to seconds */
2383 uint64_t i_frames = h * 3600 + m * 60 + s;
2385 if( c == ';' && p_rate->b_drop_allowed ) /* dropframe */
2387 /* convert to frame # to be accurate between inter drop drift
2388 * of 18 frames see http://andrewduncan.net/timecodes/ */
2389 const unsigned i_mins = h * 60 + m;
2390 i_frames = i_frames * p_rate[+1].rate.num + f
2391 - (p_rate[+1].rate.den * 2 * (i_mins - i_mins % 10));
2393 else
2395 /* convert to frame # at 29.97 */
2396 i_frames = i_frames * framerates[3].rate.num / framerates[3].rate.den + f;
2398 p_subtitle->i_start = VLC_TICK_0 + vlc_tick_from_sec(i_frames)*
2399 p_rate->rate.den / p_rate->rate.num;
2400 p_subtitle->i_stop = -1;
2402 const char *psz_text = strchr( psz_line, '\t' );
2403 if( !psz_text && !(psz_text = strchr( psz_line, ' ' )) )
2404 continue;
2406 if ( psz_text[1] == '\0' )
2407 continue;
2409 p_subtitle->psz_text = strdup( psz_text + 1 );
2410 if( !p_subtitle->psz_text )
2411 return VLC_ENOMEM;
2413 break;
2416 return VLC_SUCCESS;
2419 /* Matches filename.xx.srt */
2420 static char * get_language_from_filename( const char * psz_sub_file )
2422 char *psz_ret = NULL;
2423 char *psz_tmp, *psz_language_begin;
2425 if( !psz_sub_file ) return NULL;
2426 char *psz_work = strdup( psz_sub_file );
2428 /* Removing extension, but leaving the dot */
2429 psz_tmp = strrchr( psz_work, '.' );
2430 if( psz_tmp )
2432 psz_tmp[0] = '\0';
2433 psz_language_begin = strrchr( psz_work, '.' );
2434 if( psz_language_begin )
2435 psz_ret = strdup(++psz_language_begin);
2436 psz_tmp[0] = '.';
2439 free( psz_work );
2440 return psz_ret;