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);
72 /* TODO: reduce lock scope depending on decoder's real need */
75 /* Create the audio output stream */
76 var_Destroy( p_aout, "audio-device" );
77 var_Destroy( p_aout, "stereo-mode" );
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 )
84 aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
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);
90 assert (owner->input == NULL);
91 owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
93 if (owner->input == NULL)
95 aout_OutputDelete (p_aout);
97 aout_volume_Delete (owner->volume);
100 aout_unlock( p_aout );
105 * Stops all plugins involved in the audio output.
107 void aout_DecDelete (audio_output_t *p_aout)
109 aout_owner_t *owner = aout_owner (p_aout);
113 /* Remove the input. */
114 input = owner->input;
115 if (likely(input != NULL))
116 aout_InputDelete (p_aout, input);
119 aout_OutputDelete( p_aout );
120 aout_volume_Delete (owner->volume);
122 var_Destroy( p_aout, "audio-device" );
123 var_Destroy( p_aout, "stereo-mode" );
125 aout_unlock( p_aout );
129 #define AOUT_RESTART_OUTPUT 1
130 #define AOUT_RESTART_INPUT 2
131 static void aout_CheckRestart (audio_output_t *aout)
133 aout_owner_t *owner = aout_owner (aout);
135 aout_assert_locked (aout);
137 int restart = vlc_atomic_swap (&owner->restart, 0);
138 if (likely(restart == 0))
141 assert (restart & AOUT_RESTART_INPUT);
143 const aout_request_vout_t request_vout = owner->input->request_vout;
145 if (likely(owner->input != NULL))
146 aout_InputDelete (aout, owner->input);
149 /* Reinitializes the output */
150 if (restart & AOUT_RESTART_OUTPUT)
152 aout_OutputDelete (aout);
153 if (aout_OutputNew (aout, &owner->input_format))
155 aout_volume_Delete (owner->volume);
156 return; /* we are officially screwed */
158 aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
161 owner->input = aout_InputNew (aout, &owner->input_format,
162 &owner->mixer_format, &request_vout);
166 * Marks the audio output for restart, to update any parameter of the output
167 * plug-in (e.g. output device or channel mapping).
169 void aout_RequestRestart (audio_output_t *aout)
171 aout_owner_t *owner = aout_owner (aout);
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);
178 * This function will safely mark aout input to be restarted as soon as
179 * possible to take configuration changes into account
181 void aout_InputRequestRestart (audio_output_t *aout)
183 aout_owner_t *owner = aout_owner (aout);
185 vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
193 /*****************************************************************************
194 * aout_DecNewBuffer : ask for a new empty buffer
195 *****************************************************************************/
196 block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
198 /* NOTE: the caller is responsible for serializing input change */
199 aout_owner_t *owner = aout_owner (aout);
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) )
206 block->i_nb_samples = samples;
207 block->i_pts = block->i_length = 0;
212 /*****************************************************************************
213 * aout_DecDeleteBuffer : destroy an undecoded buffer
214 *****************************************************************************/
215 void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
218 block_Release (block);
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)
226 aout_owner_t *owner = aout_owner (p_aout);
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 );
233 p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
234 / owner->input_format.i_rate;
237 aout_CheckRestart( p_aout );
239 input = owner->input;
240 if (unlikely(input == NULL)) /* can happen due to restart */
242 aout_unlock( p_aout );
243 block_Release( p_buffer );
248 p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
250 if( p_buffer != NULL )
252 date_Increment (&owner->sync.date, p_buffer->i_nb_samples);
255 aout_volume_Amplify (owner->volume, p_buffer);
258 aout_OutputPlay( p_aout, p_buffer );
261 aout_unlock( p_aout );
265 int aout_DecGetResetLost (audio_output_t *aout)
267 aout_owner_t *owner = aout_owner (aout);
268 aout_input_t *input = owner->input;
272 if (likely(input != NULL))
274 val = input->i_buffer_lost;
275 input->i_buffer_lost = 0;
278 val = 0; /* if aout_CheckRestart() failed */
284 void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
286 aout_owner_t *owner = aout_owner (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);
295 void aout_DecFlush (audio_output_t *aout)
297 aout_owner_t *owner = aout_owner (aout);
300 date_Set (&owner->sync.date, VLC_TS_INVALID);
301 aout_OutputFlush (aout, false);
305 bool aout_DecIsEmpty (audio_output_t *aout)
307 aout_owner_t *owner = aout_owner (aout);
308 mtime_t end_date, now = mdate ();
312 end_date = date_Get (&owner->sync.date);
313 empty = end_date == VLC_TS_INVALID || end_date <= now;
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);