]> git.sesse.net Git - vlc/blob - src/input/demux.c
* demux: stream_DemuxControl: emulate seek.
[vlc] / src / input / demux.c
1 /*****************************************************************************
2  * demux.c
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id$
6  *
7  * Author: 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 #include <stdlib.h>
25 #include <vlc/vlc.h>
26 #include <vlc/input.h>
27
28 #include "input_internal.h"
29
30 /*****************************************************************************
31  * demux2_New:
32  *  if s is NULL then load a access_demux
33  *****************************************************************************/
34 demux_t *__demux2_New( vlc_object_t *p_obj,
35                        char *psz_access, char *psz_demux, char *psz_path,
36                        stream_t *s, es_out_t *out )
37 {
38     demux_t *p_demux = vlc_object_create( p_obj, VLC_OBJECT_DEMUX );
39     char *psz_module;
40
41     if( p_demux == NULL )
42     {
43         return NULL;
44     }
45
46     /* Parse URL */
47     p_demux->psz_access = strdup( psz_access );
48     p_demux->psz_demux  = strdup( psz_demux );
49     p_demux->psz_path   = strdup( psz_path );
50
51     msg_Dbg( p_obj, "demux2_New: access='%s' demux='%s' path='%s'",
52              p_demux->psz_access, p_demux->psz_demux, p_demux->psz_path );
53
54     p_demux->s          = s;
55     p_demux->out        = out;
56
57     p_demux->pf_demux   = NULL;
58     p_demux->pf_control = NULL;
59     p_demux->p_sys      = NULL;
60     p_demux->info.i_update = 0;
61     p_demux->info.i_title  = 0;
62     p_demux->info.i_seekpoint = 0;
63
64     if( s )
65         psz_module = p_demux->psz_demux;
66     else
67         psz_module = p_demux->psz_access;
68
69     if( s && *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) )
70     {
71         /* XXX: add only file without any problem here and with strong detection.
72          *  - no .mp3, .a52, ... (aac is added as it works only by file ext anyway
73          *  - wav can't be added 'cause of a52 and dts in them as raw audio
74          */
75         static struct { char *ext; char *demux; } exttodemux[] =
76         {
77             { "aac",  "aac" },
78             { "aiff", "aiff" },
79             { "asf",  "asf" }, { "wmv",  "asf" }, { "wma",  "asf" },
80             { "avi",  "avi" },
81             { "au",   "au" },
82             { "flac", "flac" },
83             { "dv",   "dv" },
84             { "m3u",  "m3u" },
85             { "mkv",  "mkv" }, { "mka",  "mkv" }, { "mks",  "mkv" },
86             { "mp4",  "mp4" }, { "m4a",  "mp4" }, { "mov",  "mp4" }, { "moov", "mp4" },
87             { "mod",  "mod" }, { "xm",   "mod" },
88             { "nsv",  "nsv" },
89             { "ogg",  "ogg" }, { "ogm",  "ogg" },
90             { "pva",  "pva" },
91             { "rm",   "rm" },
92             { "",  "" },
93         };
94
95         char *psz_ext = strrchr( p_demux->psz_path, '.' ) + 1;
96         int  i;
97
98         for( i = 0; exttodemux[i].ext != NULL; i++ )
99         {
100             if( !strcasecmp( psz_ext, exttodemux[i].ext ) )
101             {
102                 psz_module = exttodemux[i].demux;
103                 break;
104             }
105         }
106     }
107
108     /* Before module_Need (for var_Create...) */
109     vlc_object_attach( p_demux, p_obj );
110
111     if( s )
112     {
113         p_demux->p_module =
114             module_Need( p_demux, "demux2", psz_module,
115                          !strcmp( psz_module, p_demux->psz_demux ) ? VLC_TRUE : VLC_FALSE );
116     }
117     else
118     {
119         p_demux->p_module =
120             module_Need( p_demux, "access_demux", psz_module,
121                          !strcmp( psz_module, p_demux->psz_access ) ? VLC_TRUE : VLC_FALSE );
122     }
123
124     if( p_demux->p_module == NULL )
125     {
126         vlc_object_detach( p_demux );
127         free( p_demux->psz_path );
128         free( p_demux->psz_demux );
129         free( p_demux->psz_access );
130         vlc_object_destroy( p_demux );
131         return NULL;
132     }
133
134     return p_demux;
135 }
136
137 /*****************************************************************************
138  * demux2_Delete:
139  *****************************************************************************/
140 void demux2_Delete( demux_t *p_demux )
141 {
142     module_Unneed( p_demux, p_demux->p_module );
143     vlc_object_detach( p_demux );
144
145     free( p_demux->psz_path );
146     free( p_demux->psz_demux );
147     free( p_demux->psz_access );
148
149     vlc_object_destroy( p_demux );
150 }
151
152 /*****************************************************************************
153  * demux2_vaControlHelper:
154  *****************************************************************************/
155 int demux2_vaControlHelper( stream_t *s,
156                             int64_t i_start, int64_t i_end,
157                             int i_bitrate, int i_align,
158                             int i_query, va_list args )
159 {
160     int64_t i_tell;
161     double  f, *pf;
162     int64_t i64, *pi64;
163
164     if( i_end < 0 )    i_end   = stream_Size( s );
165     if( i_start < 0 )  i_start = 0;
166     if( i_align <= 0 ) i_align = 1;
167     i_tell = stream_Tell( s );
168
169     switch( i_query )
170     {
171         case DEMUX_GET_LENGTH:
172             pi64 = (int64_t*)va_arg( args, int64_t * );
173             if( i_bitrate > 0 && i_end > i_start )
174             {
175                 *pi64 = I64C(8000000) * (i_end - i_start) / i_bitrate;
176                 return VLC_SUCCESS;
177             }
178             return VLC_EGENERIC;
179
180         case DEMUX_GET_TIME:
181             pi64 = (int64_t*)va_arg( args, int64_t * );
182             if( i_bitrate > 0 && i_end > i_start )
183             {
184                 *pi64 = I64C(8000000) * (i_tell - i_start) / i_bitrate;
185                 return VLC_SUCCESS;
186             }
187             return VLC_EGENERIC;
188
189         case DEMUX_GET_POSITION:
190             pf = (double*)va_arg( args, double * );
191             if( i_start < i_end )
192             {
193                 *pf = (double)( i_tell - i_start ) /
194                       (double)( i_end  - i_start );
195                 return VLC_SUCCESS;
196             }
197             return VLC_EGENERIC;
198
199
200         case DEMUX_SET_POSITION:
201             f = (double)va_arg( args, double );
202             if( i_start < i_end && f >= 0.0 && f <= 1.0 )
203             {
204                 int64_t i_block = (f * ( i_end - i_start )) / i_align;
205
206                 if( stream_Seek( s, i_start + i_block * i_align ) )
207                 {
208                     return VLC_EGENERIC;
209                 }
210                 return VLC_SUCCESS;
211             }
212             return VLC_EGENERIC;
213
214         case DEMUX_SET_TIME:
215             i64 = (int64_t)va_arg( args, int64_t );
216             if( i_bitrate > 0 && i64 >= 0 )
217             {
218                 int64_t i_block = i64 * i_bitrate / I64C(8000000) / i_align;
219                 if( stream_Seek( s, i_start + i_block * i_align ) )
220                 {
221                     return VLC_EGENERIC;
222                 }
223                 return VLC_SUCCESS;
224             }
225             return VLC_EGENERIC;
226
227         case DEMUX_GET_FPS:
228         case DEMUX_GET_META:
229         case DEMUX_SET_NEXT_DEMUX_TIME:
230         case DEMUX_GET_TITLE_INFO:
231             return VLC_EGENERIC;
232
233         default:
234             msg_Err( s, "unknown query in demux_vaControlDefault" );
235             return VLC_EGENERIC;
236     }
237 }
238
239 /****************************************************************************
240  * stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain)
241  ****************************************************************************/
242 typedef struct
243 {
244     /* Data buffer */
245     vlc_mutex_t lock;
246     int         i_buffer;
247     int         i_buffer_size;
248     uint8_t     *p_buffer;
249
250     int64_t     i_pos;
251
252     /* Demuxer */
253     char        *psz_name;
254     es_out_t    *out;
255     demux_t     *p_demux;
256 } d_stream_sys_t;
257
258 static int DStreamRead   ( stream_t *, void *p_read, int i_read );
259 static int DStreamPeek   ( stream_t *, uint8_t **pp_peek, int i_peek );
260 static int DStreamControl( stream_t *, int i_query, va_list );
261 static int DStreamThread ( stream_t * );
262
263
264 stream_t *__stream_DemuxNew( vlc_object_t *p_obj, char *psz_demux, es_out_t *out )
265 {
266     /* We create a stream reader, and launch a thread */
267     stream_t       *s;
268     d_stream_sys_t *p_sys;
269
270     if( psz_demux == NULL || *psz_demux == '\0' )
271     {
272         return NULL;
273     }
274
275     s = vlc_object_create( p_obj, VLC_OBJECT_STREAM );
276     s->pf_block  = NULL;
277     s->pf_read   = DStreamRead;
278     s->pf_peek   = DStreamPeek;
279     s->pf_control= DStreamControl;
280
281     s->p_sys = malloc( sizeof( d_stream_sys_t) );
282     p_sys = (d_stream_sys_t*)s->p_sys;
283
284     vlc_mutex_init( s, &p_sys->lock );
285     p_sys->i_buffer = 0;
286     p_sys->i_buffer_size = 1000000;
287     p_sys->p_buffer = malloc( p_sys->i_buffer_size );
288     p_sys->i_pos = 0;
289     p_sys->psz_name = strdup( psz_demux );
290     p_sys->out = out;
291     p_sys->p_demux = NULL;
292
293     if( vlc_thread_create( s, "stream out", DStreamThread, VLC_THREAD_PRIORITY_INPUT, VLC_FALSE ) )
294     {
295         vlc_mutex_destroy( &p_sys->lock );
296         vlc_object_destroy( s );
297         free( p_sys );
298         return NULL;
299     }
300
301     return s;
302 }
303
304 void     stream_DemuxSend( stream_t *s, block_t *p_block )
305 {
306     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
307
308     if( p_block->i_buffer > 0 )
309     {
310         vlc_mutex_lock( &p_sys->lock );
311         /* Realloc if needed */
312         if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size )
313         {
314             if( p_sys->i_buffer_size > 5000000 )
315             {
316                 vlc_mutex_unlock( &p_sys->lock );
317                 msg_Err( s, "stream_DemuxSend: buffer size > 5000000" );
318                 block_Release( p_block );
319                 return;
320             }
321             /* I know, it's more than needed but that's perfect */
322             p_sys->i_buffer_size += p_block->i_buffer;
323             /* FIXME won't work with PEEK -> segfault */
324             p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size );
325             msg_Dbg( s, "stream_DemuxSend: realloc to %d", p_sys->i_buffer_size );
326         }
327
328         /* copy data */
329         memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer );
330         p_sys->i_buffer += p_block->i_buffer;
331
332         vlc_mutex_unlock( &p_sys->lock );
333     }
334
335     block_Release( p_block );
336 }
337
338 void     stream_DemuxDelete( stream_t *s )
339 {
340     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
341
342     s->b_die = VLC_TRUE;
343
344     vlc_mutex_lock( &p_sys->lock );
345     if( p_sys->p_demux )
346     {
347         p_sys->p_demux->b_die = VLC_TRUE;
348     }
349     vlc_mutex_unlock( &p_sys->lock );
350
351     vlc_thread_join( s );
352
353     if( p_sys->p_demux )
354     {
355         demux2_Delete( p_sys->p_demux );
356     }
357     vlc_mutex_destroy( &p_sys->lock );
358     free( p_sys->psz_name );
359     free( p_sys->p_buffer );
360     free( p_sys );
361     vlc_object_destroy( s );
362 }
363
364
365 static int      DStreamRead   ( stream_t *s, void *p_read, int i_read )
366 {
367     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
368     int           i_copy;
369
370     //msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read );
371     for( ;; )
372     {
373         vlc_mutex_lock( &p_sys->lock );
374         //msg_Dbg( s, "DStreamRead: buffer %d", p_sys->i_buffer );
375         if( p_sys->i_buffer >= i_read || s->b_die )
376         {
377             break;
378         }
379         vlc_mutex_unlock( &p_sys->lock );
380         msleep( 10000 );
381     }
382
383     //msg_Dbg( s, "DStreamRead: read %d buffer %d", i_read, p_sys->i_buffer );
384
385     i_copy = __MIN( i_read, p_sys->i_buffer );
386     if( i_copy > 0 )
387     {
388         if( p_read )
389         {
390             memcpy( p_read, p_sys->p_buffer, i_copy );
391         }
392         p_sys->i_buffer -= i_copy;
393         p_sys->i_pos += i_copy;
394
395         if( p_sys->i_buffer > 0 )
396         {
397             memmove( p_sys->p_buffer, &p_sys->p_buffer[i_copy], p_sys->i_buffer );
398         }
399
400     }
401     vlc_mutex_unlock( &p_sys->lock );
402
403     return i_copy;
404 }
405 static int      DStreamPeek   ( stream_t *s, uint8_t **pp_peek, int i_peek )
406 {
407     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
408     int           i_copy;
409
410     //msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek );
411     for( ;; )
412     {
413         vlc_mutex_lock( &p_sys->lock );
414         //msg_Dbg( s, "DStreamPeek: buffer %d", p_sys->i_buffer );
415         if( p_sys->i_buffer >= i_peek || s->b_die )
416         {
417             break;
418         }
419         vlc_mutex_unlock( &p_sys->lock );
420         msleep( 10000 );
421     }
422     *pp_peek = p_sys->p_buffer;
423     i_copy = __MIN( i_peek, p_sys->i_buffer );
424
425     vlc_mutex_unlock( &p_sys->lock );
426
427     return i_copy;
428 }
429
430 static int      DStreamControl( stream_t *s, int i_query, va_list args )
431 {
432     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
433     int64_t    *p_i64;
434     vlc_bool_t *p_b;
435     int        *p_int;
436     switch( i_query )
437     {
438         case STREAM_GET_SIZE:
439             p_i64 = (int64_t*) va_arg( args, int64_t * );
440             *p_i64 = 0;
441             return VLC_SUCCESS;
442
443         case STREAM_CAN_SEEK:
444             p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
445             *p_b = VLC_FALSE;
446             return VLC_SUCCESS;
447
448         case STREAM_CAN_FASTSEEK:
449             p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
450             *p_b = VLC_FALSE;
451             return VLC_SUCCESS;
452
453         case STREAM_GET_POSITION:
454             p_i64 = (int64_t*) va_arg( args, int64_t * );
455             *p_i64 = p_sys->i_pos;
456             return VLC_SUCCESS;
457
458         case STREAM_SET_POSITION:
459         {
460             int64_t i64 = (int64_t)va_arg( args, int64_t );
461             int i_skip;
462             if( i64 < p_sys->i_pos )
463                 return VLC_EGENERIC;
464             i_skip = i64 - p_sys->i_pos;
465
466             while( i_skip > 0 )
467             {
468                 int i_read = DStreamRead( s, NULL, i_skip );
469
470                 if( i_read <= 0 )
471                     return VLC_EGENERIC;
472
473                 i_skip -= i_read;
474             }
475             return VLC_SUCCESS;
476         }
477
478         case STREAM_GET_MTU:
479             p_int = (int*) va_arg( args, int * );
480             *p_int = 0;
481             return VLC_SUCCESS;
482
483         default:
484             msg_Err( s, "invalid DStreamControl query=0x%x", i_query );
485             return VLC_EGENERIC;
486     }
487 }
488
489 static int      DStreamThread ( stream_t *s )
490 {
491     d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
492     demux_t      *p_demux;
493
494     /* Create the demuxer */
495
496     if( ( p_demux = demux2_New( s, "", p_sys->psz_name, "", s, p_sys->out ) ) == NULL )
497     {
498         return VLC_EGENERIC;
499     }
500
501     vlc_mutex_lock( &p_sys->lock );
502     p_sys->p_demux = p_demux;
503     vlc_mutex_unlock( &p_sys->lock );
504
505     /* Main loop */
506     while( !s->b_die && !p_demux->b_die )
507     {
508         if( p_demux->pf_demux( p_demux ) <= 0 )
509         {
510             break;
511         }
512     }
513     p_demux->b_die = VLC_TRUE;
514     return VLC_SUCCESS;
515 }