]> git.sesse.net Git - vlc/blob - src/audio_output/input.c
ac4ff722a6a68f650658aa3b66dd77e851b2e3f4
[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.16 2002/10/09 22:54:22 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 int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
43 {
44     audio_sample_format_t intermediate_format;
45
46     /* Prepare FIFO. */
47     aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
48     p_input->p_first_byte_to_mix = NULL;
49
50     /* Create filters. */
51     memcpy( &intermediate_format, &p_aout->mixer.mixer,
52             sizeof(audio_sample_format_t) );
53     intermediate_format.i_rate = p_input->input.i_rate;
54     if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
55                                      &p_input->i_nb_filters, &p_input->input,
56                                      &intermediate_format ) < 0 )
57     {
58         msg_Err( p_aout, "couldn't set an input pipeline" );
59
60         aout_FifoDestroy( p_aout, &p_input->fifo );
61         p_input->b_error = 1;
62
63         return -1;
64     }
65
66     /* Prepare hints for the buffer allocator. */
67     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
68     p_input->input_alloc.i_bytes_per_sec = -1;
69
70     if ( AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) )
71     {
72         p_input->i_nb_resamplers = 0;
73     }
74     else
75     {
76         /* Create resamplers. */
77         intermediate_format.i_rate = (p_input->input.i_rate
78                                  * (100 + AOUT_MAX_RESAMPLING)) / 100;
79         if ( intermediate_format.i_rate == p_aout->mixer.mixer.i_rate )
80         {
81             /* Just in case... */
82             intermediate_format.i_rate++;
83         }
84         if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers,
85                                          &p_input->i_nb_resamplers,
86                                          &intermediate_format,
87                                          &p_aout->mixer.mixer ) < 0 )
88         {
89             msg_Err( p_aout, "couldn't set a resampler pipeline" );
90
91             aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
92                                          p_input->i_nb_filters );
93             aout_FifoDestroy( p_aout, &p_input->fifo );
94             p_input->b_error = 1;
95
96             return -1;
97         }
98
99         aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers,
100                                  p_input->i_nb_resamplers,
101                                  &p_input->input_alloc );
102     }
103
104     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
105     p_input->input_alloc.i_bytes_per_sec = -1;
106     aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
107                              p_input->i_nb_filters,
108                              &p_input->input_alloc );
109
110     /* i_bytes_per_sec is still == -1 if no filters */
111     p_input->input_alloc.i_bytes_per_sec = __MAX(
112                                     p_input->input_alloc.i_bytes_per_sec,
113                                     p_input->input.i_bytes_per_frame
114                                      * p_input->input.i_rate
115                                      / p_input->input.i_frame_length );
116     /* Allocate in the heap, it is more convenient for the decoder. */
117     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
118
119     p_input->b_error = 0;
120
121     return 0;
122 }
123
124 /*****************************************************************************
125  * aout_InputDelete : delete an input
126  *****************************************************************************
127  * This function must be entered with the mixer lock.
128  *****************************************************************************/
129 int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
130 {
131     if ( p_input->b_error ) return 0;
132
133     aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
134                                  p_input->i_nb_filters );
135     aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
136                                  p_input->i_nb_resamplers );
137     aout_FifoDestroy( p_aout, &p_input->fifo );
138
139     return 0;
140 }
141
142 /*****************************************************************************
143  * aout_InputPlay : play a buffer
144  *****************************************************************************
145  * This function must be entered with the input lock.
146  *****************************************************************************/
147 int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
148                     aout_buffer_t * p_buffer )
149 {
150     mtime_t start_date, duration;
151
152     /* We don't care if someone changes the start date behind our back after
153      * this. We'll deal with that when pushing the buffer, and compensate
154      * with the next incoming buffer. */
155     vlc_mutex_lock( &p_aout->input_fifos_lock );
156     start_date = aout_FifoNextStart( p_aout, &p_input->fifo );
157     vlc_mutex_unlock( &p_aout->input_fifos_lock );
158
159     if ( start_date != 0 && start_date < mdate() )
160     {
161         /* The decoder is _very_ late. This can only happen if the user
162          * pauses the stream (or if the decoder is buggy, which cannot
163          * happen :). */
164         msg_Warn( p_aout, "computed PTS is out of range (%lld), clearing out",
165                   start_date );
166         vlc_mutex_lock( &p_aout->input_fifos_lock );
167         aout_FifoSet( p_aout, &p_input->fifo, 0 );
168         vlc_mutex_unlock( &p_aout->input_fifos_lock );
169         start_date = 0;
170     } 
171
172     if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME )
173     {
174         /* The decoder gives us f*cked up PTS. It's its business, but we
175          * can't present it anyway, so drop the buffer. */
176         msg_Warn( p_aout, "PTS is out of range (%lld), dropping buffer",
177                   mdate() - p_buffer->start_date );
178         aout_BufferFree( p_buffer );
179
180         return 0;
181     }
182
183     if ( start_date == 0 ) start_date = p_buffer->start_date;
184
185     /* Run pre-filters. */
186     aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
187                       &p_buffer );
188
189     if ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE
190           || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE )
191     {
192         /* Can happen in several circumstances :
193          * 1. A problem at the input (clock drift)
194          * 2. A small pause triggered by the user
195          * 3. Some delay in the output stage, causing a loss of lip
196          *    synchronization
197          * Solution : resample the buffer to avoid a scratch.
198          */
199         int i_ratio;
200         mtime_t old_duration;
201         mtime_t drift = p_buffer->start_date - start_date;
202
203         msg_Warn( p_aout, "buffer is %lld %s, resampling",
204                          drift > 0 ? drift : -drift,
205                          drift > 0 ? "in advance" : "late" );
206         old_duration = p_buffer->end_date - p_buffer->start_date;
207         duration = p_buffer->end_date - start_date;
208         i_ratio = (duration * 100) / old_duration;
209         /* If the ratio is too != 100, the sound quality will be awful. */
210         if ( i_ratio < 100 - AOUT_MAX_RESAMPLING /* % */ )
211         {
212             duration = (old_duration * (100 - AOUT_MAX_RESAMPLING)) / 100;
213         }
214         if ( i_ratio > 100 + AOUT_MAX_RESAMPLING /* % */ )
215         {
216             duration = (old_duration * (100 + AOUT_MAX_RESAMPLING)) / 100;
217         }
218         p_input->pp_resamplers[0]->input.i_rate 
219             = (p_input->input.i_rate * old_duration) / duration;
220
221         aout_FiltersPlay( p_aout, p_input->pp_resamplers,
222                           p_input->i_nb_resamplers,
223                           &p_buffer );
224     }
225     else
226     {
227         duration = p_buffer->end_date - p_buffer->start_date;
228
229         if ( p_input->input.i_rate != p_aout->mixer.mixer.i_rate )
230         {
231             /* Standard resampling is needed ! */
232             p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
233
234             aout_FiltersPlay( p_aout, p_input->pp_resamplers,
235                               p_input->i_nb_resamplers,
236                               &p_buffer );
237         }
238     }
239
240     /* Adding the start date will be managed by aout_FifoPush(). */
241     p_buffer->start_date = start_date;
242     p_buffer->end_date = start_date + duration;
243
244     vlc_mutex_lock( &p_aout->input_fifos_lock );
245     aout_FifoPush( p_aout, &p_input->fifo, p_buffer );
246     vlc_mutex_unlock( &p_aout->input_fifos_lock );
247
248     return 0;
249 }