X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Faudio_output%2Fdec.c;h=124eb31a390fed87292130ec93bda978110a8b30;hb=1abc59b7228f817f1ebeffe113c31f18b60c417d;hp=f3bfe3e0caac7ae9ae193634cfee9d2ee9961e83;hpb=6efc8da001c90f46d2b91a09dc57dedd59a5415c;p=vlc diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c index f3bfe3e0ca..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,215 +31,172 @@ #include #include - -#ifdef HAVE_ALLOCA_H -# include -#endif - #include #include +#include #include "aout_internal.h" +#include "libvlc.h" -/***************************************************************************** - * aout_DecNew : create a decoder - *****************************************************************************/ -static aout_input_t * DecNew( aout_instance_t * p_aout, - audio_sample_format_t *p_format, - const audio_replay_gain_t *p_replay_gain, - const aout_request_vout_t *p_request_vout ) +/** + * Creates an audio output + */ +int aout_DecNew( audio_output_t *p_aout, + const audio_sample_format_t *p_format, + const audio_replay_gain_t *p_replay_gain, + const aout_request_vout_t *p_request_vout ) { - aout_input_t * p_input; - /* Sanitize audio format */ - if( p_format->i_channels > 32 ) - { - msg_Err( p_aout, "too many audio channels (%u)", - p_format->i_channels ); - return NULL; - } - if( p_format->i_channels <= 0 ) - { - msg_Err( p_aout, "no audio channels" ); - return NULL; - } if( p_format->i_channels != aout_FormatNbChannels( p_format ) ) { msg_Err( p_aout, "incompatible audio channels count with layout mask" ); - return NULL; + return -1; } if( p_format->i_rate > 192000 ) { msg_Err( p_aout, "excessive audio sample frequency (%u)", p_format->i_rate ); - return NULL; + return -1; } if( p_format->i_rate < 4000 ) { msg_Err( p_aout, "too low audio sample frequency (%u)", p_format->i_rate ); - return NULL; - } - - /* We can only be called by the decoder, so no need to lock - * p_input->lock. */ - aout_lock_mixer( p_aout ); - - if ( p_aout->i_nb_inputs >= AOUT_MAX_INPUTS ) - { - msg_Err( p_aout, "too many inputs already (%d)", p_aout->i_nb_inputs ); - goto error; + return -1; } - p_input = calloc( 1, sizeof(aout_input_t)); - if( !p_input ) - goto error; - - vlc_mutex_init( &p_input->lock ); - - p_input->b_changed = false; - p_input->b_error = true; - p_input->b_paused = false; - p_input->i_pause_date = 0; - - aout_FormatPrepare( p_format ); + aout_owner_t *owner = aout_owner(p_aout); + int ret = 0; - memcpy( &p_input->input, p_format, - sizeof(audio_sample_format_t) ); - if( p_replay_gain ) - p_input->replay_gain = *p_replay_gain; + /* TODO: reduce lock scope depending on decoder's real need */ + aout_lock( p_aout ); - aout_lock_input_fifos( p_aout ); - p_aout->pp_inputs[p_aout->i_nb_inputs] = p_input; - p_aout->i_nb_inputs++; + var_Destroy( p_aout, "stereo-mode" ); - if ( !p_aout->p_mixer ) - { - int i; - - var_Destroy( p_aout, "audio-device" ); - var_Destroy( p_aout, "audio-channels" ); - - /* Recreate the output using the new format. */ - if ( aout_OutputNew( p_aout, p_format ) < 0 ) - { - for ( i = 0; i < p_aout->i_nb_inputs - 1; i++ ) - { - aout_lock_input( p_aout, p_aout->pp_inputs[i] ); - aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); - aout_unlock_input( p_aout, p_aout->pp_inputs[i] ); - } - aout_unlock_input_fifos( p_aout ); - aout_unlock_mixer( p_aout ); - return p_input; - } + /* 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); - /* Create other input streams. */ - for ( i = 0; i < p_aout->i_nb_inputs - 1; i++ ) - { - aout_input_t *p_input = p_aout->pp_inputs[i]; + /* 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); - aout_lock_input( p_aout, p_input ); - aout_InputDelete( p_aout, p_input ); - aout_InputNew( p_aout, p_input, &p_input->request_vout ); - aout_unlock_input( p_aout, p_input ); - } - } - else + assert (owner->input == NULL); + owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format, + p_request_vout); + if (owner->input == NULL) { - aout_MixerDelete( p_aout ); + aout_OutputDelete (p_aout); +error: + aout_volume_Delete (owner->volume); + ret = -1; } + aout_unlock( p_aout ); + return ret; +} - if ( aout_MixerNew( p_aout ) == -1 ) - { - aout_OutputDelete( p_aout ); - aout_unlock_input_fifos( p_aout ); - goto error; - } +/** + * 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; - aout_InputNew( p_aout, p_input, p_request_vout ); - aout_unlock_input_fifos( p_aout ); + aout_lock( p_aout ); + /* Remove the input. */ + input = owner->input; + if (likely(input != NULL)) + aout_InputDelete (p_aout, input); + owner->input = NULL; - aout_unlock_mixer( p_aout ); + aout_OutputDelete( p_aout ); + aout_volume_Delete (owner->volume); - return p_input; + var_Destroy( p_aout, "stereo-mode" ); -error: - aout_unlock_mixer( p_aout ); - return NULL; + aout_unlock( p_aout ); + free (input); } -aout_input_t * __aout_DecNew( vlc_object_t * p_this, - aout_instance_t ** pp_aout, - audio_sample_format_t * p_format, - const audio_replay_gain_t *p_replay_gain, - const aout_request_vout_t *p_request_video ) +#define AOUT_RESTART_OUTPUT 1 +#define AOUT_RESTART_INPUT 2 +static void aout_CheckRestart (audio_output_t *aout) { - aout_instance_t *p_aout = *pp_aout; - if ( p_aout == NULL ) - { - msg_Dbg( p_this, "no aout present, spawning one" ); - p_aout = aout_New( p_this ); + aout_owner_t *owner = aout_owner (aout); - /* Everything failed, I'm a loser, I just wanna die */ - if( p_aout == NULL ) - return NULL; + aout_assert_locked (aout); - vlc_object_attach( p_aout, p_this ); - *pp_aout = p_aout; - } + int restart = vlc_atomic_swap (&owner->restart, 0); + if (likely(restart == 0)) + return; - return DecNew( p_aout, p_format, p_replay_gain, p_request_video ); -} + assert (restart & AOUT_RESTART_INPUT); -/***************************************************************************** - * aout_DecDelete : delete a decoder - *****************************************************************************/ -int aout_DecDelete( aout_instance_t * p_aout, aout_input_t * p_input ) -{ - int i_input; + const aout_request_vout_t request_vout = owner->request_vout; - /* This function can only be called by the decoder itself, so no need - * to lock p_input->lock. */ - aout_lock_mixer( p_aout ); + if (likely(owner->input != NULL)) + aout_InputDelete (aout, owner->input); + owner->input = NULL; - for ( i_input = 0; i_input < p_aout->i_nb_inputs; i_input++ ) + /* Reinitializes the output */ + if (restart & AOUT_RESTART_OUTPUT) { - if ( p_aout->pp_inputs[i_input] == p_input ) + aout_OutputDelete (aout); + if (aout_OutputNew (aout, &owner->input_format)) { - break; + aout_volume_Delete (owner->volume); + return; /* we are officially screwed */ } + aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); } - if ( i_input == p_aout->i_nb_inputs ) - { - msg_Err( p_aout, "cannot find an input to delete" ); - aout_unlock_mixer( p_aout ); - return -1; - } + owner->input = aout_InputNew (aout, &owner->input_format, + &owner->mixer_format, &request_vout); +} - /* Remove the input from the list. */ - memmove( &p_aout->pp_inputs[i_input], &p_aout->pp_inputs[i_input + 1], - (AOUT_MAX_INPUTS - i_input - 1) * sizeof(aout_input_t *) ); - p_aout->i_nb_inputs--; +/** + * 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); - aout_InputDelete( p_aout, p_input ); + /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */ + vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT); +} - vlc_mutex_destroy( &p_input->lock ); - free( p_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 ( !p_aout->i_nb_inputs ) + if (!strcmp (varname, "audio-device")) { - aout_OutputDelete( p_aout ); - aout_MixerDelete( p_aout ); - var_Destroy( p_aout, "audio-device" ); - var_Destroy( p_aout, "audio-channels" ); + /* This is supposed to be a significant change and supposes + * rebuilding the channel choices. */ + var_Destroy (aout, "stereo-mode"); } + aout_RequestRestart (aout); + return 0; +} - aout_unlock_mixer( p_aout ); +/** + * 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); - return 0; + vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT); } @@ -250,153 +207,129 @@ int aout_DecDelete( aout_instance_t * p_aout, aout_input_t * p_input ) /***************************************************************************** * aout_DecNewBuffer : ask for a new empty buffer *****************************************************************************/ -aout_buffer_t * aout_DecNewBuffer( aout_input_t * p_input, - size_t i_nb_samples ) +block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples) { - aout_buffer_t * p_buffer; - mtime_t duration; - - aout_lock_input( NULL, p_input ); + /* NOTE: the caller is responsible for serializing input change */ + aout_owner_t *owner = aout_owner (aout); - if ( p_input->b_error ) + 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) ) { - aout_unlock_input( NULL, p_input ); - return NULL; + block->i_nb_samples = samples; + block->i_pts = block->i_length = 0; } - - duration = (1000000 * (mtime_t)i_nb_samples) / p_input->input.i_rate; - - /* This necessarily allocates in the heap. */ - aout_BufferAlloc( &p_input->input_alloc, duration, NULL, p_buffer ); - if( p_buffer != NULL ) - p_buffer->i_nb_bytes = i_nb_samples * p_input->input.i_bytes_per_frame - / p_input->input.i_frame_length; - - /* Suppose the decoder doesn't have more than one buffered buffer */ - p_input->b_changed = false; - - aout_unlock_input( NULL, p_input ); - - if( p_buffer == NULL ) - return NULL; - - p_buffer->i_nb_samples = i_nb_samples; - p_buffer->start_date = p_buffer->end_date = 0; - return p_buffer; + return block; } /***************************************************************************** * aout_DecDeleteBuffer : destroy an undecoded buffer *****************************************************************************/ -void aout_DecDeleteBuffer( aout_instance_t * p_aout, aout_input_t * p_input, - aout_buffer_t * p_buffer ) +void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block) { - (void)p_aout; (void)p_input; - aout_BufferFree( p_buffer ); + (void) aout; + block_Release (block); } /***************************************************************************** * aout_DecPlay : filter & mix the decoded buffer *****************************************************************************/ -int aout_DecPlay( aout_instance_t * p_aout, aout_input_t * p_input, - aout_buffer_t * p_buffer, int i_input_rate ) +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 *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 ); - assert( p_buffer->start_date > 0 ); + p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000 + / owner->input_format.i_rate; - p_buffer->end_date = p_buffer->start_date - + (mtime_t)p_buffer->i_nb_samples * 1000000 - / p_input->input.i_rate; + aout_lock( p_aout ); + aout_CheckRestart( p_aout ); - aout_lock_input( p_aout, p_input ); - - if( p_input->b_error ) + input = owner->input; + if (unlikely(input == NULL)) /* can happen due to restart */ { - aout_unlock_input( p_aout, p_input ); - aout_BufferFree( p_buffer ); + aout_unlock( p_aout ); + block_Release( p_buffer ); return -1; } - if( p_input->b_changed ) + /* Input */ + p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate, + &owner->sync.date); + if( p_buffer != NULL ) { - /* Maybe the allocation size has changed. Re-allocate a buffer. */ - aout_buffer_t * p_new_buffer; - mtime_t duration = (1000000 * (mtime_t)p_buffer->i_nb_samples) - / p_input->input.i_rate; - - aout_BufferAlloc( &p_input->input_alloc, duration, NULL, p_new_buffer ); - vlc_memcpy( p_new_buffer->p_buffer, p_buffer->p_buffer, - p_buffer->i_nb_bytes ); - p_new_buffer->i_nb_samples = p_buffer->i_nb_samples; - p_new_buffer->i_nb_bytes = p_buffer->i_nb_bytes; - p_new_buffer->start_date = p_buffer->start_date; - p_new_buffer->end_date = p_buffer->end_date; - aout_BufferFree( p_buffer ); - p_buffer = p_new_buffer; - p_input->b_changed = false; - } + date_Increment (&owner->sync.date, p_buffer->i_nb_samples); - int i_ret = aout_InputPlay( p_aout, p_input, p_buffer, i_input_rate ); - - aout_unlock_input( p_aout, p_input ); - - if( i_ret == -1 ) - return -1; + /* Mixer */ + aout_volume_Amplify (owner->volume, p_buffer); - /* Run the mixer if it is able to run. */ - aout_lock_mixer( p_aout ); - - aout_MixerRun( p_aout ); - - aout_unlock_mixer( p_aout ); + /* Output */ + aout_OutputPlay( p_aout, p_buffer ); + } + aout_unlock( p_aout ); return 0; } -int aout_DecGetResetLost( aout_instance_t *p_aout, aout_input_t *p_input ) +int aout_DecGetResetLost (audio_output_t *aout) { - aout_lock_input( p_aout, p_input ); - int i_value = p_input->i_buffer_lost; - p_input->i_buffer_lost = 0; - aout_unlock_input( p_aout, p_input ); - - return i_value; -} + aout_owner_t *owner = aout_owner (aout); + aout_input_t *input = owner->input; + int val; -void aout_DecChangePause( aout_instance_t *p_aout, aout_input_t *p_input, bool b_paused, mtime_t i_date ) -{ - mtime_t i_duration = 0; - aout_lock_input( p_aout, p_input ); - assert( !p_input->b_paused || !b_paused ); - if( p_input->b_paused ) + aout_lock (aout); + if (likely(input != NULL)) { - i_duration = i_date - p_input->i_pause_date; + val = input->i_buffer_lost; + input->i_buffer_lost = 0; } - p_input->b_paused = b_paused; - p_input->i_pause_date = i_date; - aout_unlock_input( p_aout, p_input ); + else + val = 0; /* if aout_CheckRestart() failed */ + aout_unlock (aout); - if( i_duration != 0 ) - { - aout_lock_mixer( p_aout ); - for( aout_buffer_t *p = p_input->mixer.fifo.p_first; p != NULL; p = p->p_next ) - { - p->start_date += i_duration; - p->end_date += i_duration; - } - aout_unlock_mixer( p_aout ); - } + return val; } -void aout_DecFlush( aout_instance_t *p_aout, aout_input_t *p_input ) +void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date) { - aout_lock_input_fifos( p_aout ); + aout_owner_t *owner = aout_owner (aout); - aout_FifoSet( p_aout, &p_input->mixer.fifo, 0 ); - p_input->mixer.begin = NULL; + aout_lock (aout); + /* 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); +} + +void aout_DecFlush (audio_output_t *aout) +{ + aout_owner_t *owner = aout_owner (aout); - aout_unlock_input_fifos( p_aout ); + aout_lock (aout); + date_Set (&owner->sync.date, VLC_TS_INVALID); + aout_OutputFlush (aout, false); + aout_unlock (aout); } +bool aout_DecIsEmpty (audio_output_t *aout) +{ + aout_owner_t *owner = aout_owner (aout); + mtime_t end_date, now = mdate (); + bool empty; + + aout_lock (aout); + 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 empty; +}