]> git.sesse.net Git - vlc/blob - src/audio_output/dec.c
aout: fix assertion when S/PDIF is used
[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     var_Destroy( p_aout, "stereo-mode" );
76
77     /* Create the audio output stream */
78     owner->input_format = *p_format;
79     vlc_atomic_set (&owner->restart, 0);
80     owner->volume = aout_volume_New (p_aout, p_replay_gain);
81     if( aout_OutputNew( p_aout, p_format ) < 0 )
82         goto error;
83     aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
84
85     /* Create the audio filtering "input" pipeline */
86     if (aout_FiltersNew (p_aout, p_format, &owner->mixer_format,
87                          p_request_vout))
88     {
89         aout_OutputDelete (p_aout);
90 error:
91         aout_volume_Delete (owner->volume);
92         ret = -1;
93         goto error;
94     }
95
96     date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
97     date_Set (&owner->sync.date, VLC_TS_INVALID);
98
99     assert (owner->input == NULL);
100     owner->input = aout_InputNew (p_format);
101     aout_unlock( p_aout );
102     return ret;
103 }
104
105 /**
106  * Stops all plugins involved in the audio output.
107  */
108 void aout_DecDelete (audio_output_t *p_aout)
109 {
110     aout_owner_t *owner = aout_owner (p_aout);
111     aout_input_t *input;
112
113     aout_lock( p_aout );
114     /* Remove the input. */
115     input = owner->input;
116     aout_InputDelete (input);
117     owner->input = NULL;
118
119     aout_FiltersDelete (p_aout);
120     aout_OutputDelete( p_aout );
121     aout_volume_Delete (owner->volume);
122
123     var_Destroy( p_aout, "stereo-mode" );
124
125     aout_unlock( p_aout );
126 }
127
128 #define AOUT_RESTART_OUTPUT 1
129 #define AOUT_RESTART_INPUT  2
130 static void aout_CheckRestart (audio_output_t *aout)
131 {
132     aout_owner_t *owner = aout_owner (aout);
133
134     aout_assert_locked (aout);
135
136     int restart = vlc_atomic_swap (&owner->restart, 0);
137     if (likely(restart == 0))
138         return;
139
140     assert (restart & AOUT_RESTART_INPUT);
141
142     const aout_request_vout_t request_vout = owner->request_vout;
143
144     aout_InputDelete (owner->input);
145     owner->input = NULL;
146
147     aout_FiltersDelete (aout);
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     if (aout_FiltersNew (aout, &owner->input_format, &owner->mixer_format,
162                          &request_vout) == 0)
163         owner->input = aout_InputNew (&owner->input_format);
164 }
165
166 /**
167  * Marks the audio output for restart, to update any parameter of the output
168  * plug-in (e.g. output device or channel mapping).
169  */
170 static void aout_RequestRestart (audio_output_t *aout)
171 {
172     aout_owner_t *owner = aout_owner (aout);
173
174     /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
175     vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
176 }
177
178 int aout_ChannelsRestart (vlc_object_t *obj, const char *varname,
179                           vlc_value_t oldval, vlc_value_t newval, void *data)
180 {
181     audio_output_t *aout = (audio_output_t *)obj;
182     (void)oldval; (void)newval; (void)data;
183
184     if (!strcmp (varname, "audio-device"))
185     {
186         /* This is supposed to be a significant change and supposes
187          * rebuilding the channel choices. */
188         var_Destroy (aout, "stereo-mode");
189     }
190     aout_RequestRestart (aout);
191     return 0;
192 }
193
194 /**
195  * This function will safely mark aout input to be restarted as soon as
196  * possible to take configuration changes into account
197  */
198 void aout_InputRequestRestart (audio_output_t *aout)
199 {
200     aout_owner_t *owner = aout_owner (aout);
201
202     vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
203 }
204
205
206 /*
207  * Buffer management
208  */
209
210 /*****************************************************************************
211  * aout_DecNewBuffer : ask for a new empty buffer
212  *****************************************************************************/
213 block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
214 {
215     /* NOTE: the caller is responsible for serializing input change */
216     aout_owner_t *owner = aout_owner (aout);
217
218     size_t length = samples * owner->input_format.i_bytes_per_frame
219                             / owner->input_format.i_frame_length;
220     block_t *block = block_Alloc( length );
221     if( likely(block != NULL) )
222     {
223         block->i_nb_samples = samples;
224         block->i_pts = block->i_length = 0;
225     }
226     return block;
227 }
228
229 /*****************************************************************************
230  * aout_DecDeleteBuffer : destroy an undecoded buffer
231  *****************************************************************************/
232 void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
233 {
234     (void) aout;
235     block_Release (block);
236 }
237
238 /*****************************************************************************
239  * aout_DecPlay : filter & mix the decoded buffer
240  *****************************************************************************/
241 int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
242 {
243     aout_owner_t *owner = aout_owner (p_aout);
244     aout_input_t *input;
245
246     assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
247             i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
248     assert( p_buffer->i_pts > 0 );
249
250     p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
251                                 / owner->input_format.i_rate;
252
253     aout_lock( p_aout );
254     aout_CheckRestart( p_aout );
255
256     input = owner->input;
257     if (unlikely(input == NULL)) /* can happen due to restart */
258     {
259         aout_unlock( p_aout );
260         block_Release( p_buffer );
261         return -1;
262     }
263
264     /* Input */
265     p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
266                                &owner->sync.date);
267     if( p_buffer != NULL )
268     {
269         date_Increment (&owner->sync.date, p_buffer->i_nb_samples);
270
271         /* Mixer */
272         aout_volume_Amplify (owner->volume, p_buffer);
273
274         /* Output */
275         aout_OutputPlay( p_aout, p_buffer );
276     }
277
278     aout_unlock( p_aout );
279     return 0;
280 }
281
282 int aout_DecGetResetLost (audio_output_t *aout)
283 {
284     aout_owner_t *owner = aout_owner (aout);
285     aout_input_t *input = owner->input;
286     int val;
287
288     aout_lock (aout);
289     if (likely(input != NULL))
290     {
291         val = input->i_buffer_lost;
292         input->i_buffer_lost = 0;
293     }
294     else
295         val = 0; /* if aout_CheckRestart() failed */
296     aout_unlock (aout);
297
298     return val;
299 }
300
301 void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
302 {
303     aout_owner_t *owner = aout_owner (aout);
304
305     aout_lock (aout);
306     /* XXX: Should the date be offset by the pause duration instead? */
307     date_Set (&owner->sync.date, VLC_TS_INVALID);
308     aout_OutputPause (aout, paused, date);
309     aout_unlock (aout);
310 }
311
312 void aout_DecFlush (audio_output_t *aout)
313 {
314     aout_owner_t *owner = aout_owner (aout);
315
316     aout_lock (aout);
317     date_Set (&owner->sync.date, VLC_TS_INVALID);
318     aout_OutputFlush (aout, false);
319     aout_unlock (aout);
320 }
321
322 bool aout_DecIsEmpty (audio_output_t *aout)
323 {
324     aout_owner_t *owner = aout_owner (aout);
325     mtime_t end_date, now = mdate ();
326     bool empty;
327
328     aout_lock (aout);
329     end_date = date_Get (&owner->sync.date);
330     empty = end_date == VLC_TS_INVALID || end_date <= now;
331     if (empty)
332         /* The last PTS has elapsed already. So the underlying audio output
333          * buffer should be empty or almost. Thus draining should be fast
334          * and will not block the caller too long. */
335         aout_OutputFlush (aout, true);
336     aout_unlock (aout);
337     return empty;
338 }