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>
42 #include <vlc_demux.h>
43 #include <vlc_charset.h>
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t
*p_this
);
49 static void Close( vlc_object_t
*p_this
);
51 #define SUB_DELAY_LONGTEXT \
52 N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")
53 #define SUB_FPS_LONGTEXT \
54 N_("Override the normal frames per second settings. " \
55 "This will only work with MicroDVD and SubRIP (SRT) subtitles.")
56 #define SUB_TYPE_LONGTEXT \
57 N_("Force the subtiles format. Selecting \"auto\" means autodetection and should always work.")
58 #define SUB_DESCRIPTION_LONGTEXT \
59 N_("Override the default track description.")
61 static const char *const ppsz_sub_type
[] =
63 "auto", "microdvd", "subrip", "subviewer", "ssa1",
64 "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2",
65 "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks",
70 set_shortname( N_("Subtitles"))
71 set_description( N_("Text subtitle parser") )
72 set_capability( "demux", 0 )
73 set_category( CAT_INPUT
)
74 set_subcategory( SUBCAT_INPUT_DEMUX
)
75 add_float( "sub-fps", 0.0,
76 N_("Frames per Second"),
77 SUB_FPS_LONGTEXT
, true )
78 add_integer( "sub-delay", 0,
80 SUB_DELAY_LONGTEXT
, true )
81 add_string( "sub-type", "auto", N_("Subtitle format"),
82 SUB_TYPE_LONGTEXT
, true )
83 change_string_list( ppsz_sub_type
, ppsz_sub_type
)
84 add_string( "sub-description", NULL
, N_("Subtitle description"),
85 SUB_DESCRIPTION_LONGTEXT
, true )
86 set_callbacks( Open
, Close
)
88 add_shortcut( "subtitle" )
91 /*****************************************************************************
93 *****************************************************************************/
96 SUB_TYPE_UNKNOWN
= -1,
104 SUB_TYPE_SUBVIEWER
, /* SUBVIEWER 2 */
105 SUB_TYPE_DVDSUBTITLE
, /* Mplayer calls it subviewer2 */
114 SUB_TYPE_SUBVIEW1
, /* SUBVIEWER 1 - mplayer calls it subrip09,
115 and Gnome subtitles SubViewer 1.0 */
117 SUB_TYPE_SCC
, /* Scenarist Closed Caption */
127 static int TextLoad( text_t
*, stream_t
*s
);
128 static void TextUnload( text_t
* );
140 enum subtitle_type_e i_type
;
141 int64_t i_microsecperframe
;
143 char *psz_header
; /* SSA */
150 int i_time_resolution
;
164 const char *psz_start
;
175 int64_t i_next_demux_date
;
187 subs_properties_t props
;
189 block_t
* (*pf_convert
)( const subtitle_t
* );
192 static int ParseMicroDvd ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
193 static int ParseSubRip ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
194 static int ParseSubViewer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
195 static int ParseSSA ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
196 static int ParseVplayer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
197 static int ParseSami ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
198 static int ParseDVDSubtitle( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
199 static int ParseMPL2 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
200 static int ParseAQT ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
201 static int ParsePJS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
202 static int ParseMPSub ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
203 static int ParseJSS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
204 static int ParsePSB ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
205 static int ParseRealText ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
206 static int ParseDKS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
207 static int ParseSubViewer1 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
208 static int ParseCommonSBV ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
209 static int ParseSCC ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
213 const char *psz_type_name
;
215 const char *psz_name
;
216 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
217 } sub_read_subtitle_function
[] =
219 { "microdvd", SUB_TYPE_MICRODVD
, "MicroDVD", ParseMicroDvd
},
220 { "subrip", SUB_TYPE_SUBRIP
, "SubRIP", ParseSubRip
},
221 { "subviewer", SUB_TYPE_SUBVIEWER
, "SubViewer", ParseSubViewer
},
222 { "ssa1", SUB_TYPE_SSA1
, "SSA-1", ParseSSA
},
223 { "ssa2-4", SUB_TYPE_SSA2_4
, "SSA-2/3/4", ParseSSA
},
224 { "ass", SUB_TYPE_ASS
, "SSA/ASS", ParseSSA
},
225 { "vplayer", SUB_TYPE_VPLAYER
, "VPlayer", ParseVplayer
},
226 { "sami", SUB_TYPE_SAMI
, "SAMI", ParseSami
},
227 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE
, "DVDSubtitle", ParseDVDSubtitle
},
228 { "mpl2", SUB_TYPE_MPL2
, "MPL2", ParseMPL2
},
229 { "aqt", SUB_TYPE_AQT
, "AQTitle", ParseAQT
},
230 { "pjs", SUB_TYPE_PJS
, "PhoenixSub", ParsePJS
},
231 { "mpsub", SUB_TYPE_MPSUB
, "MPSub", ParseMPSub
},
232 { "jacosub", SUB_TYPE_JACOSUB
, "JacoSub", ParseJSS
},
233 { "psb", SUB_TYPE_PSB
, "PowerDivx", ParsePSB
},
234 { "realtext", SUB_TYPE_RT
, "RealText", ParseRealText
},
235 { "dks", SUB_TYPE_DKS
, "DKS", ParseDKS
},
236 { "subviewer1", SUB_TYPE_SUBVIEW1
, "Subviewer 1", ParseSubViewer1
},
237 { "sbv", SUB_TYPE_SBV
, "SBV", ParseCommonSBV
},
238 { "scc", SUB_TYPE_SCC
, "SCC", ParseSCC
},
239 { NULL
, SUB_TYPE_UNKNOWN
, "Unknown", NULL
}
241 /* When adding support for more formats, be sure to add their file extension
242 * to src/input/subtitles.c to enable auto-detection.
245 static int Demux( demux_t
* );
246 static int Control( demux_t
*, int, va_list );
248 static void Fix( demux_t
* );
249 static char * get_language_from_filename( const char * );
251 /*****************************************************************************
252 * Decoder format output function
253 *****************************************************************************/
255 static block_t
*ToTextBlock( const subtitle_t
*p_subtitle
)
258 size_t i_len
= strlen( p_subtitle
->psz_text
) + 1;
260 if( i_len
<= 1 || !(p_block
= block_Alloc( i_len
)) )
263 memcpy( p_block
->p_buffer
, p_subtitle
->psz_text
, i_len
);
268 static block_t
*ToEIA608Block( const subtitle_t
*p_subtitle
)
271 const size_t i_len
= strlen( p_subtitle
->psz_text
);
272 const size_t i_block
= (1 + i_len
/ 5) * 3;
274 if( i_len
< 4 || !(p_block
= block_Alloc( i_block
)) )
277 p_block
->i_buffer
= 0;
279 char *saveptr
= NULL
;
280 char *psz_tok
= strtok_r( p_subtitle
->psz_text
, " ", &saveptr
);
283 sscanf( psz_tok
, "%2x%2x", &a
, &b
) == 2 &&
284 i_block
- p_block
->i_buffer
>= 3 )
286 uint8_t *p_data
= &p_block
->p_buffer
[p_block
->i_buffer
];
290 p_block
->i_buffer
+= 3;
291 psz_tok
= strtok_r( NULL
, " ", &saveptr
);
297 /*****************************************************************************
299 *****************************************************************************/
300 static int Open ( vlc_object_t
*p_this
)
302 demux_t
*p_demux
= (demux_t
*)p_this
;
307 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
309 if( !p_demux
->obj
.force
)
311 msg_Dbg( p_demux
, "subtitle demux discarded" );
315 p_demux
->pf_demux
= Demux
;
316 p_demux
->pf_control
= Control
;
317 p_demux
->p_sys
= p_sys
= malloc( sizeof( demux_sys_t
) );
321 p_sys
->b_slave
= false;
322 p_sys
->b_first_time
= true;
323 p_sys
->i_next_demux_date
= 0;
325 p_sys
->pf_convert
= ToTextBlock
;
327 p_sys
->subtitles
.i_current
= 0;
328 p_sys
->subtitles
.i_count
= 0;
329 p_sys
->subtitles
.p_array
= NULL
;
331 p_sys
->props
.psz_header
= NULL
;
332 p_sys
->props
.i_microsecperframe
= 40000;
333 p_sys
->props
.jss
.b_inited
= false;
334 p_sys
->props
.mpsub
.b_inited
= false;
335 p_sys
->props
.sami
.psz_start
= NULL
;
338 f_fps
= p_demux
->p_input
? var_GetFloat( p_demux
->p_input
, "sub-original-fps" ) : 0.0f
;
340 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
342 msg_Dbg( p_demux
, "Movie fps: %f", (double) f_fps
);
344 /* Check for override of the fps */
345 f_fps
= var_CreateGetFloat( p_demux
, "sub-fps" );
348 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
349 msg_Dbg( p_demux
, "Override subtitle fps %f", (double) f_fps
);
352 /* Get or probe the type */
353 p_sys
->props
.i_type
= SUB_TYPE_UNKNOWN
;
354 psz_type
= var_CreateGetString( p_demux
, "sub-type" );
355 if( psz_type
&& *psz_type
)
357 for( int i
= 0; ; i
++ )
359 if( sub_read_subtitle_function
[i
].psz_type_name
== NULL
)
362 if( !strcmp( sub_read_subtitle_function
[i
].psz_type_name
,
365 p_sys
->props
.i_type
= sub_read_subtitle_function
[i
].i_type
;
373 const uint64_t i_start_pos
= vlc_stream_Tell( p_demux
->s
);
377 const uint8_t *p_peek
;
378 if( vlc_stream_Peek( p_demux
->s
, &p_peek
, 16 ) < 16 )
391 const char *psz_bom
= NULL
;
394 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
395 if( !memcmp( p_peek
, "\xEF\xBB\xBF", 3 ) )
400 else if( !memcmp( p_peek
, "\xFF\xFE", 2 ) )
403 psz_bom
= "UTF-16LE";
406 else if( !memcmp( p_peek
, "\xFE\xFF", 2 ) )
409 psz_bom
= "UTF-16BE";
414 msg_Dbg( p_demux
, "detected %s Byte Order Mark", psz_bom
);
416 i_peek
= vlc_stream_Peek( p_demux
->s
, &p_peek
, i_peek
);
417 if( unlikely(i_peek
< 16) )
423 stream_t
*p_probestream
= NULL
;
424 if( e_bom
!= UTF8BOM
&& e_bom
!= NOBOM
)
428 char *p_outbuf
= FromCharset( psz_bom
, p_peek
, i_peek
);
429 if( p_outbuf
!= NULL
)
430 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *)p_outbuf
,
432 false ); /* free p_outbuf on release */
437 const size_t i_skip
= (e_bom
== UTF8BOM
) ? 3 : 0;
438 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *) &p_peek
[i_skip
],
439 i_peek
- i_skip
, true );
442 if( p_probestream
== NULL
)
448 /* Probe if unknown type */
449 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
454 msg_Dbg( p_demux
, "autodetecting subtitle format" );
455 for( i_try
= 0; i_try
< 256; i_try
++ )
460 if( (s
= vlc_stream_ReadLine( p_probestream
) ) == NULL
)
463 if( strcasestr( s
, "<SAMI>" ) )
465 p_sys
->props
.i_type
= SUB_TYPE_SAMI
;
468 else if( sscanf( s
, "{%d}{%d}", &i_dummy
, &i_dummy
) == 2 ||
469 sscanf( s
, "{%d}{}", &i_dummy
) == 1)
471 p_sys
->props
.i_type
= SUB_TYPE_MICRODVD
;
474 else if( sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d,%d",
475 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
476 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
477 sscanf( s
, "%d:%d:%d --> %d:%d:%d,%d",
478 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
479 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
480 sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d",
481 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
482 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
483 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d.%d",
484 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
485 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
486 sscanf( s
, "%d:%d:%d --> %d:%d:%d.%d",
487 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
488 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
489 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d",
490 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
491 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
492 sscanf( s
, "%d:%d:%d --> %d:%d:%d",
493 &i_dummy
,&i_dummy
,&i_dummy
,
494 &i_dummy
,&i_dummy
,&i_dummy
) == 6 )
496 p_sys
->props
.i_type
= SUB_TYPE_SUBRIP
;
499 else if( !strncasecmp( s
, "!: This is a Sub Station Alpha v1", 33 ) )
501 p_sys
->props
.i_type
= SUB_TYPE_SSA1
;
504 else if( !strncasecmp( s
, "ScriptType: v4.00+", 18 ) )
506 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
509 else if( !strncasecmp( s
, "ScriptType: v4.00", 17 ) )
511 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
514 else if( !strncasecmp( s
, "Dialogue: Marked", 16 ) )
516 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
519 else if( !strncasecmp( s
, "Dialogue:", 9 ) )
521 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
524 else if( strcasestr( s
, "[INFORMATION]" ) )
526 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEWER
; /* I hope this will work */
529 else if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d",
530 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
531 &i_dummy
, &i_dummy
, &i_dummy
) == 7 ||
532 sscanf( s
, "@%d @%d", &i_dummy
, &i_dummy
) == 2)
534 p_sys
->props
.i_type
= SUB_TYPE_JACOSUB
;
537 else if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
538 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
539 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
) == 8 )
541 p_sys
->props
.i_type
= SUB_TYPE_SBV
;
544 else if( sscanf( s
, "%d:%d:%d:", &i_dummy
, &i_dummy
, &i_dummy
) == 3 ||
545 sscanf( s
, "%d:%d:%d ", &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
547 p_sys
->props
.i_type
= SUB_TYPE_VPLAYER
;
550 else if( sscanf( s
, "{T %d:%d:%d:%d", &i_dummy
, &i_dummy
,
551 &i_dummy
, &i_dummy
) == 4 )
553 p_sys
->props
.i_type
= SUB_TYPE_DVDSUBTITLE
;
556 else if( sscanf( s
, "[%d:%d:%d]%c",
557 &i_dummy
, &i_dummy
, &i_dummy
, &p_dummy
) == 4 )
559 p_sys
->props
.i_type
= SUB_TYPE_DKS
;
562 else if( strstr( s
, "*** START SCRIPT" ) )
564 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEW1
;
567 else if( sscanf( s
, "[%d][%d]", &i_dummy
, &i_dummy
) == 2 ||
568 sscanf( s
, "[%d][]", &i_dummy
) == 1)
570 p_sys
->props
.i_type
= SUB_TYPE_MPL2
;
573 else if( sscanf (s
, "FORMAT=%d", &i_dummy
) == 1 ||
574 ( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1
577 p_sys
->props
.i_type
= SUB_TYPE_MPSUB
;
580 else if( sscanf( s
, "-->> %d", &i_dummy
) == 1 )
582 p_sys
->props
.i_type
= SUB_TYPE_AQT
;
585 else if( sscanf( s
, "%d,%d,", &i_dummy
, &i_dummy
) == 2 )
587 p_sys
->props
.i_type
= SUB_TYPE_PJS
;
590 else if( sscanf( s
, "{%d:%d:%d}",
591 &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
593 p_sys
->props
.i_type
= SUB_TYPE_PSB
;
596 else if( strcasestr( s
, "<time" ) )
598 p_sys
->props
.i_type
= SUB_TYPE_RT
;
601 else if( !strncasecmp( s
, "WEBVTT",6 ) )
606 else if( !strncasecmp( s
, "Scenarist_SCC V1.0", 18 ) )
608 p_sys
->props
.i_type
= SUB_TYPE_SCC
;
609 p_sys
->pf_convert
= ToEIA608Block
;
620 vlc_stream_Delete( p_probestream
);
622 /* Quit on unknown subtitles */
623 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
626 /* Ensure it will work with non seekable streams */
627 assert( i_start_pos
== vlc_stream_Tell( p_demux
->s
) );
629 msg_Warn( p_demux
, "failed to recognize subtitle type" );
634 for( int i
= 0; ; i
++ )
636 if( sub_read_subtitle_function
[i
].i_type
== p_sys
->props
.i_type
)
638 msg_Dbg( p_demux
, "detected %s format",
639 sub_read_subtitle_function
[i
].psz_name
);
640 pf_read
= sub_read_subtitle_function
[i
].pf_read
;
645 msg_Dbg( p_demux
, "loading all subtitles..." );
647 if( e_bom
== UTF8BOM
&& /* skip BOM */
648 vlc_stream_Read( p_demux
->s
, NULL
, 3 ) != 3 )
654 /* Load the whole file */
656 TextLoad( &txtlines
, p_demux
->s
);
659 for( size_t i_max
= 0; i_max
< SIZE_MAX
- 500 * sizeof(subtitle_t
); )
661 if( p_sys
->subtitles
.i_count
>= i_max
)
664 subtitle_t
*p_realloc
= realloc( p_sys
->subtitles
.p_array
, sizeof(subtitle_t
) * i_max
);
665 if( p_realloc
== NULL
)
667 TextUnload( &txtlines
);
671 p_sys
->subtitles
.p_array
= p_realloc
;
674 if( pf_read( VLC_OBJECT(p_demux
), &p_sys
->props
, &txtlines
,
675 &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
],
676 p_sys
->subtitles
.i_count
) )
679 p_sys
->subtitles
.i_count
++;
682 TextUnload( &txtlines
);
684 msg_Dbg(p_demux
, "loaded %zu subtitles", p_sys
->subtitles
.i_count
);
686 /* Fix subtitle (order and time) *** */
687 p_sys
->subtitles
.i_current
= 0;
689 if( p_sys
->subtitles
.i_count
> 0 )
690 p_sys
->i_length
= p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
-1].i_stop
;
692 /* *** add subtitle ES *** */
693 if( p_sys
->props
.i_type
== SUB_TYPE_SSA1
||
694 p_sys
->props
.i_type
== SUB_TYPE_SSA2_4
||
695 p_sys
->props
.i_type
== SUB_TYPE_ASS
)
698 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SSA
);
700 else if( p_sys
->props
.i_type
== SUB_TYPE_SCC
)
702 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_CEA608
);
703 fmt
.subs
.cc
.i_reorder_depth
= -1;
706 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SUBT
);
708 /* Stupid language detection in the filename */
709 char * psz_language
= get_language_from_filename( p_demux
->psz_filepath
);
713 fmt
.psz_language
= psz_language
;
714 msg_Dbg( p_demux
, "detected language %s of subtitle: %s", psz_language
,
715 p_demux
->psz_location
);
719 fmt
.subs
.psz_encoding
= strdup( psz_bom
);
720 char *psz_description
= var_InheritString( p_demux
, "sub-description" );
721 if( psz_description
&& *psz_description
)
722 fmt
.psz_description
= psz_description
;
724 free( psz_description
);
725 if( p_sys
->props
.psz_header
!= NULL
&&
726 (fmt
.p_extra
= strdup( p_sys
->props
.psz_header
)) )
728 fmt
.i_extra
= strlen( p_sys
->props
.psz_header
) + 1;
731 p_sys
->es
= es_out_Add( p_demux
->out
, &fmt
);
732 es_format_Clean( &fmt
);
733 if( p_sys
->es
== NULL
)
742 /*****************************************************************************
743 * Close: Close subtitle demux
744 *****************************************************************************/
745 static void Close( vlc_object_t
*p_this
)
747 demux_t
*p_demux
= (demux_t
*)p_this
;
748 demux_sys_t
*p_sys
= p_demux
->p_sys
;
750 for( size_t i
= 0; i
< p_sys
->subtitles
.i_count
; i
++ )
751 free( p_sys
->subtitles
.p_array
[i
].psz_text
);
752 free( p_sys
->subtitles
.p_array
);
753 free( p_sys
->props
.psz_header
);
758 /*****************************************************************************
760 *****************************************************************************/
761 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
763 demux_sys_t
*p_sys
= p_demux
->p_sys
;
770 *va_arg( args
, bool * ) = true;
773 case DEMUX_GET_LENGTH
:
774 pi64
= va_arg( args
, int64_t * );
775 *pi64
= p_sys
->i_length
;
779 pi64
= va_arg( args
, int64_t * );
780 *pi64
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
782 *pi64
= p_sys
->i_next_demux_date
;
786 i64
= va_arg( args
, int64_t );
787 p_sys
->b_first_time
= true;
788 p_sys
->i_next_demux_date
= i64
;
789 for( size_t i
= 0; i
< p_sys
->subtitles
.i_count
; i
++ )
791 if( p_sys
->subtitles
.p_array
[i
].i_start
> i64
&& i
> 0 )
793 p_sys
->subtitles
.i_current
= i
;
797 case DEMUX_GET_POSITION
:
798 pf
= va_arg( args
, double * );
799 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
803 else if( p_sys
->subtitles
.i_count
> 0 && p_sys
->i_length
)
805 *pf
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
807 *pf
= p_sys
->i_next_demux_date
;
808 *pf
/= p_sys
->i_length
;
816 case DEMUX_SET_POSITION
:
817 f
= va_arg( args
, double );
818 if( p_sys
->subtitles
.i_count
&& p_sys
->i_length
)
820 i64
= VLC_TS_0
+ f
* p_sys
->i_length
;
821 return demux_Control( p_demux
, DEMUX_SET_TIME
, i64
);
825 case DEMUX_SET_NEXT_DEMUX_TIME
:
826 p_sys
->b_slave
= true;
827 p_sys
->i_next_demux_date
= va_arg( args
, int64_t ) - VLC_TS_0
;
830 case DEMUX_CAN_PAUSE
:
831 case DEMUX_SET_PAUSE_STATE
:
832 case DEMUX_CAN_CONTROL_PACE
:
833 return demux_vaControlHelper( p_demux
->s
, 0, -1, 0, 1, i_query
, args
);
835 case DEMUX_GET_PTS_DELAY
:
838 case DEMUX_GET_ATTACHMENTS
:
839 case DEMUX_GET_TITLE_INFO
:
840 case DEMUX_HAS_UNSUPPORTED_META
:
841 case DEMUX_CAN_RECORD
:
849 /*****************************************************************************
850 * Demux: Send subtitle to decoder
851 *****************************************************************************/
852 static int Demux( demux_t
*p_demux
)
854 demux_sys_t
*p_sys
= p_demux
->p_sys
;
856 int64_t i_barrier
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
858 i_barrier
= p_sys
->i_next_demux_date
;
860 while( p_sys
->subtitles
.i_current
< p_sys
->subtitles
.i_count
&&
861 p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
].i_start
<= i_barrier
)
863 const subtitle_t
*p_subtitle
= &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
];
865 if ( !p_sys
->b_slave
&& p_sys
->b_first_time
)
867 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
868 p_sys
->b_first_time
= false;
871 if( p_subtitle
->i_start
>= 0 )
873 block_t
*p_block
= p_sys
->pf_convert( p_subtitle
);
877 p_block
->i_pts
= VLC_TS_0
+ p_subtitle
->i_start
;
878 if( p_subtitle
->i_stop
>= 0 && p_subtitle
->i_stop
>= p_subtitle
->i_start
)
879 p_block
->i_length
= p_subtitle
->i_stop
- p_subtitle
->i_start
;
881 es_out_Send( p_demux
->out
, p_sys
->es
, p_block
);
885 p_sys
->subtitles
.i_current
++;
888 if ( !p_sys
->b_slave
)
890 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
891 p_sys
->i_next_demux_date
+= CLOCK_FREQ
/ 8;
894 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
895 return VLC_DEMUXER_EOF
;
897 return VLC_DEMUXER_SUCCESS
;
901 static int subtitle_cmp( const void *first
, const void *second
)
903 int64_t result
= ((subtitle_t
*)(first
))->i_start
- ((subtitle_t
*)(second
))->i_start
;
904 /* Return -1, 0 ,1, and not directly subtraction
905 * as result can be > INT_MAX */
906 return result
== 0 ? 0 : result
> 0 ? 1 : -1;
908 /*****************************************************************************
909 * Fix: fix time stamp and order of subtitle
910 *****************************************************************************/
911 static void Fix( demux_t
*p_demux
)
913 demux_sys_t
*p_sys
= p_demux
->p_sys
;
915 /* *** fix order (to be sure...) *** */
916 qsort( p_sys
->subtitles
.p_array
, p_sys
->subtitles
.i_count
, sizeof( p_sys
->subtitles
.p_array
[0] ), subtitle_cmp
);
919 static int TextLoad( text_t
*txt
, stream_t
*s
)
925 txt
->i_line_count
= 0;
927 txt
->line
= calloc( i_line_max
, sizeof( char * ) );
931 /* load the complete file */
934 char *psz
= vlc_stream_ReadLine( s
);
939 txt
->line
[txt
->i_line_count
] = psz
;
940 if( txt
->i_line_count
+ 1 >= i_line_max
)
943 char **p_realloc
= realloc( txt
->line
, i_line_max
* sizeof( char * ) );
944 if( p_realloc
== NULL
)
946 txt
->line
= p_realloc
;
951 if( txt
->i_line_count
== 0 )
959 static void TextUnload( text_t
*txt
)
961 if( txt
->i_line_count
)
963 for( size_t i
= 0; i
< txt
->i_line_count
; i
++ )
964 free( txt
->line
[i
] );
968 txt
->i_line_count
= 0;
971 static char *TextGetLine( text_t
*txt
)
973 if( txt
->i_line
>= txt
->i_line_count
)
976 return txt
->line
[txt
->i_line
++];
978 static void TextPreviousLine( text_t
*txt
)
980 if( txt
->i_line
> 0 )
984 /*****************************************************************************
985 * Specific Subtitle function
986 *****************************************************************************/
989 * {n1}{n2}Line1|Line2|Line3....
990 * where n1 and n2 are the video frame number (n2 can be empty)
992 static int ParseMicroDvd( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
993 text_t
*txt
, subtitle_t
*p_subtitle
,
1004 const char *s
= TextGetLine( txt
);
1006 return VLC_EGENERIC
;
1008 psz_text
= malloc( strlen(s
) + 1 );
1014 if( sscanf( s
, "{%d}{}%[^\r\n]", &i_start
, psz_text
) == 2 ||
1015 sscanf( s
, "{%d}{%d}%[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1017 if( i_start
!= 1 || i_stop
!= 1 )
1020 /* We found a possible setting of the framerate "{1}{1}23.976" */
1021 /* Check if it's usable, and if the sub-fps is not set */
1022 float f_fps
= us_strtof( psz_text
, NULL
);
1023 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1024 p_props
->i_microsecperframe
= llroundf(1000000.f
/ f_fps
);
1029 /* replace | by \n */
1030 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1032 if( psz_text
[i
] == '|' )
1037 p_subtitle
->i_start
= i_start
* p_props
->i_microsecperframe
;
1038 p_subtitle
->i_stop
= i_stop
>= 0 ? (i_stop
* p_props
->i_microsecperframe
) : -1;
1039 p_subtitle
->psz_text
= psz_text
;
1043 /* ParseSubRipSubViewer
1046 * h1:m1:s1,d1 --> h2:m2:s2,d2
1051 * Format SubViewer v1/v2
1052 * h1:m1:s1.d1,h2:m2:s2.d2
1057 * We ignore line number for SubRip
1059 static int ParseSubRipSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1060 text_t
*txt
, subtitle_t
*p_subtitle
,
1061 int (* pf_parse_timing
)(subtitle_t
*, const char *),
1065 VLC_UNUSED(p_props
);
1070 const char *s
= TextGetLine( txt
);
1073 return VLC_EGENERIC
;
1075 if( pf_parse_timing( p_subtitle
, s
) == VLC_SUCCESS
&&
1076 p_subtitle
->i_start
< p_subtitle
->i_stop
)
1082 /* Now read text until an empty line */
1083 psz_text
= strdup("");
1089 const char *s
= TextGetLine( txt
);
1093 i_len
= s
? strlen( s
) : 0;
1096 p_subtitle
->psz_text
= psz_text
;
1100 i_old
= strlen( psz_text
);
1101 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1106 strcat( psz_text
, s
);
1107 strcat( psz_text
, "\n" );
1109 /* replace [br] by \n */
1114 while( ( p
= strstr( psz_text
, "[br]" ) ) )
1117 memmove( p
, &p
[3], strlen(&p
[3])+1 );
1123 /* subtitle_ParseSubRipTimingValue
1124 * Parses SubRip timing value.
1126 static int subtitle_ParseSubRipTimingValue(int64_t *timing_value
,
1129 int h1
, m1
, s1
, d1
= 0;
1131 if ( sscanf( s
, "%d:%d:%d,%d",
1132 &h1
, &m1
, &s1
, &d1
) == 4 ||
1133 sscanf( s
, "%d:%d:%d.%d",
1134 &h1
, &m1
, &s1
, &d1
) == 4 ||
1135 sscanf( s
, "%d:%d:%d",
1136 &h1
, &m1
, &s1
) == 3 )
1138 (*timing_value
) = ( (int64_t)h1
* 3600 * 1000 +
1139 (int64_t)m1
* 60 * 1000 +
1140 (int64_t)s1
* 1000 +
1141 (int64_t)d1
) * 1000;
1146 return VLC_EGENERIC
;
1149 /* subtitle_ParseSubRipTiming
1150 * Parses SubRip timing.
1152 static int subtitle_ParseSubRipTiming( subtitle_t
*p_subtitle
,
1155 int i_result
= VLC_EGENERIC
;
1156 char *psz_start
, *psz_stop
;
1157 psz_start
= malloc( strlen(s
) + 1 );
1158 psz_stop
= malloc( strlen(s
) + 1 );
1160 if( sscanf( s
, "%s --> %s", psz_start
, psz_stop
) == 2 &&
1161 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_start
, psz_start
) == VLC_SUCCESS
&&
1162 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_stop
, psz_stop
) == VLC_SUCCESS
)
1164 i_result
= VLC_SUCCESS
;
1174 static int ParseSubRip( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1175 text_t
*txt
, subtitle_t
*p_subtitle
,
1178 VLC_UNUSED( i_idx
);
1179 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1180 &subtitle_ParseSubRipTiming
,
1184 /* subtitle_ParseSubViewerTiming
1185 * Parses SubViewer timing.
1187 static int subtitle_ParseSubViewerTiming( subtitle_t
*p_subtitle
,
1190 int h1
, m1
, s1
, d1
, h2
, m2
, s2
, d2
;
1192 if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
1193 &h1
, &m1
, &s1
, &d1
, &h2
, &m2
, &s2
, &d2
) == 8 )
1195 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1196 (int64_t)m1
* 60*1000 +
1197 (int64_t)s1
* 1000 +
1198 (int64_t)d1
) * 1000;
1200 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1201 (int64_t)m2
* 60*1000 +
1202 (int64_t)s2
* 1000 +
1203 (int64_t)d2
) * 1000;
1206 return VLC_EGENERIC
;
1211 static int ParseSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1212 text_t
*txt
, subtitle_t
*p_subtitle
,
1215 VLC_UNUSED( i_idx
);
1217 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1218 &subtitle_ParseSubViewerTiming
,
1224 static int ParseSSA( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1225 text_t
*txt
, subtitle_t
*p_subtitle
,
1229 size_t header_len
= 0;
1233 const char *s
= TextGetLine( txt
);
1234 int h1
, m1
, s1
, c1
, h2
, m2
, s2
, c2
;
1235 char *psz_text
, *psz_temp
;
1239 return VLC_EGENERIC
;
1241 /* We expect (SSA2-4):
1242 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1243 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1245 * SSA-1 is similar but only has 8 commas up untill the subtitle text. Probably the Effect field is no present, but not 100 % sure.
1249 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1250 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1253 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1254 psz_text
= malloc( strlen(s
) );
1259 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1265 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1266 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1267 if( p_props
->i_type
== SUB_TYPE_SSA1
)
1269 /* SSA1 has only 8 commas before the text starts, not 9 */
1270 memmove( &psz_text
[1], psz_text
, strlen(psz_text
)+1 );
1275 int i_layer
= ( p_props
->i_type
== SUB_TYPE_ASS
) ? atoi( temp
) : 0;
1277 /* ReadOrder, Layer, %s(rest of fields) */
1278 if( asprintf( &psz_temp
, "%zu,%d,%s", i_idx
, i_layer
, psz_text
) == -1 )
1285 psz_text
= psz_temp
;
1288 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1289 (int64_t)m1
* 60*1000 +
1290 (int64_t)s1
* 1000 +
1291 (int64_t)c1
* 10 ) * 1000;
1292 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1293 (int64_t)m2
* 60*1000 +
1294 (int64_t)s2
* 1000 +
1295 (int64_t)c2
* 10 ) * 1000;
1296 p_subtitle
->psz_text
= psz_text
;
1301 /* All the other stuff we add to the header field */
1302 if( header_len
== 0 && p_props
->psz_header
)
1303 header_len
= strlen( p_props
->psz_header
);
1305 size_t s_len
= strlen( s
);
1306 p_props
->psz_header
= realloc_or_free( p_props
->psz_header
, header_len
+ s_len
+ 2 );
1307 if( !p_props
->psz_header
)
1309 snprintf( p_props
->psz_header
+ header_len
, s_len
+ 2, "%s\n", s
);
1310 header_len
+= s_len
+ 1;
1316 * h:m:s:Line1|Line2|Line3....
1318 * h:m:s Line1|Line2|Line3....
1320 static int ParseVplayer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1321 text_t
*txt
, subtitle_t
*p_subtitle
,
1325 VLC_UNUSED(p_props
);
1326 VLC_UNUSED( i_idx
);
1331 const char *s
= TextGetLine( txt
);
1335 return VLC_EGENERIC
;
1337 psz_text
= malloc( strlen( s
) + 1 );
1341 if( sscanf( s
, "%d:%d:%d%*c%[^\r\n]",
1342 &h1
, &m1
, &s1
, psz_text
) == 4 )
1344 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1345 (int64_t)m1
* 60*1000 +
1346 (int64_t)s1
* 1000 ) * 1000;
1347 p_subtitle
->i_stop
= -1;
1353 /* replace | by \n */
1354 for( size_t i
= 0; psz_text
[i
] != '\0'; i
++ )
1356 if( psz_text
[i
] == '|' )
1359 p_subtitle
->psz_text
= psz_text
;
1365 static const char *ParseSamiSearch( text_t
*txt
,
1366 const char *psz_start
, const char *psz_str
)
1368 if( psz_start
&& strcasestr( psz_start
, psz_str
) )
1370 const char *s
= strcasestr( psz_start
, psz_str
);
1371 return &s
[strlen( psz_str
)];
1376 const char *p
= TextGetLine( txt
);
1380 const char *s
= strcasestr( p
, psz_str
);
1382 return &s
[strlen( psz_str
)];
1385 static int ParseSami( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1386 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1389 VLC_UNUSED(p_props
);
1390 VLC_UNUSED( i_idx
);
1394 unsigned int i_text
;
1395 char text
[8192]; /* Arbitrary but should be long enough */
1397 /* search "Start=" */
1398 s
= ParseSamiSearch( txt
, p_props
->sami
.psz_start
, "Start=" );
1399 p_props
->sami
.psz_start
= NULL
;
1401 return VLC_EGENERIC
;
1403 /* get start value */
1405 i_start
= strtol( s
, &psz_end
, 0 );
1409 if( !( s
= ParseSamiSearch( txt
, s
, "<P" ) ) )
1410 return VLC_EGENERIC
;
1413 if( !( s
= ParseSamiSearch( txt
, s
, ">" ) ) )
1414 return VLC_EGENERIC
;
1418 /* now get all txt until a "Start=" line */
1422 /* Search non empty line */
1423 while( s
&& *s
== '\0' )
1424 s
= TextGetLine( txt
);
1430 if( !strncasecmp( s
, "<br", 3 ) )
1434 else if( strcasestr( s
, "Start=" ) )
1436 p_props
->sami
.psz_start
= s
;
1439 s
= ParseSamiSearch( txt
, s
, ">" );
1441 else if( !strncmp( s
, " ", 6 ) )
1446 else if( *s
== '\t' )
1456 if( c
!= '\0' && i_text
+1 < sizeof(text
) )
1459 text
[i_text
] = '\0';
1463 p_subtitle
->i_start
= i_start
* 1000;
1464 p_subtitle
->i_stop
= -1;
1465 p_subtitle
->psz_text
= strdup( text
);
1477 * TODO it can have a header
1484 * LANG support would be cool
1485 * CODEPAGE is probably mandatory FIXME
1487 static int ParseDVDSubtitle(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1488 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1491 VLC_UNUSED(p_props
);
1492 VLC_UNUSED( i_idx
);
1497 const char *s
= TextGetLine( txt
);
1501 return VLC_EGENERIC
;
1505 &h1
, &m1
, &s1
, &c1
) == 4 )
1507 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1508 (int64_t)m1
* 60*1000 +
1509 (int64_t)s1
* 1000 +
1510 (int64_t)c1
* 10) * 1000;
1511 p_subtitle
->i_stop
= -1;
1516 /* Now read text until a line containing "}" */
1517 psz_text
= strdup("");
1522 const char *s
= TextGetLine( txt
);
1529 return VLC_EGENERIC
;
1532 i_len
= strlen( s
);
1533 if( i_len
== 1 && s
[0] == '}')
1535 p_subtitle
->psz_text
= psz_text
;
1539 i_old
= strlen( psz_text
);
1540 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1543 strcat( psz_text
, s
);
1544 strcat( psz_text
, "\n" );
1550 * [n1][n2]Line1|Line2|Line3...
1551 * where n1 and n2 are the video frame number (n2 can be empty)
1553 static int ParseMPL2(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1554 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1557 VLC_UNUSED(p_props
);
1558 VLC_UNUSED( i_idx
);
1564 const char *s
= TextGetLine( txt
);
1569 return VLC_EGENERIC
;
1571 psz_text
= malloc( strlen(s
) + 1 );
1577 if( sscanf( s
, "[%d][] %[^\r\n]", &i_start
, psz_text
) == 2 ||
1578 sscanf( s
, "[%d][%d] %[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1580 p_subtitle
->i_start
= (int64_t)i_start
* 100000;
1581 p_subtitle
->i_stop
= i_stop
>= 0 ? ((int64_t)i_stop
* 100000) : -1;
1587 for( i
= 0; psz_text
[i
] != '\0'; )
1589 /* replace | by \n */
1590 if( psz_text
[i
] == '|' )
1594 if( psz_text
[i
] == '/' && ( i
== 0 || psz_text
[i
-1] == '\n' ) )
1595 memmove( &psz_text
[i
], &psz_text
[i
+1], strlen(&psz_text
[i
+1])+1 );
1599 p_subtitle
->psz_text
= psz_text
;
1603 static int ParseAQT(vlc_object_t
*p_obj
, subs_properties_t
*p_props
, text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1606 VLC_UNUSED(p_props
);
1607 VLC_UNUSED( i_idx
);
1609 char *psz_text
= strdup( "" );
1611 int i_firstline
= 1;
1617 const char *s
= TextGetLine( txt
);
1622 return VLC_EGENERIC
;
1626 if( sscanf (s
, "-->> %d", &t
) == 1)
1628 p_subtitle
->i_start
= (int64_t)t
; /* * FPS*/
1629 p_subtitle
->i_stop
= -1;
1631 /* Starting of a subtitle */
1636 /* We have been too far: end of the subtitle, begin of next */
1639 TextPreviousLine( txt
);
1646 i_old
= strlen( psz_text
) + 1;
1647 psz_text
= realloc_or_free( psz_text
, i_old
+ strlen( s
) + 1 );
1650 strcat( psz_text
, s
);
1651 strcat( psz_text
, "\n" );
1652 if( txt
->i_line
== txt
->i_line_count
)
1656 p_subtitle
->psz_text
= psz_text
;
1660 static int ParsePJS(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1661 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1664 VLC_UNUSED(p_props
);
1665 VLC_UNUSED( i_idx
);
1672 const char *s
= TextGetLine( txt
);
1676 return VLC_EGENERIC
;
1678 psz_text
= malloc( strlen(s
) + 1 );
1683 if( sscanf (s
, "%d,%d,\"%[^\n\r]", &t1
, &t2
, psz_text
) == 3 )
1685 /* 1/10th of second ? Frame based ? FIXME */
1686 p_subtitle
->i_start
= 10 * t1
;
1687 p_subtitle
->i_stop
= 10 * t2
;
1688 /* Remove latest " */
1689 psz_text
[ strlen(psz_text
) - 1 ] = '\0';
1696 /* replace | by \n */
1697 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1699 if( psz_text
[i
] == '|' )
1703 p_subtitle
->psz_text
= psz_text
;
1704 msg_Dbg( p_obj
, "%s", psz_text
);
1708 static int ParseMPSub( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1709 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1711 VLC_UNUSED( i_idx
);
1713 char *psz_text
= strdup( "" );
1715 if( !p_props
->mpsub
.b_inited
)
1717 p_props
->mpsub
.f_total
= 0.0;
1718 p_props
->mpsub
.f_factor
= 0.0;
1720 p_props
->mpsub
.b_inited
= true;
1728 const char *s
= TextGetLine( txt
);
1732 return VLC_EGENERIC
;
1735 if( strstr( s
, "FORMAT" ) )
1737 if( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1 && p_dummy
== 'E')
1739 p_props
->mpsub
.f_factor
= 100.0;
1743 psz_temp
= malloc( strlen(s
) );
1750 if( sscanf( s
, "FORMAT=%[^\r\n]", psz_temp
) )
1752 float f_fps
= us_strtof( psz_temp
, NULL
);
1754 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1755 var_SetFloat( p_obj
, "sub-fps", f_fps
);
1757 p_props
->mpsub
.f_factor
= 1.f
;
1765 float f1
= us_strtof( s
, &psz_temp
);
1768 float f2
= us_strtof( psz_temp
, NULL
);
1769 p_props
->mpsub
.f_total
+= f1
* p_props
->mpsub
.f_factor
;
1770 p_subtitle
->i_start
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1771 p_props
->mpsub
.f_total
+= f2
* p_props
->mpsub
.f_factor
;
1772 p_subtitle
->i_stop
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1779 const char *s
= TextGetLine( txt
);
1784 return VLC_EGENERIC
;
1787 size_t i_len
= strlen( s
);
1791 size_t i_old
= strlen( psz_text
);
1793 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1797 strcat( psz_text
, s
);
1798 strcat( psz_text
, "\n" );
1801 p_subtitle
->psz_text
= psz_text
;
1805 static int ParseJSS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1806 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1808 VLC_UNUSED( i_idx
);
1809 char *psz_text
, *psz_orig
;
1810 char *psz_text2
, *psz_orig2
;
1812 if( !p_props
->jss
.b_inited
)
1814 p_props
->jss
.i_comment
= 0;
1815 p_props
->jss
.i_time_resolution
= 30;
1816 p_props
->jss
.i_time_shift
= 0;
1818 p_props
->jss
.b_inited
= true;
1821 /* Parse the main lines */
1824 const char *s
= TextGetLine( txt
);
1826 return VLC_EGENERIC
;
1828 size_t line_length
= strlen( s
);
1829 psz_orig
= malloc( line_length
+ 1 );
1832 psz_text
= psz_orig
;
1834 /* Complete time lines */
1835 int h1
, h2
, m1
, m2
, s1
, s2
, f1
, f2
;
1836 if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1837 &h1
, &m1
, &s1
, &f1
, &h2
, &m2
, &s2
, &f2
, psz_text
) == 9 )
1839 p_subtitle
->i_start
= ( ( (int64_t) h1
*3600 + m1
* 60 + s1
) +
1840 (int64_t)( ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1842 p_subtitle
->i_stop
= ( ( (int64_t) h2
*3600 + m2
* 60 + s2
) +
1843 (int64_t)( ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1847 /* Short time lines */
1848 else if( sscanf( s
, "@%d @%d %[^\n\r]", &f1
, &f2
, psz_text
) == 3 )
1850 p_subtitle
->i_start
= ((int64_t)
1851 ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1852 p_subtitle
->i_stop
= ((int64_t)
1853 ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1856 /* General Directive lines */
1857 /* Only TIME and SHIFT are supported so far */
1858 else if( s
[0] == '#' )
1860 int h
= 0, m
=0, sec
= 1, f
= 1;
1864 strcpy( psz_text
, s
);
1866 switch( toupper( (unsigned char)psz_text
[1] ) )
1869 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 6 : 2 ;
1870 if ( shift
> line_length
)
1873 if( sscanf( &psz_text
[shift
], "%d", &h
) )
1875 /* Negative shifting */
1882 if( sscanf( &psz_text
[shift
], "%*d:%d", &m
) )
1884 if( sscanf( &psz_text
[shift
], "%*d:%*d:%d", &sec
) )
1886 sscanf( &psz_text
[shift
], "%*d:%*d:%*d.%d", &f
);
1891 sscanf( &psz_text
[shift
], "%d:%d.%d",
1899 sscanf( &psz_text
[shift
], "%d.%d", &sec
, &f
);
1902 p_props
->jss
.i_time_shift
= ( ( h
* 3600 + m
* 60 + sec
)
1903 * p_props
->jss
.i_time_resolution
+ f
) * inv
;
1908 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 8 : 2 ;
1909 if ( shift
> line_length
)
1912 sscanf( &psz_text
[shift
], "%d", &p_props
->jss
.i_time_resolution
);
1913 if( !p_props
->jss
.i_time_resolution
)
1914 p_props
->jss
.i_time_resolution
= 30;
1921 /* Unkown type line, probably a comment */
1928 while( psz_text
[ strlen( psz_text
) - 1 ] == '\\' )
1930 const char *s2
= TextGetLine( txt
);
1935 return VLC_EGENERIC
;
1938 size_t i_len
= strlen( s2
);
1942 size_t i_old
= strlen( psz_text
);
1944 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 );
1948 psz_orig
= psz_text
;
1949 strcat( psz_text
, s2
);
1952 /* Skip the blanks */
1953 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1955 /* Parse the directives */
1956 if( isalpha( (unsigned char)*psz_text
) || *psz_text
== '[' )
1958 while( *psz_text
&& *psz_text
!= ' ' )
1961 /* Directives are NOT parsed yet */
1962 /* This has probably a better place in a decoder ? */
1963 /* directive = malloc( strlen( psz_text ) + 1 );
1964 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1967 /* Skip the blanks after directives */
1968 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1970 /* Clean all the lines from inline comments and other stuffs */
1971 psz_orig2
= calloc( strlen( psz_text
) + 1, 1 );
1972 psz_text2
= psz_orig2
;
1974 for( ; *psz_text
!= '\0' && *psz_text
!= '\n' && *psz_text
!= '\r'; )
1979 p_props
->jss
.i_comment
++;
1982 if( p_props
->jss
.i_comment
)
1984 p_props
->jss
.i_comment
= 0;
1985 if( (*(psz_text
+ 1 ) ) == ' ' ) psz_text
++;
1989 if( !p_props
->jss
.i_comment
)
1997 if( (*(psz_text
+ 1 ) ) == ' ' || (*(psz_text
+ 1 ) ) == '\t' )
1999 if( !p_props
->jss
.i_comment
)
2006 if( (*(psz_text
+ 1 ) ) == 'n' )
2013 if( ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'C' ) ||
2014 ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'F' ) )
2019 if( (*(psz_text
+ 1 ) ) == 'B' || (*(psz_text
+ 1 ) ) == 'b' ||
2020 (*(psz_text
+ 1 ) ) == 'I' || (*(psz_text
+ 1 ) ) == 'i' ||
2021 (*(psz_text
+ 1 ) ) == 'U' || (*(psz_text
+ 1 ) ) == 'u' ||
2022 (*(psz_text
+ 1 ) ) == 'D' || (*(psz_text
+ 1 ) ) == 'N' )
2027 if( (*(psz_text
+ 1 ) ) == '~' || (*(psz_text
+ 1 ) ) == '{' ||
2028 (*(psz_text
+ 1 ) ) == '\\' )
2030 else if( ( *(psz_text
+ 1 ) == '\r' || *(psz_text
+ 1 ) == '\n' ) &&
2031 *(psz_text
+ 1 ) != '\0' )
2037 if( !p_props
->jss
.i_comment
)
2039 *psz_text2
= *psz_text
;
2046 p_subtitle
->psz_text
= psz_orig2
;
2047 msg_Dbg( p_obj
, "%s", p_subtitle
->psz_text
);
2052 static int ParsePSB( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2053 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2056 VLC_UNUSED(p_props
);
2057 VLC_UNUSED( i_idx
);
2066 const char *s
= TextGetLine( txt
);
2069 return VLC_EGENERIC
;
2071 psz_text
= malloc( strlen( s
) + 1 );
2075 if( sscanf( s
, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2076 &h1
, &m1
, &s1
, &h2
, &m2
, &s2
, psz_text
) == 7 )
2078 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2079 (int64_t)m1
* 60*1000 +
2080 (int64_t)s1
* 1000 ) * 1000;
2081 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2082 (int64_t)m2
* 60*1000 +
2083 (int64_t)s2
* 1000 ) * 1000;
2089 /* replace | by \n */
2090 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
2092 if( psz_text
[i
] == '|' )
2095 p_subtitle
->psz_text
= psz_text
;
2099 static int64_t ParseRealTime( char *psz
, int *h
, int *m
, int *s
, int *f
)
2101 if( *psz
== '\0' ) return 0;
2102 if( sscanf( psz
, "%d:%d:%d.%d", h
, m
, s
, f
) == 4 ||
2103 sscanf( psz
, "%d:%d.%d", m
, s
, f
) == 3 ||
2104 sscanf( psz
, "%d.%d", s
, f
) == 2 ||
2105 sscanf( psz
, "%d:%d", m
, s
) == 2 ||
2106 sscanf( psz
, "%d", s
) == 1 )
2108 return (int64_t)((( *h
* 60 + *m
) * 60 ) + *s
) * 1000 * 1000
2109 + (int64_t)*f
* 10 * 1000;
2111 else return VLC_EGENERIC
;
2114 static int ParseRealText( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2115 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2118 VLC_UNUSED(p_props
);
2119 VLC_UNUSED( i_idx
);
2120 char *psz_text
= NULL
;
2124 int h1
= 0, m1
= 0, s1
= 0, f1
= 0;
2125 int h2
= 0, m2
= 0, s2
= 0, f2
= 0;
2126 const char *s
= TextGetLine( txt
);
2130 return VLC_EGENERIC
;
2132 psz_text
= malloc( strlen( s
) + 1 );
2136 /* Find the good begining. This removes extra spaces at the beginning
2138 char *psz_temp
= strcasestr( s
, "<time");
2139 if( psz_temp
!= NULL
)
2141 char psz_end
[12], psz_begin
[12];
2142 /* Line has begin and end */
2143 if( ( sscanf( psz_temp
,
2144 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2145 psz_begin
, psz_end
, psz_text
) != 3 ) &&
2146 /* Line has begin and no end */
2148 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2149 psz_begin
, psz_text
) != 2) )
2150 /* Line is not recognized */
2156 int64_t i_time
= ParseRealTime( psz_begin
, &h1
, &m1
, &s1
, &f1
);
2157 p_subtitle
->i_start
= i_time
>= 0 ? i_time
: 0;
2159 i_time
= ParseRealTime( psz_end
, &h2
, &m2
, &s2
, &f2
);
2160 p_subtitle
->i_stop
= i_time
>= 0 ? i_time
: -1;
2165 /* Get the following Lines */
2168 const char *s
= TextGetLine( txt
);
2173 return VLC_EGENERIC
;
2176 size_t i_len
= strlen( s
);
2177 if( i_len
== 0 ) break;
2179 if( strcasestr( s
, "<time" ) ||
2180 strcasestr( s
, "<clear/") )
2182 TextPreviousLine( txt
);
2186 size_t i_old
= strlen( psz_text
);
2188 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2192 strcat( psz_text
, s
);
2193 strcat( psz_text
, "\n" );
2196 /* Remove the starting ">" that remained after the sscanf */
2197 memmove( &psz_text
[0], &psz_text
[1], strlen( psz_text
) );
2199 p_subtitle
->psz_text
= psz_text
;
2204 static int ParseDKS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2205 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2208 VLC_UNUSED(p_props
);
2209 VLC_UNUSED( i_idx
);
2217 char *s
= TextGetLine( txt
);
2220 return VLC_EGENERIC
;
2222 psz_text
= malloc( strlen( s
) + 1 );
2226 if( sscanf( s
, "[%d:%d:%d]%[^\r\n]",
2227 &h1
, &m1
, &s1
, psz_text
) == 4 )
2229 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2230 (int64_t)m1
* 60*1000 +
2231 (int64_t)s1
* 1000 ) * 1000;
2233 s
= TextGetLine( txt
);
2237 return VLC_EGENERIC
;
2240 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2241 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2242 (int64_t)m2
* 60*1000 +
2243 (int64_t)s2
* 1000 ) * 1000;
2245 p_subtitle
->i_stop
= -1;
2251 /* replace [br] by \n */
2253 while( ( p
= strstr( psz_text
, "[br]" ) ) )
2256 memmove( p
, &p
[3], strlen(&p
[3])+1 );
2259 p_subtitle
->psz_text
= psz_text
;
2263 static int ParseSubViewer1( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2264 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2267 VLC_UNUSED(p_props
);
2268 VLC_UNUSED( i_idx
);
2275 char *s
= TextGetLine( txt
);
2278 return VLC_EGENERIC
;
2280 if( sscanf( s
, "[%d:%d:%d]", &h1
, &m1
, &s1
) == 3 )
2282 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2283 (int64_t)m1
* 60*1000 +
2284 (int64_t)s1
* 1000 ) * 1000;
2286 s
= TextGetLine( txt
);
2288 return VLC_EGENERIC
;
2290 psz_text
= strdup( s
);
2294 s
= TextGetLine( txt
);
2298 return VLC_EGENERIC
;
2301 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2302 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2303 (int64_t)m2
* 60*1000 +
2304 (int64_t)s2
* 1000 ) * 1000;
2306 p_subtitle
->i_stop
= -1;
2312 p_subtitle
->psz_text
= psz_text
;
2317 static int ParseCommonSBV( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2318 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2321 VLC_UNUSED( i_idx
);
2322 VLC_UNUSED( p_props
);
2327 const char *s
= TextGetLine( txt
);
2328 int h1
= 0, m1
= 0, s1
= 0, d1
= 0;
2329 int h2
= 0, m2
= 0, s2
= 0, d2
= 0;
2332 return VLC_EGENERIC
;
2334 if( sscanf( s
,"%d:%d:%d.%d,%d:%d:%d.%d",
2336 &h2
, &m2
, &s2
, &d2
) == 8 )
2338 p_subtitle
->i_start
= ( (int64_t)h1
* 3600 * 1000 +
2339 (int64_t)m1
* 60 * 1000 +
2340 (int64_t)s1
* 1000 +
2341 (int64_t)d1
) * 1000;
2343 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600 * 1000 +
2344 (int64_t)m2
* 60 * 1000 +
2345 (int64_t)s2
* 1000 +
2346 (int64_t)d2
) * 1000;
2347 if( p_subtitle
->i_start
< p_subtitle
->i_stop
)
2352 /* Now read text until an empty line */
2353 psz_text
= strdup("");
2359 const char *s
= TextGetLine( txt
);
2363 i_len
= s
? strlen( s
) : 0;
2366 p_subtitle
->psz_text
= psz_text
;
2370 i_old
= strlen( psz_text
);
2371 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2375 strcat( psz_text
, s
);
2376 strcat( psz_text
, "\n" );
2380 static int ParseSCC( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2381 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2384 VLC_UNUSED( i_idx
);
2385 VLC_UNUSED( p_props
);
2387 static const struct rates
2390 vlc_rational_t rate
;
2391 bool b_drop_allowed
;
2393 { 2398, { 24000, 1001 }, false },
2394 { 2400, { 24, 1 }, false },
2395 { 2500, { 25, 1 }, false },
2396 { 2997, { 30000, 1001 }, true }, /* encoding rate */
2397 { 3000, { 30, 1 }, false },
2398 { 5000, { 50, 1 }, false },
2399 { 5994, { 60000, 1001 }, true },
2400 { 6000, { 60, 1 }, false },
2402 const struct rates
*p_rate
= &framerates
[3];
2403 float f_fps
= var_GetFloat( p_obj
, "sub-fps" );
2406 for( size_t i
=0; i
<ARRAY_SIZE(framerates
); i
++ )
2408 if( (unsigned)(f_fps
* 100) == framerates
[i
].val
)
2410 p_rate
= &framerates
[i
];
2418 const char *psz_line
= TextGetLine( txt
);
2420 return VLC_EGENERIC
;
2422 unsigned h
, m
, s
, f
;
2424 if( sscanf( psz_line
, "%u:%u:%u%c%u ", &h
, &m
, &s
, &c
, &f
) != 5 ||
2425 ( c
!= ':' && c
!= ';' ) )
2428 /* convert everything to seconds */
2429 mtime_t i_frames
= h
* 3600 + m
* 60 + s
;
2431 if( c
== ';' && p_rate
->b_drop_allowed
) /* dropframe */
2433 /* convert to frame # to be accurate between inter drop drift
2434 * of 18 frames see http://andrewduncan.net/timecodes/ */
2435 const unsigned i_mins
= h
* 60 + m
;
2436 i_frames
= i_frames
* p_rate
[+1].rate
.num
+ f
2437 - (p_rate
[+1].rate
.den
* 2 * (i_mins
- i_mins
% 10));
2441 /* convert to frame # at 29.97 */
2442 i_frames
= i_frames
* framerates
[3].rate
.num
/ framerates
[3].rate
.den
+ f
;
2444 p_subtitle
->i_start
= VLC_TS_0
+ i_frames
* CLOCK_FREQ
*
2445 p_rate
->rate
.den
/ p_rate
->rate
.num
;
2446 p_subtitle
->i_stop
= -1;
2448 const char *psz_text
= strchr( psz_line
, '\t' );
2449 if( !psz_text
&& !(psz_text
= strchr( psz_line
, ' ' )) )
2452 if ( psz_text
[1] == '\0' )
2455 p_subtitle
->psz_text
= strdup( psz_text
+ 1 );
2456 if( !p_subtitle
->psz_text
)
2465 /* Matches filename.xx.srt */
2466 static char * get_language_from_filename( const char * psz_sub_file
)
2468 char *psz_ret
= NULL
;
2469 char *psz_tmp
, *psz_language_begin
;
2471 if( !psz_sub_file
) return NULL
;
2472 char *psz_work
= strdup( psz_sub_file
);
2474 /* Removing extension, but leaving the dot */
2475 psz_tmp
= strrchr( psz_work
, '.' );
2479 psz_language_begin
= strrchr( psz_work
, '.' );
2480 if( psz_language_begin
)
2481 psz_ret
= strdup(++psz_language_begin
);