/**< 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.
*/
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;
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);
#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)
audio_output_t *aout = (audio_output_t *)obj;
aout->start = Start;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = NULL;
aout->flush = NULL;
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);
}
sys->format = *fmt;
+ aout->time_get = TimeGet;
aout->play = Play;
if (snd_pcm_hw_params_can_pause (hw))
aout->pause = Pause;
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;
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 (delay < 0)
+ if (sys->format.i_format != VLC_CODEC_SPDIFL)
{
- if (sys->format.i_format != VLC_CODEC_SPDIFL)
- {
- frames = (delay * sys->format.i_rate) / -CLOCK_FREQ;
- msg_Dbg (aout, "prepending %ld zeroes", frames);
-
- void *z = calloc (frames, sys->format.i_bytes_per_frame);
- if (likely(z != NULL))
- {
- snd_pcm_writei (pcm, z, frames);
- free (z);
- delay = 0;
- }
- }
- /* Lame fallback if zero padding does not work */
- if (delay < 0)
+ frames = (delay * sys->format.i_rate) / -CLOCK_FREQ;
+ msg_Dbg (aout, "prepending %ld zeroes", frames);
+
+ void *z = calloc (frames, sys->format.i_bytes_per_frame);
+ if (likely(z != NULL))
{
- msg_Dbg (aout, "deferring start (%"PRId64" us)", -delay);
- msleep (-delay);
+ snd_pcm_writei (pcm, z, frames);
+ free (z);
+ delay = 0;
}
}
- else
- msg_Dbg (aout, "starting late (%"PRId64" us)", delay);
+ /* Lame fallback if zero padding does not work */
+ if (delay < 0)
+ {
+ msg_Dbg (aout, "deferring start (%"PRId64" us)", -delay);
+ msleep (-delay);
+ }
}
else
- *drift = delay;
+ msg_Dbg (aout, "starting late (%"PRId64" us)", delay);
}
/* TODO: better overflow handling */
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)
aout->start = Start;
aout->stop = Stop;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
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;
}
aout->sys = p_sys;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
}
/* 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;
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;
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 );
goto error;
}
+ p_aout->time_get = aout_PacketTimeGet;
p_aout->play = Play;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
* 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;
/* 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;
}
* 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
return VLC_EGENERIC;
}
+ p_aout->time_get = NULL;
p_aout->play = Play;
p_aout->pause = NULL;
p_aout->flush = NULL;
/*****************************************************************************
* 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 )
}
block_Release( p_buffer );
- (void) drift;
}
static int Open(vlc_object_t *obj)
// 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;
*****************************************************************************/
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 );
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;
/*****************************************************************************
* 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 );
}
/*****************************************************************************
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 *);
}
/* Setup audio_output_t */
+ aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
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);
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;
}
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)
"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;
/**
* 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;
}
pa_threaded_mainloop_unlock(sys->mainloop);
- (void) drift;
}
/**
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
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);
aout_FormatPrepare (fmt);
aout->sys = sys;
+ aout->time_get = TimeGet;
aout->play = Play;
aout->pause = Pause;
aout->flush = NULL; /* sndio sucks! */
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))
{
/*** 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;
+ UINT64 pos, qpcpos;
+ HRESULT hr;
+
+ if (sys->clock == NULL)
+ return -1;
+
+ Enter();
+ hr = IAudioClock_GetPosition(sys->clock, &pos, &qpcpos);
+ Leave();
+ if (FAILED(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;
sys->chans_to_reorder, sys->chans_table, sys->bits);
Enter();
- if (likely(sys->clock != NULL))
- {
- UINT64 pos, qpcpos;
-
- hr = IAudioClock_GetPosition(sys->clock, &pos, &qpcpos);
- if (SUCCEEDED(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);
- }
-
for (;;)
{
UINT32 frames;
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;
*****************************************************************************/
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
{
vlc_value_t val;
+ p_aout->time_get = aout_PacketTimeGet;
p_aout->play = Play;
p_aout->pause = aout_PacketPause;
p_aout->flush = aout_PacketFlush;
* 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 )
{
SetEvent( _p_aout->sys->new_buffer_event );
}
- aout_PacketPlay( _p_aout, block, drift );
+ aout_PacketPlay( _p_aout, block );
}
/*****************************************************************************
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);
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
* 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)
{