1 /*****************************************************************************
2 * subtitle.c: Demux for subtitle text files.
3 *****************************************************************************
4 * Copyright (C) 1999-2007 VLC authors and VideoLAN
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 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
37 #include <vlc_memory.h>
43 #include <vlc_demux.h>
44 #include <vlc_charset.h>
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Open ( vlc_object_t
*p_this
);
50 static void Close( vlc_object_t
*p_this
);
52 #define SUB_DELAY_LONGTEXT \
53 N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")
54 #define SUB_FPS_LONGTEXT \
55 N_("Override the normal frames per second settings. " \
56 "This will only work with MicroDVD and SubRIP (SRT) subtitles.")
57 #define SUB_TYPE_LONGTEXT \
58 N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
59 #define SUB_DESCRIPTION_LONGTEXT \
60 N_("Override the default track description.")
62 static const char *const ppsz_sub_type
[] =
64 "auto", "microdvd", "subrip", "subviewer", "ssa1",
65 "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
66 "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
71 set_shortname( N_("Subtitles"))
72 set_description( N_("Text subtitle parser") )
73 set_capability( "demux", 0 )
74 set_category( CAT_INPUT
)
75 set_subcategory( SUBCAT_INPUT_DEMUX
)
76 add_float( "sub-fps", 0.0,
77 N_("Frames per Second"),
78 SUB_FPS_LONGTEXT
, true )
79 add_integer( "sub-delay", 0,
81 SUB_DELAY_LONGTEXT
, true )
82 add_string( "sub-type", "auto", N_("Subtitle format"),
83 SUB_TYPE_LONGTEXT
, true )
84 change_string_list( ppsz_sub_type
, ppsz_sub_type
)
85 add_string( "sub-description", NULL
, N_("Subtitle description"),
86 SUB_DESCRIPTION_LONGTEXT
, true )
87 set_callbacks( Open
, Close
)
89 add_shortcut( "subtitle" )
92 /*****************************************************************************
94 *****************************************************************************/
97 SUB_TYPE_UNKNOWN
= -1,
105 SUB_TYPE_SUBVIEWER
, /* SUBVIEWER 2 */
106 SUB_TYPE_DVDSUBTITLE
, /* Mplayer calls it subviewer2 */
115 SUB_TYPE_SUBVIEW1
, /* SUBVIEWER 1 - mplayer calls it subrip09,
116 and Gnome subtitles SubViewer 1.0 */
118 SUB_TYPE_SCC
, /* Scenarist Closed Caption */
128 static int TextLoad( text_t
*, stream_t
*s
);
129 static void TextUnload( text_t
* );
141 enum subtitle_type_e i_type
;
142 int64_t i_microsecperframe
;
144 char *psz_header
; /* SSA */
151 int i_time_resolution
;
165 const char *psz_start
;
176 int64_t i_next_demux_date
;
188 subs_properties_t props
;
190 block_t
* (*pf_convert
)( const subtitle_t
* );
193 static int ParseMicroDvd ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
194 static int ParseSubRip ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
195 static int ParseSubViewer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
196 static int ParseSSA ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
197 static int ParseVplayer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
198 static int ParseSami ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
199 static int ParseDVDSubtitle( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
200 static int ParseMPL2 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
201 static int ParseAQT ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
202 static int ParsePJS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
203 static int ParseMPSub ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
204 static int ParseJSS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
205 static int ParsePSB ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
206 static int ParseRealText ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
207 static int ParseDKS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
208 static int ParseSubViewer1 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
209 static int ParseCommonSBV ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
210 static int ParseSCC ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
214 const char *psz_type_name
;
216 const char *psz_name
;
217 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
218 } sub_read_subtitle_function
[] =
220 { "microdvd", SUB_TYPE_MICRODVD
, "MicroDVD", ParseMicroDvd
},
221 { "subrip", SUB_TYPE_SUBRIP
, "SubRIP", ParseSubRip
},
222 { "subviewer", SUB_TYPE_SUBVIEWER
, "SubViewer", ParseSubViewer
},
223 { "ssa1", SUB_TYPE_SSA1
, "SSA-1", ParseSSA
},
224 { "ssa2-4", SUB_TYPE_SSA2_4
, "SSA-2/3/4", ParseSSA
},
225 { "ass", SUB_TYPE_ASS
, "SSA/ASS", ParseSSA
},
226 { "vplayer", SUB_TYPE_VPLAYER
, "VPlayer", ParseVplayer
},
227 { "sami", SUB_TYPE_SAMI
, "SAMI", ParseSami
},
228 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE
, "DVDSubtitle", ParseDVDSubtitle
},
229 { "mpl2", SUB_TYPE_MPL2
, "MPL2", ParseMPL2
},
230 { "aqt", SUB_TYPE_AQT
, "AQTitle", ParseAQT
},
231 { "pjs", SUB_TYPE_PJS
, "PhoenixSub", ParsePJS
},
232 { "mpsub", SUB_TYPE_MPSUB
, "MPSub", ParseMPSub
},
233 { "jacosub", SUB_TYPE_JACOSUB
, "JacoSub", ParseJSS
},
234 { "psb", SUB_TYPE_PSB
, "PowerDivx", ParsePSB
},
235 { "realtext", SUB_TYPE_RT
, "RealText", ParseRealText
},
236 { "dks", SUB_TYPE_DKS
, "DKS", ParseDKS
},
237 { "subviewer1", SUB_TYPE_SUBVIEW1
, "Subviewer 1", ParseSubViewer1
},
238 { "sbv", SUB_TYPE_SBV
, "SBV", ParseCommonSBV
},
239 { "scc", SUB_TYPE_SCC
, "SCC", ParseSCC
},
240 { NULL
, SUB_TYPE_UNKNOWN
, "Unknown", NULL
}
242 /* When adding support for more formats, be sure to add their file extension
243 * to src/input/subtitles.c to enable auto-detection.
246 static int Demux( demux_t
* );
247 static int Control( demux_t
*, int, va_list );
249 static void Fix( demux_t
* );
250 static char * get_language_from_filename( const char * );
252 /*****************************************************************************
253 * Decoder format output function
254 *****************************************************************************/
256 static block_t
*ToTextBlock( const subtitle_t
*p_subtitle
)
259 size_t i_len
= strlen( p_subtitle
->psz_text
) + 1;
261 if( i_len
<= 1 || !(p_block
= block_Alloc( i_len
)) )
264 memcpy( p_block
->p_buffer
, p_subtitle
->psz_text
, i_len
);
269 static block_t
*ToEIA608Block( const subtitle_t
*p_subtitle
)
272 const size_t i_len
= strlen( p_subtitle
->psz_text
);
273 const size_t i_block
= (1 + i_len
/ 5) * 3;
275 if( i_len
< 4 || !(p_block
= block_Alloc( i_block
)) )
278 p_block
->i_buffer
= 0;
280 char *saveptr
= NULL
;
281 char *psz_tok
= strtok_r( p_subtitle
->psz_text
, " ", &saveptr
);
284 sscanf( psz_tok
, "%2x%2x", &a
, &b
) == 2 &&
285 i_block
- p_block
->i_buffer
>= 3 )
287 uint8_t *p_data
= &p_block
->p_buffer
[p_block
->i_buffer
];
291 p_block
->i_buffer
+= 3;
292 psz_tok
= strtok_r( NULL
, " ", &saveptr
);
298 /*****************************************************************************
300 *****************************************************************************/
301 static int Open ( vlc_object_t
*p_this
)
303 demux_t
*p_demux
= (demux_t
*)p_this
;
308 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
310 if( !p_demux
->obj
.force
)
312 msg_Dbg( p_demux
, "subtitle demux discarded" );
316 p_demux
->pf_demux
= Demux
;
317 p_demux
->pf_control
= Control
;
318 p_demux
->p_sys
= p_sys
= malloc( sizeof( demux_sys_t
) );
322 p_sys
->b_slave
= false;
323 p_sys
->b_first_time
= true;
324 p_sys
->i_next_demux_date
= 0;
326 p_sys
->pf_convert
= ToTextBlock
;
328 p_sys
->subtitles
.i_current
= 0;
329 p_sys
->subtitles
.i_count
= 0;
330 p_sys
->subtitles
.p_array
= NULL
;
332 p_sys
->props
.psz_header
= NULL
;
333 p_sys
->props
.i_microsecperframe
= 40000;
334 p_sys
->props
.jss
.b_inited
= false;
335 p_sys
->props
.mpsub
.b_inited
= false;
336 p_sys
->props
.sami
.psz_start
= NULL
;
339 f_fps
= var_CreateGetFloat( p_demux
, "sub-original-fps" ); /* FIXME */
341 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
343 msg_Dbg( p_demux
, "Movie fps: %f", (double) f_fps
);
345 /* Check for override of the fps */
346 f_fps
= var_CreateGetFloat( p_demux
, "sub-fps" );
349 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
350 msg_Dbg( p_demux
, "Override subtitle fps %f", (double) f_fps
);
353 /* Get or probe the type */
354 p_sys
->props
.i_type
= SUB_TYPE_UNKNOWN
;
355 psz_type
= var_CreateGetString( p_demux
, "sub-type" );
356 if( psz_type
&& *psz_type
)
358 for( int i
= 0; ; i
++ )
360 if( sub_read_subtitle_function
[i
].psz_type_name
== NULL
)
363 if( !strcmp( sub_read_subtitle_function
[i
].psz_type_name
,
366 p_sys
->props
.i_type
= sub_read_subtitle_function
[i
].i_type
;
374 const uint64_t i_start_pos
= vlc_stream_Tell( p_demux
->s
);
378 const uint8_t *p_peek
;
379 if( vlc_stream_Peek( p_demux
->s
, &p_peek
, 16 ) < 16 )
392 const char *psz_bom
= NULL
;
395 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
396 if( !memcmp( p_peek
, "\xEF\xBB\xBF", 3 ) )
401 else if( !memcmp( p_peek
, "\xFF\xFE", 2 ) )
404 psz_bom
= "UTF-16LE";
407 else if( !memcmp( p_peek
, "\xFE\xFF", 2 ) )
410 psz_bom
= "UTF-16BE";
415 msg_Dbg( p_demux
, "detected %s Byte Order Mark", psz_bom
);
417 i_peek
= vlc_stream_Peek( p_demux
->s
, &p_peek
, i_peek
);
418 if( unlikely(i_peek
< 16) )
424 stream_t
*p_probestream
= NULL
;
425 if( e_bom
!= UTF8BOM
&& e_bom
!= NOBOM
)
429 char *p_outbuf
= FromCharset( psz_bom
, p_peek
, i_peek
);
430 if( p_outbuf
!= NULL
)
431 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *)p_outbuf
,
433 false ); /* free p_outbuf on release */
438 const size_t i_skip
= (e_bom
== UTF8BOM
) ? 3 : 0;
439 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *) &p_peek
[i_skip
],
440 i_peek
- i_skip
, true );
443 if( p_probestream
== NULL
)
449 /* Probe if unknown type */
450 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
455 msg_Dbg( p_demux
, "autodetecting subtitle format" );
456 for( i_try
= 0; i_try
< 256; i_try
++ )
461 if( (s
= vlc_stream_ReadLine( p_probestream
) ) == NULL
)
464 if( strcasestr( s
, "<SAMI>" ) )
466 p_sys
->props
.i_type
= SUB_TYPE_SAMI
;
469 else if( sscanf( s
, "{%d}{%d}", &i_dummy
, &i_dummy
) == 2 ||
470 sscanf( s
, "{%d}{}", &i_dummy
) == 1)
472 p_sys
->props
.i_type
= SUB_TYPE_MICRODVD
;
475 else if( sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d,%d",
476 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
477 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
478 sscanf( s
, "%d:%d:%d --> %d:%d:%d,%d",
479 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
480 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
481 sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d",
482 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
483 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
484 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d.%d",
485 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
486 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
487 sscanf( s
, "%d:%d:%d --> %d:%d:%d.%d",
488 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
489 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
490 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d",
491 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
492 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
493 sscanf( s
, "%d:%d:%d --> %d:%d:%d",
494 &i_dummy
,&i_dummy
,&i_dummy
,
495 &i_dummy
,&i_dummy
,&i_dummy
) == 6 )
497 p_sys
->props
.i_type
= SUB_TYPE_SUBRIP
;
500 else if( !strncasecmp( s
, "!: This is a Sub Station Alpha v1", 33 ) )
502 p_sys
->props
.i_type
= SUB_TYPE_SSA1
;
505 else if( !strncasecmp( s
, "ScriptType: v4.00+", 18 ) )
507 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
510 else if( !strncasecmp( s
, "ScriptType: v4.00", 17 ) )
512 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
515 else if( !strncasecmp( s
, "Dialogue: Marked", 16 ) )
517 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
520 else if( !strncasecmp( s
, "Dialogue:", 9 ) )
522 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
525 else if( strcasestr( s
, "[INFORMATION]" ) )
527 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEWER
; /* I hope this will work */
530 else if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d",
531 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
532 &i_dummy
, &i_dummy
, &i_dummy
) == 7 ||
533 sscanf( s
, "@%d @%d", &i_dummy
, &i_dummy
) == 2)
535 p_sys
->props
.i_type
= SUB_TYPE_JACOSUB
;
538 else if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
539 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
540 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
) == 8 )
542 p_sys
->props
.i_type
= SUB_TYPE_SBV
;
545 else if( sscanf( s
, "%d:%d:%d:", &i_dummy
, &i_dummy
, &i_dummy
) == 3 ||
546 sscanf( s
, "%d:%d:%d ", &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
548 p_sys
->props
.i_type
= SUB_TYPE_VPLAYER
;
551 else if( sscanf( s
, "{T %d:%d:%d:%d", &i_dummy
, &i_dummy
,
552 &i_dummy
, &i_dummy
) == 4 )
554 p_sys
->props
.i_type
= SUB_TYPE_DVDSUBTITLE
;
557 else if( sscanf( s
, "[%d:%d:%d]%c",
558 &i_dummy
, &i_dummy
, &i_dummy
, &p_dummy
) == 4 )
560 p_sys
->props
.i_type
= SUB_TYPE_DKS
;
563 else if( strstr( s
, "*** START SCRIPT" ) )
565 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEW1
;
568 else if( sscanf( s
, "[%d][%d]", &i_dummy
, &i_dummy
) == 2 ||
569 sscanf( s
, "[%d][]", &i_dummy
) == 1)
571 p_sys
->props
.i_type
= SUB_TYPE_MPL2
;
574 else if( sscanf (s
, "FORMAT=%d", &i_dummy
) == 1 ||
575 ( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1
578 p_sys
->props
.i_type
= SUB_TYPE_MPSUB
;
581 else if( sscanf( s
, "-->> %d", &i_dummy
) == 1 )
583 p_sys
->props
.i_type
= SUB_TYPE_AQT
;
586 else if( sscanf( s
, "%d,%d,", &i_dummy
, &i_dummy
) == 2 )
588 p_sys
->props
.i_type
= SUB_TYPE_PJS
;
591 else if( sscanf( s
, "{%d:%d:%d}",
592 &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
594 p_sys
->props
.i_type
= SUB_TYPE_PSB
;
597 else if( strcasestr( s
, "<time" ) )
599 p_sys
->props
.i_type
= SUB_TYPE_RT
;
602 else if( !strncasecmp( s
, "WEBVTT",6 ) )
607 else if( !strncasecmp( s
, "Scenarist_SCC V1.0", 18 ) )
609 p_sys
->props
.i_type
= SUB_TYPE_SCC
;
610 p_sys
->pf_convert
= ToEIA608Block
;
621 vlc_stream_Delete( p_probestream
);
623 /* Quit on unknown subtitles */
624 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
627 /* Ensure it will work with non seekable streams */
628 assert( i_start_pos
== vlc_stream_Tell( p_demux
->s
) );
630 msg_Warn( p_demux
, "failed to recognize subtitle type" );
635 for( int i
= 0; ; i
++ )
637 if( sub_read_subtitle_function
[i
].i_type
== p_sys
->props
.i_type
)
639 msg_Dbg( p_demux
, "detected %s format",
640 sub_read_subtitle_function
[i
].psz_name
);
641 pf_read
= sub_read_subtitle_function
[i
].pf_read
;
646 msg_Dbg( p_demux
, "loading all subtitles..." );
648 if( e_bom
== UTF8BOM
&& /* skip BOM */
649 vlc_stream_Read( p_demux
->s
, NULL
, 3 ) != 3 )
655 /* Load the whole file */
657 TextLoad( &txtlines
, p_demux
->s
);
660 for( size_t i_max
= 0; i_max
< SIZE_MAX
- 500 * sizeof(subtitle_t
); )
662 if( p_sys
->subtitles
.i_count
>= i_max
)
665 subtitle_t
*p_realloc
= realloc( p_sys
->subtitles
.p_array
, sizeof(subtitle_t
) * i_max
);
666 if( p_realloc
== NULL
)
668 TextUnload( &txtlines
);
672 p_sys
->subtitles
.p_array
= p_realloc
;
675 if( pf_read( VLC_OBJECT(p_demux
), &p_sys
->props
, &txtlines
,
676 &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
],
677 p_sys
->subtitles
.i_count
) )
680 p_sys
->subtitles
.i_count
++;
683 TextUnload( &txtlines
);
685 msg_Dbg(p_demux
, "loaded %zu subtitles", p_sys
->subtitles
.i_count
);
687 /* Fix subtitle (order and time) *** */
688 p_sys
->subtitles
.i_current
= 0;
690 if( p_sys
->subtitles
.i_count
> 0 )
691 p_sys
->i_length
= p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
-1].i_stop
;
693 /* *** add subtitle ES *** */
694 if( p_sys
->props
.i_type
== SUB_TYPE_SSA1
||
695 p_sys
->props
.i_type
== SUB_TYPE_SSA2_4
||
696 p_sys
->props
.i_type
== SUB_TYPE_ASS
)
699 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SSA
);
701 else if( p_sys
->props
.i_type
== SUB_TYPE_SCC
)
703 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_CEA608
);
704 fmt
.subs
.cc
.i_reorder_depth
= -1;
707 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SUBT
);
709 /* Stupid language detection in the filename */
710 char * psz_language
= get_language_from_filename( p_demux
->psz_file
);
714 fmt
.psz_language
= psz_language
;
715 msg_Dbg( p_demux
, "detected language %s of subtitle: %s", psz_language
,
716 p_demux
->psz_location
);
720 fmt
.subs
.psz_encoding
= strdup( psz_bom
);
721 char *psz_description
= var_InheritString( p_demux
, "sub-description" );
722 if( psz_description
&& *psz_description
)
723 fmt
.psz_description
= psz_description
;
725 free( psz_description
);
726 if( p_sys
->props
.psz_header
!= NULL
&&
727 (fmt
.p_extra
= strdup( p_sys
->props
.psz_header
)) )
729 fmt
.i_extra
= strlen( p_sys
->props
.psz_header
) + 1;
732 p_sys
->es
= es_out_Add( p_demux
->out
, &fmt
);
733 es_format_Clean( &fmt
);
734 if( p_sys
->es
== NULL
)
743 /*****************************************************************************
744 * Close: Close subtitle demux
745 *****************************************************************************/
746 static void Close( vlc_object_t
*p_this
)
748 demux_t
*p_demux
= (demux_t
*)p_this
;
749 demux_sys_t
*p_sys
= p_demux
->p_sys
;
751 for( size_t i
= 0; i
< p_sys
->subtitles
.i_count
; i
++ )
752 free( p_sys
->subtitles
.p_array
[i
].psz_text
);
753 free( p_sys
->subtitles
.p_array
);
754 free( p_sys
->props
.psz_header
);
759 /*****************************************************************************
761 *****************************************************************************/
762 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
764 demux_sys_t
*p_sys
= p_demux
->p_sys
;
771 *va_arg( args
, bool * ) = true;
774 case DEMUX_GET_LENGTH
:
775 pi64
= va_arg( args
, int64_t * );
776 *pi64
= p_sys
->i_length
;
780 pi64
= va_arg( args
, int64_t * );
781 *pi64
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
783 *pi64
= p_sys
->i_next_demux_date
;
787 i64
= va_arg( args
, int64_t );
788 for( size_t i
= 0; i
+ 1< p_sys
->subtitles
.i_count
; i
++ )
790 if( p_sys
->subtitles
.p_array
[i
+ 1].i_start
>= i64
)
792 p_sys
->subtitles
.i_current
= i
;
793 p_sys
->i_next_demux_date
= i64
;
794 p_sys
->b_first_time
= true;
800 case DEMUX_GET_POSITION
:
801 pf
= va_arg( args
, double * );
802 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
806 else if( p_sys
->subtitles
.i_count
> 0 && p_sys
->i_length
)
808 *pf
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
810 *pf
= p_sys
->i_next_demux_date
;
811 *pf
/= p_sys
->i_length
;
819 case DEMUX_SET_POSITION
:
820 f
= va_arg( args
, double );
821 if( p_sys
->subtitles
.i_count
&& p_sys
->i_length
)
823 i64
= VLC_TS_0
+ f
* p_sys
->i_length
;
824 return demux_Control( p_demux
, DEMUX_SET_TIME
, i64
);
828 case DEMUX_SET_NEXT_DEMUX_TIME
:
829 p_sys
->b_slave
= true;
830 p_sys
->i_next_demux_date
= va_arg( args
, int64_t ) - VLC_TS_0
;
833 case DEMUX_GET_PTS_DELAY
:
836 case DEMUX_GET_ATTACHMENTS
:
837 case DEMUX_GET_TITLE_INFO
:
838 case DEMUX_HAS_UNSUPPORTED_META
:
839 case DEMUX_CAN_RECORD
:
847 /*****************************************************************************
848 * Demux: Send subtitle to decoder
849 *****************************************************************************/
850 static int Demux( demux_t
*p_demux
)
852 demux_sys_t
*p_sys
= p_demux
->p_sys
;
854 int64_t i_barrier
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
856 i_barrier
= p_sys
->i_next_demux_date
;
858 while( p_sys
->subtitles
.i_current
< p_sys
->subtitles
.i_count
&&
859 p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
].i_start
<= i_barrier
)
861 const subtitle_t
*p_subtitle
= &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
];
863 if ( !p_sys
->b_slave
&& p_sys
->b_first_time
)
865 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
866 p_sys
->b_first_time
= false;
869 if( p_subtitle
->i_start
>= 0 )
871 block_t
*p_block
= p_sys
->pf_convert( p_subtitle
);
875 p_block
->i_pts
= VLC_TS_0
+ p_subtitle
->i_start
;
876 if( p_subtitle
->i_stop
>= 0 && p_subtitle
->i_stop
>= p_subtitle
->i_start
)
877 p_block
->i_length
= p_subtitle
->i_stop
- p_subtitle
->i_start
;
879 es_out_Send( p_demux
->out
, p_sys
->es
, p_block
);
883 p_sys
->subtitles
.i_current
++;
886 if ( !p_sys
->b_slave
)
888 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
889 p_sys
->i_next_demux_date
+= CLOCK_FREQ
/ 8;
892 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
893 return VLC_DEMUXER_EOF
;
895 return VLC_DEMUXER_SUCCESS
;
899 static int subtitle_cmp( const void *first
, const void *second
)
901 int64_t result
= ((subtitle_t
*)(first
))->i_start
- ((subtitle_t
*)(second
))->i_start
;
902 /* Return -1, 0 ,1, and not directly subtraction
903 * as result can be > INT_MAX */
904 return result
== 0 ? 0 : result
> 0 ? 1 : -1;
906 /*****************************************************************************
907 * Fix: fix time stamp and order of subtitle
908 *****************************************************************************/
909 static void Fix( demux_t
*p_demux
)
911 demux_sys_t
*p_sys
= p_demux
->p_sys
;
913 /* *** fix order (to be sure...) *** */
914 qsort( p_sys
->subtitles
.p_array
, p_sys
->subtitles
.i_count
, sizeof( p_sys
->subtitles
.p_array
[0] ), subtitle_cmp
);
917 static int TextLoad( text_t
*txt
, stream_t
*s
)
923 txt
->i_line_count
= 0;
925 txt
->line
= calloc( i_line_max
, sizeof( char * ) );
929 /* load the complete file */
932 char *psz
= vlc_stream_ReadLine( s
);
937 txt
->line
[txt
->i_line_count
] = psz
;
938 if( txt
->i_line_count
+ 1 >= i_line_max
)
941 char **p_realloc
= realloc( txt
->line
, i_line_max
* sizeof( char * ) );
942 if( p_realloc
== NULL
)
944 txt
->line
= p_realloc
;
949 if( txt
->i_line_count
== 0 )
957 static void TextUnload( text_t
*txt
)
959 if( txt
->i_line_count
)
961 for( size_t i
= 0; i
< txt
->i_line_count
; i
++ )
962 free( txt
->line
[i
] );
966 txt
->i_line_count
= 0;
969 static char *TextGetLine( text_t
*txt
)
971 if( txt
->i_line
>= txt
->i_line_count
)
974 return txt
->line
[txt
->i_line
++];
976 static void TextPreviousLine( text_t
*txt
)
978 if( txt
->i_line
> 0 )
982 /*****************************************************************************
983 * Specific Subtitle function
984 *****************************************************************************/
987 * {n1}{n2}Line1|Line2|Line3....
988 * where n1 and n2 are the video frame number (n2 can be empty)
990 static int ParseMicroDvd( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
991 text_t
*txt
, subtitle_t
*p_subtitle
,
1002 const char *s
= TextGetLine( txt
);
1004 return VLC_EGENERIC
;
1006 psz_text
= malloc( strlen(s
) + 1 );
1012 if( sscanf( s
, "{%d}{}%[^\r\n]", &i_start
, psz_text
) == 2 ||
1013 sscanf( s
, "{%d}{%d}%[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1015 if( i_start
!= 1 || i_stop
!= 1 )
1018 /* We found a possible setting of the framerate "{1}{1}23.976" */
1019 /* Check if it's usable, and if the sub-fps is not set */
1020 float f_fps
= us_strtof( psz_text
, NULL
);
1021 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1022 p_props
->i_microsecperframe
= llroundf(1000000.f
/ f_fps
);
1027 /* replace | by \n */
1028 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1030 if( psz_text
[i
] == '|' )
1035 p_subtitle
->i_start
= i_start
* p_props
->i_microsecperframe
;
1036 p_subtitle
->i_stop
= i_stop
>= 0 ? (i_stop
* p_props
->i_microsecperframe
) : -1;
1037 p_subtitle
->psz_text
= psz_text
;
1041 /* ParseSubRipSubViewer
1044 * h1:m1:s1,d1 --> h2:m2:s2,d2
1049 * Format SubViewer v1/v2
1050 * h1:m1:s1.d1,h2:m2:s2.d2
1055 * We ignore line number for SubRip
1057 static int ParseSubRipSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1058 text_t
*txt
, subtitle_t
*p_subtitle
,
1059 int (* pf_parse_timing
)(subtitle_t
*, const char *),
1063 VLC_UNUSED(p_props
);
1068 const char *s
= TextGetLine( txt
);
1071 return VLC_EGENERIC
;
1073 if( pf_parse_timing( p_subtitle
, s
) == VLC_SUCCESS
&&
1074 p_subtitle
->i_start
< p_subtitle
->i_stop
)
1080 /* Now read text until an empty line */
1081 psz_text
= strdup("");
1087 const char *s
= TextGetLine( txt
);
1091 i_len
= s
? strlen( s
) : 0;
1094 p_subtitle
->psz_text
= psz_text
;
1098 i_old
= strlen( psz_text
);
1099 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1104 strcat( psz_text
, s
);
1105 strcat( psz_text
, "\n" );
1107 /* replace [br] by \n */
1112 while( ( p
= strstr( psz_text
, "[br]" ) ) )
1115 memmove( p
, &p
[3], strlen(&p
[3])+1 );
1121 /* subtitle_ParseSubRipTimingValue
1122 * Parses SubRip timing value.
1124 static int subtitle_ParseSubRipTimingValue(int64_t *timing_value
,
1127 int h1
, m1
, s1
, d1
= 0;
1129 if ( sscanf( s
, "%d:%d:%d,%d",
1130 &h1
, &m1
, &s1
, &d1
) == 4 ||
1131 sscanf( s
, "%d:%d:%d.%d",
1132 &h1
, &m1
, &s1
, &d1
) == 4 ||
1133 sscanf( s
, "%d:%d:%d",
1134 &h1
, &m1
, &s1
) == 3 )
1136 (*timing_value
) = ( (int64_t)h1
* 3600 * 1000 +
1137 (int64_t)m1
* 60 * 1000 +
1138 (int64_t)s1
* 1000 +
1139 (int64_t)d1
) * 1000;
1144 return VLC_EGENERIC
;
1147 /* subtitle_ParseSubRipTiming
1148 * Parses SubRip timing.
1150 static int subtitle_ParseSubRipTiming( subtitle_t
*p_subtitle
,
1153 int i_result
= VLC_EGENERIC
;
1154 char *psz_start
, *psz_stop
;
1155 psz_start
= malloc( strlen(s
) + 1 );
1156 psz_stop
= malloc( strlen(s
) + 1 );
1158 if( sscanf( s
, "%s --> %s", psz_start
, psz_stop
) == 2 &&
1159 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_start
, psz_start
) == VLC_SUCCESS
&&
1160 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_stop
, psz_stop
) == VLC_SUCCESS
)
1162 i_result
= VLC_SUCCESS
;
1172 static int ParseSubRip( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1173 text_t
*txt
, subtitle_t
*p_subtitle
,
1176 VLC_UNUSED( i_idx
);
1177 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1178 &subtitle_ParseSubRipTiming
,
1182 /* subtitle_ParseSubViewerTiming
1183 * Parses SubViewer timing.
1185 static int subtitle_ParseSubViewerTiming( subtitle_t
*p_subtitle
,
1188 int h1
, m1
, s1
, d1
, h2
, m2
, s2
, d2
;
1190 if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
1191 &h1
, &m1
, &s1
, &d1
, &h2
, &m2
, &s2
, &d2
) == 8 )
1193 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1194 (int64_t)m1
* 60*1000 +
1195 (int64_t)s1
* 1000 +
1196 (int64_t)d1
) * 1000;
1198 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1199 (int64_t)m2
* 60*1000 +
1200 (int64_t)s2
* 1000 +
1201 (int64_t)d2
) * 1000;
1204 return VLC_EGENERIC
;
1209 static int ParseSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1210 text_t
*txt
, subtitle_t
*p_subtitle
,
1213 VLC_UNUSED( i_idx
);
1215 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1216 &subtitle_ParseSubViewerTiming
,
1222 static int ParseSSA( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1223 text_t
*txt
, subtitle_t
*p_subtitle
,
1227 size_t header_len
= 0;
1231 const char *s
= TextGetLine( txt
);
1232 int h1
, m1
, s1
, c1
, h2
, m2
, s2
, c2
;
1233 char *psz_text
, *psz_temp
;
1237 return VLC_EGENERIC
;
1239 /* We expect (SSA2-4):
1240 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1241 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1243 * 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.
1247 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1248 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1251 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1252 psz_text
= malloc( strlen(s
) );
1257 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1263 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1264 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1265 if( p_props
->i_type
== SUB_TYPE_SSA1
)
1267 /* SSA1 has only 8 commas before the text starts, not 9 */
1268 memmove( &psz_text
[1], psz_text
, strlen(psz_text
)+1 );
1273 int i_layer
= ( p_props
->i_type
== SUB_TYPE_ASS
) ? atoi( temp
) : 0;
1275 /* ReadOrder, Layer, %s(rest of fields) */
1276 if( asprintf( &psz_temp
, "%zu,%d,%s", i_idx
, i_layer
, psz_text
) == -1 )
1283 psz_text
= psz_temp
;
1286 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1287 (int64_t)m1
* 60*1000 +
1288 (int64_t)s1
* 1000 +
1289 (int64_t)c1
* 10 ) * 1000;
1290 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1291 (int64_t)m2
* 60*1000 +
1292 (int64_t)s2
* 1000 +
1293 (int64_t)c2
* 10 ) * 1000;
1294 p_subtitle
->psz_text
= psz_text
;
1299 /* All the other stuff we add to the header field */
1300 if( header_len
== 0 && p_props
->psz_header
)
1301 header_len
= strlen( p_props
->psz_header
);
1303 size_t s_len
= strlen( s
);
1304 p_props
->psz_header
= realloc_or_free( p_props
->psz_header
, header_len
+ s_len
+ 2 );
1305 if( !p_props
->psz_header
)
1307 snprintf( p_props
->psz_header
+ header_len
, s_len
+ 2, "%s\n", s
);
1308 header_len
+= s_len
+ 1;
1314 * h:m:s:Line1|Line2|Line3....
1316 * h:m:s Line1|Line2|Line3....
1318 static int ParseVplayer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1319 text_t
*txt
, subtitle_t
*p_subtitle
,
1323 VLC_UNUSED(p_props
);
1324 VLC_UNUSED( i_idx
);
1329 const char *s
= TextGetLine( txt
);
1333 return VLC_EGENERIC
;
1335 psz_text
= malloc( strlen( s
) + 1 );
1339 if( sscanf( s
, "%d:%d:%d%*c%[^\r\n]",
1340 &h1
, &m1
, &s1
, psz_text
) == 4 )
1342 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1343 (int64_t)m1
* 60*1000 +
1344 (int64_t)s1
* 1000 ) * 1000;
1345 p_subtitle
->i_stop
= -1;
1351 /* replace | by \n */
1352 for( size_t i
= 0; psz_text
[i
] != '\0'; i
++ )
1354 if( psz_text
[i
] == '|' )
1357 p_subtitle
->psz_text
= psz_text
;
1363 static const char *ParseSamiSearch( text_t
*txt
,
1364 const char *psz_start
, const char *psz_str
)
1366 if( psz_start
&& strcasestr( psz_start
, psz_str
) )
1368 const char *s
= strcasestr( psz_start
, psz_str
);
1369 return &s
[strlen( psz_str
)];
1374 const char *p
= TextGetLine( txt
);
1378 const char *s
= strcasestr( p
, psz_str
);
1380 return &s
[strlen( psz_str
)];
1383 static int ParseSami( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1384 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1387 VLC_UNUSED(p_props
);
1388 VLC_UNUSED( i_idx
);
1392 unsigned int i_text
;
1393 char text
[8192]; /* Arbitrary but should be long enough */
1395 /* search "Start=" */
1396 s
= ParseSamiSearch( txt
, p_props
->sami
.psz_start
, "Start=" );
1397 p_props
->sami
.psz_start
= NULL
;
1399 return VLC_EGENERIC
;
1401 /* get start value */
1403 i_start
= strtol( s
, &psz_end
, 0 );
1407 if( !( s
= ParseSamiSearch( txt
, s
, "<P" ) ) )
1408 return VLC_EGENERIC
;
1411 if( !( s
= ParseSamiSearch( txt
, s
, ">" ) ) )
1412 return VLC_EGENERIC
;
1416 /* now get all txt until a "Start=" line */
1420 /* Search non empty line */
1421 while( s
&& *s
== '\0' )
1422 s
= TextGetLine( txt
);
1428 if( !strncasecmp( s
, "<br", 3 ) )
1432 else if( strcasestr( s
, "Start=" ) )
1434 p_props
->sami
.psz_start
= s
;
1437 s
= ParseSamiSearch( txt
, s
, ">" );
1439 else if( !strncmp( s
, " ", 6 ) )
1444 else if( *s
== '\t' )
1454 if( c
!= '\0' && i_text
+1 < sizeof(text
) )
1457 text
[i_text
] = '\0';
1461 p_subtitle
->i_start
= i_start
* 1000;
1462 p_subtitle
->i_stop
= -1;
1463 p_subtitle
->psz_text
= strdup( text
);
1475 * TODO it can have a header
1482 * LANG support would be cool
1483 * CODEPAGE is probably mandatory FIXME
1485 static int ParseDVDSubtitle(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1486 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1489 VLC_UNUSED(p_props
);
1490 VLC_UNUSED( i_idx
);
1495 const char *s
= TextGetLine( txt
);
1499 return VLC_EGENERIC
;
1503 &h1
, &m1
, &s1
, &c1
) == 4 )
1505 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1506 (int64_t)m1
* 60*1000 +
1507 (int64_t)s1
* 1000 +
1508 (int64_t)c1
* 10) * 1000;
1509 p_subtitle
->i_stop
= -1;
1514 /* Now read text until a line containing "}" */
1515 psz_text
= strdup("");
1520 const char *s
= TextGetLine( txt
);
1527 return VLC_EGENERIC
;
1530 i_len
= strlen( s
);
1531 if( i_len
== 1 && s
[0] == '}')
1533 p_subtitle
->psz_text
= psz_text
;
1537 i_old
= strlen( psz_text
);
1538 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1541 strcat( psz_text
, s
);
1542 strcat( psz_text
, "\n" );
1548 * [n1][n2]Line1|Line2|Line3...
1549 * where n1 and n2 are the video frame number (n2 can be empty)
1551 static int ParseMPL2(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1552 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1555 VLC_UNUSED(p_props
);
1556 VLC_UNUSED( i_idx
);
1562 const char *s
= TextGetLine( txt
);
1567 return VLC_EGENERIC
;
1569 psz_text
= malloc( strlen(s
) + 1 );
1575 if( sscanf( s
, "[%d][] %[^\r\n]", &i_start
, psz_text
) == 2 ||
1576 sscanf( s
, "[%d][%d] %[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1578 p_subtitle
->i_start
= (int64_t)i_start
* 100000;
1579 p_subtitle
->i_stop
= i_stop
>= 0 ? ((int64_t)i_stop
* 100000) : -1;
1585 for( i
= 0; psz_text
[i
] != '\0'; )
1587 /* replace | by \n */
1588 if( psz_text
[i
] == '|' )
1592 if( psz_text
[i
] == '/' && ( i
== 0 || psz_text
[i
-1] == '\n' ) )
1593 memmove( &psz_text
[i
], &psz_text
[i
+1], strlen(&psz_text
[i
+1])+1 );
1597 p_subtitle
->psz_text
= psz_text
;
1601 static int ParseAQT(vlc_object_t
*p_obj
, subs_properties_t
*p_props
, text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1604 VLC_UNUSED(p_props
);
1605 VLC_UNUSED( i_idx
);
1607 char *psz_text
= strdup( "" );
1609 int i_firstline
= 1;
1615 const char *s
= TextGetLine( txt
);
1620 return VLC_EGENERIC
;
1624 if( sscanf (s
, "-->> %d", &t
) == 1)
1626 p_subtitle
->i_start
= (int64_t)t
; /* * FPS*/
1627 p_subtitle
->i_stop
= -1;
1629 /* Starting of a subtitle */
1634 /* We have been too far: end of the subtitle, begin of next */
1637 TextPreviousLine( txt
);
1644 i_old
= strlen( psz_text
) + 1;
1645 psz_text
= realloc_or_free( psz_text
, i_old
+ strlen( s
) + 1 );
1648 strcat( psz_text
, s
);
1649 strcat( psz_text
, "\n" );
1650 if( txt
->i_line
== txt
->i_line_count
)
1654 p_subtitle
->psz_text
= psz_text
;
1658 static int ParsePJS(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1659 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1662 VLC_UNUSED(p_props
);
1663 VLC_UNUSED( i_idx
);
1670 const char *s
= TextGetLine( txt
);
1674 return VLC_EGENERIC
;
1676 psz_text
= malloc( strlen(s
) + 1 );
1681 if( sscanf (s
, "%d,%d,\"%[^\n\r]", &t1
, &t2
, psz_text
) == 3 )
1683 /* 1/10th of second ? Frame based ? FIXME */
1684 p_subtitle
->i_start
= 10 * t1
;
1685 p_subtitle
->i_stop
= 10 * t2
;
1686 /* Remove latest " */
1687 psz_text
[ strlen(psz_text
) - 1 ] = '\0';
1694 /* replace | by \n */
1695 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1697 if( psz_text
[i
] == '|' )
1701 p_subtitle
->psz_text
= psz_text
;
1702 msg_Dbg( p_obj
, "%s", psz_text
);
1706 static int ParseMPSub( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1707 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1709 VLC_UNUSED( i_idx
);
1711 char *psz_text
= strdup( "" );
1713 if( !p_props
->mpsub
.b_inited
)
1715 p_props
->mpsub
.f_total
= 0.0;
1716 p_props
->mpsub
.f_factor
= 0.0;
1718 p_props
->mpsub
.b_inited
= true;
1726 const char *s
= TextGetLine( txt
);
1730 return VLC_EGENERIC
;
1733 if( strstr( s
, "FORMAT" ) )
1735 if( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1 && p_dummy
== 'E')
1737 p_props
->mpsub
.f_factor
= 100.0;
1741 psz_temp
= malloc( strlen(s
) );
1748 if( sscanf( s
, "FORMAT=%[^\r\n]", psz_temp
) )
1750 float f_fps
= us_strtof( psz_temp
, NULL
);
1752 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1753 var_SetFloat( p_obj
, "sub-fps", f_fps
);
1755 p_props
->mpsub
.f_factor
= 1.f
;
1763 float f1
= us_strtof( s
, &psz_temp
);
1766 float f2
= us_strtof( psz_temp
, NULL
);
1767 p_props
->mpsub
.f_total
+= f1
* p_props
->mpsub
.f_factor
;
1768 p_subtitle
->i_start
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1769 p_props
->mpsub
.f_total
+= f2
* p_props
->mpsub
.f_factor
;
1770 p_subtitle
->i_stop
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1777 const char *s
= TextGetLine( txt
);
1782 return VLC_EGENERIC
;
1785 size_t i_len
= strlen( s
);
1789 size_t i_old
= strlen( psz_text
);
1791 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1795 strcat( psz_text
, s
);
1796 strcat( psz_text
, "\n" );
1799 p_subtitle
->psz_text
= psz_text
;
1803 static int ParseJSS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1804 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1806 VLC_UNUSED( i_idx
);
1807 char *psz_text
, *psz_orig
;
1808 char *psz_text2
, *psz_orig2
;
1810 if( !p_props
->jss
.b_inited
)
1812 p_props
->jss
.i_comment
= 0;
1813 p_props
->jss
.i_time_resolution
= 30;
1814 p_props
->jss
.i_time_shift
= 0;
1816 p_props
->jss
.b_inited
= true;
1819 /* Parse the main lines */
1822 const char *s
= TextGetLine( txt
);
1824 return VLC_EGENERIC
;
1826 size_t line_length
= strlen( s
);
1827 psz_orig
= malloc( line_length
+ 1 );
1830 psz_text
= psz_orig
;
1832 /* Complete time lines */
1833 int h1
, h2
, m1
, m2
, s1
, s2
, f1
, f2
;
1834 if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1835 &h1
, &m1
, &s1
, &f1
, &h2
, &m2
, &s2
, &f2
, psz_text
) == 9 )
1837 p_subtitle
->i_start
= ( ( (int64_t) h1
*3600 + m1
* 60 + s1
) +
1838 (int64_t)( ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1840 p_subtitle
->i_stop
= ( ( (int64_t) h2
*3600 + m2
* 60 + s2
) +
1841 (int64_t)( ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1845 /* Short time lines */
1846 else if( sscanf( s
, "@%d @%d %[^\n\r]", &f1
, &f2
, psz_text
) == 3 )
1848 p_subtitle
->i_start
= ((int64_t)
1849 ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1850 p_subtitle
->i_stop
= ((int64_t)
1851 ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1854 /* General Directive lines */
1855 /* Only TIME and SHIFT are supported so far */
1856 else if( s
[0] == '#' )
1858 int h
= 0, m
=0, sec
= 1, f
= 1;
1862 strcpy( psz_text
, s
);
1864 switch( toupper( (unsigned char)psz_text
[1] ) )
1867 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 6 : 2 ;
1868 if ( shift
> line_length
)
1871 if( sscanf( &psz_text
[shift
], "%d", &h
) )
1873 /* Negative shifting */
1880 if( sscanf( &psz_text
[shift
], "%*d:%d", &m
) )
1882 if( sscanf( &psz_text
[shift
], "%*d:%*d:%d", &sec
) )
1884 sscanf( &psz_text
[shift
], "%*d:%*d:%*d.%d", &f
);
1889 sscanf( &psz_text
[shift
], "%d:%d.%d",
1897 sscanf( &psz_text
[shift
], "%d.%d", &sec
, &f
);
1900 p_props
->jss
.i_time_shift
= ( ( h
* 3600 + m
* 60 + sec
)
1901 * p_props
->jss
.i_time_resolution
+ f
) * inv
;
1906 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 8 : 2 ;
1907 if ( shift
> line_length
)
1910 sscanf( &psz_text
[shift
], "%d", &p_props
->jss
.i_time_resolution
);
1917 /* Unkown type line, probably a comment */
1924 while( psz_text
[ strlen( psz_text
) - 1 ] == '\\' )
1926 const char *s2
= TextGetLine( txt
);
1931 return VLC_EGENERIC
;
1934 size_t i_len
= strlen( s2
);
1938 size_t i_old
= strlen( psz_text
);
1940 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 );
1944 psz_orig
= psz_text
;
1945 strcat( psz_text
, s2
);
1948 /* Skip the blanks */
1949 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1951 /* Parse the directives */
1952 if( isalpha( (unsigned char)*psz_text
) || *psz_text
== '[' )
1954 while( *psz_text
&& *psz_text
!= ' ' )
1957 /* Directives are NOT parsed yet */
1958 /* This has probably a better place in a decoder ? */
1959 /* directive = malloc( strlen( psz_text ) + 1 );
1960 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1963 /* Skip the blanks after directives */
1964 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1966 /* Clean all the lines from inline comments and other stuffs */
1967 psz_orig2
= calloc( strlen( psz_text
) + 1, 1 );
1968 psz_text2
= psz_orig2
;
1970 for( ; *psz_text
!= '\0' && *psz_text
!= '\n' && *psz_text
!= '\r'; )
1975 p_props
->jss
.i_comment
++;
1978 if( p_props
->jss
.i_comment
)
1980 p_props
->jss
.i_comment
= 0;
1981 if( (*(psz_text
+ 1 ) ) == ' ' ) psz_text
++;
1985 if( !p_props
->jss
.i_comment
)
1993 if( (*(psz_text
+ 1 ) ) == ' ' || (*(psz_text
+ 1 ) ) == '\t' )
1995 if( !p_props
->jss
.i_comment
)
2002 if( (*(psz_text
+ 1 ) ) == 'n' )
2009 if( ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'C' ) ||
2010 ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'F' ) )
2015 if( (*(psz_text
+ 1 ) ) == 'B' || (*(psz_text
+ 1 ) ) == 'b' ||
2016 (*(psz_text
+ 1 ) ) == 'I' || (*(psz_text
+ 1 ) ) == 'i' ||
2017 (*(psz_text
+ 1 ) ) == 'U' || (*(psz_text
+ 1 ) ) == 'u' ||
2018 (*(psz_text
+ 1 ) ) == 'D' || (*(psz_text
+ 1 ) ) == 'N' )
2023 if( (*(psz_text
+ 1 ) ) == '~' || (*(psz_text
+ 1 ) ) == '{' ||
2024 (*(psz_text
+ 1 ) ) == '\\' )
2026 else if( ( *(psz_text
+ 1 ) == '\r' || *(psz_text
+ 1 ) == '\n' ) &&
2027 *(psz_text
+ 1 ) != '\0' )
2033 if( !p_props
->jss
.i_comment
)
2035 *psz_text2
= *psz_text
;
2042 p_subtitle
->psz_text
= psz_orig2
;
2043 msg_Dbg( p_obj
, "%s", p_subtitle
->psz_text
);
2048 static int ParsePSB( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2049 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2052 VLC_UNUSED(p_props
);
2053 VLC_UNUSED( i_idx
);
2062 const char *s
= TextGetLine( txt
);
2065 return VLC_EGENERIC
;
2067 psz_text
= malloc( strlen( s
) + 1 );
2071 if( sscanf( s
, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2072 &h1
, &m1
, &s1
, &h2
, &m2
, &s2
, psz_text
) == 7 )
2074 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2075 (int64_t)m1
* 60*1000 +
2076 (int64_t)s1
* 1000 ) * 1000;
2077 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2078 (int64_t)m2
* 60*1000 +
2079 (int64_t)s2
* 1000 ) * 1000;
2085 /* replace | by \n */
2086 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
2088 if( psz_text
[i
] == '|' )
2091 p_subtitle
->psz_text
= psz_text
;
2095 static int64_t ParseRealTime( char *psz
, int *h
, int *m
, int *s
, int *f
)
2097 if( *psz
== '\0' ) return 0;
2098 if( sscanf( psz
, "%d:%d:%d.%d", h
, m
, s
, f
) == 4 ||
2099 sscanf( psz
, "%d:%d.%d", m
, s
, f
) == 3 ||
2100 sscanf( psz
, "%d.%d", s
, f
) == 2 ||
2101 sscanf( psz
, "%d:%d", m
, s
) == 2 ||
2102 sscanf( psz
, "%d", s
) == 1 )
2104 return (int64_t)((( *h
* 60 + *m
) * 60 ) + *s
) * 1000 * 1000
2105 + (int64_t)*f
* 10 * 1000;
2107 else return VLC_EGENERIC
;
2110 static int ParseRealText( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2111 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2114 VLC_UNUSED(p_props
);
2115 VLC_UNUSED( i_idx
);
2116 char *psz_text
= NULL
;
2120 int h1
= 0, m1
= 0, s1
= 0, f1
= 0;
2121 int h2
= 0, m2
= 0, s2
= 0, f2
= 0;
2122 const char *s
= TextGetLine( txt
);
2126 return VLC_EGENERIC
;
2128 psz_text
= malloc( strlen( s
) + 1 );
2132 /* Find the good begining. This removes extra spaces at the beginning
2134 char *psz_temp
= strcasestr( s
, "<time");
2135 if( psz_temp
!= NULL
)
2137 char psz_end
[12], psz_begin
[12];
2138 /* Line has begin and end */
2139 if( ( sscanf( psz_temp
,
2140 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2141 psz_begin
, psz_end
, psz_text
) != 3 ) &&
2142 /* Line has begin and no end */
2144 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2145 psz_begin
, psz_text
) != 2) )
2146 /* Line is not recognized */
2152 int64_t i_time
= ParseRealTime( psz_begin
, &h1
, &m1
, &s1
, &f1
);
2153 p_subtitle
->i_start
= i_time
>= 0 ? i_time
: 0;
2155 i_time
= ParseRealTime( psz_end
, &h2
, &m2
, &s2
, &f2
);
2156 p_subtitle
->i_stop
= i_time
>= 0 ? i_time
: -1;
2161 /* Get the following Lines */
2164 const char *s
= TextGetLine( txt
);
2169 return VLC_EGENERIC
;
2172 size_t i_len
= strlen( s
);
2173 if( i_len
== 0 ) break;
2175 if( strcasestr( s
, "<time" ) ||
2176 strcasestr( s
, "<clear/") )
2178 TextPreviousLine( txt
);
2182 size_t i_old
= strlen( psz_text
);
2184 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2188 strcat( psz_text
, s
);
2189 strcat( psz_text
, "\n" );
2192 /* Remove the starting ">" that remained after the sscanf */
2193 memmove( &psz_text
[0], &psz_text
[1], strlen( psz_text
) );
2195 p_subtitle
->psz_text
= psz_text
;
2200 static int ParseDKS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2201 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2204 VLC_UNUSED(p_props
);
2205 VLC_UNUSED( i_idx
);
2213 char *s
= TextGetLine( txt
);
2216 return VLC_EGENERIC
;
2218 psz_text
= malloc( strlen( s
) + 1 );
2222 if( sscanf( s
, "[%d:%d:%d]%[^\r\n]",
2223 &h1
, &m1
, &s1
, psz_text
) == 4 )
2225 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2226 (int64_t)m1
* 60*1000 +
2227 (int64_t)s1
* 1000 ) * 1000;
2229 s
= TextGetLine( txt
);
2233 return VLC_EGENERIC
;
2236 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2237 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2238 (int64_t)m2
* 60*1000 +
2239 (int64_t)s2
* 1000 ) * 1000;
2241 p_subtitle
->i_stop
= -1;
2247 /* replace [br] by \n */
2249 while( ( p
= strstr( psz_text
, "[br]" ) ) )
2252 memmove( p
, &p
[3], strlen(&p
[3])+1 );
2255 p_subtitle
->psz_text
= psz_text
;
2259 static int ParseSubViewer1( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2260 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2263 VLC_UNUSED(p_props
);
2264 VLC_UNUSED( i_idx
);
2271 char *s
= TextGetLine( txt
);
2274 return VLC_EGENERIC
;
2276 if( sscanf( s
, "[%d:%d:%d]", &h1
, &m1
, &s1
) == 3 )
2278 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2279 (int64_t)m1
* 60*1000 +
2280 (int64_t)s1
* 1000 ) * 1000;
2282 s
= TextGetLine( txt
);
2284 return VLC_EGENERIC
;
2286 psz_text
= strdup( s
);
2290 s
= TextGetLine( txt
);
2294 return VLC_EGENERIC
;
2297 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2298 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2299 (int64_t)m2
* 60*1000 +
2300 (int64_t)s2
* 1000 ) * 1000;
2302 p_subtitle
->i_stop
= -1;
2308 p_subtitle
->psz_text
= psz_text
;
2313 static int ParseCommonSBV( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2314 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2317 VLC_UNUSED( i_idx
);
2318 VLC_UNUSED( p_props
);
2323 const char *s
= TextGetLine( txt
);
2324 int h1
= 0, m1
= 0, s1
= 0, d1
= 0;
2325 int h2
= 0, m2
= 0, s2
= 0, d2
= 0;
2328 return VLC_EGENERIC
;
2330 if( sscanf( s
,"%d:%d:%d.%d,%d:%d:%d.%d",
2332 &h2
, &m2
, &s2
, &d2
) == 8 )
2334 p_subtitle
->i_start
= ( (int64_t)h1
* 3600 * 1000 +
2335 (int64_t)m1
* 60 * 1000 +
2336 (int64_t)s1
* 1000 +
2337 (int64_t)d1
) * 1000;
2339 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600 * 1000 +
2340 (int64_t)m2
* 60 * 1000 +
2341 (int64_t)s2
* 1000 +
2342 (int64_t)d2
) * 1000;
2343 if( p_subtitle
->i_start
< p_subtitle
->i_stop
)
2348 /* Now read text until an empty line */
2349 psz_text
= strdup("");
2355 const char *s
= TextGetLine( txt
);
2359 i_len
= s
? strlen( s
) : 0;
2362 p_subtitle
->psz_text
= psz_text
;
2366 i_old
= strlen( psz_text
);
2367 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2371 strcat( psz_text
, s
);
2372 strcat( psz_text
, "\n" );
2376 static int ParseSCC( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2377 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2380 VLC_UNUSED( i_idx
);
2381 VLC_UNUSED( p_props
);
2385 const char *psz_line
= TextGetLine( txt
);
2387 return VLC_EGENERIC
;
2389 unsigned h
, m
, s
, f
;
2390 if( sscanf( psz_line
, "%u:%u:%u:%u ", &h
, &m
, &s
, &f
) != 4 )
2393 p_subtitle
->i_start
= CLOCK_FREQ
* ( h
* 3600 + m
* 60 + s
) +
2394 f
* p_props
->i_microsecperframe
;
2395 p_subtitle
->i_stop
= -1;
2397 const char *psz_text
= strchr( psz_line
, '\t' );
2398 if( !psz_text
&& !(psz_text
= strchr( psz_line
, ' ' )) )
2401 if ( psz_text
[1] == '\0' )
2404 p_subtitle
->psz_text
= strdup( psz_text
+ 1 );
2405 if( !p_subtitle
->psz_text
)
2414 /* Matches filename.xx.srt */
2415 static char * get_language_from_filename( const char * psz_sub_file
)
2417 char *psz_ret
= NULL
;
2418 char *psz_tmp
, *psz_language_begin
;
2420 if( !psz_sub_file
) return NULL
;
2421 char *psz_work
= strdup( psz_sub_file
);
2423 /* Removing extension, but leaving the dot */
2424 psz_tmp
= strrchr( psz_work
, '.' );
2428 psz_language_begin
= strrchr( psz_work
, '.' );
2429 if( psz_language_begin
)
2430 psz_ret
= strdup(++psz_language_begin
);