X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Faudio_output%2Fdec.c;h=124eb31a390fed87292130ec93bda978110a8b30;hb=1abc59b7228f817f1ebeffe113c31f18b60c417d;hp=5a83978669f4e31cd7eaf3c9255dae354cc37300;hpb=7d6f5894d5d6226b2d8ca43f21a7a320cc92bd2f;p=vlc diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c index 5a83978669..124eb31a39 100644 --- a/src/audio_output/dec.c +++ b/src/audio_output/dec.c @@ -1,24 +1,24 @@ /***************************************************************************** * dec.c : audio output API towards decoders ***************************************************************************** - * Copyright (C) 2002-2007 the VideoLAN team + * Copyright (C) 2002-2007 VLC authors and VideoLAN * $Id$ * * Authors: Christophe Massiot * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -31,9 +31,9 @@ #include #include - #include #include +#include #include "aout_internal.h" #include "libvlc.h" @@ -47,17 +47,6 @@ int aout_DecNew( audio_output_t *p_aout, const aout_request_vout_t *p_request_vout ) { /* Sanitize audio format */ - if( p_format->i_channels > 32 ) - { - msg_Err( p_aout, "too many audio channels (%u)", - p_format->i_channels ); - return -1; - } - if( p_format->i_channels <= 0 ) - { - msg_Err( p_aout, "no audio channels" ); - return -1; - } if( p_format->i_channels != aout_FormatNbChannels( p_format ) ) { msg_Err( p_aout, "incompatible audio channels count with layout mask" ); @@ -77,110 +66,137 @@ int aout_DecNew( audio_output_t *p_aout, return -1; } - aout_input_t *p_input = calloc( 1, sizeof(aout_input_t)); - if( !p_input ) - return -1; - - p_input->b_error = true; - - memcpy( &p_input->input, p_format, - sizeof(audio_sample_format_t) ); - if( p_replay_gain ) - p_input->replay_gain = *p_replay_gain; - - /* We can only be called by the decoder, so no need to lock - * p_input->lock. */ aout_owner_t *owner = aout_owner(p_aout); + int ret = 0; + + /* TODO: reduce lock scope depending on decoder's real need */ aout_lock( p_aout ); - assert (owner->input == NULL); - var_Destroy( p_aout, "audio-device" ); - var_Destroy( p_aout, "audio-channels" ); + var_Destroy( p_aout, "stereo-mode" ); - /* Recreate the output using the new format. */ + /* Create the audio output stream */ + owner->input_format = *p_format; + vlc_atomic_set (&owner->restart, 0); + owner->volume = aout_volume_New (p_aout, p_replay_gain); if( aout_OutputNew( p_aout, p_format ) < 0 ) goto error; + aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); - assert (owner->volume.mixer == NULL); - owner->volume.mixer = aout_MixerNew (p_aout, owner->mixer_format.i_format); - if (owner->volume.mixer == NULL) - { - aout_OutputDelete( p_aout ); - goto error; - } + /* Create the audio filtering "input" pipeline */ + date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1); + date_Set (&owner->sync.date, VLC_TS_INVALID); - owner->input = p_input; - aout_InputNew( p_aout, p_input, p_request_vout ); - aout_unlock( p_aout ); - return 0; + assert (owner->input == NULL); + owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format, + p_request_vout); + if (owner->input == NULL) + { + aout_OutputDelete (p_aout); error: + aout_volume_Delete (owner->volume); + ret = -1; + } aout_unlock( p_aout ); - free( p_input ); - return -1; + return ret; } -/***************************************************************************** - * aout_DecDelete : delete a decoder - *****************************************************************************/ -void aout_DecDelete( audio_output_t * p_aout ) +/** + * Stops all plugins involved in the audio output. + */ +void aout_DecDelete (audio_output_t *p_aout) { aout_owner_t *owner = aout_owner (p_aout); aout_input_t *input; - struct audio_mixer *mixer; aout_lock( p_aout ); /* Remove the input. */ input = owner->input; - aout_InputDelete (p_aout, input); + if (likely(input != NULL)) + aout_InputDelete (p_aout, input); owner->input = NULL; aout_OutputDelete( p_aout ); - mixer = owner->volume.mixer; - owner->volume.mixer = NULL; - var_Destroy( p_aout, "audio-device" ); - var_Destroy( p_aout, "audio-channels" ); + aout_volume_Delete (owner->volume); - aout_unlock( p_aout ); + var_Destroy( p_aout, "stereo-mode" ); - aout_MixerDelete (mixer); + aout_unlock( p_aout ); free (input); } +#define AOUT_RESTART_OUTPUT 1 +#define AOUT_RESTART_INPUT 2 static void aout_CheckRestart (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); - aout_input_t *input = owner->input; aout_assert_locked (aout); - if (likely(!owner->need_restart)) + int restart = vlc_atomic_swap (&owner->restart, 0); + if (likely(restart == 0)) return; - owner->need_restart = false; - /* Reinitializes the output */ - aout_InputDelete (aout, owner->input); - aout_MixerDelete (owner->volume.mixer); - owner->volume.mixer = NULL; - aout_OutputDelete (aout); + assert (restart & AOUT_RESTART_INPUT); + + const aout_request_vout_t request_vout = owner->request_vout; + + if (likely(owner->input != NULL)) + aout_InputDelete (aout, owner->input); + owner->input = NULL; - if (aout_OutputNew (aout, &input->input)) + /* Reinitializes the output */ + if (restart & AOUT_RESTART_OUTPUT) { -error: - input->b_error = true; - return; /* we are officially screwed */ + aout_OutputDelete (aout); + if (aout_OutputNew (aout, &owner->input_format)) + { + aout_volume_Delete (owner->volume); + return; /* we are officially screwed */ + } + aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); } - owner->volume.mixer = aout_MixerNew (aout, owner->mixer_format.i_format); - if (owner->volume.mixer == NULL) + owner->input = aout_InputNew (aout, &owner->input_format, + &owner->mixer_format, &request_vout); +} + +/** + * Marks the audio output for restart, to update any parameter of the output + * plug-in (e.g. output device or channel mapping). + */ +static void aout_RequestRestart (audio_output_t *aout) +{ + aout_owner_t *owner = aout_owner (aout); + + /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */ + vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT); +} + +int aout_ChannelsRestart (vlc_object_t *obj, const char *varname, + vlc_value_t oldval, vlc_value_t newval, void *data) +{ + audio_output_t *aout = (audio_output_t *)obj; + (void)oldval; (void)newval; (void)data; + + if (!strcmp (varname, "audio-device")) { - aout_OutputDelete (aout); - goto error; + /* This is supposed to be a significant change and supposes + * rebuilding the channel choices. */ + var_Destroy (aout, "stereo-mode"); } + aout_RequestRestart (aout); + return 0; +} - if (aout_InputNew (aout, input, &input->request_vout)) - assert (input->b_error); - else - assert (!input->b_error); +/** + * This function will safely mark aout input to be restarted as soon as + * possible to take configuration changes into account + */ +void aout_InputRequestRestart (audio_output_t *aout) +{ + aout_owner_t *owner = aout_owner (aout); + + vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT); } @@ -195,10 +211,9 @@ block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples) { /* NOTE: the caller is responsible for serializing input change */ aout_owner_t *owner = aout_owner (aout); - aout_input_t *input = owner->input; - size_t length = samples * input->input.i_bytes_per_frame - / input->input.i_frame_length; + size_t length = samples * owner->input_format.i_bytes_per_frame + / owner->input_format.i_frame_length; block_t *block = block_Alloc( length ); if( likely(block != NULL) ) { @@ -214,7 +229,7 @@ block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples) void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block) { (void) aout; - aout_BufferFree (block); + block_Release (block); } /***************************************************************************** @@ -223,34 +238,35 @@ void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block) int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate) { aout_owner_t *owner = aout_owner (p_aout); - aout_input_t *p_input = owner->input; + aout_input_t *input; assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE && i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE ); assert( p_buffer->i_pts > 0 ); p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000 - / p_input->input.i_rate; + / owner->input_format.i_rate; aout_lock( p_aout ); - if( p_input->b_error ) + aout_CheckRestart( p_aout ); + + input = owner->input; + if (unlikely(input == NULL)) /* can happen due to restart */ { aout_unlock( p_aout ); - aout_BufferFree( p_buffer ); + block_Release( p_buffer ); return -1; } - aout_CheckRestart( p_aout ); - aout_InputCheckAndRestart( p_aout, p_input ); - /* Input */ - p_buffer = aout_InputPlay( p_aout, p_input, p_buffer, i_input_rate ); - + p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate, + &owner->sync.date); if( p_buffer != NULL ) { + date_Increment (&owner->sync.date, p_buffer->i_nb_samples); + /* Mixer */ - float amp = owner->volume.multiplier * p_input->multiplier; - aout_MixerRun (owner->volume.mixer, p_buffer, amp); + aout_volume_Amplify (owner->volume, p_buffer); /* Output */ aout_OutputPlay( p_aout, p_buffer ); @@ -267,8 +283,13 @@ int aout_DecGetResetLost (audio_output_t *aout) int val; aout_lock (aout); - val = input->i_buffer_lost; - input->i_buffer_lost = 0; + if (likely(input != NULL)) + { + val = input->i_buffer_lost; + input->i_buffer_lost = 0; + } + else + val = 0; /* if aout_CheckRestart() failed */ aout_unlock (aout); return val; @@ -277,12 +298,10 @@ int aout_DecGetResetLost (audio_output_t *aout) void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date) { aout_owner_t *owner = aout_owner (aout); - aout_input_t *p_input = owner->input; aout_lock (aout); - - /* XXX: Should the input date be offset by the pause duration instead? */ - date_Set (&p_input->date, VLC_TS_INVALID); + /* XXX: Should the date be offset by the pause duration instead? */ + date_Set (&owner->sync.date, VLC_TS_INVALID); aout_OutputPause (aout, paused, date); aout_unlock (aout); } @@ -290,10 +309,9 @@ void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date) void aout_DecFlush (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); - aout_input_t *input = owner->input; aout_lock (aout); - date_Set (&input->date, VLC_TS_INVALID); + date_Set (&owner->sync.date, VLC_TS_INVALID); aout_OutputFlush (aout, false); aout_unlock (aout); } @@ -301,12 +319,17 @@ void aout_DecFlush (audio_output_t *aout) bool aout_DecIsEmpty (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); - aout_input_t *input = owner->input; - mtime_t end_date; + mtime_t end_date, now = mdate (); + bool empty; aout_lock (aout); - /* FIXME: tell output to drain */ - end_date = date_Get (&input->date); + end_date = date_Get (&owner->sync.date); + empty = end_date == VLC_TS_INVALID || end_date <= now; + if (empty) + /* The last PTS has elapsed already. So the underlying audio output + * buffer should be empty or almost. Thus draining should be fast + * and will not block the caller too long. */ + aout_OutputFlush (aout, true); aout_unlock (aout); - return end_date == VLC_TS_INVALID || end_date <= mdate(); + return empty; }