1 /*****************************************************************************
2 * subtitle.c: Demux vobsub files.
3 *****************************************************************************
4 * Copyright (C) 1999-2004 the VideoLAN team
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
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
36 #include <sys/types.h>
38 #include <vlc_demux.h>
39 #include <vlc_charset.h>
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t
*p_this
);
49 static void Close( vlc_object_t
*p_this
);
52 set_description( N_("Vobsub subtitles parser") );
53 set_category( CAT_INPUT
);
54 set_subcategory( SUBCAT_INPUT_DEMUX
);
55 set_capability( "demux", 1 );
57 set_callbacks( Open
, Close
);
59 add_shortcut( "vobsub" );
60 add_shortcut( "subtitle" );
63 /*****************************************************************************
65 *****************************************************************************/
73 static int TextLoad( text_t
*, stream_t
*s
);
74 static void TextUnload( text_t
* );
79 int i_vobsub_location
;
88 int i_current_subtitle
;
90 subtitle_t
*p_subtitles
;
97 int64_t i_next_demux_date
;
101 stream_t
*p_vobsub_stream
;
105 vobsub_track_t
*track
;
107 int i_original_frame_width
;
108 int i_original_frame_height
;
110 uint32_t palette
[16];
113 static int Demux( demux_t
* );
114 static int Control( demux_t
*, int, va_list );
116 static int ParseVobSubIDX( demux_t
* );
117 static int DemuxVobSub( demux_t
*, block_t
*);
119 /*****************************************************************************
121 *****************************************************************************/
122 static int Open ( vlc_object_t
*p_this
)
124 demux_t
*p_demux
= (demux_t
*)p_this
;
126 char *psz_vobname
, *s
;
129 if( ( s
= stream_ReadLine( p_demux
->s
) ) != NULL
)
131 if( !strcasestr( s
, "# VobSub index file" ) )
133 msg_Dbg( p_demux
, "this doesn't seem to be a vobsub file" );
135 if( stream_Seek( p_demux
->s
, 0 ) )
137 msg_Warn( p_demux
, "failed to rewind" );
146 msg_Dbg( p_demux
, "could not read vobsub IDX file" );
150 p_demux
->pf_demux
= Demux
;
151 p_demux
->pf_control
= Control
;
152 p_demux
->p_sys
= p_sys
= malloc( sizeof( demux_sys_t
) );
154 p_sys
->p_vobsub_stream
= NULL
;
156 p_sys
->track
= (vobsub_track_t
*)malloc( sizeof( vobsub_track_t
) );
157 p_sys
->i_original_frame_width
= -1;
158 p_sys
->i_original_frame_height
= -1;
159 p_sys
->b_palette
= false;
160 memset( p_sys
->palette
, 0, 16 * sizeof( uint32_t ) );
162 /* Load the whole file */
163 TextLoad( &p_sys
->txt
, p_demux
->s
);
166 ParseVobSubIDX( p_demux
);
169 TextUnload( &p_sys
->txt
);
171 /* Find the total length of the vobsubs */
172 if( p_sys
->i_tracks
> 0 )
175 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
177 if( p_sys
->track
[i
].i_subtitles
> 1 )
179 if( p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_subtitles
-1].i_start
> p_sys
->i_length
)
180 p_sys
->i_length
= (int64_t) p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_subtitles
-1].i_start
+ ( 1 *1000 *1000 );
185 if( asprintf( &psz_vobname
, "%s://%s", p_demux
->psz_access
, p_demux
->psz_path
) == -1 )
190 i_len
= strlen( psz_vobname
);
191 if( i_len
>= 4 ) memcpy( psz_vobname
+ i_len
- 4, ".sub", 4 );
194 p_sys
->p_vobsub_stream
= stream_UrlNew( p_demux
, psz_vobname
);
195 if( p_sys
->p_vobsub_stream
== NULL
)
197 msg_Err( p_demux
, "couldn't open .sub Vobsub file: %s",
208 /*****************************************************************************
209 * Close: Close subtitle demux
210 *****************************************************************************/
211 static void Close( vlc_object_t
*p_this
)
214 demux_t
*p_demux
= (demux_t
*)p_this
;
215 demux_sys_t
*p_sys
= p_demux
->p_sys
;
217 /* Clean all subs from all tracks */
218 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
219 free( p_sys
->track
[i
].p_subtitles
);
221 free( p_sys
->track
);
223 if( p_sys
->p_vobsub_stream
)
224 stream_Delete( p_sys
->p_vobsub_stream
);
229 /*****************************************************************************
231 *****************************************************************************/
232 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
234 demux_sys_t
*p_sys
= p_demux
->p_sys
;
241 case DEMUX_GET_LENGTH
:
242 pi64
= (int64_t*)va_arg( args
, int64_t * );
243 *pi64
= (int64_t) p_sys
->i_length
;
247 pi64
= (int64_t*)va_arg( args
, int64_t * );
248 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
251 /* Check the ES is selected */
252 es_out_Control( p_demux
->out
, ES_OUT_GET_ES_STATE
,
253 p_sys
->track
[i
].p_es
, &b_selected
);
254 if( b_selected
) break;
256 if( i
< p_sys
->i_tracks
&& p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
)
258 *pi64
= p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
;
264 i64
= (int64_t)va_arg( args
, int64_t );
265 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
267 p_sys
->track
[i
].i_current_subtitle
= 0;
268 while( p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
&&
269 p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
< i64
)
271 p_sys
->track
[i
].i_current_subtitle
++;
274 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
279 case DEMUX_GET_POSITION
:
280 pf
= (double*)va_arg( args
, double * );
281 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
284 /* Check the ES is selected */
285 es_out_Control( p_demux
->out
, ES_OUT_GET_ES_STATE
,
286 p_sys
->track
[i
].p_es
, &b_selected
);
287 if( b_selected
) break;
289 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
293 else if( p_sys
->track
[i
].i_subtitles
> 0 )
295 *pf
= (double)p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
/
296 (double)p_sys
->i_length
;
304 case DEMUX_SET_POSITION
:
305 f
= (double)va_arg( args
, double );
306 i64
= (int64_t) f
* p_sys
->i_length
;
308 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
310 p_sys
->track
[i
].i_current_subtitle
= 0;
311 while( p_sys
->track
[i
].i_current_subtitle
< p_sys
->track
[i
].i_subtitles
&&
312 p_sys
->track
[i
].p_subtitles
[p_sys
->track
[i
].i_current_subtitle
].i_start
< i64
)
314 p_sys
->track
[i
].i_current_subtitle
++;
316 if( p_sys
->track
[i
].i_current_subtitle
>= p_sys
->track
[i
].i_subtitles
)
321 case DEMUX_SET_NEXT_DEMUX_TIME
:
322 p_sys
->i_next_demux_date
= (int64_t)va_arg( args
, int64_t );
327 case DEMUX_GET_TITLE_INFO
:
328 case DEMUX_HAS_UNSUPPORTED_META
:
329 case DEMUX_GET_ATTACHMENTS
:
330 case DEMUX_CAN_RECORD
:
334 msg_Warn( p_demux
, "unknown query in subtitle control" );
339 /*****************************************************************************
340 * Demux: Send subtitle to decoder
341 *****************************************************************************/
342 static int Demux( demux_t
*p_demux
)
344 demux_sys_t
*p_sys
= p_demux
->p_sys
;
348 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
350 #define tk p_sys->track[i]
351 if( tk
.i_current_subtitle
>= tk
.i_subtitles
)
354 i_maxdate
= p_sys
->i_next_demux_date
;
355 if( i_maxdate
<= 0 && tk
.i_current_subtitle
< tk
.i_subtitles
)
357 /* Should not happen */
358 i_maxdate
= tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
+ 1;
361 while( tk
.i_current_subtitle
< tk
.i_subtitles
&&
362 tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
< i_maxdate
)
364 int i_pos
= tk
.p_subtitles
[tk
.i_current_subtitle
].i_vobsub_location
;
368 /* first compute SPU size */
369 if( tk
.i_current_subtitle
+ 1 < tk
.i_subtitles
)
371 i_size
= tk
.p_subtitles
[tk
.i_current_subtitle
+1].i_vobsub_location
- i_pos
;
373 if( i_size
<= 0 ) i_size
= 65535; /* Invalid or EOF */
375 /* Seek at the right place */
376 if( stream_Seek( p_sys
->p_vobsub_stream
, i_pos
) )
379 "cannot seek in the VobSub to the correct time %d", i_pos
);
380 tk
.i_current_subtitle
++;
384 /* allocate a packet */
385 if( ( p_block
= block_New( p_demux
, i_size
) ) == NULL
)
387 tk
.i_current_subtitle
++;
392 i_read
= stream_Read( p_sys
->p_vobsub_stream
, p_block
->p_buffer
, i_size
);
395 block_Release( p_block
);
396 tk
.i_current_subtitle
++;
399 p_block
->i_buffer
= i_read
;
402 p_block
->i_pts
= tk
.p_subtitles
[tk
.i_current_subtitle
].i_start
;
404 /* demux this block */
405 DemuxVobSub( p_demux
, p_block
);
407 tk
.i_current_subtitle
++;
413 p_sys
->i_next_demux_date
= 0;
418 static int TextLoad( text_t
*txt
, stream_t
*s
)
424 txt
->i_line_count
= 0;
426 txt
->line
= calloc( i_line_max
, sizeof( char * ) );
430 /* load the complete file */
433 char *psz
= stream_ReadLine( s
);
438 txt
->line
[txt
->i_line_count
++] = psz
;
439 if( txt
->i_line_count
>= i_line_max
)
441 char **ppsz_old
= txt
->line
;
444 txt
->line
= realloc( txt
->line
, i_line_max
* sizeof( char*) );
453 if( txt
->i_line_count
<= 0 )
461 static void TextUnload( text_t
*txt
)
465 for( i
= 0; i
< txt
->i_line_count
; i
++ )
466 free( txt
->line
[i
] );
470 txt
->i_line_count
= 0;
473 static char *TextGetLine( text_t
*txt
)
475 if( txt
->i_line
>= txt
->i_line_count
)
478 return txt
->line
[txt
->i_line
++];
481 static int ParseVobSubIDX( demux_t
*p_demux
)
483 demux_sys_t
*p_sys
= p_demux
->p_sys
;
484 text_t
*txt
= &p_sys
->txt
;
486 vobsub_track_t
*current_tk
= NULL
;
490 if( ( line
= TextGetLine( txt
) ) == NULL
)
492 return( VLC_EGENERIC
);
495 if( *line
== 0 || *line
== '\r' || *line
== '\n' || *line
== '#' )
499 else if( !strncmp( "size:", line
, 5 ) )
501 /* Store the original size of the video */
502 if( sscanf( line
, "size: %dx%d",
503 &p_sys
->i_original_frame_width
, &p_sys
->i_original_frame_height
) == 2 )
505 msg_Dbg( p_demux
, "original frame size: %dx%d", p_sys
->i_original_frame_width
, p_sys
->i_original_frame_height
);
509 msg_Warn( p_demux
, "reading original frame size failed" );
512 else if( !strncmp( "palette:", line
, 8 ) )
516 /* Store the palette of the subs */
517 if( sscanf( line
, "palette: %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x",
518 &p_sys
->palette
[0], &p_sys
->palette
[1], &p_sys
->palette
[2], &p_sys
->palette
[3],
519 &p_sys
->palette
[4], &p_sys
->palette
[5], &p_sys
->palette
[6], &p_sys
->palette
[7],
520 &p_sys
->palette
[8], &p_sys
->palette
[9], &p_sys
->palette
[10], &p_sys
->palette
[11],
521 &p_sys
->palette
[12], &p_sys
->palette
[13], &p_sys
->palette
[14], &p_sys
->palette
[15] ) == 16 )
523 for( i
= 0; i
< 16; i
++ )
525 uint8_t r
= 0, g
= 0, b
= 0;
526 uint8_t y
= 0, u
= 0, v
= 0;
527 r
= (p_sys
->palette
[i
] >> 16) & 0xff;
528 g
= (p_sys
->palette
[i
] >> 8) & 0xff;
529 b
= (p_sys
->palette
[i
] >> 0) & 0xff;
530 /* msg_Dbg( p_demux, "palette %d: R=%x, G=%x, B=%x", i, r, g, b ); */
531 y
= (uint8_t) __MIN(abs(r
* 2104 + g
* 4130 + b
* 802 + 4096 + 131072) >> 13, 235);
532 u
= (uint8_t) __MIN(abs(r
* -1214 + g
* -2384 + b
* 3598 + 4096 + 1048576) >> 13, 240);
533 v
= (uint8_t) __MIN(abs(r
* 3598 + g
* -3013 + b
* -585 + 4096 + 1048576) >> 13, 240);
534 p_sys
->palette
[i
] = 0;
535 p_sys
->palette
[i
] |= (y
&0xff)<<16;
536 p_sys
->palette
[i
] |= (u
&0xff);
537 p_sys
->palette
[i
] |= (v
&0xff)<<8;
538 /* msg_Dbg( p_demux, "palette %d: y=%x, u=%x, v=%x", i, y, u, v ); */
541 p_sys
->b_palette
= true;
542 msg_Dbg( p_demux
, "vobsub palette read" );
546 msg_Warn( p_demux
, "reading original palette failed" );
549 else if( !strncmp( "id:", line
, 3 ) )
555 /* Lets start a new track */
556 if( sscanf( line
, "id: %2s, index: %d",
557 language
, &i_track_id
) == 2 )
560 p_sys
->track
= realloc( p_sys
->track
, sizeof( vobsub_track_t
) * (p_sys
->i_tracks
+ 1 ) );
563 current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
564 memset( current_tk
, 0, sizeof( vobsub_track_t
) );
565 current_tk
->i_current_subtitle
= 0;
566 current_tk
->i_subtitles
= 0;
567 current_tk
->p_subtitles
= malloc( sizeof( subtitle_t
) );;
568 current_tk
->i_track_id
= i_track_id
;
569 current_tk
->i_delay
= (int64_t)0;
571 es_format_Init( &fmt
, SPU_ES
, VLC_FOURCC( 's','p','u',' ' ) );
572 fmt
.subs
.spu
.i_original_frame_width
= p_sys
->i_original_frame_width
;
573 fmt
.subs
.spu
.i_original_frame_height
= p_sys
->i_original_frame_height
;
574 fmt
.psz_language
= strdup( language
);
575 if( p_sys
->b_palette
)
577 fmt
.subs
.spu
.palette
[0] = 0xBeef;
578 memcpy( &fmt
.subs
.spu
.palette
[1], p_sys
->palette
, 16 * sizeof( uint32_t ) );
581 current_tk
->p_es
= es_out_Add( p_demux
->out
, &fmt
);
582 msg_Dbg( p_demux
, "new vobsub track detected" );
586 msg_Warn( p_demux
, "reading new track failed" );
589 else if( !strncmp( line
, "timestamp:", 10 ) )
592 * timestamp: [sign]hh:mm:ss:mss, filepos: loc
593 * loc is the hex location of the spu in the .sub file
595 int h
, m
, s
, ms
, count
, loc
= 0;
597 int64_t i_start
, i_location
= 0;
599 if( p_sys
->i_tracks
> 0 &&
600 sscanf( line
, "timestamp: %d%n:%d:%d:%d, filepos: %x",
601 &h
, &count
, &m
, &s
, &ms
, &loc
) >= 5 )
603 vobsub_track_t
*current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
604 subtitle_t
*current_sub
;
606 if( line
[count
-3] == '-' )
611 i_start
= (int64_t) ( h
* 3600*1000 +
617 current_tk
->i_subtitles
++;
618 current_tk
->p_subtitles
= realloc( current_tk
->p_subtitles
, sizeof( subtitle_t
) * (current_tk
->i_subtitles
+ 1 ) );
619 current_sub
= ¤t_tk
->p_subtitles
[current_tk
->i_subtitles
- 1];
621 current_sub
->i_start
= i_start
* i_sign
;
622 current_sub
->i_start
+= current_tk
->i_delay
;
623 current_sub
->i_vobsub_location
= i_location
;
627 msg_Warn( p_demux
, "reading timestamp failed" );
630 else if( !strncasecmp( line
, "delay:", 6 ) )
633 * delay: [sign]hh:mm:ss:mss
635 int h
, m
, s
, ms
, count
= 0;
639 if( p_sys
->i_tracks
> 0 &&
640 sscanf( line
, "%*celay: %d%n:%d:%d:%d",
641 &h
, &count
, &m
, &s
, &ms
) >= 4 )
643 vobsub_track_t
*current_tk
= &p_sys
->track
[p_sys
->i_tracks
- 1];
644 if( line
[count
-3] == '-' )
649 i_gap
= (int64_t) ( h
* 3600*1000 +
654 current_tk
->i_delay
= current_tk
->i_delay
+ (i_gap
* i_sign
);
655 msg_Dbg( p_demux
, "sign: %+d gap: %+lld global delay: %+lld",
656 i_sign
, (long long)i_gap
,
657 (long long)current_tk
->i_delay
);
661 msg_Warn( p_demux
, "reading delay failed" );
668 static int DemuxVobSub( demux_t
*p_demux
, block_t
*p_bk
)
670 demux_sys_t
*p_sys
= p_demux
->p_sys
;
671 uint8_t *p
= p_bk
->p_buffer
;
672 uint8_t *p_end
= &p_bk
->p_buffer
[p_bk
->i_buffer
];
675 while( p
+ 6 < p_end
)
677 int i_size
= ps_pkt_size( p
, p_end
- p
);
685 if( i_size
> p_end
- p
)
687 msg_Warn( p_demux
, "broken PES size" );
691 if( p
[0] != 0 || p
[1] != 0 || p
[2] != 0x01 )
693 msg_Warn( p_demux
, "invalid PES" );
699 /* msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] ); */
705 p_pkt
= block_New( p_demux
, i_size
);
706 memcpy( p_pkt
->p_buffer
, p
, i_size
);
709 i_id
= ps_pkt_id( p_pkt
);
710 if( (i_id
&0xffe0) != 0xbd20 ||
711 ps_pkt_parse_pes( p_pkt
, 1 ) )
713 block_Release( p_pkt
);
717 /* msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size ); */
719 for( i
= 0; i
< p_sys
->i_tracks
; i
++ )
721 vobsub_track_t
*p_tk
= &p_sys
->track
[i
];
723 p_pkt
->i_dts
= p_pkt
->i_pts
= p_bk
->i_pts
;
726 if( p_tk
->p_es
&& p_tk
->i_track_id
== i_spu
)
728 es_out_Send( p_demux
->out
, p_tk
->p_es
, p_pkt
);
729 p_bk
->i_pts
= 0; /*only first packet has a pts */
733 if( i
>= p_sys
->i_tracks
)
735 block_Release( p_pkt
);