X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Faudio_output%2Fdec.c;h=b7d102b73a687c4284fa8c818e846a28420954f1;hb=f26c823b76c930c2b94aab9e396a0c7b9ba8e209;hp=412a11af4c579093d1342d45a1ed344d77a3e4c7;hpb=4f6b904c37e1f62109e46f8d16bb2d801435d4c1;p=vlc diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c index 412a11af4c..b7d102b73a 100644 --- a/src/audio_output/dec.c +++ b/src/audio_output/dec.c @@ -1,251 +1,220 @@ /***************************************************************************** * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* calloc(), malloc(), free() */ -#include - -#include - -#ifdef HAVE_ALLOCA_H -# include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif +#include + +#include #include #include +#include #include "aout_internal.h" +#include "libvlc.h" -/** FIXME: Ugly but needed to access the counters */ -#include "input/input_internal.h" - -/***************************************************************************** - * aout_DecNew : create a decoder - *****************************************************************************/ -static aout_input_t * DecNew( vlc_object_t * p_this, aout_instance_t * p_aout, - audio_sample_format_t * p_format ) +/** + * 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; - input_thread_t * p_input_thread; - vlc_value_t val; - /* Sanitize audio format */ - if( p_format->i_channels > 32 ) + if( p_format->i_channels != aout_FormatNbChannels( p_format ) ) { - msg_Err( p_aout, "too many audio channels (%u)", - p_format->i_channels ); - goto error; + msg_Err( p_aout, "incompatible audio channels count with layout mask" ); + return -1; } if( p_format->i_rate > 192000 ) { msg_Err( p_aout, "excessive audio sample frequency (%u)", p_format->i_rate ); - goto error; + return -1; } - - /* We can only be called by the decoder, so no need to lock - * p_input->lock. */ - vlc_mutex_lock( &p_aout->mixer_lock ); - - if ( p_aout->i_nb_inputs >= AOUT_MAX_INPUTS ) + if( p_format->i_rate < 4000 ) { - msg_Err( p_aout, "too many inputs already (%d)", p_aout->i_nb_inputs ); - goto error; + msg_Err( p_aout, "too low audio sample frequency (%u)", + p_format->i_rate ); + return -1; } - p_input = malloc(sizeof(aout_input_t)); - if ( p_input == NULL ) - { - msg_Err( p_aout, "out of memory" ); - goto error; + aout_owner_t *owner = aout_owner(p_aout); +#ifdef RECYCLE + /* Calling decoder is responsible for serializing aout_DecNew() and + * aout_DecDelete(). So no need to lock to _read_ those properties. */ + if (owner->module != NULL) /* <- output exists */ + { /* Check if we can recycle the existing output and pipelines */ + if (AOUT_FMTS_IDENTICAL(&owner->input_format, p_format)) + return 0; + + /* TODO? If the new input format is closer to the output format than + * the old input format was, then the output could be recycled. The + * input pipeline however would need to be restarted. */ + + /* No recycling: delete everything and restart from scratch */ + aout_Shutdown (p_aout); } +#endif + int ret = 0; - vlc_mutex_init( p_aout, &p_input->lock ); - - p_input->b_changed = 0; - p_input->b_error = 1; - aout_FormatPrepare( p_format ); - memcpy( &p_input->input, p_format, - sizeof(audio_sample_format_t) ); - - p_aout->pp_inputs[p_aout->i_nb_inputs] = p_input; - p_aout->i_nb_inputs++; + /* TODO: reduce lock scope depending on decoder's real need */ + aout_lock( p_aout ); + assert (owner->module == NULL); - if ( p_aout->mixer.b_error ) - { - int i; + /* Create the audio output stream */ + var_Destroy( p_aout, "audio-device" ); + var_Destroy( p_aout, "audio-channels" ); - 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++ ) - { - vlc_mutex_lock( &p_aout->pp_inputs[i]->lock ); - aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); - vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); - } - vlc_mutex_unlock( &p_aout->mixer_lock ); - return p_input; - } + 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++ ) - { - vlc_mutex_lock( &p_aout->pp_inputs[i]->lock ); - aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); - aout_InputNew( p_aout, p_aout->pp_inputs[i] ); - vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); - } - } - else - { - aout_MixerDelete( p_aout ); - } + /* 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); - if ( aout_MixerNew( p_aout ) == -1 ) + 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 ); - goto error; + aout_OutputDelete (p_aout); +error: + aout_volume_Delete (owner->volume); + ret = -1; } + aout_unlock( p_aout ); + return ret; +} - aout_InputNew( p_aout, p_input ); +/** + * Stops all plugins involved in the audio output. + */ +void aout_Shutdown (audio_output_t *p_aout) +{ + aout_owner_t *owner = aout_owner (p_aout); + aout_input_t *input; - vlc_mutex_unlock( &p_aout->mixer_lock ); - var_Create( p_this, "audio-desync", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_this, "audio-desync", &val ); - p_input->i_desync = val.i_int * 1000; + aout_lock( p_aout ); + /* Remove the input. */ + input = owner->input; + if (likely(input != NULL)) + aout_InputDelete (p_aout, input); + owner->input = NULL; - p_input_thread = (input_thread_t *)vlc_object_find( p_this, - VLC_OBJECT_INPUT, FIND_PARENT ); - if( p_input_thread ) - { - p_input->i_pts_delay = p_input_thread->i_pts_delay; - p_input->i_pts_delay += p_input->i_desync; - p_input->p_input_thread = p_input_thread; - vlc_object_release( p_input_thread ); - } - else + if (likely(owner->module != NULL)) { - p_input->i_pts_delay = DEFAULT_PTS_DELAY; - p_input->i_pts_delay += p_input->i_desync; - p_input->p_input_thread = NULL; + aout_OutputDelete( p_aout ); + aout_volume_Delete (owner->volume); } + var_Destroy( p_aout, "audio-device" ); + var_Destroy( p_aout, "audio-channels" ); - return p_input; - -error: - vlc_mutex_unlock( &p_aout->mixer_lock ); - 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 ) +/** + * Stops the decoded audio input. + * @note Due to output recycling, this function is esssentially a stub. + */ +void aout_DecDelete (audio_output_t *aout) { - if ( *pp_aout == NULL ) - { - /* Create an audio output if there is none. */ - *pp_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE ); - - if( *pp_aout == NULL ) - { - msg_Dbg( p_this, "no aout present, spawning one" ); - - *pp_aout = aout_New( p_this ); - /* Everything failed, I'm a loser, I just wanna die */ - if( *pp_aout == NULL ) - { - return NULL; - } - vlc_object_attach( *pp_aout, p_this->p_libvlc ); - } - else - { - vlc_object_release( *pp_aout ); - } - } - - return DecNew( p_this, *pp_aout, p_format ); +#ifdef RECYCLE + (void) aout; +#else + aout_Shutdown (aout); +#endif } -/***************************************************************************** - * aout_DecDelete : delete a decoder - *****************************************************************************/ -int aout_DecDelete( aout_instance_t * p_aout, aout_input_t * p_input ) +#define AOUT_RESTART_OUTPUT 1 +#define AOUT_RESTART_INPUT 2 +static void aout_CheckRestart (audio_output_t *aout) { - int i_input; + aout_owner_t *owner = aout_owner (aout); - /* This function can only be called by the decoder itself, so no need - * to lock p_input->lock. */ - vlc_mutex_lock( &p_aout->mixer_lock ); + aout_assert_locked (aout); - for ( i_input = 0; i_input < p_aout->i_nb_inputs; i_input++ ) - { - if ( p_aout->pp_inputs[i_input] == p_input ) - { - break; - } - } - - if ( i_input == p_aout->i_nb_inputs ) - { - msg_Err( p_aout, "cannot find an input to delete" ); - return -1; - } + int restart = vlc_atomic_swap (&owner->restart, 0); + if (likely(restart == 0)) + return; - /* 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--; + assert (restart & AOUT_RESTART_INPUT); - aout_InputDelete( p_aout, p_input ); + const aout_request_vout_t request_vout = owner->input->request_vout; - vlc_mutex_destroy( &p_input->lock ); - free( p_input ); + if (likely(owner->input != NULL)) + aout_InputDelete (aout, owner->input); + owner->input = NULL; - if ( !p_aout->i_nb_inputs ) + /* Reinitializes the output */ + if (restart & AOUT_RESTART_OUTPUT) { - aout_OutputDelete( p_aout ); - aout_MixerDelete( p_aout ); - if ( var_Type( p_aout, "audio-device" ) != 0 ) - { - var_Destroy( p_aout, "audio-device" ); - } - if ( var_Type( p_aout, "audio-channels" ) != 0 ) + aout_OutputDelete (aout); + if (aout_OutputNew (aout, &owner->input_format)) { - var_Destroy( p_aout, "audio-channels" ); + aout_volume_Delete (owner->volume); + return; /* we are officially screwed */ } + aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); } - vlc_mutex_unlock( &p_aout->mixer_lock ); + owner->input = aout_InputNew (aout, &owner->input_format, + &owner->mixer_format, &request_vout); +} - return 0; +/** + * Marks the audio output for restart, to update any parameter of the output + * plug-in (e.g. output device or channel mapping). + */ +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); +} + +/** + * 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); } @@ -256,145 +225,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_instance_t * p_aout, - 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; - - vlc_mutex_lock( &p_input->lock ); + /* 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) ) { - vlc_mutex_unlock( &p_input->lock ); - 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 ); - p_buffer->i_nb_samples = i_nb_samples; - 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 = 0; - - vlc_mutex_unlock( &p_input->lock ); - - if ( p_buffer == NULL ) - { - msg_Err( p_aout, "NULL buffer !" ); - } - else - { - 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 aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate) { - if ( p_buffer->start_date == 0 ) - { - msg_Warn( p_aout, "non-dated buffer received" ); - aout_BufferFree( p_buffer ); - return -1; - } + aout_owner_t *owner = aout_owner (p_aout); + aout_input_t *input; - /* Apply the desynchronisation requested by the user */ - p_buffer->start_date += p_input->i_desync; - p_buffer->end_date += p_input->i_desync; + 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 ); - if ( p_buffer->start_date > mdate() + p_input->i_pts_delay + - AOUT_MAX_ADVANCE_TIME ) - { - msg_Warn( p_aout, "received buffer in the future ("I64Fd")", - p_buffer->start_date - mdate()); - if( p_input->p_input_thread ) - { - vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock); - stats_UpdateInteger( p_aout, - p_input->p_input_thread->p->counters.p_lost_abuffers, - 1, NULL ); - vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock); - } - aout_BufferFree( p_buffer ); - return -1; - } + 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 ); - vlc_mutex_lock( &p_input->lock ); - - if ( p_input->b_error ) + input = owner->input; + if (unlikely(input == NULL)) /* can happen due to restart */ { - vlc_mutex_unlock( &p_input->lock ); - 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 ); - p_aout->p_libvlc->pf_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 = 0; - } + date_Increment (&owner->sync.date, p_buffer->i_nb_samples); - /* If the buffer is too early, wait a while. */ - mwait( p_buffer->start_date - AOUT_MAX_PREPARE_TIME ); + /* Mixer */ + aout_volume_Amplify (owner->volume, p_buffer); - if ( aout_InputPlay( p_aout, p_input, p_buffer ) == -1 ) - { - vlc_mutex_unlock( &p_input->lock ); - return -1; + /* Output */ + aout_OutputPlay( p_aout, p_buffer ); } - vlc_mutex_unlock( &p_input->lock ); + aout_unlock( p_aout ); + return 0; +} - /* Run the mixer if it is able to run. */ - vlc_mutex_lock( &p_aout->mixer_lock ); - aout_MixerRun( p_aout ); - if( p_input->p_input_thread ) +int aout_DecGetResetLost (audio_output_t *aout) +{ + aout_owner_t *owner = aout_owner (aout); + aout_input_t *input = owner->input; + int val; + + aout_lock (aout); + if (likely(input != NULL)) { - vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock); - stats_UpdateInteger( p_aout, - p_input->p_input_thread->p->counters.p_played_abuffers, - 1, NULL ); - vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock); + val = input->i_buffer_lost; + input->i_buffer_lost = 0; } - vlc_mutex_unlock( &p_aout->mixer_lock ); + else + val = 0; /* if aout_CheckRestart() failed */ + aout_unlock (aout); - return 0; + return val; +} + +void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date) +{ + aout_owner_t *owner = aout_owner (aout); + + 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_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; }