]> git.sesse.net Git - vlc/blob - modules/demux/vobsub.c
* split vobsub and textual demuxers.
[vlc] / modules / demux / vobsub.c
1 /*****************************************************************************
2  * subtitle.c: Demux vobsub files.
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
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.
14  *
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.
19  *
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29
30 #include <errno.h>
31 #include <sys/types.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/input.h>
35 #include "vlc_video.h"
36
37 #include "ps.h"
38
39 #define MAX_LINE 8192
40
41 /*****************************************************************************
42  * Module descriptor
43  *****************************************************************************/
44 static int  Open ( vlc_object_t *p_this );
45 static void Close( vlc_object_t *p_this );
46
47 vlc_module_begin();
48     set_description( _("Vobsub subtitles demux") );
49     set_capability( "demux2", 0 );
50     
51     set_callbacks( Open, Close );
52
53     add_shortcut( "vobsub" );
54 vlc_module_end();
55
56 /*****************************************************************************
57  * Prototypes:
58  *****************************************************************************/
59
60 typedef struct
61 {
62     int     i_line_count;
63     int     i_line;
64     char    **line;
65 } text_t;
66 static int  TextLoad( text_t *, stream_t *s );
67 static void TextUnload( text_t * );
68
69 typedef struct
70 {
71     mtime_t i_start;
72     mtime_t i_stop;
73
74     int     i_vobsub_location;
75 } subtitle_t;
76
77
78 struct demux_sys_t
79 {
80     int         i_type;
81     text_t      txt;
82     es_out_id_t *es;
83
84     int64_t     i_next_demux_date;
85
86     int         i_subtitle;
87     int         i_subtitles;
88     subtitle_t  *subtitle;
89     FILE        *p_vobsub_file;
90
91     int64_t     i_length;
92 };
93
94 static int Demux( demux_t * );
95 static int Control( demux_t *, int, va_list );
96
97 static int  ParseVobSubIDX( demux_t *, subtitle_t * );
98 static int  DemuxVobSub( demux_t *f, block_t *);
99
100 /*****************************************************************************
101  * Module initializer
102  *****************************************************************************/
103 static int Open ( vlc_object_t *p_this )
104 {
105     demux_t     *p_demux = (demux_t*)p_this;
106     demux_sys_t *p_sys;
107     es_format_t fmt;
108     int i_max;
109
110     if( strcmp( p_demux->psz_demux, "vobsub" ) )
111     {
112         msg_Dbg( p_demux, "vobsub demux discarded" );
113         return VLC_EGENERIC;
114     }
115
116     p_demux->pf_demux = Demux;
117     p_demux->pf_control = Control;
118     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
119     p_sys->i_subtitle = 0;
120     p_sys->i_subtitles= 0;
121     p_sys->subtitle   = NULL;
122     p_sys->p_vobsub_file = NULL;
123
124     char *s = NULL;
125     if( ( s = stream_ReadLine( p_demux->s ) ) != NULL )
126     {
127         if( !strcasestr( s, "# VobSub index file" ) )
128         {
129             msg_Err( p_demux, "this doesn't seem to be a vobsub file, bailing" );
130             free( s );
131             return VLC_EGENERIC;
132         }
133         free( s );
134         s = NULL;
135
136         if( stream_Seek( p_demux->s, 0 ) )
137         {
138             msg_Warn( p_demux, "failed to rewind" );
139         }
140     }
141     else
142     {
143         msg_Err( p_demux, "could not read vobsub IDX file" );
144         return VLC_EGENERIC;
145     }
146
147     /* Load the whole file */
148     TextLoad( &p_sys->txt, p_demux->s );
149
150     /* Parse it */
151     for( i_max = 0;; )
152     {
153         if( p_sys->i_subtitles >= i_max )
154         {
155             i_max += 500;
156             if( !( p_sys->subtitle = realloc( p_sys->subtitle,
157                                               sizeof(subtitle_t) * i_max ) ) )
158             {
159                 msg_Err( p_demux, "out of memory");
160                 return VLC_ENOMEM;
161             }
162         }
163
164         if( ParseVobSubIDX( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) )
165             break;
166
167         p_sys->i_subtitles++;
168     }
169     /* Unload */
170     TextUnload( &p_sys->txt );
171
172     msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles );
173
174     /* Fix subtitle (order and time) *** */
175     p_sys->i_subtitle = 0;
176     p_sys->i_length = 0;
177     if( p_sys->i_subtitles > 0 )
178     {
179         p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop;
180         /* +1 to avoid 0 */
181         if( p_sys->i_length <= 0 )
182             p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1;
183     }
184
185     int i_len = strlen( p_demux->psz_path );
186     char *psz_vobname = strdup( p_demux->psz_path );
187
188     strcpy( psz_vobname + i_len - 4, ".sub" );
189
190     /* open file */
191     if( !( p_sys->p_vobsub_file = fopen( psz_vobname, "rb" ) ) )
192     {
193         msg_Err( p_demux, "couldn't open .sub Vobsub file: %s",
194                  psz_vobname );
195     }
196     free( psz_vobname );
197
198     es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
199
200     p_sys->es = es_out_Add( p_demux->out, &fmt );
201
202     return VLC_SUCCESS;
203 }
204
205 /*****************************************************************************
206  * Close: Close subtitle demux
207  *****************************************************************************/
208 static void Close( vlc_object_t *p_this )
209 {
210     demux_t *p_demux = (demux_t*)p_this;
211     demux_sys_t *p_sys = p_demux->p_sys;
212
213     if( p_sys->subtitle )
214         free( p_sys->subtitle );
215
216     if( p_sys->p_vobsub_file )
217         fclose( p_sys->p_vobsub_file );
218
219     free( p_sys );
220 }
221
222 /*****************************************************************************
223  * Control:
224  *****************************************************************************/
225 static int Control( demux_t *p_demux, int i_query, va_list args )
226 {
227     demux_sys_t *p_sys = p_demux->p_sys;
228     int64_t *pi64, i64;
229     double *pf, f;
230
231     switch( i_query )
232     {
233         case DEMUX_GET_LENGTH:
234             pi64 = (int64_t*)va_arg( args, int64_t * );
235             *pi64 = p_sys->i_length;
236             return VLC_SUCCESS;
237
238         case DEMUX_GET_TIME:
239             pi64 = (int64_t*)va_arg( args, int64_t * );
240             if( p_sys->i_subtitle < p_sys->i_subtitles )
241             {
242                 *pi64 = p_sys->subtitle[p_sys->i_subtitle].i_start;
243                 return VLC_SUCCESS;
244             }
245             return VLC_EGENERIC;
246
247         case DEMUX_SET_TIME:
248             i64 = (int64_t)va_arg( args, int64_t );
249             p_sys->i_subtitle = 0;
250             while( p_sys->i_subtitle < p_sys->i_subtitles &&
251                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
252             {
253                 p_sys->i_subtitle++;
254             }
255
256             if( p_sys->i_subtitle >= p_sys->i_subtitles )
257                 return VLC_EGENERIC;
258             return VLC_SUCCESS;
259
260         case DEMUX_GET_POSITION:
261             pf = (double*)va_arg( args, double * );
262             if( p_sys->i_subtitle >= p_sys->i_subtitles )
263             {
264                 *pf = 1.0;
265             }
266             else if( p_sys->i_subtitles > 0 )
267             {
268                 *pf = (double)p_sys->subtitle[p_sys->i_subtitle].i_start /
269                       (double)p_sys->i_length;
270             }
271             else
272             {
273                 *pf = 0.0;
274             }
275             return VLC_SUCCESS;
276
277         case DEMUX_SET_POSITION:
278             f = (double)va_arg( args, double );
279             i64 = f * p_sys->i_length;
280
281             p_sys->i_subtitle = 0;
282             while( p_sys->i_subtitle < p_sys->i_subtitles &&
283                    p_sys->subtitle[p_sys->i_subtitle].i_start < i64 )
284             {
285                 p_sys->i_subtitle++;
286             }
287             if( p_sys->i_subtitle >= p_sys->i_subtitles )
288                 return VLC_EGENERIC;
289             return VLC_SUCCESS;
290
291         case DEMUX_SET_NEXT_DEMUX_TIME:
292             p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t );
293             return VLC_SUCCESS;
294
295         case DEMUX_GET_FPS:
296         case DEMUX_GET_META:
297         case DEMUX_GET_TITLE_INFO:
298             return VLC_EGENERIC;
299
300         default:
301             msg_Err( p_demux, "unknown query in subtitle control" );
302             return VLC_EGENERIC;
303     }
304 }
305
306 /*****************************************************************************
307  * Demux: Send subtitle to decoder
308  *****************************************************************************/
309 static int Demux( demux_t *p_demux )
310 {
311     demux_sys_t *p_sys = p_demux->p_sys;
312     int64_t i_maxdate;
313
314     if( p_sys->i_subtitle >= p_sys->i_subtitles )
315         return 0;
316
317     i_maxdate = p_sys->i_next_demux_date;
318     if( i_maxdate <= 0 && p_sys->i_subtitle < p_sys->i_subtitles )
319     {
320         /* Should not happen */
321         i_maxdate = p_sys->subtitle[p_sys->i_subtitle].i_start + 1;
322     }
323
324       while( p_sys->i_subtitle < p_sys->i_subtitles &&
325            p_sys->subtitle[p_sys->i_subtitle].i_start < i_maxdate )
326     {
327         int i_pos = p_sys->subtitle[p_sys->i_subtitle].i_vobsub_location;
328         block_t *p_block;
329         int i_size = 0;
330
331         /* first compute SPU size */
332         if( p_sys->i_subtitle + 1 < p_sys->i_subtitles )
333         {
334             i_size = p_sys->subtitle[p_sys->i_subtitle+1].i_vobsub_location - i_pos;
335         }
336         if( i_size <= 0 ) i_size = 65535;   /* Invalid or EOF */
337
338         /* Seek at the right place */
339         if( fseek( p_sys->p_vobsub_file, i_pos, SEEK_SET ) )
340         {
341             msg_Warn( p_demux,
342                       "cannot seek at right vobsub location %d", i_pos );
343             p_sys->i_subtitle++;
344             continue;
345         }
346
347         /* allocate a packet */
348         if( ( p_block = block_New( p_demux, i_size ) ) == NULL )
349         {
350             p_sys->i_subtitle++;
351             continue;
352         }
353
354         /* read data */
355         p_block->i_buffer = fread( p_block->p_buffer, 1, i_size,
356                                    p_sys->p_vobsub_file );
357         if( p_block->i_buffer <= 6 )
358         {
359             block_Release( p_block );
360             p_sys->i_subtitle++;
361             continue;
362         }
363
364         /* pts */
365         p_block->i_pts = p_sys->subtitle[p_sys->i_subtitle].i_start;
366
367         /* demux this block */
368         DemuxVobSub( p_demux, p_block );
369
370         p_sys->i_subtitle++;
371     }
372
373     /* */
374     p_sys->i_next_demux_date = 0;
375
376     return 1;
377 }
378
379 static int TextLoad( text_t *txt, stream_t *s )
380 {
381     int   i_line_max;
382
383     /* init txt */
384     i_line_max          = 500;
385     txt->i_line_count   = 0;
386     txt->i_line         = 0;
387     txt->line           = calloc( i_line_max, sizeof( char * ) );
388
389     /* load the complete file */
390     for( ;; )
391     {
392         char *psz = stream_ReadLine( s );
393
394         if( psz == NULL )
395             break;
396
397         txt->line[txt->i_line_count++] = psz;
398         if( txt->i_line_count >= i_line_max )
399         {
400             i_line_max += 100;
401             txt->line = realloc( txt->line, i_line_max * sizeof( char*) );
402         }
403     }
404
405     if( txt->i_line_count <= 0 )
406     {
407         free( txt->line );
408         return VLC_EGENERIC;
409     }
410
411     return VLC_SUCCESS;
412 }
413 static void TextUnload( text_t *txt )
414 {
415     int i;
416
417     for( i = 0; i < txt->i_line_count; i++ )
418     {
419         free( txt->line[i] );
420     }
421     free( txt->line );
422     txt->i_line       = 0;
423     txt->i_line_count = 0;
424 }
425
426 static char *TextGetLine( text_t *txt )
427 {
428     if( txt->i_line >= txt->i_line_count )
429         return( NULL );
430
431     return txt->line[txt->i_line++];
432 }
433 static void TextPreviousLine( text_t *txt )
434 {
435     if( txt->i_line > 0 )
436         txt->i_line--;
437 }
438
439 static int  ParseVobSubIDX( demux_t *p_demux, subtitle_t *p_subtitle )
440 {
441     demux_sys_t *p_sys = p_demux->p_sys;
442     text_t      *txt = &p_sys->txt;
443
444     /*
445      * Parse the idx file. Each line:
446      * timestamp: hh:mm:ss:mss, filepos: loc
447      * hexint is the hex location of the vobsub in the .sub file
448      *
449      */
450     char *p;
451     char buffer_text[MAX_LINE + 1];
452     unsigned int    i_start, i_location;
453
454     p_subtitle->i_start = 0;
455     p_subtitle->i_stop  = 0;
456     p_subtitle->i_vobsub_location  = 0;
457
458     for( ;; )
459     {
460         unsigned int h, m, s, ms, loc;
461
462         if( ( p = TextGetLine( txt ) ) == NULL )
463         {
464             return( VLC_EGENERIC );
465         }
466         i_start = 0;
467
468         memset( buffer_text, '\0', MAX_LINE );
469         if( sscanf( p, "timestamp: %d:%d:%d:%d, filepos: %x%[^\r\n]",
470                     &h, &m, &s, &ms, &loc, buffer_text ) == 5 )
471         {
472             i_start = ( (mtime_t)h * 3600*1000 +
473                         (mtime_t)m * 60*1000 +
474                         (mtime_t)s * 1000 +
475                         (mtime_t)ms ) * 1000;
476             i_location = loc;
477             break;
478         }
479     }
480     p_subtitle->i_start = (mtime_t)i_start;
481     p_subtitle->i_stop  = 0;
482     p_subtitle->i_vobsub_location = i_location;
483     fprintf( stderr, "time: %x, location: %x\n", i_start, i_location );
484     return( 0 );
485 }
486
487 static int  DemuxVobSub( demux_t *p_demux, block_t *p_bk )
488 {
489     demux_sys_t *p_sys = p_demux->p_sys;
490     uint8_t     *p = p_bk->p_buffer;
491     uint8_t     *p_end = &p_bk->p_buffer[p_bk->i_buffer];
492
493     while( p < p_end )
494     {
495         int i_size = ps_pkt_size( p, p_end - p );
496         block_t *p_pkt;
497         int      i_id;
498         int      i_spu;
499
500         if( i_size <= 0 )
501         {
502             break;
503         }
504         if( p[0] != 0 || p[1] != 0 || p[2] != 0x01 )
505         {
506             msg_Warn( p_demux, "invalid PES" );
507             break;
508         }
509
510         if( p[3] != 0xbd )
511         {
512             msg_Dbg( p_demux, "we don't need these ps packets (id=0x1%2.2x)", p[3] );
513             p += i_size;
514             continue;
515         }
516
517         /* Create a block */
518         p_pkt = block_New( p_demux, i_size );
519         memcpy( p_pkt->p_buffer, p, i_size);
520         p += i_size;
521
522         i_id = ps_pkt_id( p_pkt );
523         if( (i_id&0xffe0) != 0xbd20 ||
524             ps_pkt_parse_pes( p_pkt, 1 ) )
525         {
526             block_Release( p_pkt );
527             continue;
528         }
529         i_spu = i_id&0x1f;
530         msg_Dbg( p_demux, "SPU track %d size %d", i_spu, i_size );
531
532         /* FIXME i_spu == determines which of the spu tracks we will show. */
533         if( p_sys->es && i_spu == 0 )
534         {
535             p_pkt->i_dts = p_pkt->i_pts = p_bk->i_pts;
536             p_pkt->i_length = 0;
537             es_out_Send( p_demux->out, p_sys->es, p_pkt );
538
539             p_bk->i_pts = 0;    /* only first packet has a pts */
540         }
541         else
542         {
543             block_Release( p_pkt );
544             continue;
545         }
546     }
547
548     return VLC_SUCCESS;
549 }