/** * 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; struct audio_mixer *mixer; aout_lock( p_aout ); /* Remove the input. */ input = owner->input; if (likely(input != NULL)) aout_InputDelete (p_aout, input); owner->input = NULL; mixer = owner->volume.mixer; owner->volume.mixer = NULL; var_DelCallback (p_aout, "audio-replay-gain-mode", ReplayGainCallback, owner); aout_OutputDelete( p_aout ); var_Destroy( p_aout, "audio-device" ); var_Destroy( p_aout, "audio-channels" ); aout_unlock( p_aout ); aout_MixerDelete (mixer); free (input); }
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); }
/***************************************************************************** * aout_DecPlay : filter & mix the decoded buffer *****************************************************************************/ int aout_DecPlay (audio_output_t *aout, block_t *block, int input_rate) { aout_owner_t *owner = aout_owner (aout); assert (input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE); assert (input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE); assert (block->i_pts >= VLC_TS_0); block->i_length = CLOCK_FREQ * block->i_nb_samples / owner->input_format.i_rate; aout_lock (aout); if (unlikely(aout_CheckReady (aout))) goto drop; /* Pipeline is unrecoverably broken :-( */ const mtime_t now = mdate (), advance = block->i_pts - now; if (advance < -AOUT_MAX_PTS_DELAY) { /* Late buffer can be caused by bugs in the decoder, by scheduling * latency spikes (excessive load, SIGSTOP, etc.) or if buffering is * insufficient. We assume the PTS is wrong and play the buffer anyway: * Hopefully video has encountered a similar PTS problem as audio. */ msg_Warn (aout, "buffer too late (%"PRId64" us): dropped", advance); goto drop; } if (advance > AOUT_MAX_ADVANCE_TIME) { /* Early buffers can only be caused by bugs in the decoder. */ msg_Err (aout, "buffer too early (%"PRId64" us): dropped", advance); goto drop; } if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) owner->sync.discontinuity = true; block = aout_FiltersPlay (aout, block, input_rate); if (block == NULL) goto lost; /* Software volume */ aout_volume_Amplify (owner->volume, block); /* Drift correction */ aout_DecSynchronize (aout, block->i_pts, input_rate); /* Output */ owner->sync.end = block->i_pts + block->i_length + 1; owner->sync.discontinuity = false; aout_OutputPlay (aout, block); out: aout_unlock (aout); return 0; drop: owner->sync.discontinuity = true; block_Release (block); lost: atomic_fetch_add(&owner->buffers_lost, 1); goto out; }
/** * Selects an audio output device. * \param id device ID to select, or NULL for the default device * \return zero on success, non-zero on error. */ int aout_DeviceSet (audio_output_t *aout, const char *id) { int ret = -1; aout_lock (aout); if (aout->device_select != NULL) ret = aout->device_select (aout, id); aout_unlock (aout); return ret; }
/** * Enumerates possible audio output devices. * * The function will heap-allocate two tables of heap-allocated strings; * the caller is responsible for freeing all strings and both tables. * * \param ids pointer to a table of device identifiers [OUT] * \param names pointer to a table of device human-readable descriptions [OUT] * \return the number of devices, or negative on error. * \note In case of error, *ids and *names are undefined. */ int aout_DevicesList (audio_output_t *aout, char ***ids, char ***names) { int ret = -1; aout_lock (aout); if (aout->device_enum != NULL) ret = aout->device_enum (aout, ids, names); aout_unlock (aout); return ret; }
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); owner->sync.end = VLC_TS_INVALID; if (owner->mixer_format.i_format) aout_OutputFlush (aout, false); aout_unlock (aout); }
/** * Sets the audio output stream mute flag. * \return 0 on success, -1 on failure. */ int aout_MuteSet (audio_output_t *aout, bool mute) { int ret = -1; aout_lock (aout); if (aout->mute_set != NULL) ret = aout->mute_set (aout, mute); aout_unlock (aout); return ret; }
/** * Sets the volume of the audio output stream. * \note The mute status is not changed. * \return 0 on success, -1 on failure. */ int aout_VolumeSet (audio_output_t *aout, float vol) { int ret = -1; aout_lock (aout); if (aout->volume_set != NULL) ret = aout->volume_set (aout, vol); aout_unlock (aout); return ret; }
/** * Stops all plugins involved in the audio output. */ void aout_DecDelete (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); aout_lock (aout); if (owner->mixer_format.i_format) { aout_FiltersDelete (aout); aout_OutputDelete (aout); } aout_volume_Delete (owner->volume); aout_unlock (aout); var_Destroy (aout, "stereo-mode"); }
/***************************************************************************** * aout_DecPlay : filter & mix the decoded buffer *****************************************************************************/ 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 ); p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000 / owner->input_format.i_rate; aout_lock( p_aout ); aout_CheckRestart( p_aout ); input = owner->input; if (unlikely(input == NULL)) /* can happen due to restart */ { aout_unlock( p_aout ); aout_BufferFree( p_buffer ); return -1; } /* Input */ 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 */ if (owner->volume.mixer != NULL) { float amp = owner->volume.multiplier * vlc_atomic_getf (&owner->gain.multiplier); aout_MixerRun (owner->volume.mixer, p_buffer, amp); } /* Output */ aout_OutputPlay( p_aout, p_buffer ); } aout_unlock( p_aout ); #ifdef __unix__ sched_yield(); #endif return 0; }
void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date) { aout_owner_t *owner = aout_owner (aout); aout_lock (aout); if (owner->sync.end != VLC_TS_INVALID) { if (paused) owner->sync.end -= date; else owner->sync.end += date; } if (owner->mixer_format.i_format) aout_OutputPause (aout, paused, date); aout_unlock (aout); }
/** * Deinitializes an audio output module and destroys an audio output object. */ void aout_Destroy (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); aout_lock (aout); module_unneed (aout, owner->module); /* Protect against late call from intf.c */ aout->volume_set = NULL; aout->mute_set = NULL; aout_unlock (aout); var_DelCallback (aout, "mute", var_Copy, aout->p_parent); var_SetFloat (aout, "volume", -1.f); var_DelCallback (aout, "volume", var_Copy, aout->p_parent); vlc_object_release (aout); }
bool aout_DecIsEmpty (audio_output_t *aout) { aout_owner_t *owner = aout_owner (aout); mtime_t now = mdate (); bool empty = true; aout_lock (aout); if (owner->sync.end != VLC_TS_INVALID) empty = owner->sync.end <= now; if (empty && owner->mixer_format.i_format) /* 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; }
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; }
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)) { val = input->i_buffer_lost; input->i_buffer_lost = 0; } else val = 0; /* if aout_CheckRestart() failed */ aout_unlock (aout); return val; }
/***************************************************************************** * aout_OutputNextBuffer : give the audio output plug-in the right buffer ***************************************************************************** * If b_can_sleek is 1, the aout core functions won't try to resample * new buffers to catch up - that is we suppose that the output plug-in can * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1. * This function is entered with no lock at all :-). *****************************************************************************/ aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout, mtime_t start_date, bool b_can_sleek ) { aout_fifo_t *p_fifo = &p_aout->output.fifo; aout_buffer_t * p_buffer; mtime_t now = mdate(); aout_lock( p_aout ); /* Drop the audio sample if the audio output is really late. * In the case of b_can_sleek, we don't use a resampler so we need to be * a lot more severe. */ while( ((p_buffer = p_fifo->p_first) != NULL) && p_buffer->i_pts < (b_can_sleek ? start_date : now) - AOUT_MAX_PTS_DELAY ) { msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), " "trashing %"PRId64"us", now - p_buffer->i_pts, p_buffer->i_length ); aout_BufferFree( aout_FifoPop( p_fifo ) ); } if( p_buffer == NULL ) { #if 0 /* This is bad because the audio output might just be trying to fill * in its internal buffers. And anyway, it's up to the audio output * to deal with this kind of starvation. */ /* Set date to 0, to allow the mixer to send a new buffer ASAP */ aout_FifoSet( &p_aout->output.fifo, 0 ); if ( !p_aout->output.b_starving ) msg_Dbg( p_aout, "audio output is starving (no input), playing silence" ); p_aout->output.b_starving = true; #endif goto out; }
/** * 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 ) { /* Sanitize audio format */ if( p_format->i_channels != aout_FormatNbChannels( p_format ) ) { 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 ); return -1; } if( p_format->i_rate < 4000 ) { msg_Err( p_aout, "too low audio sample frequency (%u)", p_format->i_rate ); return -1; } aout_owner_t *owner = aout_owner(p_aout); /* TODO: reduce lock scope depending on decoder's real need */ aout_lock( p_aout ); var_Destroy( p_aout, "stereo-mode" ); /* Create the audio output stream */ owner->volume = aout_volume_New (p_aout, p_replay_gain); atomic_store (&owner->restart, 0); owner->input_format = *p_format; owner->mixer_format = owner->input_format; if (aout_OutputNew (p_aout, &owner->mixer_format)) goto error; aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); /* Create the audio filtering "input" pipeline */ if (aout_FiltersNew (p_aout, p_format, &owner->mixer_format, p_request_vout)) { aout_OutputDelete (p_aout); error: aout_volume_Delete (owner->volume); aout_unlock (p_aout); return -1; } owner->sync.end = VLC_TS_INVALID; owner->sync.resamp_type = AOUT_RESAMPLING_NONE; owner->sync.discontinuity = true; aout_unlock( p_aout ); atomic_init (&owner->buffers_lost, 0); return 0; }
/** * 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 ) { /* 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" ); return -1; } if( p_format->i_rate > 192000 ) { msg_Err( p_aout, "excessive audio sample frequency (%u)", p_format->i_rate ); return -1; } if( p_format->i_rate < 4000 ) { msg_Err( p_aout, "too low audio sample frequency (%u)", p_format->i_rate ); return -1; } 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; /* TODO: reduce lock scope depending on decoder's real need */ aout_lock( p_aout ); assert (owner->module == NULL); /* Create the audio output stream */ var_Destroy( p_aout, "audio-device" ); var_Destroy( p_aout, "audio-channels" ); owner->input_format = *p_format; vlc_atomic_set (&owner->restart, 0); if( aout_OutputNew( p_aout, p_format ) < 0 ) { ret = -1; goto error; } /* Allocate a software mixer */ assert (owner->volume.mixer == NULL); owner->volume.mixer = aout_MixerNew (p_aout, owner->mixer_format.i_format); aout_ReplayGainInit (&owner->gain.data, p_replay_gain); var_AddCallback (p_aout, "audio-replay-gain-mode", ReplayGainCallback, owner); var_TriggerCallback (p_aout, "audio-replay-gain-mode"); /* 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); assert (owner->input == NULL); owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format, p_request_vout); if (owner->input == NULL) { struct audio_mixer *mixer = owner->volume.mixer; owner->volume.mixer = NULL; aout_OutputDelete (p_aout); aout_unlock (p_aout); aout_MixerDelete (mixer); return -1; } error: aout_unlock( p_aout ); return ret; }