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

aout: separate time and play callbacks

parent f492f85c
......@@ -150,7 +150,14 @@ struct audio_output
/**< Stops the existing stream (optional, may be NULL).
* \note A stream must have been started when called.
*/
void (*play)(audio_output_t *, block_t *, mtime_t *);
int (*time_get)(audio_output_t *, mtime_t *write_pts);
/**< Estimates the date/time of the playback buffer write offset
* (optional, may be NULL). The read offset is not returned since it is
* always implicitly equal to the current time (mdate()).
* \param write_pts timestamp of the write offset [OUT]
* \note A stream must have been started when called.
*/
void (*play)(audio_output_t *, block_t *);
/**< Queues a block of samples for playback (mandatory, cannot be NULL).
* \note A stream must have been started when called.
*/
......@@ -311,7 +318,6 @@ typedef struct
aout_fifo_t partial; /**< Audio blocks before packetization */
aout_fifo_t fifo; /**< Packetized audio blocks */
mtime_t pause_date; /**< Date when paused or VLC_TS_INVALID */
mtime_t time_report; /**< Desynchronization estimate or VLC_TS_INVALID */
unsigned samples; /**< Samples per packet */
bool starving; /**< Whether currently starving (to limit error messages) */
} aout_packet_t;
......@@ -319,7 +325,8 @@ typedef struct
VLC_DEPRECATED void aout_PacketInit(audio_output_t *, aout_packet_t *, unsigned, const audio_sample_format_t *);
VLC_DEPRECATED void aout_PacketDestroy(audio_output_t *);
VLC_DEPRECATED void aout_PacketPlay(audio_output_t *, block_t *, mtime_t *);
VLC_DEPRECATED int aout_PacketTimeGet(audio_output_t *, mtime_t *);
VLC_DEPRECATED void aout_PacketPlay(audio_output_t *, block_t *);
VLC_DEPRECATED void aout_PacketPause(audio_output_t *, bool, mtime_t);
VLC_DEPRECATED void aout_PacketFlush(audio_output_t *, bool);
......
......@@ -42,11 +42,10 @@ vlc_module_end ()
#define A52_FRAME_NB 1536
static void Play( audio_output_t *aout, block_t *block, mtime_t *drift )
static void Play(audio_output_t *aout, block_t *block)
{
block_Release( block );
(void) aout;
(void) drift;
}
static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
......@@ -68,6 +67,7 @@ static int Open(vlc_object_t *obj)
audio_output_t *aout = (audio_output_t *)obj;
aout->start = Start;
aout->time_get = NULL;
aout->play = Play;
aout->pause = NULL;
aout->flush = NULL;
......
......@@ -153,7 +153,8 @@ static int DeviceChanged (vlc_object_t *obj, const char *varname,
return VLC_SUCCESS;
}
static void Play (audio_output_t *, block_t *, mtime_t *);
static int TimeGet (audio_output_t *aout, mtime_t *);
static void Play (audio_output_t *, block_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);
......@@ -508,6 +509,7 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
sys->format = *fmt;
aout->time_get = TimeGet;
aout->play = Play;
if (snd_pcm_hw_params_can_pause (hw))
aout->pause = Pause;
......@@ -540,11 +542,33 @@ error:
return VLC_EGENERIC;
}
static int TimeGet (audio_output_t *aout, mtime_t *restrict pts)
{
aout_sys_t *sys = aout->sys;
snd_pcm_t *pcm = sys->pcm;
snd_pcm_status_t *status;
int val;
snd_pcm_status_alloca (&status);
val = snd_pcm_status (pcm, status);
if (val < 0)
{
msg_Err (aout, "cannot get status: %s", snd_strerror (val));
return -1;
}
if (snd_pcm_status_get_state (status) != SND_PCM_STATE_RUNNING)
return -1;
snd_pcm_sframes_t frames = snd_pcm_status_get_delay (status);
*pts = mdate () + (frames * CLOCK_FREQ / sys->format.i_rate);
return 0;
}
/**
* Queues one audio buffer to the hardware.
*/
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
static void Play (audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
......@@ -561,13 +585,12 @@ static void Play (audio_output_t *aout, block_t *block,
if (val < 0)
msg_Err (aout, "cannot get status: %s", snd_strerror (val));
else
if (snd_pcm_status_get_state (status) != SND_PCM_STATE_RUNNING)
{
snd_pcm_sframes_t frames = snd_pcm_status_get_delay (status);
mtime_t delay = frames * CLOCK_FREQ / sys->format.i_rate;
delay += mdate () - block->i_pts;
if (snd_pcm_status_get_state (status) != SND_PCM_STATE_RUNNING)
{
delay += mdate () - block->i_pts;
if (delay < 0)
{
if (sys->format.i_format != VLC_CODEC_SPDIFL)
......@@ -593,9 +616,6 @@ static void Play (audio_output_t *aout, block_t *block,
else
msg_Dbg (aout, "starting late (%"PRId64" us)", delay);
}
else
*drift = delay;
}
/* TODO: better overflow handling */
/* TODO: no period wake ups */
......
......@@ -75,15 +75,13 @@ struct aout_sys_t
bool mute;
};
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
static void Play (audio_output_t *aout, block_t *block)
{
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)
......@@ -251,6 +249,7 @@ static int Open (vlc_object_t *obj)
aout->start = Start;
aout->stop = Stop;
aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......
......@@ -123,6 +123,7 @@ static int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
fmt->i_physical_channels = AOUT_CHANS_STEREO;
fmt->i_rate = 44100;
aout_PacketInit(p_aout, &p_sys->packet, FRAME_SIZE, fmt);
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = aout_PacketPlay;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......
......@@ -266,6 +266,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
aout->sys = p_sys;
aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
......@@ -287,9 +288,8 @@ static void Stop(audio_output_t* p_aout)
}
/* FIXME: lipsync */
static void Play(audio_output_t* p_aout, block_t* p_buffer, mtime_t* restrict drift)
static void Play(audio_output_t* p_aout, block_t* p_buffer)
{
VLC_UNUSED(drift);
aout_sys_t *p_sys = p_aout->sys;
size_t length = 0;
......
......@@ -172,6 +172,7 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
p_sys->b_changed_mixing = false;
memset(p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE);
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = aout_PacketPlay;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......
......@@ -96,7 +96,7 @@ struct aout_sys_t
static int Open( vlc_object_t * );
static void Close( vlc_object_t * );
static void Stop( audio_output_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
static void Play ( audio_output_t *, block_t * );
static int VolumeSet ( audio_output_t *, float );
static int MuteSet ( audio_output_t *, bool );
......@@ -282,6 +282,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
goto error;
}
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = Play;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......@@ -509,8 +510,7 @@ static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt )
* 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,
mtime_t *restrict drift )
static void Play( audio_output_t *p_aout, block_t *p_buffer )
{
/* get the playing date of the first aout buffer */
p_aout->sys->notif.start_date = p_buffer->i_pts;
......@@ -521,7 +521,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, drift );
aout_PacketPlay( p_aout, p_buffer );
p_aout->play = aout_PacketPlay;
}
......
......@@ -71,7 +71,7 @@ static const int pi_channels_maps[CHANNELS_MAX+1] =
* Local prototypes.
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
static void Play ( audio_output_t *, block_t * );
/*****************************************************************************
* Module descriptor
......@@ -154,6 +154,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
return VLC_EGENERIC;
}
p_aout->time_get = NULL;
p_aout->play = Play;
p_aout->pause = NULL;
p_aout->flush = NULL;
......@@ -296,8 +297,7 @@ static void Stop( audio_output_t *p_aout )
/*****************************************************************************
* Play: pretend to play a sound
*****************************************************************************/
static void Play( audio_output_t * p_aout, block_t *p_buffer,
mtime_t *restrict drift )
static void Play( audio_output_t * p_aout, block_t *p_buffer )
{
if( fwrite( p_buffer->p_buffer, p_buffer->i_buffer, 1,
p_aout->sys->p_file ) != 1 )
......@@ -312,7 +312,6 @@ static void Play( audio_output_t * p_aout, block_t *p_buffer,
}
block_Release( p_buffer );
(void) drift;
}
static int Open(vlc_object_t *obj)
......
......@@ -132,6 +132,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
// TODO add buffer size callback
fmt->i_rate = jack_get_sample_rate( p_sys->p_jack_client );
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = aout_PacketPlay;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......
......@@ -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 *_p_aout, block_t *block, mtime_t * );
static void Play ( audio_output_t *_p_aout, block_t *block );
static ULONG APIENTRY KaiCallback ( PVOID, PVOID, ULONG );
......@@ -195,6 +195,7 @@ static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
p_sys->format = *fmt = format;
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = Play;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......@@ -244,14 +245,13 @@ exit_kai_done :
/*****************************************************************************
* Play: play a sound samples buffer
*****************************************************************************/
static void Play (audio_output_t *p_aout, block_t *block,
mtime_t *restrict drift)
static void Play (audio_output_t *p_aout, block_t *block)
{
aout_sys_t *p_sys = p_aout->sys;
kaiPlay( p_sys->hkai );
aout_PacketPlay( p_aout, block, drift );
aout_PacketPlay( p_aout, block );
}
/*****************************************************************************
......
......@@ -75,7 +75,8 @@ vlc_module_begin ()
set_callbacks (Open, NULL)
vlc_module_end ()
static void Play (audio_output_t *, block_t *, mtime_t *);
static int TimeGet (audio_output_t *, mtime_t *);
static void Play (audio_output_t *, block_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static void Flush (audio_output_t *, bool);
static int VolumeSync (audio_output_t *);
......@@ -219,6 +220,7 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
/* Setup audio_output_t */
aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......@@ -311,38 +313,30 @@ static void Stop (audio_output_t *aout)
free (sys);
}
/**
* Queues one audio buffer to the hardware.
*/
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
static int TimeGet (audio_output_t *aout, mtime_t *restrict pts)
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
int delay;
if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) >= 0)
if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) < 0)
{
mtime_t latency = (delay * CLOCK_FREQ * sys->format.i_frame_length)
/ (sys->format.i_rate * sys->format.i_bytes_per_frame);
*drift = mdate () + latency - block->i_pts;
}
else
msg_Warn (aout, "cannot get delay: %m");
if (sys->starting)
{ /* Start on time */
/* TODO: resync on pause resumption and underflow recovery */
mtime_t delta = -*drift;
if (delta > 0) {
msg_Dbg(aout, "deferring start (%"PRId64" us)", delta);
msleep(delta);
*drift = 0;
} else
msg_Warn(aout, "starting late (%"PRId64" us)", delta);
sys->starting = false;
return -1;
}
*pts = mdate () + ((delay * CLOCK_FREQ * sys->format.i_frame_length)
/ (sys->format.i_rate * sys->format.i_bytes_per_frame));
return 0;
}
/**
* Queues one audio buffer to the hardware.
*/
static void Play (audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
while (block->i_buffer > 0)
{
ssize_t bytes = write (fd, block->p_buffer, block->i_buffer);
......
......@@ -144,7 +144,6 @@ void aout_PacketInit (audio_output_t *aout, aout_packet_t *p, unsigned samples,
aout_FifoInit (&p->partial, p->format.i_rate);
aout_FifoInit (&p->fifo, p->format.i_rate);
p->pause_date = VLC_TS_INVALID;
p->time_report = INT64_MIN;
p->samples = samples;
p->starving = true;
}
......@@ -158,25 +157,32 @@ void aout_PacketDestroy (audio_output_t *aout)
vlc_mutex_destroy (&p->lock);
}
int aout_PacketTimeGet (audio_output_t *aout, mtime_t *restrict pts)
{
aout_packet_t *p = aout_packet (aout);
mtime_t time_report;
vlc_mutex_lock (&p->lock);
time_report = date_Get (&p->fifo.end_date);
vlc_mutex_unlock (&p->lock);
if (time_report == VLC_TS_INVALID)
return -1;
*pts = time_report;
return 0;
}
static block_t *aout_OutputSlice (audio_output_t *);
void aout_PacketPlay (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
void aout_PacketPlay (audio_output_t *aout, block_t *block)
{
aout_packet_t *p = aout_packet (aout);
mtime_t time_report;
vlc_mutex_lock (&p->lock);
aout_FifoPush (&p->partial, block);
while ((block = aout_OutputSlice (aout)) != NULL)
aout_FifoPush (&p->fifo, block);
time_report = p->time_report;
p->time_report = INT64_MIN;
vlc_mutex_unlock (&p->lock);
if (time_report != INT64_MIN)
*drift = time_report;
}
void aout_PacketPause (audio_output_t *aout, bool pause, mtime_t date)
......@@ -375,7 +381,6 @@ block_t *aout_PacketNext (audio_output_t *p_aout, mtime_t start_date)
"adjusting dates (%"PRId64" us)", delta);
aout_FifoMoveDates (&p->partial, delta);
aout_FifoMoveDates (p_fifo, delta);
p->time_report = delta;
}
vlc_mutex_unlock( &p->lock );
return p_buffer;
......
......@@ -539,7 +539,7 @@ static void *data_convert(block_t **pp)
/**
* Queue one audio frame to the playback stream
*/
static void Play(audio_output_t *aout, block_t *block, mtime_t *restrict drift)
static void Play(audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
pa_stream *s = sys->stream;
......@@ -581,7 +581,6 @@ static void Play(audio_output_t *aout, block_t *block, mtime_t *restrict drift)
}
pa_threaded_mainloop_unlock(sys->mainloop);
(void) drift;
}
/**
......@@ -1030,6 +1029,7 @@ static int Open(vlc_object_t *obj)
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......
......@@ -43,7 +43,8 @@ vlc_module_begin ()
set_callbacks (Open, Close)
vlc_module_end ()
static void Play (audio_output_t *, block_t *, mtime_t *);
static int TimeGet (audio_output, mtime_t *);
static void Play (audio_output_t *, block_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static int VolumeSet (audio_output_t *, float);
static int MuteSet (audio_output_t *, bool);
......@@ -149,6 +150,7 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
aout_FormatPrepare (fmt);
aout->sys = sys;
aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = NULL; /* sndio sucks! */
......@@ -179,18 +181,21 @@ static void Close (vlc_object_t *obj)
sio_close (sys->hdl);
}
static void Play (audio_output_t *aout, block_t *block,
mtime_t *restrict drift)
static int TimeGet (audio_output_t *aout, mtime_t *restrict pts)
{
aout_sys_t *sys = aout->sys;
struct sio_par par;
if (sio_getpar (sys->hdl, &par) == 0)
{
mtime_t delay = par.bufsz * CLOCK_FREQ / aout->format.i_rate;
if (sio_getpar (sys->hdl, &par))
return -1;
*drift = mdate () + delay - block->i_pts;
}
*pts = mdate () + (par.bufsz * CLOCK_FREQ / aout->format.i_rate);
return 0;
}
static void Play (audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
while (block->i_buffer > 0 && !sio_eof (sys->hdl))
{
......
......@@ -135,32 +135,43 @@ struct aout_sys_t
/*** VLC audio output callbacks ***/
static void Play(audio_output_t *aout, block_t *block, mtime_t *restrict drift)
static int TimeGet(audio_output_t *aout, mtime_t *restrict pts)
{
aout_sys_t *sys = aout->sys;
HRESULT hr = S_OK;
UINT64 pos, qpcpos;
HRESULT hr;
if (sys->chans_to_reorder)
aout_ChannelReorder(block->p_buffer, block->i_buffer,
sys->chans_to_reorder, sys->chans_table, sys->bits);
if (sys->clock == NULL)
return -1;
Enter();
if (likely(sys->clock != NULL))
{
UINT64 pos, qpcpos;
hr = IAudioClock_GetPosition(sys->clock, &pos, &qpcpos);
if (SUCCEEDED(hr))
Leave();
if (FAILED(hr))
{
qpcpos = GetQPC() - qpcpos;
static_assert((10000000 % CLOCK_FREQ) == 0,
"Frequency conversion broken");
*drift = qpcpos / (10000000 / CLOCK_FREQ);
}
else
msg_Warn(aout, "cannot get position (error 0x%lx)", hr);
msg_Err(aout, "cannot get position (error 0x%lx)", hr);
return -1;
}
mtime_t delay = ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
if (delay < 1000) /* device is still buffering, time is screwed */
return -1;
*pts += mdate () + delay;
return 0;
}
static void Play(audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
HRESULT hr = S_OK;
if (sys->chans_to_reorder)
aout_ChannelReorder(block->p_buffer, block->i_buffer,
sys->chans_to_reorder, sys->chans_table, sys->bits);
Enter();
for (;;)
{
UINT32 frames;
......@@ -799,6 +810,7 @@ retry:
sys->rate = fmt->i_rate;
sys->bytes_per_frame = fmt->i_bytes_per_frame;
aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......
......@@ -50,7 +50,7 @@
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Play ( audio_output_t *, block_t *, mtime_t * );
static void Play ( audio_output_t *, block_t * );
/*****************************************************************************
* notification_thread_t: waveOut event thread
......@@ -160,6 +160,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
{
vlc_value_t val;
p_aout->time_get = aout_PacketTimeGet;
p_aout->play = Play;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
......@@ -449,8 +450,7 @@ static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt )
* 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,
mtime_t *restrict drift )
static void Play( audio_output_t *_p_aout, block_t *block )
{
if( !_p_aout->sys->b_playing )
{
......@@ -467,7 +467,7 @@ static void Play( audio_output_t *_p_aout, block_t *block,
SetEvent( _p_aout->sys->new_buffer_event );
}
aout_PacketPlay( _p_aout, block, drift );
aout_PacketPlay( _p_aout, block );
}
/*****************************************************************************
......
......@@ -440,7 +440,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;
mtime_t drift;
aout_assert_locked (aout);
......@@ -454,7 +454,12 @@ void aout_OutputPlay (audio_output_t *aout, block_t *block)
return;
}
aout->play (aout, block, &drift);
if (aout->time_get != NULL && aout->time_get (aout, &drift) == 0)
drift -= block->i_pts;
else
drift = 0;
aout->play (aout, block);
/**
* Notifies the audio input of the drift from the requested audio
* playback timestamp (@ref block_t.i_pts) to the anticipated playback time
......@@ -463,14 +468,9 @@ void aout_OutputPlay (audio_output_t *aout, block_t *block)
* 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.
* The audio output plugin is responsible for estimating the time. Typically,
* the audio output can estimate the total buffer delay. Then:
* pts = mdate() + delay
*/
if (drift < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < 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