]> git.sesse.net Git - vlc/blob - src/input/stream_demux.c
decoder: fix data race in input_DecoderFrameNext()
[vlc] / src / input / stream_demux.c
1 /*****************************************************************************
2  * stream_demux.c
3  *****************************************************************************
4  * Copyright (C) 1999-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <limits.h>
28
29 #include "demux.h"
30 #include <libvlc.h>
31 #include <vlc_codec.h>
32 #include <vlc_atomic.h>
33
34 /****************************************************************************
35  * stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain)
36  ****************************************************************************/
37 struct stream_sys_t
38 {
39     /* Data buffer */
40     block_fifo_t *p_fifo;
41     block_t      *p_block;
42
43     uint64_t    i_pos;
44
45     /* Demuxer */
46     char        *psz_name;
47     es_out_t    *out;
48
49     atomic_bool  active;
50     vlc_thread_t thread;
51     vlc_mutex_t  lock;
52     struct
53     {
54         double  position;
55         int64_t length;
56         int64_t time;
57     } stats;
58 };
59
60 static int  DStreamRead   ( stream_t *, void *p_read, unsigned int i_read );
61 static int  DStreamPeek   ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
62 static int  DStreamControl( stream_t *, int i_query, va_list );
63 static void DStreamDelete ( stream_t * );
64 static void* DStreamThread ( void * );
65
66
67 stream_t *stream_DemuxNew( demux_t *p_demux, const char *psz_demux, es_out_t *out )
68 {
69     vlc_object_t *p_obj = VLC_OBJECT(p_demux);
70     /* We create a stream reader, and launch a thread */
71     stream_t     *s;
72     stream_sys_t *p_sys;
73
74     s = stream_CommonNew( p_obj );
75     if( s == NULL )
76         return NULL;
77     s->p_input = p_demux->p_input;
78     s->psz_path  = strdup(""); /* N/A */
79     s->pf_read   = DStreamRead;
80     s->pf_peek   = DStreamPeek;
81     s->pf_control= DStreamControl;
82     s->pf_destroy= DStreamDelete;
83
84     s->p_sys = p_sys = malloc( sizeof( *p_sys) );
85     if( !s->psz_path || !s->p_sys )
86     {
87         stream_CommonDelete( s );
88         return NULL;
89     }
90
91     p_sys->i_pos = 0;
92     p_sys->out = out;
93     p_sys->p_block = NULL;
94     p_sys->psz_name = strdup( psz_demux );
95     p_sys->stats.position = 0.;
96     p_sys->stats.length = 0;
97     p_sys->stats.time = 0;
98
99     /* decoder fifo */
100     if( ( p_sys->p_fifo = block_FifoNew() ) == NULL )
101     {
102         stream_CommonDelete( s );
103         free( p_sys->psz_name );
104         free( p_sys );
105         return NULL;
106     }
107
108     atomic_init( &p_sys->active, true );
109     vlc_mutex_init( &p_sys->lock );
110
111     if( vlc_clone( &p_sys->thread, DStreamThread, s, VLC_THREAD_PRIORITY_INPUT ) )
112     {
113         vlc_mutex_destroy( &p_sys->lock );
114         block_FifoRelease( p_sys->p_fifo );
115         stream_CommonDelete( s );
116         free( p_sys->psz_name );
117         free( p_sys );
118         return NULL;
119     }
120
121     return s;
122 }
123
124 void stream_DemuxSend( stream_t *s, block_t *p_block )
125 {
126     stream_sys_t *p_sys = s->p_sys;
127     block_FifoPut( p_sys->p_fifo, p_block );
128 }
129
130 int stream_DemuxControlVa( stream_t *s, int query, va_list args )
131 {
132     stream_sys_t *sys = s->p_sys;
133
134     switch( query )
135     {
136         case DEMUX_GET_POSITION:
137             vlc_mutex_lock( &sys->lock );
138             *va_arg( args, double * ) = sys->stats.position;
139             vlc_mutex_unlock( &sys->lock );
140             break;
141         case DEMUX_GET_LENGTH:
142             vlc_mutex_lock( &sys->lock );
143             *va_arg( args, int64_t * ) = sys->stats.length;
144             vlc_mutex_unlock( &sys->lock );
145             break;
146         case DEMUX_GET_TIME:
147             vlc_mutex_lock( &sys->lock );
148             *va_arg( args, int64_t * ) = sys->stats.time;
149             vlc_mutex_unlock( &sys->lock );
150             break;
151         default:
152             return VLC_EGENERIC;
153     }
154     return VLC_SUCCESS;
155 }
156
157 static void DStreamDelete( stream_t *s )
158 {
159     stream_sys_t *p_sys = s->p_sys;
160     block_t *p_empty;
161
162     atomic_store( &p_sys->active, false );
163     p_empty = block_Alloc( 0 );
164     block_FifoPut( p_sys->p_fifo, p_empty );
165     vlc_join( p_sys->thread, NULL );
166     vlc_mutex_destroy( &p_sys->lock );
167
168     if( p_sys->p_block )
169         block_Release( p_sys->p_block );
170
171     block_FifoRelease( p_sys->p_fifo );
172     free( p_sys->psz_name );
173     free( p_sys );
174     stream_CommonDelete( s );
175 }
176
177
178 static int DStreamRead( stream_t *s, void *p_read, unsigned int i_read )
179 {
180     stream_sys_t *p_sys = s->p_sys;
181     uint8_t *p_out = p_read;
182     int i_out = 0;
183
184     //msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read );
185
186     while( atomic_load( &p_sys->active ) && !s->b_error && i_read )
187     {
188         block_t *p_block = p_sys->p_block;
189         int i_copy;
190
191         if( !p_block )
192         {
193             p_block = block_FifoGet( p_sys->p_fifo );
194             if( !p_block ) s->b_error = 1;
195             p_sys->p_block = p_block;
196         }
197
198         if( p_block && i_read )
199         {
200             i_copy = __MIN( i_read, p_block->i_buffer );
201             if( p_out && i_copy ) memcpy( p_out, p_block->p_buffer, i_copy );
202             i_read -= i_copy;
203             if ( p_out ) p_out += i_copy;
204             i_out += i_copy;
205             p_block->i_buffer -= i_copy;
206             p_block->p_buffer += i_copy;
207
208             if( !p_block->i_buffer )
209             {
210                 block_Release( p_block );
211                 p_sys->p_block = NULL;
212             }
213         }
214     }
215
216     p_sys->i_pos += i_out;
217     return i_out;
218 }
219
220 static int DStreamPeek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
221 {
222     stream_sys_t *p_sys = s->p_sys;
223     block_t **pp_block = &p_sys->p_block;
224     int i_out = 0;
225     *pp_peek = 0;
226
227     //msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek );
228
229     while( atomic_load( &p_sys->active ) && !s->b_error && i_peek )
230     {
231         int i_copy;
232
233         if( !*pp_block )
234         {
235             *pp_block = block_FifoGet( p_sys->p_fifo );
236             if( !*pp_block ) s->b_error = 1;
237         }
238
239         if( *pp_block && i_peek )
240         {
241             i_copy = __MIN( i_peek, (*pp_block)->i_buffer );
242             i_peek -= i_copy;
243             i_out += i_copy;
244
245             if( i_peek ) pp_block = &(*pp_block)->p_next;
246         }
247     }
248
249     if( p_sys->p_block )
250     {
251         p_sys->p_block = block_ChainGather( p_sys->p_block );
252         *pp_peek = p_sys->p_block->p_buffer;
253     }
254
255     return i_out;
256 }
257
258 static int DStreamControl( stream_t *s, int i_query, va_list args )
259 {
260     stream_sys_t *p_sys = s->p_sys;
261     uint64_t    *p_i64;
262
263     switch( i_query )
264     {
265         case STREAM_GET_SIZE:
266             p_i64 = va_arg( args, uint64_t * );
267             *p_i64 = 0;
268             return VLC_SUCCESS;
269
270         case STREAM_CAN_SEEK:
271         case STREAM_CAN_FASTSEEK:
272         case STREAM_CAN_PAUSE:
273         case STREAM_CAN_CONTROL_PACE:
274             *va_arg( args, bool * ) = false;
275             return VLC_SUCCESS;
276
277         case STREAM_GET_POSITION:
278             p_i64 = va_arg( args, uint64_t * );
279             *p_i64 = p_sys->i_pos;
280             return VLC_SUCCESS;
281
282         case STREAM_SET_POSITION:
283         {
284             uint64_t i64 = va_arg( args, uint64_t );
285             if( i64 < p_sys->i_pos )
286                 return VLC_EGENERIC;
287
288             uint64_t i_skip = i64 - p_sys->i_pos;
289             while( i_skip > 0 )
290             {
291                 int i_read = DStreamRead( s, NULL, __MIN(i_skip, INT_MAX) );
292                 if( i_read <= 0 )
293                     return VLC_EGENERIC;
294                 i_skip -= i_read;
295             }
296             return VLC_SUCCESS;
297         }
298
299         case STREAM_GET_PTS_DELAY:
300             *va_arg( args, int64_t * ) = DEFAULT_PTS_DELAY;
301             return VLC_SUCCESS;
302
303         case STREAM_GET_TITLE_INFO:
304         case STREAM_GET_TITLE:
305         case STREAM_GET_SEEKPOINT:
306         case STREAM_GET_META:
307         case STREAM_GET_CONTENT_TYPE:
308         case STREAM_GET_SIGNAL:
309         case STREAM_SET_PAUSE_STATE:
310         case STREAM_SET_TITLE:
311         case STREAM_SET_SEEKPOINT:
312         case STREAM_SET_RECORD_STATE:
313         case STREAM_SET_PRIVATE_ID_STATE:
314         case STREAM_SET_PRIVATE_ID_CA:
315         case STREAM_GET_PRIVATE_ID_STATE:
316             return VLC_EGENERIC;
317
318         default:
319             msg_Err( s, "invalid DStreamControl query=0x%x", i_query );
320             return VLC_EGENERIC;
321     }
322 }
323
324 static void* DStreamThread( void *obj )
325 {
326     stream_t *s = (stream_t *)obj;
327     stream_sys_t *p_sys = s->p_sys;
328     demux_t *p_demux;
329
330     /* Create the demuxer */
331     p_demux = demux_New( s, s->p_input, "", p_sys->psz_name, "", s, p_sys->out,
332                          false );
333     if( p_demux == NULL )
334         return NULL;
335
336     /* stream_Demux cannot apply DVB filters.
337      * Get all programs and let the E/S output sort them out. */
338     demux_Control( p_demux, DEMUX_SET_GROUP, -1, NULL );
339
340     /* Main loop */
341     mtime_t next_update = 0;
342     while( atomic_load( &p_sys->active ) )
343     {
344         if( p_demux->info.i_update || mdate() >= next_update )
345         {
346             double newpos;
347             int64_t newlen, newtime;
348
349             if( demux_Control( p_demux, DEMUX_GET_POSITION, &newpos ) )
350                 newpos = 0.;
351             if( demux_Control( p_demux, DEMUX_GET_LENGTH, &newlen ) )
352                 newlen = 0;
353             if( demux_Control( p_demux, DEMUX_GET_TIME, &newtime ) )
354                 newtime = 0;
355
356             vlc_mutex_lock( &p_sys->lock );
357             p_sys->stats.position = newpos;
358             p_sys->stats.length = newlen;
359             p_sys->stats.time = newtime;
360             vlc_mutex_unlock( &p_sys->lock );
361
362             p_demux->info.i_update = 0;
363             next_update = mdate() + (CLOCK_FREQ / 4);
364         }
365
366         if( demux_Demux( p_demux ) <= 0 )
367             break;
368     }
369
370     /* Explicit kludge: the stream is destroyed by the owner of the
371      * streamDemux, not here. */
372     p_demux->s = NULL;
373     demux_Delete( p_demux );
374
375     return NULL;
376 }