1 /*****************************************************************************
2 * vobsub.c: Demux vobsub files.
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Derk-Jan Hartman <hartman at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_demux.h>
42 #include "subtitle_helper.h"
44 /*****************************************************************************
46 *****************************************************************************/
47 static int Open ( vlc_object_t
*p_this
);
48 static void Close( vlc_object_t
*p_this
);
51 set_description( N_("Vobsub subtitles parser") )
52 set_category( CAT_INPUT
)
53 set_subcategory( SUBCAT_INPUT_DEMUX
)
54 set_capability( "demux", 1 )
56 set_callbacks( Open
, Close
)
58 add_shortcut( "vobsub", "subtitle" )
61 /*****************************************************************************
63 *****************************************************************************/
75 int i_vobsub_location
;
83 int i_current_subtitle
;
85 subtitle_t
*p_subtitles
;
92 vlc_tick_t i_next_demux_date
;
96 stream_t
*p_vobsub_stream
;
100 vobsub_track_t
*track
;
102 int i_original_frame_width
;
103 int i_original_frame_height
;
105 uint32_t palette
[16];
109 static int Demux( demux_t
* );
110 static int Control( demux_t
*, int, va_list );
112 static int TextLoad( text_t
*, stream_t
*s
);
113 static void TextUnload( text_t
* );
114 static int ParseVobSubIDX( demux_t
* );
115 static int DemuxVobSub( demux_t
*, block_t
*);
117 /*****************************************************************************
119 *****************************************************************************/
120 static int Open ( vlc_object_t
*p_this
)
122 demux_t
*p_demux
= (demux_t
*)p_this
;
124 char *psz_vobname
, *s
;
126 uint64_t i_read_offset
= 0;
128 if( ( s
= peek_Readline( p_demux
->s
, &i_read_offset
) ) != NULL
)
130 if( !strcasestr( s
, "# VobSub index file" ) )
132 msg_Dbg( p_demux
, "this doesn't seem to be a vobsub file" );
140 msg_Dbg( p_demux
, "could not read vobsub IDX file" );
145 p_demux
->p_sys
= p_sys
= malloc( sizeof( demux_sys_t
) );
146 if( unlikely( !p_sys
) )
150 p_sys
->p_vobsub_stream
= NULL
;
152 p_sys
->track
= malloc( sizeof( vobsub_track_t
) );
153 if( unlikely( !p_sys
->track
) )
155 p_sys
->i_original_frame_width
= -1;
156 p_sys
->i_original_frame_height
= -1;
157 p_sys
->b_palette
= false;
158 memset( p_sys
->palette
, 0, 16 * sizeof( uint32_t ) );
160 /* Load the whole file */
161 TextLoad( &p_sys
->txt
, p_demux
->s
);
164 ParseVobSubIDX( p_demux
);
167 TextUnload( &p_sys
->txt
);
169 /* Find the total length of the vobsubs */
170 if( p_sys
->i_tracks
> 0 )
172 for( int i
= 0; i
< p_sys
->i_tracks
; i
++ )
174 if( p_sys
->track
[i
].i_subtitles
> 1 )
176 if( p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_subtitles
-1].i_start
> p_sys
->i_length
)
177 p_sys
->i_length
= (int64_t) p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_subtitles
-1].i_start
+ ( 1 *1000 *1000 );
182 psz_vobname
= strdup( p_demux
->psz_url
);
183 if( psz_vobname
== NULL
)
186 i_len
= strlen( psz_vobname
);
187 if( i_len
>= 4 ) memcpy( psz_vobname
+ i_len
- 4, ".sub", 4 );
190 p_sys
->p_vobsub_stream
= vlc_stream_NewURL( p_demux
, psz_vobname
);
191 if( p_sys
->p_vobsub_stream
== NULL
)
193 msg_Err( p_demux
, "couldn't open .sub Vobsub file: %s",
200 p_demux
->pf_demux
= Demux
;
201 p_demux
->pf_control
= Control
;
206 /* Clean all subs from all tracks */
207 for( int i
= 0; i
< p_sys
->i_tracks
; i
++ )
208 free( p_sys
->track
[i
].p_subtitles
);
209 free( p_sys
->track
);
215 /*****************************************************************************
216 * Close: Close subtitle demux
217 *****************************************************************************/
218 static void Close( vlc_object_t
*p_this
)
220 demux_t
*p_demux
= (demux_t
*)p_this
;
221 demux_sys_t
*p_sys
= p_demux
->p_sys
;
223 if( p_sys
->p_vobsub_stream
)
224 vlc_stream_Delete( p_sys
->p_vobsub_stream
);
226 /* Clean all subs from all tracks */
227 for( int i
= 0; i
< p_sys
->i_tracks
; i
++ )
228 free( p_sys
->track
[i
].p_subtitles
);
229 free( p_sys
->track
);
233 /*****************************************************************************
235 *****************************************************************************/
236 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
238 demux_sys_t
*p_sys
= p_demux
->p_sys
;
246 *va_arg( args
, bool * ) = true;
249 case DEMUX_GET_LENGTH
:
250 pi64
= va_arg( args
, int64_t * );
251 *pi64
= (int64_t) p_sys
->i_length
;
255 pi64
= va_arg( args
, int64_t * );
256 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
259 /* Check the ES is selected */
260 es_out_Control( p_demux
->out
, ES_OUT_GET_ES_STATE
,
261 p_sys
->track
[i
].p_es
, &b_selected
);
262 if( b_selected
) break;
264 if( i
< p_sys
->i_tracks
&& p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
)
266 *pi64
= p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
;
272 i64
= va_arg( args
, int64_t );
273 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
275 p_sys
->track
[i
].i_current_subtitle
= 0;
276 while( p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
&&
277 p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
< i64
)
279 p_sys
->track
[i
].i_current_subtitle
++;
282 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
287 case DEMUX_GET_POSITION
:
288 pf
= va_arg( args
, double * );
289 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
292 /* Check the ES is selected */
293 es_out_Control( p_demux
->out
, ES_OUT_GET_ES_STATE
,
294 p_sys
->track
[i
].p_es
, &b_selected
);
295 if( b_selected
) break;
297 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
301 else if( p_sys
->track
[i
].i_subtitles
> 0 )
303 *pf
= (double)p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
/
304 (double)p_sys
->i_length
;
312 case DEMUX_SET_POSITION
:
313 f
= va_arg( args
, double );
314 i64
= (int64_t) f
* p_sys
->i_length
;
316 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
318 p_sys
->track
[i
].i_current_subtitle
= 0;
319 while( p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
&&
320 p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
< i64
)
322 p_sys
->track
[i
].i_current_subtitle
++;
324 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
329 case DEMUX_SET_NEXT_DEMUX_TIME
:
330 p_sys
->i_next_demux_date
= va_arg( args
, vlc_tick_t
);
333 case DEMUX_CAN_PAUSE
:
334 case DEMUX_SET_PAUSE_STATE
:
335 case DEMUX_CAN_CONTROL_PACE
:
336 return demux_vaControlHelper( p_demux
->s
, 0, -1, 0, 1, i_query
, args
);
338 case DEMUX_GET_PTS_DELAY
:
341 case DEMUX_GET_TITLE_INFO
:
342 case DEMUX_HAS_UNSUPPORTED_META
:
343 case DEMUX_GET_ATTACHMENTS
:
344 case DEMUX_CAN_RECORD
:
348 msg_Warn( p_demux
, "unknown query in subtitle control" );
353 /*****************************************************************************
354 * Demux: Send subtitle to decoder
355 *****************************************************************************/
356 static int Demux( demux_t
*p_demux
)
358 demux_sys_t
*p_sys
= p_demux
->p_sys
;
359 vlc_tick_t i_maxdate
;
362 for( int i
= 0; i
< p_sys
->i_tracks
; i
++ )
364 #define tk p_sys->track[i]
365 if( tk
.i_current_subtitle
>= tk
.i_subtitles
)
368 i_maxdate
= p_sys
->i_next_demux_date
;
369 if( i_maxdate
<= 0 && tk
.i_current_subtitle
< tk
.i_subtitles
)
371 /* Should not happen */
372 i_maxdate
= tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
+ 1;
375 while( tk
.i_current_subtitle
< tk
.i_subtitles
&&
376 tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
< i_maxdate
)
378 int i_pos
= tk
.p_subtitles
[tk
.i_current_subtitle
].i_vobsub_location
;
382 /* first compute SPU size */
383 if( tk
.i_current_subtitle
+ 1 < tk
.i_subtitles
)
385 i_size
= tk
.p_subtitles
[tk
.i_current_subtitle
+1].i_vobsub_location
- i_pos
;
387 if( i_size
<= 0 ) i_size
= 65535; /* Invalid or EOF */
389 /* Seek at the right place */
390 if( vlc_stream_Seek( p_sys
->p_vobsub_stream
, i_pos
) )
393 "cannot seek in the VobSub to the correct time %d", i_pos
);
394 tk
.i_current_subtitle
++;
398 /* allocate a packet */
399 if( ( p_block
= block_Alloc( i_size
) ) == NULL
)
401 tk
.i_current_subtitle
++;
406 i_read
= vlc_stream_Read( p_sys
->p_vobsub_stream
, p_block
->p_buffer
, i_size
);
409 block_Release( p_block
);
410 tk
.i_current_subtitle
++;
413 p_block
->i_buffer
= i_read
;
416 p_block
->i_pts
= VLC_TICK_0
+ tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
;
418 /* demux this block */
419 DemuxVobSub( p_demux
, p_block
);
421 block_Release( p_block
);
423 tk
.i_current_subtitle
++;
429 p_sys
->i_next_demux_date
= 0;
434 static int TextLoad( text_t
*txt
, stream_t
*s
)
439 /* load the complete file */
442 char *psz
= vlc_stream_ReadLine( s
);
445 if( psz
== NULL
|| (n
>= INT_MAX
/sizeof(char *)) )
451 ppsz_new
= realloc( lines
, (n
+ 1) * sizeof (char *) );
452 if( ppsz_new
== NULL
)
461 txt
->i_line_count
= n
;
468 static void TextUnload( text_t
*txt
)
470 for( int i
= 0; i
< txt
->i_line_count
; i
++ )
471 free( txt
->line
[i
] );
475 txt
->i_line_count
= 0;
478 static char *TextGetLine( text_t
*txt
)
480 if( txt
->i_line
>= txt
->i_line_count
)
483 return txt
->line
[txt
->i_line
++];
486 static int ParseVobSubIDX( demux_t
*p_demux
)
488 demux_sys_t
*p_sys
= p_demux
->p_sys
;
489 text_t
*txt
= &p_sys
->txt
;
494 if( ( line
= TextGetLine( txt
) ) == NULL
)
496 return( VLC_EGENERIC
);
499 if( *line
== 0 || *line
== '\r' || *line
== '\n' || *line
== '#' )
503 else if( !strncmp( "size:", line
, 5 ) )
505 /* Store the original size of the video */
506 if( vobsub_size_parse( line
, &p_sys
->i_original_frame_width
,
507 &p_sys
->i_original_frame_height
) == VLC_SUCCESS
)
509 msg_Dbg( p_demux
, "original frame size: %dx%d", p_sys
->i_original_frame_width
, p_sys
->i_original_frame_height
);
513 msg_Warn( p_demux
, "reading original frame size failed" );
516 else if( !strncmp( "palette:", line
, 8 ) )
518 if( vobsub_palette_parse( line
, p_sys
->palette
) == VLC_SUCCESS
)
520 p_sys
->b_palette
= true;
521 msg_Dbg( p_demux
, "vobsub palette read" );
525 msg_Warn( p_demux
, "reading original palette failed" );
528 else if( !strncmp( "id:", line
, 3 ) )
530 char language
[33]; /* Usually 2 or 3 letters, sometimes more.
531 Spec (or lack of) doesn't define any limit */
535 /* Lets start a new track */
536 if( sscanf( line
, "id: %32[^ ,], index: %d",
537 language
, &i_track_id
) != 2 )
539 if( sscanf( line
, "id: , index: %d", &i_track_id
) != 1 )
541 msg_Warn( p_demux
, "reading new track failed" );
548 p_sys
->track
= xrealloc( p_sys
->track
,
549 sizeof( vobsub_track_t
) * (p_sys
->i_tracks
+ 1 ) );
552 vobsub_track_t
*current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
553 memset( current_tk
, 0, sizeof( vobsub_track_t
) );
554 current_tk
->i_current_subtitle
= 0;
555 current_tk
->i_subtitles
= 0;
556 current_tk
->p_subtitles
= xmalloc( sizeof( subtitle_t
) );
557 current_tk
->i_track_id
= i_track_id
;
558 current_tk
->i_delay
= (int64_t)0;
560 es_format_Init( &fmt
, SPU_ES
, VLC_CODEC_SPU
);
561 fmt
.subs
.spu
.i_original_frame_width
= p_sys
->i_original_frame_width
;
562 fmt
.subs
.spu
.i_original_frame_height
= p_sys
->i_original_frame_height
;
563 fmt
.psz_language
= language
;
564 if( p_sys
->b_palette
)
566 fmt
.subs
.spu
.palette
[0] = SPU_PALETTE_DEFINED
;
567 memcpy( &fmt
.subs
.spu
.palette
[1], p_sys
->palette
, 16 * sizeof( uint32_t ) );
570 current_tk
->p_es
= es_out_Add( p_demux
->out
, &fmt
);
571 msg_Dbg( p_demux
, "New vobsub track detected: %i [%s]", i_track_id
, language
);
573 else if( !strncmp( line
, "timestamp:", 10 ) )
576 * timestamp: [sign]hh:mm:ss:mss, filepos: loc
577 * loc is the hex location of the spu in the .sub file
579 int h
, m
, s
, ms
, count
, loc
= 0;
581 int64_t i_start
, i_location
= 0;
583 if( p_sys
->i_tracks
> 0 &&
584 sscanf( line
, "timestamp: %d%n:%d:%d:%d, filepos: %x",
585 &h
, &count
, &m
, &s
, &ms
, &loc
) >= 5 )
587 vobsub_track_t
*current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
588 subtitle_t
*current_sub
;
590 if( line
[count
-3] == '-' )
595 i_start
= (int64_t) ( h
* 3600*1000 +
601 current_tk
->i_subtitles
++;
602 current_tk
->p_subtitles
=
603 xrealloc( current_tk
->p_subtitles
,
604 sizeof( subtitle_t
) * (current_tk
->i_subtitles
+ 1 ) );
605 current_sub
= ¤t_tk
->p_subtitles
[current_tk
->i_subtitles
- 1];
607 current_sub
->i_start
= i_start
* i_sign
;
608 current_sub
->i_start
+= current_tk
->i_delay
;
609 current_sub
->i_vobsub_location
= i_location
;
613 msg_Warn( p_demux
, "reading timestamp failed" );
616 else if( !strncasecmp( line
, "delay:", 6 ) )
619 * delay: [sign]hh:mm:ss:mss
621 int h
, m
, s
, ms
, count
= 0;
625 if( p_sys
->i_tracks
> 0 &&
626 sscanf( line
, "%*celay: %d%n:%d:%d:%d",
627 &h
, &count
, &m
, &s
, &ms
) >= 4 )
629 vobsub_track_t
*current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
630 if( line
[count
-3] == '-' )
635 i_gap
= (int64_t) ( h
* 3600*1000 +
640 current_tk
->i_delay
= current_tk
->i_delay
+ (i_gap
* i_sign
);
641 msg_Dbg( p_demux
, "sign: %+d gap: %+"PRId64
" global delay: %+"PRId64
"",
642 i_sign
, i_gap
, current_tk
->i_delay
);
646 msg_Warn( p_demux
, "reading delay failed" );
653 static int DemuxVobSub( demux_t
*p_demux
, block_t
*p_bk
)
655 demux_sys_t
*p_sys
= p_demux
->p_sys
;
656 uint8_t *p
= p_bk
->p_buffer
;
657 uint8_t *p_end
= &p_bk
->p_buffer
[p_bk
->i_buffer
];
660 while( p
+ 6 < p_end
)
662 int i_size
= ps_pkt_size( p
, p_end
- p
);
670 if( i_size
> p_end
- p
)
672 msg_Warn( p_demux
, "broken PES size" );
676 if( p
[0] != 0 || p
[1] != 0 || p
[2] != 0x01 )
678 msg_Warn( p_demux
, "invalid PES" );
684 /* msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] ); */
690 p_pkt
= block_Alloc( i_size
);
691 if( unlikely(p_pkt
== NULL
) )
693 memcpy( p_pkt
->p_buffer
, p
, i_size
);
696 i_id
= ps_pkt_id( p_pkt
);
697 if( (i_id
&0xffe0) != 0xbd20 ||
698 ps_pkt_parse_pes( VLC_OBJECT(p_demux
), p_pkt
, 1 ) )
700 block_Release( p_pkt
);
704 /* msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size ); */
706 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
708 vobsub_track_t
*p_tk
= &p_sys
->track
[i
];
710 p_pkt
->i_dts
= p_pkt
->i_pts
= p_bk
->i_pts
;
713 if( p_tk
->p_es
&& p_tk
->i_track_id
== i_spu
)
715 es_out_Send( p_demux
->out
, p_tk
->p_es
, p_pkt
);
716 p_bk
->i_pts
= VLC_TICK_INVALID
; /*only first packet has a pts */
720 if( i
>= p_sys
->i_tracks
)
722 block_Release( p_pkt
);