]> git.sesse.net Git - vlc/blob - modules/demux/nsv.c
* nsv: display sub stream and skip extention data (initial patch from
[vlc] / modules / demux / nsv.c
1 /*****************************************************************************
2  * nsv.c: NullSoft Video demuxer.
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: nsv.c,v 1.9 2004/02/15 16:59:18 fenrir Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 /* TODO:
33  *  - implement NSVf parsing (to get meta data)
34  *  - implement missing Control (and in the right way)
35  *  - ...
36  */
37
38 /*****************************************************************************
39  * Module descriptor
40  *****************************************************************************/
41 static int  Open    ( vlc_object_t * );
42 static void Close  ( vlc_object_t * );
43
44 vlc_module_begin();
45     set_description( _("NullSoft demuxer" ) );
46     set_capability( "demux2", 10 );
47     set_callbacks( Open, Close );
48     add_shortcut( "nsv" );
49 vlc_module_end();
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54
55 struct demux_sys_t
56 {
57     es_format_t  fmt_audio;
58     es_out_id_t *p_audio;
59
60     es_format_t  fmt_video;
61     es_out_id_t *p_video;
62
63     es_format_t  fmt_sub;
64     es_out_id_t  *p_sub;
65
66     int64_t     i_pcr;
67     int64_t     i_time;
68     int64_t     i_pcr_inc;
69 };
70
71 static int Demux  ( demux_t *p_demux );
72 static int Control( demux_t *p_demux, int i_query, va_list args );
73
74 static int ReSynch( demux_t *p_demux );
75
76 static int ReadNSVf( demux_t *p_demux );
77 static int ReadNSVs( demux_t *p_demux );
78
79 /*****************************************************************************
80  * Open
81  *****************************************************************************/
82 static int Open( vlc_object_t *p_this )
83 {
84     demux_t     *p_demux = (demux_t*)p_this;
85     demux_sys_t *p_sys;
86
87     uint8_t     *p_peek;
88     int         i;
89
90     if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
91     {
92         msg_Err( p_demux, "cannot peek" );
93         return VLC_EGENERIC;
94     }
95     if( strncmp( p_peek, "NSVf", 4 ) && strncmp( p_peek, "NSVs", 4 ))
96     {
97        /* In case we had force this demuxer we try to resynch */
98         if( strcmp( p_demux->psz_demux, "nsv" ) || ReSynch( p_demux ) )
99         {
100             msg_Warn( p_demux, "NSV module discarded" );
101             return VLC_EGENERIC;
102         }
103     }
104
105     /* Fill p_demux field */
106     p_demux->pf_demux = Demux;
107     p_demux->pf_control = Control;
108     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
109
110     es_format_Init( &p_sys->fmt_audio, AUDIO_ES, 0 );
111     p_sys->p_audio = NULL;
112
113     es_format_Init( &p_sys->fmt_video, VIDEO_ES, 0 );
114     p_sys->p_video = NULL;
115
116     es_format_Init( &p_sys->fmt_sub, SPU_ES, 0 );
117     p_sys->p_sub = NULL;
118
119     p_sys->i_pcr   = 1;
120     p_sys->i_time  = 0;
121     p_sys->i_pcr_inc = 0;
122
123     return VLC_SUCCESS;
124 }
125
126 /*****************************************************************************
127  * Close
128  *****************************************************************************/
129 static void Close( vlc_object_t *p_this )
130 {
131     demux_t     *p_demux = (demux_t*)p_this;
132     demux_sys_t *p_sys = p_demux->p_sys;
133
134     free( p_sys );
135 }
136
137
138 /*****************************************************************************
139  * Demux:
140  *****************************************************************************/
141 static int Demux( demux_t *p_demux )
142 {
143     demux_sys_t *p_sys = p_demux->p_sys;
144
145     uint8_t     header[5];
146     uint8_t     *p_peek;
147
148     int         i_size;
149     block_t     *p_frame;
150
151     for( ;; )
152     {
153         if( stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
154         {
155             msg_Warn( p_demux, "cannot peek" );
156             return 0;
157         }
158
159         if( !strncmp( p_peek, "NSVf", 4 ) )
160         {
161             if( ReadNSVf( p_demux ) )
162             {
163                 return -1;
164             }
165         }
166         else if( !strncmp( p_peek, "NSVs", 4 ) )
167         {
168             if( ReadNSVs( p_demux ) )
169             {
170                 return -1;
171             }
172             break;
173         }
174         else if( GetWLE( p_peek ) == 0xbeef )
175         {
176             /* Next frame of the current NSVs chunk */
177             if( stream_Read( p_demux->s, NULL, 2 ) < 2 )
178             {
179                 msg_Warn( p_demux, "cannot read" );
180                 return 0;
181             }
182             break;
183         }
184         else
185         {
186             msg_Err( p_demux, "invalid signature 0x%x (%4.4s)", *(uint32_t*)p_peek, (char*)p_peek );
187             if( ReSynch( p_demux ) )
188             {
189                 return -1;
190             }
191         }
192     }
193
194     if( stream_Read( p_demux->s, header, 5 ) < 5 )
195     {
196         msg_Warn( p_demux, "cannot read" );
197         return 0;
198     }
199
200     /* Set PCR */
201     es_out_Control( p_demux->out, ES_OUT_SET_PCR, (int64_t)p_sys->i_pcr );
202
203     /* Read video */
204     i_size = ( header[0] >> 4 ) | ( header[1] << 4 ) | ( header[2] << 12 );
205     if( i_size > 0 )
206     {
207         /* extra data ? */
208         if( (header[0]&0x0f) != 0x0 )
209         {
210             uint8_t      aux[6];
211             int          i_aux;
212             vlc_fourcc_t fcc;
213             if( stream_Read( p_demux->s, aux, 6 ) < 6 )
214             {
215                 msg_Warn( p_demux, "cannot read" );
216                 return 0;
217             }
218             i_aux = GetWLE( aux );
219             fcc   = VLC_FOURCC( aux[2], aux[3], aux[4], aux[5] );
220
221             msg_Dbg( p_demux, "Belekas: %d - size=%d fcc=%4.4s",
222                      header[0]&0xf, i_aux, (char*)&fcc );
223
224             if( fcc == VLC_FOURCC( 'S', 'U', 'B', 'T' ) && i_aux > 2 )
225             {
226                 if( p_sys->p_sub == NULL )
227                 {
228                     p_sys->fmt_sub.i_codec = VLC_FOURCC( 's', 'u', 'b', 't' );
229                     p_sys->p_sub = es_out_Add( p_demux->out, &p_sys->fmt_sub );
230                     es_out_Control( p_demux->out, ES_OUT_SET_ES, p_sys->p_sub );
231                 }
232                 stream_Read( p_demux->s, NULL, 2 );
233
234                 if( ( p_frame = stream_Block( p_demux->s, i_aux - 2 ) ) )
235                 {
236                     uint8_t *p = p_frame->p_buffer;
237
238                     while( p < &p_frame->p_buffer[p_frame->i_buffer] && *p != 0 )
239                     {
240                         p++;
241                     }
242                     if( *p == 0 && p + 1 < &p_frame->p_buffer[p_frame->i_buffer] )
243                     {
244                         p_frame->i_buffer -= p + 1 - p_frame->p_buffer;
245                         p_frame->p_buffer = p + 1;
246                     }
247
248                     /* Skip the first part (it is the language name) */
249                     p_frame->i_pts = p_sys->i_pcr;
250                     p_frame->i_dts = p_sys->i_pcr + 4000000;    /* 4s */
251
252                     es_out_Send( p_demux->out, p_sys->p_sub, p_frame );
253                 }
254             }
255             else
256             {
257                 /* We skip this extra data */
258                 if( stream_Read( p_demux->s, NULL, i_aux ) < i_aux )
259                 {
260                     msg_Warn( p_demux, "cannot read" );
261                     return 0;
262                 }
263             }
264             i_size -= 6 + i_aux;
265         }
266
267         /* msg_Dbg( p_demux, "frame video size=%d", i_size ); */
268         if( i_size > 0 && ( p_frame = stream_Block( p_demux->s, i_size ) ) )
269         {
270             p_frame->i_dts = p_sys->i_pcr;
271             es_out_Send( p_demux->out, p_sys->p_video, p_frame );
272         }
273     }
274
275     /* Read audio */
276     i_size = header[3] | ( header[4] << 8 );
277     if( i_size > 0 )
278     {
279         /* msg_Dbg( p_demux, "frame audio size=%d", i_size ); */
280         if( p_sys->fmt_audio.i_codec == VLC_FOURCC( 'a', 'r', 'a', 'w' ) )
281         {
282             uint8_t h[4];
283             stream_Read( p_demux->s, h, 4 );
284
285             p_sys->fmt_audio.audio.i_channels = h[1];
286             p_sys->fmt_audio.audio.i_rate = GetWLE( &h[2] );
287
288             i_size -= 4;
289         }
290         if( p_sys->p_audio == NULL )
291         {
292             p_sys->p_audio = es_out_Add( p_demux->out, &p_sys->fmt_audio );
293         }
294
295         if( ( p_frame = stream_Block( p_demux->s, i_size ) ) )
296         {
297             p_frame->i_dts =
298             p_frame->i_pts = p_sys->i_pcr;
299             es_out_Send( p_demux->out, p_sys->p_audio, p_frame );
300         }
301     }
302
303     p_sys->i_pcr += p_sys->i_pcr_inc;
304     if( p_sys->i_time >= 0 )
305     {
306         p_sys->i_time += p_sys->i_pcr_inc;
307     }
308
309     return 1;
310 }
311
312 /*****************************************************************************
313  * Control:
314  *****************************************************************************/
315 static int Control( demux_t *p_demux, int i_query, va_list args )
316 {
317     demux_sys_t *p_sys = p_demux->p_sys;
318     double f, *pf;
319     int64_t i64, *pi64;
320
321     switch( i_query )
322     {
323         case DEMUX_GET_POSITION:
324             pf = (double*) va_arg( args, double* );
325             i64 = stream_Size( p_demux->s );
326             if( i64 > 0 )
327             {
328                 *pf = (double)stream_Tell( p_demux->s ) / (double)i64;
329             }
330             else
331             {
332                 *pf = 0.0;
333             }
334             return VLC_SUCCESS;
335
336         case DEMUX_SET_POSITION:
337             f = (double) va_arg( args, double );
338             i64 = stream_Size( p_demux->s );
339
340             es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
341             if( stream_Seek( p_demux->s, (int64_t)(i64 * f) ) || ReSynch( p_demux ) )
342             {
343                 return VLC_EGENERIC;
344             }
345             p_sys->i_time = -1; /* Invalidate time display */
346             return VLC_SUCCESS;
347
348         case DEMUX_GET_TIME:
349             pi64 = (int64_t*)va_arg( args, int64_t * );
350             if( p_sys->i_time < 0 )
351             {
352                 *pi64 = 0;
353                 return VLC_EGENERIC;
354             }
355             *pi64 = p_sys->i_time;
356             return VLC_SUCCESS;
357
358 #if 0
359         case DEMUX_GET_LENGTH:
360             pi64 = (int64_t*)va_arg( args, int64_t * );
361             if( p_sys->i_mux_rate > 0 )
362             {
363                 *pi64 = (int64_t)1000000 * ( stream_Size( p_demux->s ) / 50 ) / p_sys->i_mux_rate;
364                 return VLC_SUCCESS;
365             }
366             *pi64 = 0;
367             return VLC_EGENERIC;
368
369         case DEMUX_SET_TIME:
370 #endif
371         case DEMUX_GET_FPS:
372             pf = (double*)va_arg( args, double * );
373             *pf = (double)1000000.0 / (double)p_sys->i_pcr_inc;
374             return VLC_SUCCESS;
375
376         default:
377             return VLC_EGENERIC;
378     }
379 }
380
381 /*****************************************************************************
382  * ReSynch:
383  *****************************************************************************/
384 static int ReSynch( demux_t *p_demux )
385 {
386     uint8_t *p_peek;
387     int      i_skip;
388     int      i_peek;
389
390     while( !p_demux->b_die )
391     {
392         if( ( i_peek = stream_Peek( p_demux->s, &p_peek, 1024 ) ) < 8 )
393         {
394             return VLC_EGENERIC;
395         }
396         i_skip = 0;
397
398         while( i_skip < i_peek - 4 )
399         {
400             if( !strncmp( p_peek, "NSVf", 4 ) || !strncmp( p_peek, "NSVs", 4 ) )
401             {
402                 if( i_skip > 0 )
403                 {
404                     stream_Read( p_demux->s, NULL, i_skip );
405                 }
406                 return VLC_SUCCESS;
407             }
408             p_peek++;
409             i_skip++;
410         }
411
412         stream_Read( p_demux->s, NULL, i_skip );
413     }
414     return VLC_EGENERIC;
415 }
416
417 /*****************************************************************************
418  * ReadNSVf:
419  *****************************************************************************/
420 static int ReadNSVf( demux_t *p_demux )
421 {
422     demux_sys_t *p_sys = p_demux->p_sys;
423     uint8_t     *p;
424     int         i_size;
425
426     msg_Dbg( p_demux, "new NSVf chunk" );
427     if( stream_Peek( p_demux->s, &p, 8 ) < 8 )
428     {
429         return VLC_EGENERIC;
430     }
431
432     i_size = GetDWLE( &p[4] );
433     msg_Dbg( p_demux, "    - size=%d", i_size );
434
435     return stream_Read( p_demux->s, NULL, i_size ) == i_size ? VLC_SUCCESS : VLC_EGENERIC;
436 }
437 /*****************************************************************************
438  * ReadNSVf:
439  *****************************************************************************/
440 static int ReadNSVs( demux_t *p_demux )
441 {
442     demux_sys_t *p_sys = p_demux->p_sys;
443     uint8_t      header[19];
444     vlc_fourcc_t fcc;
445
446     if( stream_Read( p_demux->s, header, 19 ) < 19 )
447     {
448         msg_Warn( p_demux, "cannot read" );
449         return VLC_EGENERIC;
450     }
451
452     msg_Dbg( p_demux, "new NSVs chunk" );
453     /* Video */
454     switch( ( fcc = VLC_FOURCC( header[4], header[5], header[6], header[7] ) ) )
455     {
456         case VLC_FOURCC( 'V', 'P', '3', ' ' ):
457         case VLC_FOURCC( 'V', 'P', '3', '1' ):
458             fcc = VLC_FOURCC( 'V', 'P', '3', '1' );
459             break;
460         case VLC_FOURCC( 'N', 'O', 'N', 'E' ):
461             break;
462         default:
463             msg_Warn( p_demux, "unknown codec" );
464             break;
465     }
466     if( fcc != VLC_FOURCC( 'N', 'O', 'N', 'E' ) && fcc != p_sys->fmt_video.i_codec  )
467     {
468         es_format_Init( &p_sys->fmt_video, VIDEO_ES, fcc );
469         p_sys->fmt_video.video.i_width = GetWLE( &header[12] );
470         p_sys->fmt_video.video.i_height = GetWLE( &header[14] );
471         if( p_sys->p_video )
472         {
473             es_out_Del( p_demux->out, p_sys->p_video );
474         }
475         p_sys->p_video = es_out_Add( p_demux->out, &p_sys->fmt_video );
476
477         msg_Dbg( p_demux, "    - video `%4.4s' %dx%d",
478                  (char*)&fcc,
479                  p_sys->fmt_video.video.i_width,
480                  p_sys->fmt_video.video.i_height );
481     }
482
483     /* Audio */
484     switch( ( fcc = VLC_FOURCC( header[8], header[9], header[10], header[11] ) ) )
485     {
486         case VLC_FOURCC( 'M', 'P', '3', ' ' ):
487             fcc = VLC_FOURCC( 'm', 'p', 'g', 'a' );
488             break;
489         case VLC_FOURCC( 'P', 'C', 'M', ' ' ):
490             fcc = VLC_FOURCC( 'a', 'r', 'a', 'w' );
491             break;
492         case VLC_FOURCC( 'A', 'A', 'C', ' ' ):
493             fcc = VLC_FOURCC( 'm', 'p', '4', 'a' );
494             break;
495         case VLC_FOURCC( 'N', 'O', 'N', 'E' ):
496             break;
497         default:
498             msg_Warn( p_demux, "unknown codec" );
499             break;
500     }
501
502     if( fcc != VLC_FOURCC( 'N', 'O', 'N', 'E' ) && fcc != p_sys->fmt_audio.i_codec )
503     {
504         msg_Dbg( p_demux, "    - audio `%4.4s'", (char*)&fcc );
505
506         if( p_sys->p_audio )
507         {
508             es_out_Del( p_demux->out, p_sys->p_audio );
509             p_sys->p_audio = NULL;
510         }
511         es_format_Init( &p_sys->fmt_audio, AUDIO_ES, fcc );
512     }
513
514     if( header[16]&0x80 )
515     {
516         /* Fractional frame rate */
517         switch( header[16]&0x03 )
518         {
519             case 0: /* 30 fps */
520                 p_sys->i_pcr_inc = 33333; /* 300000/9 */
521                 break;
522             case 1: /* 29.97 fps */
523                 p_sys->i_pcr_inc = 33367; /* 300300/9 */
524                 break;
525             case 2: /* 25 fps */
526                 p_sys->i_pcr_inc = 40000; /* 360000/9 */
527                 break;
528             case 3: /* 23.98 fps */
529                 p_sys->i_pcr_inc = 41700; /* 375300/9 */
530                 break;
531         }
532
533         if( header[16] < 0xc0 )
534             p_sys->i_pcr_inc = p_sys->i_pcr_inc * (((header[16] ^ 0x80) >> 2 ) +1 );
535         else
536             p_sys->i_pcr_inc = p_sys->i_pcr_inc / (((header[16] ^ 0xc0) >> 2 ) +1 );
537     }
538     else if( header[16] != 0 )
539     {
540         /* Integer frame rate */
541         p_sys->i_pcr_inc = 1000000 / header[16];
542     }
543     else
544     {
545         msg_Dbg( p_demux, "invalid fps (0x00)" );
546         p_sys->i_pcr_inc = 40000;
547     }
548     msg_Dbg( p_demux, "    - fps=%.3f", 1000000.0 / (double)p_sys->i_pcr_inc );
549
550     return VLC_SUCCESS;
551 }
552