#include <vlc_common.h>
#include <vlc_aout.h>
#include <vlc_input.h>
-#include <vlc_atomic.h>
#include "aout_internal.h"
#include "libvlc.h"
return -1;
}
- if( p_format->i_rate > 192000 )
+ if( p_format->i_rate > 352800 )
{
msg_Err( p_aout, "excessive audio sample frequency (%u)",
p_format->i_rate );
}
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 );
+ aout_OutputLock (p_aout);
var_Destroy( p_aout, "stereo-mode" );
/* Create the audio output stream */
owner->volume = aout_volume_New (p_aout, p_replay_gain);
- vlc_atomic_set (&owner->restart, 0);
+ atomic_store (&owner->restart, 0);
owner->input_format = *p_format;
owner->mixer_format = owner->input_format;
+ owner->request_vout = *p_request_vout;
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))
+ owner->filters = aout_FiltersNew (p_aout, p_format, &owner->mixer_format,
+ &owner->request_vout);
+ if (owner->filters == NULL)
{
aout_OutputDelete (p_aout);
error:
aout_volume_Delete (owner->volume);
- ret = -1;
- goto error;
+ aout_OutputUnlock (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 );
+ aout_OutputUnlock (p_aout);
atomic_init (&owner->buffers_lost, 0);
-
- return ret;
+ return 0;
}
/**
* 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_lock( p_aout );
- aout_FiltersDelete (p_aout);
- aout_OutputDelete( p_aout );
- aout_volume_Delete (owner->volume);
-
- var_Destroy( p_aout, "stereo-mode" );
-
- aout_unlock( p_aout );
-}
-
-#define AOUT_RESTART_OUTPUT 1
-#define AOUT_RESTART_INPUT 2
-static int aout_CheckRestart (audio_output_t *aout)
+void aout_DecDelete (audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
- aout_assert_locked (aout);
-
- int restart = vlc_atomic_swap (&owner->restart, 0);
- if (likely(restart == 0))
- return 0;
-
- assert (restart & AOUT_RESTART_INPUT);
-
- const aout_request_vout_t request_vout = owner->request_vout;
-
- aout_FiltersDelete (aout);
-
- /* Reinitializes the output */
- if (restart & AOUT_RESTART_OUTPUT)
+ aout_OutputLock (aout);
+ if (owner->mixer_format.i_format)
{
+ aout_FiltersDelete (aout, owner->filters);
aout_OutputDelete (aout);
- owner->mixer_format = owner->input_format;
- if (aout_OutputNew (aout, &owner->mixer_format))
- abort (); /* FIXME we are officially screwed */
- aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
- }
-
- owner->sync.end = VLC_TS_INVALID;
- owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
-
- if (aout_FiltersNew (aout, &owner->input_format, &owner->mixer_format,
- &request_vout))
- {
- abort (); /* FIXME */
}
- return 0;
+ aout_volume_Delete (owner->volume);
+ aout_OutputUnlock (aout);
+ var_Destroy (aout, "stereo-mode");
}
-/**
- * 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)
+static int aout_CheckReady (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"))
+ int restart = atomic_exchange (&owner->restart, 0);
+ if (unlikely(restart))
{
- /* This is supposed to be a significant change and supposes
- * rebuilding the channel choices. */
- var_Destroy (aout, "stereo-mode");
+ if (owner->mixer_format.i_format)
+ aout_FiltersDelete (aout, owner->filters);
+
+ if (restart & AOUT_RESTART_OUTPUT)
+ { /* Reinitializes the output */
+ msg_Dbg (aout, "restarting output...");
+ if (owner->mixer_format.i_format)
+ aout_OutputDelete (aout);
+ owner->mixer_format = owner->input_format;
+ if (aout_OutputNew (aout, &owner->mixer_format))
+ owner->mixer_format.i_format = 0;
+ aout_volume_SetFormat (owner->volume,
+ owner->mixer_format.i_format);
+ }
+
+ msg_Dbg (aout, "restarting filters...");
+ owner->sync.end = VLC_TS_INVALID;
+ owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
+
+ if (owner->mixer_format.i_format)
+ {
+ owner->filters = aout_FiltersNew (aout, &owner->input_format,
+ &owner->mixer_format,
+ &owner->request_vout);
+ if (owner->filters == NULL)
+ {
+ aout_OutputDelete (aout);
+ owner->mixer_format.i_format = 0;
+ }
+ }
+ /* TODO: This would be a good time to call clean up any video output
+ * left over by an audio visualization:
+ input_resource_TerminatVout(MAGIC HERE); */
}
- aout_RequestRestart (aout);
- return 0;
+ return (owner->mixer_format.i_format) ? 0 : -1;
}
/**
- * This function will safely mark aout input to be restarted as soon as
- * possible to take configuration changes into account
+ * Marks the audio output for restart, to update any parameter of the output
+ * plug-in (e.g. output device or channel mapping).
*/
-void aout_InputRequestRestart (audio_output_t *aout)
+void aout_RequestRestart (audio_output_t *aout, unsigned mode)
{
aout_owner_t *owner = aout_owner (aout);
-
- vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
+ atomic_fetch_or (&owner->restart, mode);
+ msg_Dbg (aout, "restart requested (%u)", mode);
}
-
/*
* Buffer management
*/
-/*****************************************************************************
- * aout_DecNewBuffer : ask for a new empty buffer
- *****************************************************************************/
-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);
-
- 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) )
- {
- block->i_nb_samples = samples;
- block->i_pts = block->i_length = 0;
- }
- return block;
-}
-
-/*****************************************************************************
- * aout_DecDeleteBuffer : destroy an undecoded buffer
- *****************************************************************************/
-void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
-{
- (void) aout;
- block_Release (block);
-}
-
static void aout_StopResampling (audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
- aout_FiltersAdjustResampling (aout, 0);
+ aout_FiltersAdjustResampling (owner->filters, 0);
}
-static void aout_DecSilence (audio_output_t *aout, mtime_t length)
+static void aout_DecSilence (audio_output_t *aout, mtime_t length, mtime_t pts)
{
aout_owner_t *owner = aout_owner (aout);
const audio_sample_format_t *fmt = &owner->mixer_format;
msg_Dbg (aout, "inserting %zu zeroes", frames);
memset (block->p_buffer, 0, block->i_buffer);
block->i_nb_samples = frames;
+ block->i_pts = pts;
+ block->i_dts = pts;
block->i_length = length;
- /* FIXME: PTS... */
aout_OutputPlay (aout, block);
}
int input_rate)
{
aout_owner_t *owner = aout_owner (aout);
- mtime_t aout_pts, drift;
+ mtime_t drift;
/**
* Depending on the drift between the actual and intended playback times,
* all samples in the buffer will have been played. Then:
* pts = mdate() + delay
*/
- if (aout_OutputTimeGet (aout, &aout_pts) != 0)
+ if (aout_OutputTimeGet (aout, &drift) != 0)
return; /* nothing can be done if timing is unknown */
- drift = aout_pts - dec_pts;
+ drift += mdate () - dec_pts;
/* Late audio output.
* This can happen due to insufficient caching, scheduling jitter
owner->sync.discontinuity = true;
/* Now the output might be too early... Recheck. */
- if (aout_OutputTimeGet (aout, &aout_pts) != 0)
+ if (aout_OutputTimeGet (aout, &drift) != 0)
return; /* nothing can be done if timing is unknown */
- drift = aout_pts - dec_pts;
+ drift += mdate () - dec_pts;
}
/* Early audio output.
if (!owner->sync.discontinuity)
msg_Warn (aout, "playback way too early (%"PRId64"): "
"playing silence", drift);
- aout_DecSilence (aout, -drift);
+ aout_DecSilence (aout, -drift, dec_pts);
aout_StopResampling (aout);
owner->sync.discontinuity = true;
* value, then it is time to switch back the resampling direction. */
adj *= -1;
- if (!aout_FiltersAdjustResampling (aout, adj))
+ if (!aout_FiltersAdjustResampling (owner->filters, adj))
{ /* Everything is back to normal: stop resampling. */
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
msg_Dbg (aout, "resampling stopped (drift: %"PRId64" us)", drift);
block->i_length = CLOCK_FREQ * block->i_nb_samples
/ owner->input_format.i_rate;
- aout_lock (aout);
- if (unlikely(aout_CheckRestart (aout)))
+ aout_OutputLock (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_MIN_PREPARE_TIME)
+ 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:
if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
owner->sync.discontinuity = true;
- block = aout_FiltersPlay (aout, block, input_rate);
+ block = aout_FiltersPlay (owner->filters, block, input_rate);
if (block == NULL)
goto lost;
owner->sync.discontinuity = false;
aout_OutputPlay (aout, block);
out:
- aout_unlock (aout);
+ aout_OutputUnlock (aout);
return 0;
drop:
owner->sync.discontinuity = true;
{
aout_owner_t *owner = aout_owner (aout);
- aout_lock (aout);
+ aout_OutputLock (aout);
if (owner->sync.end != VLC_TS_INVALID)
{
if (paused)
else
owner->sync.end += date;
}
- aout_OutputPause (aout, paused, date);
- aout_unlock (aout);
+ if (owner->mixer_format.i_format)
+ aout_OutputPause (aout, paused, date);
+ aout_OutputUnlock (aout);
}
void aout_DecFlush (audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
- aout_lock (aout);
+ aout_OutputLock (aout);
owner->sync.end = VLC_TS_INVALID;
- aout_OutputFlush (aout, false);
- aout_unlock (aout);
+ if (owner->mixer_format.i_format)
+ aout_OutputFlush (aout, false);
+ aout_OutputUnlock (aout);
}
bool aout_DecIsEmpty (audio_output_t *aout)
mtime_t now = mdate ();
bool empty = true;
- aout_lock (aout);
+ aout_OutputLock (aout);
if (owner->sync.end != VLC_TS_INVALID)
empty = owner->sync.end <= now;
- if (empty)
+ 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);
+ aout_OutputUnlock (aout);
return empty;
}