]> git.sesse.net Git - vlc/blob - src/audio_output/input.c
6a71c85c71bc295be5d89ad8d3be4f679309a733
[vlc] / src / audio_output / input.c
1 /*****************************************************************************
2  * input.c : internal management of input streams for the audio output
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: input.c,v 1.11 2002/08/30 22:22:24 massiot Exp $
6  *
7  * Authors: Christophe Massiot <massiot@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>                            /* calloc(), malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31
32 #ifdef HAVE_ALLOCA_H
33 #   include <alloca.h>
34 #endif
35
36 #include "audio_output.h"
37 #include "aout_internal.h"
38
39 /*****************************************************************************
40  * aout_InputNew : allocate a new input and rework the filter pipeline
41  *****************************************************************************/
42 static aout_input_t * InputNew( aout_instance_t * p_aout,
43                                 audio_sample_format_t * p_format )
44 {
45     aout_input_t * p_input;
46
47     vlc_mutex_lock( &p_aout->mixer_lock );
48
49     if ( p_aout->i_nb_inputs >= AOUT_MAX_INPUTS )
50     {
51         msg_Err( p_aout, "too many inputs already (%d)", p_aout->i_nb_inputs );
52         vlc_mutex_unlock( &p_aout->mixer_lock );
53         return NULL;
54     }
55
56     p_input = malloc(sizeof(aout_input_t));
57     if ( p_input == NULL )
58     {
59         msg_Err( p_aout, "out of memory" );
60         vlc_mutex_unlock( &p_aout->mixer_lock );
61         return NULL;
62     }
63
64     vlc_mutex_init( p_aout, &p_input->lock );
65     vlc_mutex_lock( &p_input->lock );
66
67     if ( p_aout->i_nb_inputs == 0 )
68     {
69         /* Recreate the output using the new format. */
70         if ( aout_OutputNew( p_aout, p_format ) < 0 )
71         {
72             vlc_mutex_unlock( &p_input->lock );
73             vlc_mutex_destroy( &p_input->lock );
74             free( p_input );
75             vlc_mutex_unlock( &p_aout->mixer_lock );
76             return NULL;
77         }
78     }
79     else
80     {
81         aout_MixerDelete( p_aout );
82     }
83
84     memcpy( &p_input->input, p_format,
85             sizeof(audio_sample_format_t) );
86     aout_FormatPrepare( &p_input->input );
87
88     /* Prepare FIFO. */
89     aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
90     p_input->p_first_byte_to_mix = NULL;
91
92     p_aout->pp_inputs[p_aout->i_nb_inputs] = p_input;
93     p_aout->i_nb_inputs++;
94
95     if ( aout_MixerNew( p_aout ) < 0 )
96     {
97         p_aout->i_nb_inputs--;
98         aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
99                                      p_input->i_nb_filters );
100         aout_FifoDestroy( p_aout, &p_input->fifo );
101
102         if ( !p_aout->i_nb_inputs )
103         {
104             aout_OutputDelete( p_aout );
105         }
106         else
107         {
108             aout_MixerNew( p_aout );
109         }
110         vlc_mutex_unlock( &p_input->lock );
111         vlc_mutex_destroy( &p_input->lock );
112         free( p_input );
113         vlc_mutex_unlock( &p_aout->mixer_lock );
114
115         return NULL;
116     }
117
118     vlc_mutex_unlock( &p_aout->mixer_lock );
119
120     /* Create filters. */
121     if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
122                                      &p_input->i_nb_filters, &p_input->input,
123                                      &p_aout->mixer.mixer ) < 0 )
124     {
125         msg_Err( p_aout, "couldn't set an input pipeline" );
126
127         aout_FifoDestroy( p_aout, &p_input->fifo );
128
129         vlc_mutex_unlock( &p_input->lock );
130         vlc_mutex_destroy( &p_input->lock );
131         free( p_input );
132         vlc_mutex_unlock( &p_aout->mixer_lock );
133
134         if ( !p_aout->i_nb_inputs )
135         {
136             aout_OutputDelete( p_aout );
137         }
138         return NULL;
139     }
140
141     /* Prepare hints for the buffer allocator. */
142     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
143     p_input->input_alloc.i_bytes_per_sec = -1;
144
145     aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
146                              p_input->i_nb_filters,
147                              &p_input->input_alloc );
148
149     /* i_bytes_per_sec is still == -1 if no filters */
150     p_input->input_alloc.i_bytes_per_sec = __MAX(
151                                     p_input->input_alloc.i_bytes_per_sec,
152                                     p_input->input.i_bytes_per_frame
153                                      * p_input->input.i_rate
154                                      / p_input->input.i_frame_length );
155     /* Allocate in the heap, it is more convenient for the decoder. */
156     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
157
158     vlc_mutex_unlock( &p_input->lock );
159
160     msg_Dbg( p_aout, "input 0x%x created", p_input );
161     return p_input;
162 }
163
164 aout_input_t * __aout_InputNew( vlc_object_t * p_this,
165                                 aout_instance_t ** pp_aout,
166                                 audio_sample_format_t * p_format )
167 {
168     /* Create an audio output if there is none. */
169     *pp_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
170
171     if( *pp_aout == NULL )
172     {
173         msg_Dbg( p_this, "no aout present, spawning one" );
174
175         *pp_aout = aout_NewInstance( p_this );
176         /* Everything failed, I'm a loser, I just wanna die */
177         if( *pp_aout == NULL )
178         {
179             return NULL;
180         }
181     }
182     else
183     {
184         vlc_object_release( *pp_aout );
185     }
186
187     return InputNew( *pp_aout, p_format );
188 }
189
190 /*****************************************************************************
191  * aout_InputDelete : delete an input
192  *****************************************************************************/
193 void aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
194 {
195     int i_input;
196
197     vlc_mutex_lock( &p_aout->mixer_lock );
198     vlc_mutex_lock( &p_input->lock );
199
200     for ( i_input = 0; i_input < p_aout->i_nb_inputs; i_input++ )
201     {
202         if ( p_aout->pp_inputs[i_input] == p_input )
203         {
204             break;
205         }
206     }
207
208     if ( i_input == p_aout->i_nb_inputs )
209     {
210         msg_Err( p_aout, "cannot find an input to delete" );
211         return;
212     }
213
214     /* Remove the input from the list. */
215     memmove( &p_aout->pp_inputs[i_input], &p_aout->pp_inputs[i_input + 1],
216              (AOUT_MAX_INPUTS - i_input - 1) * sizeof(aout_input_t *) );
217     p_aout->i_nb_inputs--;
218
219     if ( !p_aout->i_nb_inputs )
220     {
221         aout_OutputDelete( p_aout );
222         aout_MixerDelete( p_aout );
223     }
224
225     vlc_mutex_unlock( &p_aout->mixer_lock );
226
227     aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
228                                  p_input->i_nb_filters );
229     aout_FifoDestroy( p_aout, &p_input->fifo );
230
231     vlc_mutex_unlock( &p_input->lock );
232     vlc_mutex_destroy( &p_input->lock );
233     free( p_input );
234
235     msg_Dbg( p_aout, "input 0x%x destroyed", p_input );
236 }
237
238 /*****************************************************************************
239  * aout_InputPlay : play a buffer
240  *****************************************************************************/
241 void aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
242                      aout_buffer_t * p_buffer )
243 {
244     mtime_t start_date, duration;
245
246     vlc_mutex_lock( &p_input->lock );
247
248     /* We don't care if someone changes the start date behind our back after
249      * this. We'll deal with that when pushing the buffer, and compensate
250      * with the next incoming buffer. */
251     start_date = aout_FifoNextStart( p_aout, &p_input->fifo );
252
253     if ( start_date != 0 && start_date < mdate() )
254     {
255         /* The decoder is _very_ late. This can only happen if the user
256          * pauses the stream (or if the decoder is buggy, which cannot
257          * happen :). */
258         msg_Warn( p_aout, "computed PTS is out of range (%lld), clearing out",
259                   start_date );
260         vlc_mutex_lock( &p_aout->mixer_lock );
261         aout_FifoSet( p_aout, &p_input->fifo, 0 );
262         vlc_mutex_unlock( &p_aout->mixer_lock );
263         start_date = 0;
264     } 
265
266     if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME )
267     {
268         /* The decoder gives us f*cked up PTS. It's its business, but we
269          * can't present it anyway, so drop the buffer. */
270         msg_Warn( p_aout, "PTS is out of range (%lld), dropping buffer",
271                   mdate() - p_buffer->start_date );
272         aout_BufferFree( p_buffer );
273
274         vlc_mutex_unlock( &p_input->lock );
275         return;
276     }
277
278     if ( start_date == 0 ) start_date = p_buffer->start_date;
279
280     if ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE
281           || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE )
282     {
283         /* Can happen in several circumstances :
284          * 1. A problem at the input (clock drift)
285          * 2. A small pause triggered by the user
286          * 3. Some delay in the output stage, causing a loss of lip
287          *    synchronization
288          * Solution : resample the buffer to avoid a scratch.
289          */
290         audio_sample_format_t new_input;
291         int i_ratio, i_nb_filters;
292         mtime_t old_duration;
293         aout_filter_t * pp_filters[AOUT_MAX_FILTERS];
294         aout_buffer_t * p_new_buffer;
295         aout_alloc_t dummy_alloc;
296         mtime_t drift = p_buffer->start_date - start_date;
297
298         msg_Warn( p_aout, "buffer is %lld %s, resampling",
299                          drift > 0 ? drift : -drift,
300                          drift > 0 ? "in advance" : "late" );
301         memcpy( &new_input, &p_input->input,
302                 sizeof(audio_sample_format_t) );
303         old_duration = p_buffer->end_date - p_buffer->start_date;
304         duration = p_buffer->end_date - start_date;
305         i_ratio = duration * 100 / old_duration;
306         /* If the ratio is too != 100, the sound quality will be awful. */
307         if ( i_ratio < 66 /* % */ )
308         {
309             duration = old_duration * 66 / 100;
310         }
311         if ( i_ratio > 150 /* % */ )
312         {
313             duration = old_duration * 150 / 100;
314         }
315         new_input.i_rate = new_input.i_rate * old_duration / duration;
316         aout_FormatPrepare( &new_input );
317
318         if ( aout_FiltersCreatePipeline( p_aout, pp_filters,
319                                          &i_nb_filters, &new_input,
320                                          &p_aout->mixer.mixer ) < 0 )
321         {
322             msg_Err( p_aout, "couldn't set an input pipeline for resampling" );
323             vlc_mutex_lock( &p_aout->mixer_lock );
324             aout_FifoSet( p_aout, &p_input->fifo, 0 );
325             vlc_mutex_unlock( &p_aout->mixer_lock );
326             aout_BufferFree( p_buffer );
327
328             vlc_mutex_unlock( &p_input->lock );
329             return;
330         }
331
332         dummy_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
333         dummy_alloc.i_bytes_per_sec = -1;
334         aout_FiltersHintBuffers( p_aout, pp_filters, i_nb_filters,
335                                  &dummy_alloc );
336         dummy_alloc.i_bytes_per_sec = __MAX(
337                                     dummy_alloc.i_bytes_per_sec,
338                                     new_input.i_bytes_per_frame
339                                      * new_input.i_rate
340                                      / new_input.i_frame_length );
341         dummy_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
342
343         aout_BufferAlloc( &dummy_alloc, duration, NULL, p_new_buffer );
344         memcpy( p_new_buffer->p_buffer, p_buffer->p_buffer,
345                 p_buffer->i_nb_bytes );
346         p_new_buffer->i_nb_samples = p_buffer->i_nb_samples;
347         p_new_buffer->i_nb_bytes = p_buffer->i_nb_bytes;
348
349         aout_BufferFree( p_buffer );
350         p_buffer = p_new_buffer;
351
352         aout_FiltersPlay( p_aout, pp_filters, i_nb_filters,
353                           &p_buffer );
354
355         aout_FiltersDestroyPipeline( p_aout, pp_filters,
356                                      i_nb_filters );
357     }
358     else
359     {
360         /* No resampling needed (except maybe the one imposed by the
361          * output). */
362         duration = p_buffer->end_date - p_buffer->start_date;
363         aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
364                           &p_buffer );
365     }
366
367     vlc_mutex_unlock( &p_input->lock );
368
369     vlc_mutex_lock( &p_aout->input_fifos_lock );
370     /* Adding the start date will be managed by aout_FifoPush(). */
371     p_buffer->start_date = start_date;
372     p_buffer->end_date = start_date + duration;
373     aout_FifoPush( p_aout, &p_input->fifo, p_buffer );
374     vlc_mutex_unlock( &p_aout->input_fifos_lock );
375 }