]> git.sesse.net Git - vlc/blob - src/audio_output/dec.c
92e4fdf9a051a701ca474bc9b4a1a44c39a89bb4
[vlc] / src / audio_output / dec.c
1 /*****************************************************************************
2  * dec.c : audio output API towards decoders
3  *****************************************************************************
4  * Copyright (C) 2002-2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_aout.h>
35 #include <vlc_input.h>
36 #include <vlc_atomic.h>
37
38 #include "aout_internal.h"
39 #include "libvlc.h"
40
41 /**
42  * Creates an audio output
43  */
44 int aout_DecNew( audio_output_t *p_aout,
45                  const audio_sample_format_t *p_format,
46                  const audio_replay_gain_t *p_replay_gain,
47                  const aout_request_vout_t *p_request_vout )
48 {
49     /* Sanitize audio format */
50     if( p_format->i_channels != aout_FormatNbChannels( p_format ) )
51     {
52         msg_Err( p_aout, "incompatible audio channels count with layout mask" );
53         return -1;
54     }
55
56     if( p_format->i_rate > 192000 )
57     {
58         msg_Err( p_aout, "excessive audio sample frequency (%u)",
59                  p_format->i_rate );
60         return -1;
61     }
62     if( p_format->i_rate < 4000 )
63     {
64         msg_Err( p_aout, "too low audio sample frequency (%u)",
65                  p_format->i_rate );
66         return -1;
67     }
68
69     aout_owner_t *owner = aout_owner(p_aout);
70     int ret = 0;
71
72     /* TODO: reduce lock scope depending on decoder's real need */
73     aout_lock( p_aout );
74
75     /* Create the audio output stream */
76     var_Destroy( p_aout, "audio-device" );
77     var_Destroy( p_aout, "stereo-mode" );
78
79     owner->input_format = *p_format;
80     vlc_atomic_set (&owner->restart, 0);
81     owner->volume = aout_volume_New (p_aout, p_replay_gain);
82     if( aout_OutputNew( p_aout, p_format ) < 0 )
83         goto error;
84     aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
85
86     /* Create the audio filtering "input" pipeline */
87     date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
88     date_Set (&owner->sync.date, VLC_TS_INVALID);
89
90     assert (owner->input == NULL);
91     owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
92                                   p_request_vout);
93     if (owner->input == NULL)
94     {
95         aout_OutputDelete (p_aout);
96 error:
97         aout_volume_Delete (owner->volume);
98         ret = -1;
99     }
100     aout_unlock( p_aout );
101     return ret;
102 }
103
104 /**
105  * Stops all plugins involved in the audio output.
106  */
107 void aout_DecDelete (audio_output_t *p_aout)
108 {
109     aout_owner_t *owner = aout_owner (p_aout);
110     aout_input_t *input;
111
112     aout_lock( p_aout );
113     /* Remove the input. */
114     input = owner->input;
115     if (likely(input != NULL))
116         aout_InputDelete (p_aout, input);
117     owner->input = NULL;
118
119     aout_OutputDelete( p_aout );
120     aout_volume_Delete (owner->volume);
121
122     var_Destroy( p_aout, "audio-device" );
123     var_Destroy( p_aout, "stereo-mode" );
124
125     aout_unlock( p_aout );
126     free (input);
127 }
128
129 #define AOUT_RESTART_OUTPUT 1
130 #define AOUT_RESTART_INPUT  2
131 static void aout_CheckRestart (audio_output_t *aout)
132 {
133     aout_owner_t *owner = aout_owner (aout);
134
135     aout_assert_locked (aout);
136
137     int restart = vlc_atomic_swap (&owner->restart, 0);
138     if (likely(restart == 0))
139         return;
140
141     assert (restart & AOUT_RESTART_INPUT);
142
143     const aout_request_vout_t request_vout = owner->input->request_vout;
144
145     if (likely(owner->input != NULL))
146         aout_InputDelete (aout, owner->input);
147     owner->input = NULL;
148
149     /* Reinitializes the output */
150     if (restart & AOUT_RESTART_OUTPUT)
151     {
152         aout_OutputDelete (aout);
153         if (aout_OutputNew (aout, &owner->input_format))
154         {
155             aout_volume_Delete (owner->volume);
156             return; /* we are officially screwed */
157         }
158         aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
159     }
160
161     owner->input = aout_InputNew (aout, &owner->input_format,
162                                   &owner->mixer_format, &request_vout);
163 }
164
165 /**
166  * Marks the audio output for restart, to update any parameter of the output
167  * plug-in (e.g. output device or channel mapping).
168  */
169 void aout_RequestRestart (audio_output_t *aout)
170 {
171     aout_owner_t *owner = aout_owner (aout);
172
173     /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
174     vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
175 }
176
177 /**
178  * This function will safely mark aout input to be restarted as soon as
179  * possible to take configuration changes into account
180  */
181 void aout_InputRequestRestart (audio_output_t *aout)
182 {
183     aout_owner_t *owner = aout_owner (aout);
184
185     vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
186 }
187
188
189 /*
190  * Buffer management
191  */
192
193 /*****************************************************************************
194  * aout_DecNewBuffer : ask for a new empty buffer
195  *****************************************************************************/
196 block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
197 {
198     /* NOTE: the caller is responsible for serializing input change */
199     aout_owner_t *owner = aout_owner (aout);
200
201     size_t length = samples * owner->input_format.i_bytes_per_frame
202                             / owner->input_format.i_frame_length;
203     block_t *block = block_Alloc( length );
204     if( likely(block != NULL) )
205     {
206         block->i_nb_samples = samples;
207         block->i_pts = block->i_length = 0;
208     }
209     return block;
210 }
211
212 /*****************************************************************************
213  * aout_DecDeleteBuffer : destroy an undecoded buffer
214  *****************************************************************************/
215 void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
216 {
217     (void) aout;
218     block_Release (block);
219 }
220
221 /*****************************************************************************
222  * aout_DecPlay : filter & mix the decoded buffer
223  *****************************************************************************/
224 int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
225 {
226     aout_owner_t *owner = aout_owner (p_aout);
227     aout_input_t *input;
228
229     assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
230             i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
231     assert( p_buffer->i_pts > 0 );
232
233     p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
234                                 / owner->input_format.i_rate;
235
236     aout_lock( p_aout );
237     aout_CheckRestart( p_aout );
238
239     input = owner->input;
240     if (unlikely(input == NULL)) /* can happen due to restart */
241     {
242         aout_unlock( p_aout );
243         block_Release( p_buffer );
244         return -1;
245     }
246
247     /* Input */
248     p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
249                                &owner->sync.date);
250     if( p_buffer != NULL )
251     {
252         date_Increment (&owner->sync.date, p_buffer->i_nb_samples);
253
254         /* Mixer */
255         aout_volume_Amplify (owner->volume, p_buffer);
256
257         /* Output */
258         aout_OutputPlay( p_aout, p_buffer );
259     }
260
261     aout_unlock( p_aout );
262     return 0;
263 }
264
265 int aout_DecGetResetLost (audio_output_t *aout)
266 {
267     aout_owner_t *owner = aout_owner (aout);
268     aout_input_t *input = owner->input;
269     int val;
270
271     aout_lock (aout);
272     if (likely(input != NULL))
273     {
274         val = input->i_buffer_lost;
275         input->i_buffer_lost = 0;
276     }
277     else
278         val = 0; /* if aout_CheckRestart() failed */
279     aout_unlock (aout);
280
281     return val;
282 }
283
284 void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
285 {
286     aout_owner_t *owner = aout_owner (aout);
287
288     aout_lock (aout);
289     /* XXX: Should the date be offset by the pause duration instead? */
290     date_Set (&owner->sync.date, VLC_TS_INVALID);
291     aout_OutputPause (aout, paused, date);
292     aout_unlock (aout);
293 }
294
295 void aout_DecFlush (audio_output_t *aout)
296 {
297     aout_owner_t *owner = aout_owner (aout);
298
299     aout_lock (aout);
300     date_Set (&owner->sync.date, VLC_TS_INVALID);
301     aout_OutputFlush (aout, false);
302     aout_unlock (aout);
303 }
304
305 bool aout_DecIsEmpty (audio_output_t *aout)
306 {
307     aout_owner_t *owner = aout_owner (aout);
308     mtime_t end_date, now = mdate ();
309     bool empty;
310
311     aout_lock (aout);
312     end_date = date_Get (&owner->sync.date);
313     empty = end_date == VLC_TS_INVALID || end_date <= now;
314     if (empty)
315         /* The last PTS has elapsed already. So the underlying audio output
316          * buffer should be empty or almost. Thus draining should be fast
317          * and will not block the caller too long. */
318         aout_OutputFlush (aout, true);
319     aout_unlock (aout);
320     return empty;
321 }