Commit 32aa1b26 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

aout: report drift via parameter rather than callback

The time report callback could only be called from pf_play() so this is
cleaner and less prone to coding mistake.

Also pass the relative drift instead of the absolute play time stamp.
The drift is less subject to scheduling noise, i.e. drift evolves much
slower than current time advances.
parent 25f0a8f9
......@@ -143,7 +143,8 @@ struct audio_output
only when succesfully probed and not afterward) */
struct aout_sys_t *sys; /**< Output plugin private data */
void (*pf_play)(audio_output_t *, block_t *); /**< Audio buffer callback */
void (*pf_play)(audio_output_t *, block_t *, mtime_t *); /**< Play callback
- queue a block for playback */
void (* pf_pause)( audio_output_t *, bool, mtime_t ); /**< Pause/resume
callback (optional, may be NULL) */
void (* pf_flush)( audio_output_t *, bool ); /**< Flush/drain callback
......@@ -152,7 +153,6 @@ struct audio_output
int (*mute_set)(audio_output_t *, bool); /**< Mute setter (or NULL) */
struct {
void (*time_report)(audio_output_t *, mtime_t);
void (*volume_report)(audio_output_t *, float);
void (*mute_report)(audio_output_t *, bool);
int (*gain_request)(audio_output_t *, float);
......@@ -223,11 +223,6 @@ VLC_API void aout_FormatPrint(vlc_object_t *, const char *,
#define aout_FormatPrint(o, t, f) aout_FormatPrint(VLC_OBJECT(o), t, f)
VLC_API const char * aout_FormatPrintChannels( const audio_sample_format_t * ) VLC_USED;
static inline void aout_TimeReport(audio_output_t *aout, mtime_t date)
{
aout->event.time_report(aout, date);
}
static inline void aout_VolumeReport(audio_output_t *aout, float volume)
{
aout->event.volume_report(aout, volume);
......@@ -271,7 +266,7 @@ typedef struct
VLC_DEPRECATED void aout_PacketInit(audio_output_t *, aout_packet_t *, unsigned);
VLC_DEPRECATED void aout_PacketDestroy(audio_output_t *);
VLC_DEPRECATED void aout_PacketPlay(audio_output_t *, block_t *);
VLC_DEPRECATED void aout_PacketPlay(audio_output_t *, block_t *, mtime_t *);
VLC_DEPRECATED void aout_PacketPause(audio_output_t *, bool, mtime_t);
VLC_DEPRECATED void aout_PacketFlush(audio_output_t *, bool);
......
......@@ -49,7 +49,7 @@ vlc_module_end ()
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static void Play( audio_output_t *, block_t * );
static void Play( audio_output_t *, block_t *, mtime_t * );
/*****************************************************************************
* OpenAudio: open a dummy audio device
......@@ -80,8 +80,9 @@ static int Open( vlc_object_t * p_this )
/*****************************************************************************
* Play: pretend to play a sound
*****************************************************************************/
static void Play( audio_output_t *aout, block_t *block )
static void Play( audio_output_t *aout, block_t *block, mtime_t *drift )
{
block_Release( block );
(void) aout;
(void) drift;
}
......@@ -160,7 +160,7 @@ static int DeviceChanged (vlc_object_t *obj, const char *varname,
return VLC_SUCCESS;
}
static void Play (audio_output_t *, block_t *);
static void Play (audio_output_t *, block_t *, mtime_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static void PauseDummy (audio_output_t *, bool, mtime_t);
static void Flush (audio_output_t *, bool);
......@@ -564,7 +564,8 @@ error:
/**
* Queues one audio buffer to the hardware.
*/
static void Play (audio_output_t *aout, block_t *block)
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
......@@ -579,15 +580,15 @@ static void Play (audio_output_t *aout, block_t *block)
if (snd_pcm_delay (pcm, &frames) == 0)
{
mtime_t delay = frames * CLOCK_FREQ / aout->format.i_rate;
delay += mdate () - block->i_pts;
if (state != SND_PCM_STATE_RUNNING)
{
delay = block->i_pts - (mdate () + delay);
if (delay > 0)
if (delay < 0)
{
if (aout->format.i_format != VLC_CODEC_SPDIFL)
{
frames = (delay * aout->format.i_rate) / CLOCK_FREQ;
frames = (delay * aout->format.i_rate) / -CLOCK_FREQ;
msg_Dbg (aout, "prepending %ld zeroes", frames);
void *z = calloc (frames, aout->format.i_bytes_per_frame);
......@@ -599,17 +600,17 @@ static void Play (audio_output_t *aout, block_t *block)
}
}
/* Lame fallback if zero padding does not work */
if (delay > 0)
if (delay < 0)
{
msg_Dbg (aout, "deferring start (%"PRId64" us)", delay);
msleep (delay);
msg_Dbg (aout, "deferring start (%"PRId64" us)", -delay);
msleep (-delay);
}
}
else
msg_Dbg (aout, "starting late (%"PRId64" us)", delay);
}
else
aout_TimeReport (aout, block->i_pts - delay);
*drift = delay;
}
/* TODO: better overflow handling */
......
......@@ -66,13 +66,15 @@ struct aout_sys_t
bool mute;
};
static void Play (audio_output_t *aout, block_t *block)
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
sys->play (sys->opaque, block->p_buffer, block->i_nb_samples,
block->i_pts);
block_Release (block);
(void) drift;
}
static void Pause (audio_output_t *aout, bool paused, mtime_t date)
......
......@@ -57,7 +57,7 @@ struct aout_sys_t
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( audio_output_t *, block_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
static void AudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef);
#include "volume.h"
......
......@@ -100,7 +100,7 @@ struct aout_sys_t
*****************************************************************************/
static int OpenAudio ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
static void Play ( audio_output_t *, block_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
static int VolumeSet ( audio_output_t *, float );
static int MuteSet ( audio_output_t *, bool );
......@@ -576,7 +576,8 @@ static void Probe( audio_output_t * p_aout )
* we know the first buffer has been put in the aout fifo and we also
* know its date.
*****************************************************************************/
static void Play( audio_output_t *p_aout, block_t *p_buffer )
static void Play( audio_output_t *p_aout, block_t *p_buffer,
mtime_t *restrict drift )
{
/* get the playing date of the first aout buffer */
p_aout->sys->notif.start_date = p_buffer->i_pts;
......@@ -587,7 +588,7 @@ static void Play( audio_output_t *p_aout, block_t *p_buffer )
/* wake up the audio output thread */
SetEvent( p_aout->sys->notif.event );
aout_PacketPlay( p_aout, p_buffer );
aout_PacketPlay( p_aout, p_buffer, drift );
p_aout->pf_play = aout_PacketPlay;
}
......
......@@ -72,7 +72,7 @@ static const int pi_channels_maps[CHANNELS_MAX+1] =
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( audio_output_t *, block_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
/*****************************************************************************
* Module descriptor
......@@ -308,7 +308,8 @@ static void Close( vlc_object_t * p_this )
/*****************************************************************************
* Play: pretend to play a sound
*****************************************************************************/
static void Play( audio_output_t * p_aout, block_t *p_buffer )
static void Play( audio_output_t * p_aout, block_t *p_buffer,
mtime_t *restrict drift )
{
if( fwrite( p_buffer->p_buffer, p_buffer->i_buffer, 1,
p_aout->sys->p_file ) != 1 )
......@@ -323,4 +324,5 @@ static void Play( audio_output_t * p_aout, block_t *p_buffer )
}
block_Release( p_buffer );
(void) drift;
}
......@@ -56,7 +56,7 @@ struct aout_sys_t
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( audio_output_t *_p_aout, block_t *block );
static void Play ( audio_output_t *_p_aout, block_t *block, mtime_t * );
static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
......@@ -255,13 +255,14 @@ exit_free_sys :
/*****************************************************************************
* Play: play a sound samples buffer
*****************************************************************************/
static void Play (audio_output_t *p_aout, block_t *block)
static void Play (audio_output_t *p_aout, block_t *block,
mtime_t *restrict drift)
{
aout_sys_t *p_sys = p_aout->sys;
kaiPlay( p_sys->hkai );
aout_PacketPlay( p_aout, block );
aout_PacketPlay( p_aout, block, drift );
}
/*****************************************************************************
......
......@@ -141,7 +141,8 @@ static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
/*****************************************************************************
* Play: play a sound
*****************************************************************************/
static void Play( audio_output_t *p_aout, block_t *p_buffer )
static void Play( audio_output_t *p_aout, block_t *p_buffer,
mtime_t *restrict drift )
{
aout_sys_t *p_sys = p_aout->sys;
int tries = 5;
......@@ -170,9 +171,8 @@ static void Play( audio_output_t *p_aout, block_t *p_buffer )
block_ChainLastAppend( &p_sys->pp_last, p_buffer );
vlc_mutex_unlock( &p_sys->lock );
if (delay && st.count) {
aout_TimeReport(p_aout, p_buffer->i_pts - delay);
}
if (delay && st.count)
*drift = mdate() + delay - p_buffer->i_pts;
for (;;)
{
......
......@@ -74,7 +74,7 @@ vlc_module_begin ()
set_callbacks( Open, Close )
vlc_module_end ()
static void Play (audio_output_t *, block_t *);
static void Play (audio_output_t *, block_t *, mtime_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static void Flush (audio_output_t *, bool);
static int VolumeSync (audio_output_t *);
......@@ -318,7 +318,8 @@ static void Close (vlc_object_t *obj)
/**
* Queues one audio buffer to the hardware.
*/
static void Play (audio_output_t *aout, block_t *block)
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
......@@ -329,7 +330,7 @@ static void Play (audio_output_t *aout, block_t *block)
mtime_t latency = (delay * CLOCK_FREQ * aout->format.i_frame_length)
/ (aout->format.i_rate * aout->format.i_bytes_per_frame);
/* TODO: insert zeroes when starting playback */
aout_TimeReport (aout, block->i_pts - latency);
*drift = mdate () + latency - block->i_pts;
}
else
msg_Warn (aout, "cannot get delay: %m");
......
......@@ -158,7 +158,8 @@ void aout_PacketDestroy (audio_output_t *aout)
static block_t *aout_OutputSlice (audio_output_t *);
void aout_PacketPlay (audio_output_t *aout, block_t *block)
void aout_PacketPlay (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
{
aout_packet_t *p = aout_packet (aout);
mtime_t time_report;
......@@ -173,7 +174,7 @@ void aout_PacketPlay (audio_output_t *aout, block_t *block)
vlc_mutex_unlock (&p->lock);
if (time_report != INT64_MIN)
aout_TimeReport (aout, mdate () - time_report);
*drift = time_report;
}
void aout_PacketPause (audio_output_t *aout, bool pause, mtime_t date)
......
......@@ -515,7 +515,7 @@ static void *data_convert(block_t **pp)
/**
* Queue one audio frame to the playabck stream
*/
static void Play(audio_output_t *aout, block_t *block)
static void Play(audio_output_t *aout, block_t *block, mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
pa_stream *s = sys->stream;
......@@ -554,6 +554,7 @@ static void Play(audio_output_t *aout, block_t *block)
}
pa_threaded_mainloop_unlock(sys->mainloop);
(void) drift;
}
/**
......
......@@ -43,7 +43,7 @@ vlc_module_begin ()
set_callbacks (Open, Close )
vlc_module_end ()
static void Play (audio_output_t *, block_t *);
static void Play (audio_output_t *, block_t *, mtime_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static int VolumeSet (audio_output_t *, float);
static int MuteSet (audio_output_t *, bool);
......@@ -183,7 +183,8 @@ static void Close (vlc_object_t *obj)
free (sys);
}
static void Play (audio_output_t *aout, block_t *block)
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
struct sio_par par;
......@@ -192,8 +193,7 @@ static void Play (audio_output_t *aout, block_t *block)
{
mtime_t delay = par.bufsz * CLOCK_FREQ / aout->format.i_rate;
delay = block->i_pts - (mdate () - delay);
aout_TimeReport (aout, block->i_pts - delay);
*drift = mdate () + delay - block->i_pts;
}
while (block->i_buffer > 0 && !sio_eof (sys->hdl))
......
......@@ -81,7 +81,7 @@ struct aout_sys_t
HANDLE done; /**< Semaphore to MTA thread */
};
static void Play(audio_output_t *aout, block_t *block)
static void Play(audio_output_t *aout, block_t *block, mtime_t *restrict drift)
{
aout_sys_t *sys = aout->sys;
HRESULT hr;
......@@ -94,7 +94,7 @@ static void Play(audio_output_t *aout, block_t *block)
IAudioClock_GetPosition(sys->clock, &pos, &qpcpos);
qpcpos = (qpcpos + 5) / 10; /* 100ns -> 1µs */
/* NOTE: this assumes mdate() uses QPC() (which it currently does). */
aout_TimeReport(aout, qpcpos);
*drift = mdate() - qpcpos;
}
for (;;)
......
......@@ -51,7 +51,7 @@
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( audio_output_t *, block_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
/*****************************************************************************
* notification_thread_t: waveOut event thread
......@@ -491,7 +491,8 @@ static void Probe( audio_output_t * p_aout )
* This doesn't actually play the buffer. This just stores the buffer so it
* can be played by the callback thread.
*****************************************************************************/
static void Play( audio_output_t *_p_aout, block_t *block )
static void Play( audio_output_t *_p_aout, block_t *block,
mtime_t *restrict drift )
{
if( !_p_aout->sys->b_playing )
{
......@@ -508,7 +509,7 @@ static void Play( audio_output_t *_p_aout, block_t *block )
SetEvent( _p_aout->sys->new_buffer_event );
}
aout_PacketPlay( _p_aout, block );
aout_PacketPlay( _p_aout, block, drift );
}
/*****************************************************************************
......
......@@ -73,7 +73,6 @@ audio_output_t *aout_New( vlc_object_t * p_parent )
owner->module = NULL;
owner->input = NULL;
aout->pf_play = aout_DecDeleteBuffer;
aout->volume_set = NULL;
aout->mute_set = NULL;
vlc_object_set_destructor (aout, aout_Destructor);
......
......@@ -36,49 +36,6 @@
#include "libvlc.h"
#include "aout_internal.h"
/**
* Notifies the audio input of the drift from the requested audio
* playback timestamp (@ref block_t.i_pts) to the anticipated playback time
* as reported by the audio output hardware.
* Depending on the drift amplitude, the input core may ignore the drift
* trigger upsampling or downsampling, or even discard samples.
* Future VLC versions may instead adjust the input decoding speed.
*
* The audio output plugin is responsible for estimating the ideal current
* playback time defined as follows:
* ideal time = buffer timestamp - (output latency + pending buffer duration)
*
* Practically, this is the PTS (block_t.i_pts) of the current buffer minus
* the latency reported by the output programming interface.
* Computing the estimated drift directly would probably be more intuitive.
* However the use of an absolute time value does not introduce extra
* measurement errors due to the CPU scheduling jitter and clock resolution.
* Furthermore, the ideal while it is an abstract value, is easy for most
* audio output plugins to compute.
* The following definition is equivalent but depends on the clock time:
* ideal time = real time + drift
* @note If aout_LatencyReport() is never called, the core will assume that
* there is no drift.
*
* @param ideal estimated ideal time as defined above.
*/
static void aout_OutputTimeReport (audio_output_t *aout, mtime_t ideal)
{
mtime_t delta = mdate() - ideal /* = -drift */;
aout_assert_locked (aout);
if (delta < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < delta)
{
aout_owner_t *owner = aout_owner (aout);
msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
delta);
if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
date_Move (&owner->sync.date, delta);
}
}
/**
* Supply or update the current custom ("hardware") volume.
* @note This only makes sense after calling aout_VolumeHardInit().
......@@ -123,7 +80,6 @@ int aout_OutputNew( audio_output_t *p_aout,
p_aout->format = *p_format;
aout_FormatPrepare( &p_aout->format );
p_aout->event.time_report = aout_OutputTimeReport;
p_aout->event.volume_report = aout_OutputVolumeReport;
p_aout->event.mute_report = aout_OutputMuteReport;
p_aout->event.gain_request = aout_OutputGainRequest;
......@@ -275,10 +231,6 @@ void aout_OutputDelete (audio_output_t *aout)
return;
module_unneed (aout, owner->module);
/* Clear callbacks */
aout->pf_play = aout_DecDeleteBuffer; /* gruik */
aout->pf_pause = NULL;
aout->pf_flush = NULL;
aout->volume_set = NULL;
aout->mute_set = NULL;
owner->module = NULL;
......@@ -291,6 +243,7 @@ void aout_OutputDelete (audio_output_t *aout)
void aout_OutputPlay (audio_output_t *aout, block_t *block)
{
aout_owner_t *owner = aout_owner (aout);
mtime_t drift = 0;
aout_assert_locked (aout);
......@@ -303,7 +256,34 @@ void aout_OutputPlay (audio_output_t *aout, block_t *block)
return;
}
aout->pf_play (aout, block);
if (likely(owner->module != NULL))
aout->pf_play (aout, block, &drift);
else
block_Release (block);
/**
* Notifies the audio input of the drift from the requested audio
* playback timestamp (@ref block_t.i_pts) to the anticipated playback time
* as reported by the audio output hardware.
* Depending on the drift amplitude, the input core may ignore the drift
* trigger upsampling or downsampling, or even discard samples.
* Future VLC versions may instead adjust the input decoding speed.
*
* The audio output plugin is responsible for estimating the drift. A negative
* value means playback is ahead of the intended time and a positive value
* means playback is late from the intended time. In most cases, the audio
* output can estimate the delay until playback of the next sample to be
* queued. Then, before the block is queued:
* drift = mdate() + delay - block->i_pts
* where mdate() + delay is the estimated time when the sample will be rendered
* and block->i_pts is the intended time.
*/
if (drift < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < drift)
{
msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
drift);
if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
date_Move (&owner->sync.date, drift);
}
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment