1 /*****************************************************************************
2 * dec.c : audio output API towards decoders
3 *****************************************************************************
4 * Copyright (C) 2002-2007 VLC authors and VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <vlc_input.h>
36 #include <vlc_atomic.h>
38 #include "aout_internal.h"
42 * Creates an audio output
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 )
49 /* Sanitize audio format */
50 if( p_format->i_channels != aout_FormatNbChannels( p_format ) )
52 msg_Err( p_aout, "incompatible audio channels count with layout mask" );
56 if( p_format->i_rate > 192000 )
58 msg_Err( p_aout, "excessive audio sample frequency (%u)",
62 if( p_format->i_rate < 4000 )
64 msg_Err( p_aout, "too low audio sample frequency (%u)",
69 aout_owner_t *owner = aout_owner(p_aout);
71 /* Calling decoder is responsible for serializing aout_DecNew() and
72 * aout_DecDelete(). So no need to lock to _read_ those properties. */
73 if (owner->module != NULL) /* <- output exists */
74 { /* Check if we can recycle the existing output and pipelines */
75 if (AOUT_FMTS_IDENTICAL(&owner->input_format, p_format))
78 /* TODO? If the new input format is closer to the output format than
79 * the old input format was, then the output could be recycled. The
80 * input pipeline however would need to be restarted. */
82 /* No recycling: delete everything and restart from scratch */
83 aout_Shutdown (p_aout);
88 /* TODO: reduce lock scope depending on decoder's real need */
90 assert (owner->module == NULL);
92 /* Create the audio output stream */
93 var_Destroy( p_aout, "audio-device" );
94 var_Destroy( p_aout, "audio-channels" );
96 owner->input_format = *p_format;
97 vlc_atomic_set (&owner->restart, 0);
98 owner->volume = aout_volume_New (p_aout, p_replay_gain);
99 if( aout_OutputNew( p_aout, p_format ) < 0 )
101 aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
103 /* Create the audio filtering "input" pipeline */
104 date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
105 date_Set (&owner->sync.date, VLC_TS_INVALID);
107 assert (owner->input == NULL);
108 owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
110 if (owner->input == NULL)
112 aout_OutputDelete (p_aout);
114 aout_volume_Delete (owner->volume);
117 aout_unlock( p_aout );
122 * Stops all plugins involved in the audio output.
124 void aout_Shutdown (audio_output_t *p_aout)
126 aout_owner_t *owner = aout_owner (p_aout);
130 /* Remove the input. */
131 input = owner->input;
132 if (likely(input != NULL))
133 aout_InputDelete (p_aout, input);
136 if (likely(owner->module != NULL))
138 aout_OutputDelete( p_aout );
139 aout_volume_Delete (owner->volume);
141 var_Destroy( p_aout, "audio-device" );
142 var_Destroy( p_aout, "audio-channels" );
144 aout_unlock( p_aout );
149 * Stops the decoded audio input.
150 * @note Due to output recycling, this function is esssentially a stub.
152 void aout_DecDelete (audio_output_t *aout)
157 aout_Shutdown (aout);
161 #define AOUT_RESTART_OUTPUT 1
162 #define AOUT_RESTART_INPUT 2
163 static void aout_CheckRestart (audio_output_t *aout)
165 aout_owner_t *owner = aout_owner (aout);
167 aout_assert_locked (aout);
169 int restart = vlc_atomic_swap (&owner->restart, 0);
170 if (likely(restart == 0))
173 assert (restart & AOUT_RESTART_INPUT);
175 const aout_request_vout_t request_vout = owner->input->request_vout;
177 if (likely(owner->input != NULL))
178 aout_InputDelete (aout, owner->input);
181 /* Reinitializes the output */
182 if (restart & AOUT_RESTART_OUTPUT)
184 aout_OutputDelete (aout);
185 if (aout_OutputNew (aout, &owner->input_format))
187 aout_volume_Delete (owner->volume);
188 return; /* we are officially screwed */
190 aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
193 owner->input = aout_InputNew (aout, &owner->input_format,
194 &owner->mixer_format, &request_vout);
198 * Marks the audio output for restart, to update any parameter of the output
199 * plug-in (e.g. output device or channel mapping).
201 void aout_RequestRestart (audio_output_t *aout)
203 aout_owner_t *owner = aout_owner (aout);
205 /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
206 vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
210 * This function will safely mark aout input to be restarted as soon as
211 * possible to take configuration changes into account
213 void aout_InputRequestRestart (audio_output_t *aout)
215 aout_owner_t *owner = aout_owner (aout);
217 vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
225 /*****************************************************************************
226 * aout_DecNewBuffer : ask for a new empty buffer
227 *****************************************************************************/
228 block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
230 /* NOTE: the caller is responsible for serializing input change */
231 aout_owner_t *owner = aout_owner (aout);
233 size_t length = samples * owner->input_format.i_bytes_per_frame
234 / owner->input_format.i_frame_length;
235 block_t *block = block_Alloc( length );
236 if( likely(block != NULL) )
238 block->i_nb_samples = samples;
239 block->i_pts = block->i_length = 0;
244 /*****************************************************************************
245 * aout_DecDeleteBuffer : destroy an undecoded buffer
246 *****************************************************************************/
247 void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
250 block_Release (block);
253 /*****************************************************************************
254 * aout_DecPlay : filter & mix the decoded buffer
255 *****************************************************************************/
256 int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
258 aout_owner_t *owner = aout_owner (p_aout);
261 assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
262 i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
263 assert( p_buffer->i_pts > 0 );
265 p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
266 / owner->input_format.i_rate;
269 aout_CheckRestart( p_aout );
271 input = owner->input;
272 if (unlikely(input == NULL)) /* can happen due to restart */
274 aout_unlock( p_aout );
275 block_Release( p_buffer );
280 p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
282 if( p_buffer != NULL )
284 date_Increment (&owner->sync.date, p_buffer->i_nb_samples);
287 aout_volume_Amplify (owner->volume, p_buffer);
290 aout_OutputPlay( p_aout, p_buffer );
293 aout_unlock( p_aout );
297 int aout_DecGetResetLost (audio_output_t *aout)
299 aout_owner_t *owner = aout_owner (aout);
300 aout_input_t *input = owner->input;
304 if (likely(input != NULL))
306 val = input->i_buffer_lost;
307 input->i_buffer_lost = 0;
310 val = 0; /* if aout_CheckRestart() failed */
316 void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
318 aout_owner_t *owner = aout_owner (aout);
321 /* XXX: Should the date be offset by the pause duration instead? */
322 date_Set (&owner->sync.date, VLC_TS_INVALID);
323 aout_OutputPause (aout, paused, date);
327 void aout_DecFlush (audio_output_t *aout)
329 aout_owner_t *owner = aout_owner (aout);
332 date_Set (&owner->sync.date, VLC_TS_INVALID);
333 aout_OutputFlush (aout, false);
337 bool aout_DecIsEmpty (audio_output_t *aout)
339 aout_owner_t *owner = aout_owner (aout);
340 mtime_t end_date, now = mdate ();
344 end_date = date_Get (&owner->sync.date);
345 empty = end_date == VLC_TS_INVALID || end_date <= now;
347 /* The last PTS has elapsed already. So the underlying audio output
348 * buffer should be empty or almost. Thus draining should be fast
349 * and will not block the caller too long. */
350 aout_OutputFlush (aout, true);