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",
67 "subviewer1", "vtt", "sbv"
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 */
119 SUB_TYPE_SCC
, /* Scenarist Closed Caption */
129 static int TextLoad( text_t
*, stream_t
*s
);
130 static void TextUnload( text_t
* );
142 enum subtitle_type_e i_type
;
143 int64_t i_microsecperframe
;
145 char *psz_header
; /* SSA */
152 int i_time_resolution
;
166 const char *psz_start
;
177 int64_t i_next_demux_date
;
189 subs_properties_t props
;
191 block_t
* (*pf_convert
)( const subtitle_t
* );
194 static int ParseMicroDvd ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
195 static int ParseSubRip ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
196 static int ParseSubViewer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
197 static int ParseSSA ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
198 static int ParseVplayer ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
199 static int ParseSami ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
200 static int ParseDVDSubtitle( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
201 static int ParseMPL2 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
202 static int ParseAQT ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
203 static int ParsePJS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
204 static int ParseMPSub ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
205 static int ParseJSS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
206 static int ParsePSB ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
207 static int ParseRealText ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
208 static int ParseDKS ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
209 static int ParseSubViewer1 ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
210 static int ParseCommonVTTSBV( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
211 static int ParseSCC ( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
215 const char *psz_type_name
;
217 const char *psz_name
;
218 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
219 } sub_read_subtitle_function
[] =
221 { "microdvd", SUB_TYPE_MICRODVD
, "MicroDVD", ParseMicroDvd
},
222 { "subrip", SUB_TYPE_SUBRIP
, "SubRIP", ParseSubRip
},
223 { "subviewer", SUB_TYPE_SUBVIEWER
, "SubViewer", ParseSubViewer
},
224 { "ssa1", SUB_TYPE_SSA1
, "SSA-1", ParseSSA
},
225 { "ssa2-4", SUB_TYPE_SSA2_4
, "SSA-2/3/4", ParseSSA
},
226 { "ass", SUB_TYPE_ASS
, "SSA/ASS", ParseSSA
},
227 { "vplayer", SUB_TYPE_VPLAYER
, "VPlayer", ParseVplayer
},
228 { "sami", SUB_TYPE_SAMI
, "SAMI", ParseSami
},
229 { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE
, "DVDSubtitle", ParseDVDSubtitle
},
230 { "mpl2", SUB_TYPE_MPL2
, "MPL2", ParseMPL2
},
231 { "aqt", SUB_TYPE_AQT
, "AQTitle", ParseAQT
},
232 { "pjs", SUB_TYPE_PJS
, "PhoenixSub", ParsePJS
},
233 { "mpsub", SUB_TYPE_MPSUB
, "MPSub", ParseMPSub
},
234 { "jacosub", SUB_TYPE_JACOSUB
, "JacoSub", ParseJSS
},
235 { "psb", SUB_TYPE_PSB
, "PowerDivx", ParsePSB
},
236 { "realtext", SUB_TYPE_RT
, "RealText", ParseRealText
},
237 { "dks", SUB_TYPE_DKS
, "DKS", ParseDKS
},
238 { "subviewer1", SUB_TYPE_SUBVIEW1
, "Subviewer 1", ParseSubViewer1
},
239 { "text/vtt", SUB_TYPE_VTT
, "WebVTT", ParseCommonVTTSBV
},
240 { "sbv", SUB_TYPE_SBV
, "SBV", ParseCommonVTTSBV
},
241 { "scc", SUB_TYPE_SCC
, "SCC", ParseSCC
},
242 { NULL
, SUB_TYPE_UNKNOWN
, "Unknown", NULL
}
244 /* When adding support for more formats, be sure to add their file extension
245 * to src/input/subtitles.c to enable auto-detection.
248 static int Demux( demux_t
* );
249 static int Control( demux_t
*, int, va_list );
251 static void Fix( demux_t
* );
252 static char * get_language_from_filename( const char * );
254 /*****************************************************************************
255 * Decoder format output function
256 *****************************************************************************/
258 static block_t
*ToTextBlock( const subtitle_t
*p_subtitle
)
261 size_t i_len
= strlen( p_subtitle
->psz_text
) + 1;
263 if( i_len
<= 1 || !(p_block
= block_Alloc( i_len
)) )
266 memcpy( p_block
->p_buffer
, p_subtitle
->psz_text
, i_len
);
271 static block_t
*ToEIA608Block( const subtitle_t
*p_subtitle
)
274 const size_t i_len
= strlen( p_subtitle
->psz_text
);
275 const size_t i_block
= (1 + i_len
/ 5) * 3;
277 if( i_len
< 4 || !(p_block
= block_Alloc( i_block
)) )
280 p_block
->i_buffer
= 0;
282 char *saveptr
= NULL
;
283 char *psz_tok
= strtok_r( p_subtitle
->psz_text
, " ", &saveptr
);
286 sscanf( psz_tok
, "%2x%2x", &a
, &b
) == 2 &&
287 i_block
- p_block
->i_buffer
>= 3 )
289 uint8_t *p_data
= &p_block
->p_buffer
[p_block
->i_buffer
];
293 p_block
->i_buffer
+= 3;
294 psz_tok
= strtok_r( NULL
, " ", &saveptr
);
300 /*****************************************************************************
302 *****************************************************************************/
303 static int Open ( vlc_object_t
*p_this
)
305 demux_t
*p_demux
= (demux_t
*)p_this
;
310 int (*pf_read
)( vlc_object_t
*, subs_properties_t
*, text_t
*, subtitle_t
*, size_t );
312 if( !p_demux
->obj
.force
)
314 msg_Dbg( p_demux
, "subtitle demux discarded" );
318 p_demux
->pf_demux
= Demux
;
319 p_demux
->pf_control
= Control
;
320 p_demux
->p_sys
= p_sys
= malloc( sizeof( demux_sys_t
) );
324 p_sys
->b_slave
= false;
325 p_sys
->b_first_time
= true;
326 p_sys
->i_next_demux_date
= 0;
328 p_sys
->pf_convert
= ToTextBlock
;
330 p_sys
->subtitles
.i_current
= 0;
331 p_sys
->subtitles
.i_count
= 0;
332 p_sys
->subtitles
.p_array
= NULL
;
334 p_sys
->props
.psz_header
= NULL
;
335 p_sys
->props
.i_microsecperframe
= 40000;
336 p_sys
->props
.jss
.b_inited
= false;
337 p_sys
->props
.mpsub
.b_inited
= false;
338 p_sys
->props
.sami
.psz_start
= NULL
;
341 f_fps
= var_CreateGetFloat( p_demux
, "sub-original-fps" ); /* FIXME */
343 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
345 msg_Dbg( p_demux
, "Movie fps: %f", (double) f_fps
);
347 /* Check for override of the fps */
348 f_fps
= var_CreateGetFloat( p_demux
, "sub-fps" );
351 p_sys
->props
.i_microsecperframe
= llroundf( 1000000.f
/ f_fps
);
352 msg_Dbg( p_demux
, "Override subtitle fps %f", (double) f_fps
);
355 /* Get or probe the type */
356 p_sys
->props
.i_type
= SUB_TYPE_UNKNOWN
;
357 psz_type
= var_CreateGetString( p_demux
, "sub-type" );
358 if( psz_type
&& *psz_type
)
360 for( int i
= 0; ; i
++ )
362 if( sub_read_subtitle_function
[i
].psz_type_name
== NULL
)
365 if( !strcmp( sub_read_subtitle_function
[i
].psz_type_name
,
368 p_sys
->props
.i_type
= sub_read_subtitle_function
[i
].i_type
;
376 const uint64_t i_start_pos
= vlc_stream_Tell( p_demux
->s
);
380 const uint8_t *p_peek
;
381 if( vlc_stream_Peek( p_demux
->s
, &p_peek
, 16 ) < 16 )
394 const char *psz_bom
= NULL
;
397 /* Detect Unicode while skipping the UTF-8 Byte Order Mark */
398 if( !memcmp( p_peek
, "\xEF\xBB\xBF", 3 ) )
403 else if( !memcmp( p_peek
, "\xFF\xFE", 2 ) )
406 psz_bom
= "UTF-16LE";
409 else if( !memcmp( p_peek
, "\xFE\xFF", 2 ) )
412 psz_bom
= "UTF-16BE";
417 msg_Dbg( p_demux
, "detected %s Byte Order Mark", psz_bom
);
419 i_peek
= vlc_stream_Peek( p_demux
->s
, &p_peek
, i_peek
);
420 if( unlikely(i_peek
< 16) )
426 stream_t
*p_probestream
= NULL
;
427 if( e_bom
!= UTF8BOM
&& e_bom
!= NOBOM
)
431 vlc_iconv_t handle
= vlc_iconv_open( "UTF-8", psz_bom
);
434 char *p_outbuf
= malloc( i_peek
);
437 const char *p_inbuf
= (const char *) p_peek
;
438 char *psz_converted
= p_outbuf
;
439 const size_t i_outbuf_size
= i_peek
;
440 size_t i_inbuf_remain
= i_peek
;
441 size_t i_outbuf_remain
= i_peek
;
442 if ( VLC_ICONV_ERR
!= vlc_iconv( handle
,
443 &p_inbuf
, &i_inbuf_remain
,
444 &p_outbuf
, &i_outbuf_remain
) )
446 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *) psz_converted
,
447 i_outbuf_size
- i_outbuf_remain
,
448 false ); /* free p_outbuf on release */
450 else free( p_outbuf
);
452 vlc_iconv_close( handle
);
458 const size_t i_skip
= (e_bom
== UTF8BOM
) ? 3 : 0;
459 p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *) &p_peek
[i_skip
],
460 i_peek
- i_skip
, true );
463 if( p_probestream
== NULL
)
469 /* Probe if unknown type */
470 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
475 msg_Dbg( p_demux
, "autodetecting subtitle format" );
476 for( i_try
= 0; i_try
< 256; i_try
++ )
481 if( (s
= vlc_stream_ReadLine( p_probestream
) ) == NULL
)
484 if( strcasestr( s
, "<SAMI>" ) )
486 p_sys
->props
.i_type
= SUB_TYPE_SAMI
;
489 else if( sscanf( s
, "{%d}{%d}", &i_dummy
, &i_dummy
) == 2 ||
490 sscanf( s
, "{%d}{}", &i_dummy
) == 1)
492 p_sys
->props
.i_type
= SUB_TYPE_MICRODVD
;
495 else if( sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d,%d",
496 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
497 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
498 sscanf( s
, "%d:%d:%d --> %d:%d:%d,%d",
499 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
500 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
501 sscanf( s
, "%d:%d:%d,%d --> %d:%d:%d",
502 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
503 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
504 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d.%d",
505 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
506 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
) == 8 ||
507 sscanf( s
, "%d:%d:%d --> %d:%d:%d.%d",
508 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
509 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
510 sscanf( s
, "%d:%d:%d.%d --> %d:%d:%d",
511 &i_dummy
,&i_dummy
,&i_dummy
,&i_dummy
,
512 &i_dummy
,&i_dummy
,&i_dummy
) == 7 ||
513 sscanf( s
, "%d:%d:%d --> %d:%d:%d",
514 &i_dummy
,&i_dummy
,&i_dummy
,
515 &i_dummy
,&i_dummy
,&i_dummy
) == 6 )
517 p_sys
->props
.i_type
= SUB_TYPE_SUBRIP
;
520 else if( !strncasecmp( s
, "!: This is a Sub Station Alpha v1", 33 ) )
522 p_sys
->props
.i_type
= SUB_TYPE_SSA1
;
525 else if( !strncasecmp( s
, "ScriptType: v4.00+", 18 ) )
527 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
530 else if( !strncasecmp( s
, "ScriptType: v4.00", 17 ) )
532 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
535 else if( !strncasecmp( s
, "Dialogue: Marked", 16 ) )
537 p_sys
->props
.i_type
= SUB_TYPE_SSA2_4
;
540 else if( !strncasecmp( s
, "Dialogue:", 9 ) )
542 p_sys
->props
.i_type
= SUB_TYPE_ASS
;
545 else if( strcasestr( s
, "[INFORMATION]" ) )
547 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEWER
; /* I hope this will work */
550 else if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d",
551 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
552 &i_dummy
, &i_dummy
, &i_dummy
) == 7 ||
553 sscanf( s
, "@%d @%d", &i_dummy
, &i_dummy
) == 2)
555 p_sys
->props
.i_type
= SUB_TYPE_JACOSUB
;
558 else if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
559 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
,
560 &i_dummy
, &i_dummy
, &i_dummy
, &i_dummy
) == 8 )
562 p_sys
->props
.i_type
= SUB_TYPE_SBV
;
565 else if( sscanf( s
, "%d:%d:%d:", &i_dummy
, &i_dummy
, &i_dummy
) == 3 ||
566 sscanf( s
, "%d:%d:%d ", &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
568 p_sys
->props
.i_type
= SUB_TYPE_VPLAYER
;
571 else if( sscanf( s
, "{T %d:%d:%d:%d", &i_dummy
, &i_dummy
,
572 &i_dummy
, &i_dummy
) == 4 )
574 p_sys
->props
.i_type
= SUB_TYPE_DVDSUBTITLE
;
577 else if( sscanf( s
, "[%d:%d:%d]%c",
578 &i_dummy
, &i_dummy
, &i_dummy
, &p_dummy
) == 4 )
580 p_sys
->props
.i_type
= SUB_TYPE_DKS
;
583 else if( strstr( s
, "*** START SCRIPT" ) )
585 p_sys
->props
.i_type
= SUB_TYPE_SUBVIEW1
;
588 else if( sscanf( s
, "[%d][%d]", &i_dummy
, &i_dummy
) == 2 ||
589 sscanf( s
, "[%d][]", &i_dummy
) == 1)
591 p_sys
->props
.i_type
= SUB_TYPE_MPL2
;
594 else if( sscanf (s
, "FORMAT=%d", &i_dummy
) == 1 ||
595 ( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1
598 p_sys
->props
.i_type
= SUB_TYPE_MPSUB
;
601 else if( sscanf( s
, "-->> %d", &i_dummy
) == 1 )
603 p_sys
->props
.i_type
= SUB_TYPE_AQT
;
606 else if( sscanf( s
, "%d,%d,", &i_dummy
, &i_dummy
) == 2 )
608 p_sys
->props
.i_type
= SUB_TYPE_PJS
;
611 else if( sscanf( s
, "{%d:%d:%d}",
612 &i_dummy
, &i_dummy
, &i_dummy
) == 3 )
614 p_sys
->props
.i_type
= SUB_TYPE_PSB
;
617 else if( strcasestr( s
, "<time" ) )
619 p_sys
->props
.i_type
= SUB_TYPE_RT
;
622 else if( !strncasecmp( s
, "WEBVTT",6 ) )
624 p_sys
->props
.i_type
= SUB_TYPE_VTT
;
627 else if( !strncasecmp( s
, "Scenarist_SCC V1.0", 18 ) )
629 p_sys
->props
.i_type
= SUB_TYPE_SCC
;
630 p_sys
->pf_convert
= ToEIA608Block
;
641 vlc_stream_Delete( p_probestream
);
643 /* Quit on unknown subtitles */
644 if( p_sys
->props
.i_type
== SUB_TYPE_UNKNOWN
)
647 /* Ensure it will work with non seekable streams */
648 assert( i_start_pos
== vlc_stream_Tell( p_demux
->s
) );
650 msg_Warn( p_demux
, "failed to recognize subtitle type" );
655 for( int i
= 0; ; i
++ )
657 if( sub_read_subtitle_function
[i
].i_type
== p_sys
->props
.i_type
)
659 msg_Dbg( p_demux
, "detected %s format",
660 sub_read_subtitle_function
[i
].psz_name
);
661 pf_read
= sub_read_subtitle_function
[i
].pf_read
;
666 msg_Dbg( p_demux
, "loading all subtitles..." );
668 if( e_bom
== UTF8BOM
&& /* skip BOM */
669 vlc_stream_Read( p_demux
->s
, NULL
, 3 ) != 3 )
675 /* Load the whole file */
677 TextLoad( &txtlines
, p_demux
->s
);
680 for( size_t i_max
= 0; i_max
< SIZE_MAX
- 500 * sizeof(subtitle_t
); )
682 if( p_sys
->subtitles
.i_count
>= i_max
)
685 subtitle_t
*p_realloc
= realloc( p_sys
->subtitles
.p_array
, sizeof(subtitle_t
) * i_max
);
686 if( p_realloc
== NULL
)
688 TextUnload( &txtlines
);
692 p_sys
->subtitles
.p_array
= p_realloc
;
695 if( pf_read( VLC_OBJECT(p_demux
), &p_sys
->props
, &txtlines
,
696 &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
],
697 p_sys
->subtitles
.i_count
) )
700 p_sys
->subtitles
.i_count
++;
703 TextUnload( &txtlines
);
705 msg_Dbg(p_demux
, "loaded %zu subtitles", p_sys
->subtitles
.i_count
);
707 /* Fix subtitle (order and time) *** */
708 p_sys
->subtitles
.i_current
= 0;
710 if( p_sys
->subtitles
.i_count
> 0 )
711 p_sys
->i_length
= p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_count
-1].i_stop
;
713 /* *** add subtitle ES *** */
714 if( p_sys
->props
.i_type
== SUB_TYPE_SSA1
||
715 p_sys
->props
.i_type
== SUB_TYPE_SSA2_4
||
716 p_sys
->props
.i_type
== SUB_TYPE_ASS
)
719 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SSA
);
721 else if( p_sys
->props
.i_type
== SUB_TYPE_SCC
)
723 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_CEA608
);
724 fmt
.subs
.cc
.i_reorder_depth
= -1;
727 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SUBT
);
729 /* Stupid language detection in the filename */
730 char * psz_language
= get_language_from_filename( p_demux
->psz_file
);
734 fmt
.psz_language
= psz_language
;
735 msg_Dbg( p_demux
, "detected language %s of subtitle: %s", psz_language
,
736 p_demux
->psz_location
);
740 fmt
.subs
.psz_encoding
= strdup( psz_bom
);
741 char *psz_description
= var_InheritString( p_demux
, "sub-description" );
742 if( psz_description
&& *psz_description
)
743 fmt
.psz_description
= psz_description
;
745 free( psz_description
);
746 if( p_sys
->props
.psz_header
!= NULL
&&
747 (fmt
.p_extra
= strdup( p_sys
->props
.psz_header
)) )
749 fmt
.i_extra
= strlen( p_sys
->props
.psz_header
) + 1;
752 p_sys
->es
= es_out_Add( p_demux
->out
, &fmt
);
753 es_format_Clean( &fmt
);
754 if( p_sys
->es
== NULL
)
763 /*****************************************************************************
764 * Close: Close subtitle demux
765 *****************************************************************************/
766 static void Close( vlc_object_t
*p_this
)
768 demux_t
*p_demux
= (demux_t
*)p_this
;
769 demux_sys_t
*p_sys
= p_demux
->p_sys
;
771 for( size_t i
= 0; i
< p_sys
->subtitles
.i_count
; i
++ )
772 free( p_sys
->subtitles
.p_array
[i
].psz_text
);
773 free( p_sys
->subtitles
.p_array
);
774 free( p_sys
->props
.psz_header
);
779 /*****************************************************************************
781 *****************************************************************************/
782 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
784 demux_sys_t
*p_sys
= p_demux
->p_sys
;
791 *va_arg( args
, bool * ) = true;
794 case DEMUX_GET_LENGTH
:
795 pi64
= va_arg( args
, int64_t * );
796 *pi64
= p_sys
->i_length
;
800 pi64
= va_arg( args
, int64_t * );
801 *pi64
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
803 *pi64
= p_sys
->i_next_demux_date
;
807 i64
= va_arg( args
, int64_t );
808 for( size_t i
= 0; i
+ 1< p_sys
->subtitles
.i_count
; i
++ )
810 if( p_sys
->subtitles
.p_array
[i
+ 1].i_start
>= i64
)
812 p_sys
->subtitles
.i_current
= i
;
813 p_sys
->i_next_demux_date
= i64
;
814 p_sys
->b_first_time
= true;
820 case DEMUX_GET_POSITION
:
821 pf
= va_arg( args
, double * );
822 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
826 else if( p_sys
->subtitles
.i_count
> 0 && p_sys
->i_length
)
828 *pf
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
830 *pf
= p_sys
->i_next_demux_date
;
831 *pf
/= p_sys
->i_length
;
839 case DEMUX_SET_POSITION
:
840 f
= va_arg( args
, double );
841 if( p_sys
->subtitles
.i_count
&& p_sys
->i_length
)
843 i64
= VLC_TS_0
+ f
* p_sys
->i_length
;
844 return demux_Control( p_demux
, DEMUX_SET_TIME
, i64
);
848 case DEMUX_SET_NEXT_DEMUX_TIME
:
849 p_sys
->b_slave
= true;
850 p_sys
->i_next_demux_date
= va_arg( args
, int64_t ) - VLC_TS_0
;
853 case DEMUX_GET_PTS_DELAY
:
856 case DEMUX_GET_ATTACHMENTS
:
857 case DEMUX_GET_TITLE_INFO
:
858 case DEMUX_HAS_UNSUPPORTED_META
:
859 case DEMUX_CAN_RECORD
:
867 /*****************************************************************************
868 * Demux: Send subtitle to decoder
869 *****************************************************************************/
870 static int Demux( demux_t
*p_demux
)
872 demux_sys_t
*p_sys
= p_demux
->p_sys
;
874 int64_t i_barrier
= p_sys
->i_next_demux_date
- var_GetInteger( p_demux
->obj
.parent
, "spu-delay" );
876 i_barrier
= p_sys
->i_next_demux_date
;
878 while( p_sys
->subtitles
.i_current
< p_sys
->subtitles
.i_count
&&
879 p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
].i_start
<= i_barrier
)
881 const subtitle_t
*p_subtitle
= &p_sys
->subtitles
.p_array
[p_sys
->subtitles
.i_current
];
883 if ( !p_sys
->b_slave
&& p_sys
->b_first_time
)
885 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
886 p_sys
->b_first_time
= false;
889 if( p_subtitle
->i_start
>= 0 )
891 block_t
*p_block
= p_sys
->pf_convert( p_subtitle
);
895 p_block
->i_pts
= VLC_TS_0
+ p_subtitle
->i_start
;
896 if( p_subtitle
->i_stop
>= 0 && p_subtitle
->i_stop
>= p_subtitle
->i_start
)
897 p_block
->i_length
= p_subtitle
->i_stop
- p_subtitle
->i_start
;
899 es_out_Send( p_demux
->out
, p_sys
->es
, p_block
);
903 p_sys
->subtitles
.i_current
++;
906 if ( !p_sys
->b_slave
)
908 es_out_SetPCR( p_demux
->out
, VLC_TS_0
+ i_barrier
);
909 p_sys
->i_next_demux_date
+= CLOCK_FREQ
/ 8;
912 if( p_sys
->subtitles
.i_current
>= p_sys
->subtitles
.i_count
)
913 return VLC_DEMUXER_EOF
;
915 return VLC_DEMUXER_SUCCESS
;
919 static int subtitle_cmp( const void *first
, const void *second
)
921 int64_t result
= ((subtitle_t
*)(first
))->i_start
- ((subtitle_t
*)(second
))->i_start
;
922 /* Return -1, 0 ,1, and not directly substraction
923 * as result can be > INT_MAX */
924 return result
== 0 ? 0 : result
> 0 ? 1 : -1;
926 /*****************************************************************************
927 * Fix: fix time stamp and order of subtitle
928 *****************************************************************************/
929 static void Fix( demux_t
*p_demux
)
931 demux_sys_t
*p_sys
= p_demux
->p_sys
;
933 /* *** fix order (to be sure...) *** */
934 qsort( p_sys
->subtitles
.p_array
, p_sys
->subtitles
.i_count
, sizeof( p_sys
->subtitles
.p_array
[0] ), subtitle_cmp
);
937 static int TextLoad( text_t
*txt
, stream_t
*s
)
943 txt
->i_line_count
= 0;
945 txt
->line
= calloc( i_line_max
, sizeof( char * ) );
949 /* load the complete file */
952 char *psz
= vlc_stream_ReadLine( s
);
957 txt
->line
[txt
->i_line_count
] = psz
;
958 if( txt
->i_line_count
+ 1 >= i_line_max
)
961 char **p_realloc
= realloc( txt
->line
, i_line_max
* sizeof( char * ) );
962 if( p_realloc
== NULL
)
964 txt
->line
= p_realloc
;
969 if( txt
->i_line_count
== 0 )
977 static void TextUnload( text_t
*txt
)
979 if( txt
->i_line_count
)
981 for( size_t i
= 0; i
< txt
->i_line_count
; i
++ )
982 free( txt
->line
[i
] );
986 txt
->i_line_count
= 0;
989 static char *TextGetLine( text_t
*txt
)
991 if( txt
->i_line
>= txt
->i_line_count
)
994 return txt
->line
[txt
->i_line
++];
996 static void TextPreviousLine( text_t
*txt
)
998 if( txt
->i_line
> 0 )
1002 /*****************************************************************************
1003 * Specific Subtitle function
1004 *****************************************************************************/
1007 * {n1}{n2}Line1|Line2|Line3....
1008 * where n1 and n2 are the video frame number (n2 can be empty)
1010 static int ParseMicroDvd( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1011 text_t
*txt
, subtitle_t
*p_subtitle
,
1014 VLC_UNUSED( i_idx
);
1022 const char *s
= TextGetLine( txt
);
1024 return VLC_EGENERIC
;
1026 psz_text
= malloc( strlen(s
) + 1 );
1032 if( sscanf( s
, "{%d}{}%[^\r\n]", &i_start
, psz_text
) == 2 ||
1033 sscanf( s
, "{%d}{%d}%[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1035 if( i_start
!= 1 || i_stop
!= 1 )
1038 /* We found a possible setting of the framerate "{1}{1}23.976" */
1039 /* Check if it's usable, and if the sub-fps is not set */
1040 float f_fps
= us_strtof( psz_text
, NULL
);
1041 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1042 p_props
->i_microsecperframe
= llroundf(1000000.f
/ f_fps
);
1047 /* replace | by \n */
1048 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1050 if( psz_text
[i
] == '|' )
1055 p_subtitle
->i_start
= i_start
* p_props
->i_microsecperframe
;
1056 p_subtitle
->i_stop
= i_stop
>= 0 ? (i_stop
* p_props
->i_microsecperframe
) : -1;
1057 p_subtitle
->psz_text
= psz_text
;
1061 /* ParseSubRipSubViewer
1064 * h1:m1:s1,d1 --> h2:m2:s2,d2
1069 * Format SubViewer v1/v2
1070 * h1:m1:s1.d1,h2:m2:s2.d2
1075 * We ignore line number for SubRip
1077 static int ParseSubRipSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1078 text_t
*txt
, subtitle_t
*p_subtitle
,
1079 int (* pf_parse_timing
)(subtitle_t
*, const char *),
1083 VLC_UNUSED(p_props
);
1088 const char *s
= TextGetLine( txt
);
1091 return VLC_EGENERIC
;
1093 if( pf_parse_timing( p_subtitle
, s
) == VLC_SUCCESS
&&
1094 p_subtitle
->i_start
< p_subtitle
->i_stop
)
1100 /* Now read text until an empty line */
1101 psz_text
= strdup("");
1107 const char *s
= TextGetLine( txt
);
1111 i_len
= s
? strlen( s
) : 0;
1114 p_subtitle
->psz_text
= psz_text
;
1118 i_old
= strlen( psz_text
);
1119 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1124 strcat( psz_text
, s
);
1125 strcat( psz_text
, "\n" );
1127 /* replace [br] by \n */
1132 while( ( p
= strstr( psz_text
, "[br]" ) ) )
1135 memmove( p
, &p
[3], strlen(&p
[3])+1 );
1141 /* subtitle_ParseSubRipTimingValue
1142 * Parses SubRip timing value.
1144 static int subtitle_ParseSubRipTimingValue(int64_t *timing_value
,
1147 int h1
, m1
, s1
, d1
= 0;
1149 if ( sscanf( s
, "%d:%d:%d,%d",
1150 &h1
, &m1
, &s1
, &d1
) == 4 ||
1151 sscanf( s
, "%d:%d:%d.%d",
1152 &h1
, &m1
, &s1
, &d1
) == 4 ||
1153 sscanf( s
, "%d:%d:%d",
1154 &h1
, &m1
, &s1
) == 3 )
1156 (*timing_value
) = ( (int64_t)h1
* 3600 * 1000 +
1157 (int64_t)m1
* 60 * 1000 +
1158 (int64_t)s1
* 1000 +
1159 (int64_t)d1
) * 1000;
1164 return VLC_EGENERIC
;
1167 /* subtitle_ParseSubRipTiming
1168 * Parses SubRip timing.
1170 static int subtitle_ParseSubRipTiming( subtitle_t
*p_subtitle
,
1173 int i_result
= VLC_EGENERIC
;
1174 char *psz_start
, *psz_stop
;
1175 psz_start
= malloc( strlen(s
) + 1 );
1176 psz_stop
= malloc( strlen(s
) + 1 );
1178 if( sscanf( s
, "%s --> %s", psz_start
, psz_stop
) == 2 &&
1179 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_start
, psz_start
) == VLC_SUCCESS
&&
1180 subtitle_ParseSubRipTimingValue( &p_subtitle
->i_stop
, psz_stop
) == VLC_SUCCESS
)
1182 i_result
= VLC_SUCCESS
;
1192 static int ParseSubRip( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1193 text_t
*txt
, subtitle_t
*p_subtitle
,
1196 VLC_UNUSED( i_idx
);
1197 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1198 &subtitle_ParseSubRipTiming
,
1202 /* subtitle_ParseSubViewerTiming
1203 * Parses SubViewer timing.
1205 static int subtitle_ParseSubViewerTiming( subtitle_t
*p_subtitle
,
1208 int h1
, m1
, s1
, d1
, h2
, m2
, s2
, d2
;
1210 if( sscanf( s
, "%d:%d:%d.%d,%d:%d:%d.%d",
1211 &h1
, &m1
, &s1
, &d1
, &h2
, &m2
, &s2
, &d2
) == 8 )
1213 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1214 (int64_t)m1
* 60*1000 +
1215 (int64_t)s1
* 1000 +
1216 (int64_t)d1
) * 1000;
1218 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1219 (int64_t)m2
* 60*1000 +
1220 (int64_t)s2
* 1000 +
1221 (int64_t)d2
) * 1000;
1224 return VLC_EGENERIC
;
1229 static int ParseSubViewer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1230 text_t
*txt
, subtitle_t
*p_subtitle
,
1233 VLC_UNUSED( i_idx
);
1235 return ParseSubRipSubViewer( p_obj
, p_props
, txt
, p_subtitle
,
1236 &subtitle_ParseSubViewerTiming
,
1242 static int ParseSSA( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1243 text_t
*txt
, subtitle_t
*p_subtitle
,
1247 size_t header_len
= 0;
1251 const char *s
= TextGetLine( txt
);
1252 int h1
, m1
, s1
, c1
, h2
, m2
, s2
, c2
;
1253 char *psz_text
, *psz_temp
;
1257 return VLC_EGENERIC
;
1259 /* We expect (SSA2-4):
1260 * Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1261 * Dialogue: Marked=0,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1263 * 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.
1267 * Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
1268 * Dialogue: Layer#,0:02:40.65,0:02:41.79,Wolf main,Cher,0000,0000,0000,,Et les enregistrements de ses ondes delta ?
1271 /* The output text is - at least, not removing numbers - 18 chars shorter than the input text. */
1272 psz_text
= malloc( strlen(s
) );
1277 "Dialogue: %15[^,],%d:%d:%d.%d,%d:%d:%d.%d,%[^\r\n]",
1283 /* The dec expects: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
1284 /* (Layer comes from ASS specs ... it's empty for SSA.) */
1285 if( p_props
->i_type
== SUB_TYPE_SSA1
)
1287 /* SSA1 has only 8 commas before the text starts, not 9 */
1288 memmove( &psz_text
[1], psz_text
, strlen(psz_text
)+1 );
1293 int i_layer
= ( p_props
->i_type
== SUB_TYPE_ASS
) ? atoi( temp
) : 0;
1295 /* ReadOrder, Layer, %s(rest of fields) */
1296 if( asprintf( &psz_temp
, "%zu,%d,%s", i_idx
, i_layer
, psz_text
) == -1 )
1303 psz_text
= psz_temp
;
1306 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1307 (int64_t)m1
* 60*1000 +
1308 (int64_t)s1
* 1000 +
1309 (int64_t)c1
* 10 ) * 1000;
1310 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
1311 (int64_t)m2
* 60*1000 +
1312 (int64_t)s2
* 1000 +
1313 (int64_t)c2
* 10 ) * 1000;
1314 p_subtitle
->psz_text
= psz_text
;
1319 /* All the other stuff we add to the header field */
1320 if( header_len
== 0 && p_props
->psz_header
)
1321 header_len
= strlen( p_props
->psz_header
);
1323 size_t s_len
= strlen( s
);
1324 p_props
->psz_header
= realloc_or_free( p_props
->psz_header
, header_len
+ s_len
+ 2 );
1325 if( !p_props
->psz_header
)
1327 snprintf( p_props
->psz_header
+ header_len
, s_len
+ 2, "%s\n", s
);
1328 header_len
+= s_len
+ 1;
1334 * h:m:s:Line1|Line2|Line3....
1336 * h:m:s Line1|Line2|Line3....
1338 static int ParseVplayer( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1339 text_t
*txt
, subtitle_t
*p_subtitle
,
1343 VLC_UNUSED(p_props
);
1344 VLC_UNUSED( i_idx
);
1349 const char *s
= TextGetLine( txt
);
1353 return VLC_EGENERIC
;
1355 psz_text
= malloc( strlen( s
) + 1 );
1359 if( sscanf( s
, "%d:%d:%d%*c%[^\r\n]",
1360 &h1
, &m1
, &s1
, psz_text
) == 4 )
1362 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1363 (int64_t)m1
* 60*1000 +
1364 (int64_t)s1
* 1000 ) * 1000;
1365 p_subtitle
->i_stop
= -1;
1371 /* replace | by \n */
1372 for( size_t i
= 0; psz_text
[i
] != '\0'; i
++ )
1374 if( psz_text
[i
] == '|' )
1377 p_subtitle
->psz_text
= psz_text
;
1383 static const char *ParseSamiSearch( text_t
*txt
,
1384 const char *psz_start
, const char *psz_str
)
1386 if( psz_start
&& strcasestr( psz_start
, psz_str
) )
1388 const char *s
= strcasestr( psz_start
, psz_str
);
1389 return &s
[strlen( psz_str
)];
1394 const char *p
= TextGetLine( txt
);
1398 const char *s
= strcasestr( p
, psz_str
);
1400 return &s
[strlen( psz_str
)];
1403 static int ParseSami( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1404 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1407 VLC_UNUSED(p_props
);
1408 VLC_UNUSED( i_idx
);
1412 unsigned int i_text
;
1413 char text
[8192]; /* Arbitrary but should be long enough */
1415 /* search "Start=" */
1416 s
= ParseSamiSearch( txt
, p_props
->sami
.psz_start
, "Start=" );
1417 p_props
->sami
.psz_start
= NULL
;
1419 return VLC_EGENERIC
;
1421 /* get start value */
1423 i_start
= strtol( s
, &psz_end
, 0 );
1427 if( !( s
= ParseSamiSearch( txt
, s
, "<P" ) ) )
1428 return VLC_EGENERIC
;
1431 if( !( s
= ParseSamiSearch( txt
, s
, ">" ) ) )
1432 return VLC_EGENERIC
;
1436 /* now get all txt until a "Start=" line */
1440 /* Search non empty line */
1441 while( s
&& *s
== '\0' )
1442 s
= TextGetLine( txt
);
1448 if( !strncasecmp( s
, "<br", 3 ) )
1452 else if( strcasestr( s
, "Start=" ) )
1454 p_props
->sami
.psz_start
= s
;
1457 s
= ParseSamiSearch( txt
, s
, ">" );
1459 else if( !strncmp( s
, " ", 6 ) )
1464 else if( *s
== '\t' )
1474 if( c
!= '\0' && i_text
+1 < sizeof(text
) )
1477 text
[i_text
] = '\0';
1481 p_subtitle
->i_start
= i_start
* 1000;
1482 p_subtitle
->i_stop
= -1;
1483 p_subtitle
->psz_text
= strdup( text
);
1495 * TODO it can have a header
1502 * LANG support would be cool
1503 * CODEPAGE is probably mandatory FIXME
1505 static int ParseDVDSubtitle(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1506 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1509 VLC_UNUSED(p_props
);
1510 VLC_UNUSED( i_idx
);
1515 const char *s
= TextGetLine( txt
);
1519 return VLC_EGENERIC
;
1523 &h1
, &m1
, &s1
, &c1
) == 4 )
1525 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
1526 (int64_t)m1
* 60*1000 +
1527 (int64_t)s1
* 1000 +
1528 (int64_t)c1
* 10) * 1000;
1529 p_subtitle
->i_stop
= -1;
1534 /* Now read text until a line containing "}" */
1535 psz_text
= strdup("");
1540 const char *s
= TextGetLine( txt
);
1547 return VLC_EGENERIC
;
1550 i_len
= strlen( s
);
1551 if( i_len
== 1 && s
[0] == '}')
1553 p_subtitle
->psz_text
= psz_text
;
1557 i_old
= strlen( psz_text
);
1558 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1561 strcat( psz_text
, s
);
1562 strcat( psz_text
, "\n" );
1568 * [n1][n2]Line1|Line2|Line3...
1569 * where n1 and n2 are the video frame number (n2 can be empty)
1571 static int ParseMPL2(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1572 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1575 VLC_UNUSED(p_props
);
1576 VLC_UNUSED( i_idx
);
1582 const char *s
= TextGetLine( txt
);
1587 return VLC_EGENERIC
;
1589 psz_text
= malloc( strlen(s
) + 1 );
1595 if( sscanf( s
, "[%d][] %[^\r\n]", &i_start
, psz_text
) == 2 ||
1596 sscanf( s
, "[%d][%d] %[^\r\n]", &i_start
, &i_stop
, psz_text
) == 3)
1598 p_subtitle
->i_start
= (int64_t)i_start
* 100000;
1599 p_subtitle
->i_stop
= i_stop
>= 0 ? ((int64_t)i_stop
* 100000) : -1;
1605 for( i
= 0; psz_text
[i
] != '\0'; )
1607 /* replace | by \n */
1608 if( psz_text
[i
] == '|' )
1612 if( psz_text
[i
] == '/' && ( i
== 0 || psz_text
[i
-1] == '\n' ) )
1613 memmove( &psz_text
[i
], &psz_text
[i
+1], strlen(&psz_text
[i
+1])+1 );
1617 p_subtitle
->psz_text
= psz_text
;
1621 static int ParseAQT(vlc_object_t
*p_obj
, subs_properties_t
*p_props
, text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1624 VLC_UNUSED(p_props
);
1625 VLC_UNUSED( i_idx
);
1627 char *psz_text
= strdup( "" );
1629 int i_firstline
= 1;
1635 const char *s
= TextGetLine( txt
);
1640 return VLC_EGENERIC
;
1644 if( sscanf (s
, "-->> %d", &t
) == 1)
1646 p_subtitle
->i_start
= (int64_t)t
; /* * FPS*/
1647 p_subtitle
->i_stop
= -1;
1649 /* Starting of a subtitle */
1654 /* We have been too far: end of the subtitle, begin of next */
1657 TextPreviousLine( txt
);
1664 i_old
= strlen( psz_text
) + 1;
1665 psz_text
= realloc_or_free( psz_text
, i_old
+ strlen( s
) + 1 );
1668 strcat( psz_text
, s
);
1669 strcat( psz_text
, "\n" );
1670 if( txt
->i_line
== txt
->i_line_count
)
1674 p_subtitle
->psz_text
= psz_text
;
1678 static int ParsePJS(vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1679 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1682 VLC_UNUSED(p_props
);
1683 VLC_UNUSED( i_idx
);
1690 const char *s
= TextGetLine( txt
);
1694 return VLC_EGENERIC
;
1696 psz_text
= malloc( strlen(s
) + 1 );
1701 if( sscanf (s
, "%d,%d,\"%[^\n\r]", &t1
, &t2
, psz_text
) == 3 )
1703 /* 1/10th of second ? Frame based ? FIXME */
1704 p_subtitle
->i_start
= 10 * t1
;
1705 p_subtitle
->i_stop
= 10 * t2
;
1706 /* Remove latest " */
1707 psz_text
[ strlen(psz_text
) - 1 ] = '\0';
1714 /* replace | by \n */
1715 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
1717 if( psz_text
[i
] == '|' )
1721 p_subtitle
->psz_text
= psz_text
;
1722 msg_Dbg( p_obj
, "%s", psz_text
);
1726 static int ParseMPSub( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1727 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1729 VLC_UNUSED( i_idx
);
1731 char *psz_text
= strdup( "" );
1733 if( !p_props
->mpsub
.b_inited
)
1735 p_props
->mpsub
.f_total
= 0.0;
1736 p_props
->mpsub
.f_factor
= 0.0;
1738 p_props
->mpsub
.b_inited
= true;
1746 const char *s
= TextGetLine( txt
);
1750 return VLC_EGENERIC
;
1753 if( strstr( s
, "FORMAT" ) )
1755 if( sscanf (s
, "FORMAT=TIM%c", &p_dummy
) == 1 && p_dummy
== 'E')
1757 p_props
->mpsub
.f_factor
= 100.0;
1761 psz_temp
= malloc( strlen(s
) );
1768 if( sscanf( s
, "FORMAT=%[^\r\n]", psz_temp
) )
1770 float f_fps
= us_strtof( psz_temp
, NULL
);
1772 if( f_fps
> 0.f
&& var_GetFloat( p_obj
, "sub-fps" ) <= 0.f
)
1773 var_SetFloat( p_obj
, "sub-fps", f_fps
);
1775 p_props
->mpsub
.f_factor
= 1.f
;
1783 float f1
= us_strtof( s
, &psz_temp
);
1786 float f2
= us_strtof( psz_temp
, NULL
);
1787 p_props
->mpsub
.f_total
+= f1
* p_props
->mpsub
.f_factor
;
1788 p_subtitle
->i_start
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1789 p_props
->mpsub
.f_total
+= f2
* p_props
->mpsub
.f_factor
;
1790 p_subtitle
->i_stop
= llroundf(10000.f
* p_props
->mpsub
.f_total
);
1797 const char *s
= TextGetLine( txt
);
1802 return VLC_EGENERIC
;
1805 size_t i_len
= strlen( s
);
1809 size_t i_old
= strlen( psz_text
);
1811 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
1815 strcat( psz_text
, s
);
1816 strcat( psz_text
, "\n" );
1819 p_subtitle
->psz_text
= psz_text
;
1823 static int ParseJSS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
1824 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
1826 VLC_UNUSED( i_idx
);
1827 char *psz_text
, *psz_orig
;
1828 char *psz_text2
, *psz_orig2
;
1830 if( !p_props
->jss
.b_inited
)
1832 p_props
->jss
.i_comment
= 0;
1833 p_props
->jss
.i_time_resolution
= 30;
1834 p_props
->jss
.i_time_shift
= 0;
1836 p_props
->jss
.b_inited
= true;
1839 /* Parse the main lines */
1842 const char *s
= TextGetLine( txt
);
1844 return VLC_EGENERIC
;
1846 size_t line_length
= strlen( s
);
1847 psz_orig
= malloc( line_length
+ 1 );
1850 psz_text
= psz_orig
;
1852 /* Complete time lines */
1853 int h1
, h2
, m1
, m2
, s1
, s2
, f1
, f2
;
1854 if( sscanf( s
, "%d:%d:%d.%d %d:%d:%d.%d %[^\n\r]",
1855 &h1
, &m1
, &s1
, &f1
, &h2
, &m2
, &s2
, &f2
, psz_text
) == 9 )
1857 p_subtitle
->i_start
= ( ( (int64_t) h1
*3600 + m1
* 60 + s1
) +
1858 (int64_t)( ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1860 p_subtitle
->i_stop
= ( ( (int64_t) h2
*3600 + m2
* 60 + s2
) +
1861 (int64_t)( ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
) )
1865 /* Short time lines */
1866 else if( sscanf( s
, "@%d @%d %[^\n\r]", &f1
, &f2
, psz_text
) == 3 )
1868 p_subtitle
->i_start
= ((int64_t)
1869 ( f1
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1870 p_subtitle
->i_stop
= ((int64_t)
1871 ( f2
+ p_props
->jss
.i_time_shift
) / p_props
->jss
.i_time_resolution
* 1000000.0 );
1874 /* General Directive lines */
1875 /* Only TIME and SHIFT are supported so far */
1876 else if( s
[0] == '#' )
1878 int h
= 0, m
=0, sec
= 1, f
= 1;
1882 strcpy( psz_text
, s
);
1884 switch( toupper( (unsigned char)psz_text
[1] ) )
1887 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 6 : 2 ;
1888 if ( shift
> line_length
)
1891 if( sscanf( &psz_text
[shift
], "%d", &h
) )
1893 /* Negative shifting */
1900 if( sscanf( &psz_text
[shift
], "%*d:%d", &m
) )
1902 if( sscanf( &psz_text
[shift
], "%*d:%*d:%d", &sec
) )
1904 sscanf( &psz_text
[shift
], "%*d:%*d:%*d.%d", &f
);
1909 sscanf( &psz_text
[shift
], "%d:%d.%d",
1917 sscanf( &psz_text
[shift
], "%d.%d", &sec
, &f
);
1920 p_props
->jss
.i_time_shift
= ( ( h
* 3600 + m
* 60 + sec
)
1921 * p_props
->jss
.i_time_resolution
+ f
) * inv
;
1926 shift
= isalpha( (unsigned char)psz_text
[2] ) ? 8 : 2 ;
1927 if ( shift
> line_length
)
1930 sscanf( &psz_text
[shift
], "%d", &p_props
->jss
.i_time_resolution
);
1937 /* Unkown type line, probably a comment */
1944 while( psz_text
[ strlen( psz_text
) - 1 ] == '\\' )
1946 const char *s2
= TextGetLine( txt
);
1951 return VLC_EGENERIC
;
1954 size_t i_len
= strlen( s2
);
1958 size_t i_old
= strlen( psz_text
);
1960 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 );
1964 psz_orig
= psz_text
;
1965 strcat( psz_text
, s2
);
1968 /* Skip the blanks */
1969 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1971 /* Parse the directives */
1972 if( isalpha( (unsigned char)*psz_text
) || *psz_text
== '[' )
1974 while( *psz_text
&& *psz_text
!= ' ' )
1977 /* Directives are NOT parsed yet */
1978 /* This has probably a better place in a decoder ? */
1979 /* directive = malloc( strlen( psz_text ) + 1 );
1980 if( sscanf( psz_text, "%s %[^\n\r]", directive, psz_text2 ) == 2 )*/
1983 /* Skip the blanks after directives */
1984 while( *psz_text
== ' ' || *psz_text
== '\t' ) psz_text
++;
1986 /* Clean all the lines from inline comments and other stuffs */
1987 psz_orig2
= calloc( strlen( psz_text
) + 1, 1 );
1988 psz_text2
= psz_orig2
;
1990 for( ; *psz_text
!= '\0' && *psz_text
!= '\n' && *psz_text
!= '\r'; )
1995 p_props
->jss
.i_comment
++;
1998 if( p_props
->jss
.i_comment
)
2000 p_props
->jss
.i_comment
= 0;
2001 if( (*(psz_text
+ 1 ) ) == ' ' ) psz_text
++;
2005 if( !p_props
->jss
.i_comment
)
2013 if( (*(psz_text
+ 1 ) ) == ' ' || (*(psz_text
+ 1 ) ) == '\t' )
2015 if( !p_props
->jss
.i_comment
)
2022 if( (*(psz_text
+ 1 ) ) == 'n' )
2029 if( ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'C' ) ||
2030 ( toupper((unsigned char)*(psz_text
+ 1 ) ) == 'F' ) )
2035 if( (*(psz_text
+ 1 ) ) == 'B' || (*(psz_text
+ 1 ) ) == 'b' ||
2036 (*(psz_text
+ 1 ) ) == 'I' || (*(psz_text
+ 1 ) ) == 'i' ||
2037 (*(psz_text
+ 1 ) ) == 'U' || (*(psz_text
+ 1 ) ) == 'u' ||
2038 (*(psz_text
+ 1 ) ) == 'D' || (*(psz_text
+ 1 ) ) == 'N' )
2043 if( (*(psz_text
+ 1 ) ) == '~' || (*(psz_text
+ 1 ) ) == '{' ||
2044 (*(psz_text
+ 1 ) ) == '\\' )
2046 else if( ( *(psz_text
+ 1 ) == '\r' || *(psz_text
+ 1 ) == '\n' ) &&
2047 *(psz_text
+ 1 ) != '\0' )
2053 if( !p_props
->jss
.i_comment
)
2055 *psz_text2
= *psz_text
;
2062 p_subtitle
->psz_text
= psz_orig2
;
2063 msg_Dbg( p_obj
, "%s", p_subtitle
->psz_text
);
2068 static int ParsePSB( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2069 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2072 VLC_UNUSED(p_props
);
2073 VLC_UNUSED( i_idx
);
2082 const char *s
= TextGetLine( txt
);
2085 return VLC_EGENERIC
;
2087 psz_text
= malloc( strlen( s
) + 1 );
2091 if( sscanf( s
, "{%d:%d:%d}{%d:%d:%d}%[^\r\n]",
2092 &h1
, &m1
, &s1
, &h2
, &m2
, &s2
, psz_text
) == 7 )
2094 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2095 (int64_t)m1
* 60*1000 +
2096 (int64_t)s1
* 1000 ) * 1000;
2097 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2098 (int64_t)m2
* 60*1000 +
2099 (int64_t)s2
* 1000 ) * 1000;
2105 /* replace | by \n */
2106 for( i
= 0; psz_text
[i
] != '\0'; i
++ )
2108 if( psz_text
[i
] == '|' )
2111 p_subtitle
->psz_text
= psz_text
;
2115 static int64_t ParseRealTime( char *psz
, int *h
, int *m
, int *s
, int *f
)
2117 if( *psz
== '\0' ) return 0;
2118 if( sscanf( psz
, "%d:%d:%d.%d", h
, m
, s
, f
) == 4 ||
2119 sscanf( psz
, "%d:%d.%d", m
, s
, f
) == 3 ||
2120 sscanf( psz
, "%d.%d", s
, f
) == 2 ||
2121 sscanf( psz
, "%d:%d", m
, s
) == 2 ||
2122 sscanf( psz
, "%d", s
) == 1 )
2124 return (int64_t)((( *h
* 60 + *m
) * 60 ) + *s
) * 1000 * 1000
2125 + (int64_t)*f
* 10 * 1000;
2127 else return VLC_EGENERIC
;
2130 static int ParseRealText( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2131 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2134 VLC_UNUSED(p_props
);
2135 VLC_UNUSED( i_idx
);
2136 char *psz_text
= NULL
;
2140 int h1
= 0, m1
= 0, s1
= 0, f1
= 0;
2141 int h2
= 0, m2
= 0, s2
= 0, f2
= 0;
2142 const char *s
= TextGetLine( txt
);
2146 return VLC_EGENERIC
;
2148 psz_text
= malloc( strlen( s
) + 1 );
2152 /* Find the good begining. This removes extra spaces at the beginning
2154 char *psz_temp
= strcasestr( s
, "<time");
2155 if( psz_temp
!= NULL
)
2157 char psz_end
[12], psz_begin
[12];
2158 /* Line has begin and end */
2159 if( ( sscanf( psz_temp
,
2160 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\" %*[e|E]nd=\"%11[^\"]%*[^>]%[^\n\r]",
2161 psz_begin
, psz_end
, psz_text
) != 3 ) &&
2162 /* Line has begin and no end */
2164 "<%*[t|T]ime %*[b|B]egin=\"%11[^\"]\"%*[^>]%[^\n\r]",
2165 psz_begin
, psz_text
) != 2) )
2166 /* Line is not recognized */
2172 int64_t i_time
= ParseRealTime( psz_begin
, &h1
, &m1
, &s1
, &f1
);
2173 p_subtitle
->i_start
= i_time
>= 0 ? i_time
: 0;
2175 i_time
= ParseRealTime( psz_end
, &h2
, &m2
, &s2
, &f2
);
2176 p_subtitle
->i_stop
= i_time
>= 0 ? i_time
: -1;
2181 /* Get the following Lines */
2184 const char *s
= TextGetLine( txt
);
2189 return VLC_EGENERIC
;
2192 size_t i_len
= strlen( s
);
2193 if( i_len
== 0 ) break;
2195 if( strcasestr( s
, "<time" ) ||
2196 strcasestr( s
, "<clear/") )
2198 TextPreviousLine( txt
);
2202 size_t i_old
= strlen( psz_text
);
2204 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2208 strcat( psz_text
, s
);
2209 strcat( psz_text
, "\n" );
2212 /* Remove the starting ">" that remained after the sscanf */
2213 memmove( &psz_text
[0], &psz_text
[1], strlen( psz_text
) );
2215 p_subtitle
->psz_text
= psz_text
;
2220 static int ParseDKS( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2221 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2224 VLC_UNUSED(p_props
);
2225 VLC_UNUSED( i_idx
);
2233 char *s
= TextGetLine( txt
);
2236 return VLC_EGENERIC
;
2238 psz_text
= malloc( strlen( s
) + 1 );
2242 if( sscanf( s
, "[%d:%d:%d]%[^\r\n]",
2243 &h1
, &m1
, &s1
, psz_text
) == 4 )
2245 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2246 (int64_t)m1
* 60*1000 +
2247 (int64_t)s1
* 1000 ) * 1000;
2249 s
= TextGetLine( txt
);
2253 return VLC_EGENERIC
;
2256 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2257 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2258 (int64_t)m2
* 60*1000 +
2259 (int64_t)s2
* 1000 ) * 1000;
2261 p_subtitle
->i_stop
= -1;
2267 /* replace [br] by \n */
2269 while( ( p
= strstr( psz_text
, "[br]" ) ) )
2272 memmove( p
, &p
[3], strlen(&p
[3])+1 );
2275 p_subtitle
->psz_text
= psz_text
;
2279 static int ParseSubViewer1( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2280 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2283 VLC_UNUSED(p_props
);
2284 VLC_UNUSED( i_idx
);
2291 char *s
= TextGetLine( txt
);
2294 return VLC_EGENERIC
;
2296 if( sscanf( s
, "[%d:%d:%d]", &h1
, &m1
, &s1
) == 3 )
2298 p_subtitle
->i_start
= ( (int64_t)h1
* 3600*1000 +
2299 (int64_t)m1
* 60*1000 +
2300 (int64_t)s1
* 1000 ) * 1000;
2302 s
= TextGetLine( txt
);
2304 return VLC_EGENERIC
;
2306 psz_text
= strdup( s
);
2310 s
= TextGetLine( txt
);
2314 return VLC_EGENERIC
;
2317 if( sscanf( s
, "[%d:%d:%d]", &h2
, &m2
, &s2
) == 3 )
2318 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600*1000 +
2319 (int64_t)m2
* 60*1000 +
2320 (int64_t)s2
* 1000 ) * 1000;
2322 p_subtitle
->i_stop
= -1;
2328 p_subtitle
->psz_text
= psz_text
;
2333 /* Common code for VTT/SBV since they just differ in timestamps */
2334 static int ParseCommonVTTSBV( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2335 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2338 VLC_UNUSED( i_idx
);
2343 const char *s
= TextGetLine( txt
);
2344 int h1
= 0, m1
= 0, s1
= 0, d1
= 0;
2345 int h2
= 0, m2
= 0, s2
= 0, d2
= 0;
2348 return VLC_EGENERIC
;
2350 bool b_matched
= false;
2352 if( p_props
->i_type
== SUB_TYPE_VTT
)
2355 ( sscanf( s
,"%d:%d.%d --> %d:%d.%d",
2357 &m2
, &s2
, &d2
) == 6 ||
2358 sscanf( s
,"%d:%d.%d --> %d:%d:%d.%d",
2360 &h2
, &m2
, &s2
, &d2
) == 7 ||
2361 sscanf( s
,"%d:%d:%d.%d --> %d:%d.%d",
2363 &m2
, &s2
, &d2
) == 7 ||
2364 sscanf( s
,"%d:%d:%d.%d --> %d:%d:%d.%d",
2366 &h2
, &m2
, &s2
, &d2
) == 8 );
2368 else if( p_props
->i_type
== SUB_TYPE_SBV
)
2371 ( sscanf( s
,"%d:%d:%d.%d,%d:%d:%d.%d",
2373 &h2
, &m2
, &s2
, &d2
) == 8 );
2378 p_subtitle
->i_start
= ( (int64_t)h1
* 3600 * 1000 +
2379 (int64_t)m1
* 60 * 1000 +
2380 (int64_t)s1
* 1000 +
2381 (int64_t)d1
) * 1000;
2383 p_subtitle
->i_stop
= ( (int64_t)h2
* 3600 * 1000 +
2384 (int64_t)m2
* 60 * 1000 +
2385 (int64_t)s2
* 1000 +
2386 (int64_t)d2
) * 1000;
2387 if( p_subtitle
->i_start
< p_subtitle
->i_stop
)
2392 /* Now read text until an empty line */
2393 psz_text
= strdup("");
2399 const char *s
= TextGetLine( txt
);
2403 i_len
= s
? strlen( s
) : 0;
2406 p_subtitle
->psz_text
= psz_text
;
2410 i_old
= strlen( psz_text
);
2411 psz_text
= realloc_or_free( psz_text
, i_old
+ i_len
+ 1 + 1 );
2415 strcat( psz_text
, s
);
2416 strcat( psz_text
, "\n" );
2420 static int ParseSCC( vlc_object_t
*p_obj
, subs_properties_t
*p_props
,
2421 text_t
*txt
, subtitle_t
*p_subtitle
, size_t i_idx
)
2424 VLC_UNUSED( i_idx
);
2425 VLC_UNUSED( p_props
);
2429 const char *psz_line
= TextGetLine( txt
);
2431 return VLC_EGENERIC
;
2433 unsigned h
, m
, s
, f
;
2434 if( sscanf( psz_line
, "%u:%u:%u:%u ", &h
, &m
, &s
, &f
) != 4 )
2437 p_subtitle
->i_start
= CLOCK_FREQ
* ( h
* 3600 + m
* 60 + s
) +
2438 f
* p_props
->i_microsecperframe
;
2439 p_subtitle
->i_stop
= -1;
2441 const char *psz_text
= strchr( psz_line
, '\t' );
2442 if( !psz_text
&& !(psz_text
= strchr( psz_line
, ' ' )) )
2445 if ( psz_text
[1] == '\0' )
2448 p_subtitle
->psz_text
= strdup( psz_text
+ 1 );
2449 if( !p_subtitle
->psz_text
)
2458 /* Matches filename.xx.srt */
2459 static char * get_language_from_filename( const char * psz_sub_file
)
2461 char *psz_ret
= NULL
;
2462 char *psz_tmp
, *psz_language_begin
;
2464 if( !psz_sub_file
) return NULL
;
2465 char *psz_work
= strdup( psz_sub_file
);
2467 /* Removing extension, but leaving the dot */
2468 psz_tmp
= strrchr( psz_work
, '.' );
2472 psz_language_begin
= strrchr( psz_work
, '.' );
2473 if( psz_language_begin
)
2474 psz_ret
= strdup(++psz_language_begin
);