[vlc-commits] [Git][videolan/vlc][master] 20 commits: clock: also trace clock input offset
Steve Lhomme (@robUx4)
gitlab at videolan.org
Wed Jun 29 08:58:59 UTC 2022
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
91f181d1 by Thomas Guillem at 2022-06-29T08:43:09+00:00
clock: also trace clock input offset
- - - - -
aad924cd by Thomas Guillem at 2022-06-29T08:43:09+00:00
clock: trace the calculated offset
- - - - -
53d8690a by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: move/hide aout_TimeGet implementation
Refs #27023
- - - - -
049885a2 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: don't require a valid time_get cb
Refs #27023
- - - - -
45774a95 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: file: don't implement time_get
Refs #27023
- - - - -
f4b126d6 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: amem: don't implement time_get
Refs #27023
- - - - -
c37efd75 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: remove (now) unused aout_TimeGetDefault
Refs #27023
- - - - -
4d4dc45a by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: rename aout_TimeGet to stream_TimeGet
Refs #27023
- - - - -
c2d1cb5d by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: rework stream_RequestRetiming
Rename it to stream_HandleDrift() and don't handle clock updates.
Refs #27023
- - - - -
dda2830c by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: factor discontinuity in a new function
Refs #27023
- - - - -
d652f402 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: initialize the stream members earlier
Refs #27023
- - - - -
ad8db30e by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: fix possible data-race
Initialize main_stream before start since it can be read once the stream
is started from an event.
Refs #27023
- - - - -
08cd94d4 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: add a reworked timing_report event
This partially reverts commit 02293b1df3581f21be77b639153ad16207d94bec.
Don't update the audio clock from this event but store the points in a
circular buffer. The play function will update the clock according to
these points.
Refs #27023
- - - - -
be67e2a8 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: add vlc_aout_stream_UpdateLatency()
This will send all timing points to the clock.
Must be serialized with vlc_aout_stream_Play().
Refs #27023
- - - - -
72a9a5a4 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: add a cb to notify new timing points
Refs #27023
- - - - -
343b7816 by Thomas Guillem at 2022-06-29T08:43:09+00:00
decoder: update the audio stream latency when requested
Refs #27023
- - - - -
ca9025b8 by Thomas Guillem at 2022-06-29T08:43:09+00:00
aout: dec: reset the stream after the flush
Since timings point can be updated asynchronously until the flush is
processed.
Refs #27023
- - - - -
242c4954 by Thomas Guillem at 2022-06-29T08:43:09+00:00
pulse: wait for the stream to stop
This will be necessary in the next commit, in order to not send timing
points after a flush, or while paused.
Refs #27023
- - - - -
d71e7f56 by Thomas Guillem at 2022-06-29T08:43:09+00:00
pulse: use aout_TimingReport
Refs #27023
- - - - -
9eabb282 by Thomas Guillem at 2022-06-29T08:43:09+00:00
pulse: don't interpolate timing
The new aout_TimingReport() API doesn't require timing points to be in
the present.
Refs #27023
- - - - -
9 changed files:
- include/vlc_aout.h
- modules/audio_output/amem.c
- modules/audio_output/file.c
- modules/audio_output/pulse.c
- src/audio_output/aout_internal.h
- src/audio_output/dec.c
- src/audio_output/output.c
- src/clock/clock.c
- src/input/decoder.c
Changes:
=====================================
include/vlc_aout.h
=====================================
@@ -125,6 +125,7 @@
*/
struct vlc_audio_output_events {
+ void (*timing_report)(audio_output_t *, vlc_tick_t system_ts, vlc_tick_t audio_ts);
void (*drained_report)(audio_output_t *);
void (*volume_report)(audio_output_t *, float);
void (*mute_report)(audio_output_t *, bool);
@@ -181,7 +182,7 @@ struct audio_output
*/
int (*time_get)(audio_output_t *, vlc_tick_t *delay);
- /**< Estimates playback buffer latency (mandatory, cannot be NULL).
+ /**< Estimates playback buffer latency (can be NULL).
*
* This callback computes an estimation of the delay until the current
* tail of the audio output buffer would be rendered. This is essential
@@ -189,16 +190,18 @@ struct audio_output
* clock and the media upstream clock (if any).
*
* If the audio output clock is exactly synchronized with the system
- * monotonic clock (i.e. vlc_tick_now()), then aout_TimeGetDefault() can
- * implement this callback. In that case, drain must be implemented (since
- * the default implementation uses the delay to wait for the end of the
- * stream).
+ * monotonic clock (i.e. vlc_tick_now()), then this callback is not
+ * mandatory. In that case, drain must be implemented (since the default
+ * implementation uses the delay to wait for the end of the stream).
*
* This callback is called before the first play() in order to get the
* initial delay (the hw latency). Most modules won't be able to know this
* latency before the first play. In that case, they should return -1 and
* handle the first play() date, cf. play() documentation.
*
+ * \warning It is recommended to report the audio delay via
+ * aout_TimingReport(). In that case, time_get should not be implemented.
+ *
* \param delay pointer to the delay until the next sample to be written
* to the playback buffer is rendered [OUT]
* \return 0 on success, non-zero on failure or lack of data
@@ -297,9 +300,23 @@ struct audio_output
const struct vlc_audio_output_events *events;
};
-static inline int aout_TimeGet(audio_output_t *aout, vlc_tick_t *delay)
+/**
+ * Report a new timing point
+ *
+ * system_ts doesn't have to be close to vlc_tick_now(). Any valid { system_ts,
+ * audio_ts } points in the past are sufficient to update the clock.
+ *
+ * \note audio_ts starts at 0 and should not take the block PTS into account.
+ *
+ * It is important to report the first point as soon as possible (and the
+ * following points if the audio delay take some time to be stabilized). Once
+ * the audio is stabilized, it is recommended to report timing points every few
+ * seconds.
+ */
+static inline void aout_TimingReport(audio_output_t *aout, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts)
{
- return aout->time_get(aout, delay);
+ return aout->events->timing_report(aout, system_ts, audio_ts);
}
/**
@@ -369,16 +386,6 @@ static inline void aout_RestartRequest(audio_output_t *aout, unsigned mode)
aout->events->restart_request(aout, mode);
}
-/**
- * Default implementation for audio_output_t.time_get
- */
-static inline int aout_TimeGetDefault(audio_output_t *aout,
- vlc_tick_t *restrict delay)
-{
- (void) aout; (void) delay;
- return -1;
-}
-
/**
* Default implementation for audio_output_t.pause
*
=====================================
modules/audio_output/amem.c
=====================================
@@ -374,7 +374,7 @@ static int Open (vlc_object_t *obj)
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
- aout->time_get = aout_TimeGetDefault;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
=====================================
modules/audio_output/file.c
=====================================
@@ -161,7 +161,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
return VLC_EGENERIC;
}
- p_aout->time_get = aout_TimeGetDefault;
+ p_aout->time_get = NULL;
p_aout->play = Play;
p_aout->pause = Pause;
p_aout->flush = Flush;
=====================================
modules/audio_output/pulse.c
=====================================
@@ -70,6 +70,7 @@ typedef struct
bool draining;
pa_cvolume cvolume; /**< actual sink input volume */
vlc_tick_t last_date; /**< Play system timestamp of last buffer */
+ pa_usec_t flush_rt;
pa_volume_t volume_force; /**< Forced volume (stream must be NULL) */
pa_stream_flags_t flags_force; /**< Forced flags (stream must be NULL) */
@@ -102,6 +103,12 @@ static void drain_trigger_cb(pa_mainloop_api *api, pa_time_event *e,
(void) api; (void) e; (void) tv;
}
+static void stream_wait_cb(pa_stream *s, int success, void *userdata)
+{
+ (void) s; (void) success;
+ pa_threaded_mainloop_signal(userdata, 0);
+}
+
static int TriggerDrain(audio_output_t *aout)
{
aout_sys_t *sys = aout->sys;
@@ -227,9 +234,13 @@ static void stream_stop(pa_stream *s, audio_output_t *aout)
sys->trigger = NULL;
}
- op = pa_stream_cork(s, 1, NULL, NULL);
+ op = pa_stream_cork(s, 1, stream_wait_cb, sys->mainloop);
if (op != NULL)
+ {
+ while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait(sys->mainloop);
pa_operation_unref(op);
+ }
}
static void stream_trigger_cb(pa_mainloop_api *api, pa_time_event *e,
@@ -295,8 +306,35 @@ static void stream_latency_cb(pa_stream *s, void *userdata)
TriggerDrain(aout);
return; /* nothing to do if buffers are (still) empty */
}
+
if (pa_stream_is_corked(s) > 0)
stream_start(s, aout, sys->last_date);
+
+ if (pa_stream_is_corked(s) == 0)
+ {
+ pa_usec_t rt;
+ if (pa_stream_get_time(s, &rt) == 0 && rt > 0)
+ {
+ if (likely(rt >= sys->flush_rt))
+ {
+ rt -= sys->flush_rt;
+ aout_TimingReport(aout, vlc_tick_now(), rt);
+ }
+#ifndef NDEBUG
+ else
+ {
+ /* The time returned by pa_stream_get_time() might be smaller
+ * than flush_rt just after a flush (depending on
+ * transport_usec, sink_usec), but the current read index
+ * should always be superior or equal. */
+ const pa_sample_spec *ss = pa_stream_get_sample_spec(s);
+ const pa_timing_info *ti = pa_stream_get_timing_info(s);
+ if (ti != NULL && !ti->read_index_corrupt)
+ assert(pa_bytes_to_usec(ti->read_index, ss) >= sys->flush_rt);
+ }
+#endif
+ }
+ }
}
@@ -474,26 +512,6 @@ static void context_cb(pa_context *ctx, pa_subscription_event_type_t type,
/*** VLC audio output callbacks ***/
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
- aout_sys_t *sys = aout->sys;
- pa_stream *s = sys->stream;
- int ret = -1;
-
- pa_threaded_mainloop_lock(sys->mainloop);
- if (pa_stream_is_corked(s) <= 0)
- { /* latency is relevant only if not corked */
- vlc_tick_t delta = vlc_pa_get_latency(aout, sys->context, s);
- if (delta != VLC_TICK_INVALID)
- {
- *delay = delta;
- ret = 0;
- }
- }
- pa_threaded_mainloop_unlock(sys->mainloop);
- return ret;
-}
-
static void data_free(void *data)
{
block_Release(data);
@@ -517,7 +535,13 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
sys->last_date = date;
if (pa_stream_is_corked(s) > 0)
- stream_start(s, aout, date);
+ {
+ /* Trigger a latency update, that will update the stream_start timer
+ * using the last date. */
+ pa_operation *op = pa_stream_update_timing_info(s, NULL, NULL);
+ if (op != NULL)
+ pa_operation_unref(op);
+ }
#if 0 /* Fault injector to test underrun recovery */
static volatile unsigned u = 0;
@@ -580,8 +604,14 @@ static void Flush(audio_output_t *aout)
if (op != NULL)
pa_operation_unref(op);
sys->last_date = VLC_TICK_INVALID;
+
stream_stop(s, aout);
+ const pa_sample_spec *ss = pa_stream_get_sample_spec(s);
+ const pa_timing_info *ti = pa_stream_get_timing_info(s);
+ if (ti != NULL && !ti->read_index_corrupt)
+ sys->flush_rt = pa_bytes_to_usec(ti->read_index, ss);
+
pa_threaded_mainloop_unlock(sys->mainloop);
}
@@ -615,11 +645,18 @@ static void Drain(audio_output_t *aout)
else
{
sys->last_date = VLC_TICK_INVALID;
+ sys->flush_rt = 0;
/* XXX: Loosy drain emulation.
* See #18141: drain callback is never received */
sys->draining = true;
- TriggerDrain(aout);
+
+ /* Trigger a latency update, that will update the drain timer */
+ op = pa_stream_update_timing_info(s, NULL, NULL);
+ if (op != NULL)
+ pa_operation_unref(op);
+ else
+ aout_DrainedReport(aout);
}
pa_threaded_mainloop_unlock(sys->mainloop);
@@ -826,7 +863,6 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
/* Stream parameters */
pa_stream_flags_t flags = sys->flags_force
| PA_STREAM_START_CORKED
- | PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_NOT_MONOTONIC
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_FIX_RATE;
@@ -852,6 +888,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
sys->draining = false;
pa_cvolume_init(&sys->cvolume);
sys->last_date = VLC_TICK_INVALID;
+ sys->flush_rt = 0;
pa_format_info *formatv = pa_format_info_new();
formatv->encoding = encoding;
@@ -1068,7 +1105,7 @@ static int Open(vlc_object_t *obj)
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
- aout->time_get = TimeGet;
+ aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
=====================================
src/audio_output/aout_internal.h
=====================================
@@ -139,6 +139,8 @@ struct vlc_aout_stream_cfg
struct vlc_clock_t *clock;
const char *str_id;
const audio_replay_gain_t *replay_gain;
+ void (*notify_latency_cb)(void *data);
+ void *notify_latency_data;
};
vlc_aout_stream *vlc_aout_stream_New(audio_output_t *p_aout,
@@ -151,14 +153,19 @@ void vlc_aout_stream_ChangeRate(vlc_aout_stream *stream, float rate);
void vlc_aout_stream_ChangeDelay(vlc_aout_stream *stream, vlc_tick_t delay);
void vlc_aout_stream_Flush(vlc_aout_stream *stream);
void vlc_aout_stream_Drain(vlc_aout_stream *stream);
+void vlc_aout_stream_UpdateLatency(vlc_aout_stream *stream);
/* Contrary to other vlc_aout_stream_*() functions, this function can be called from
* any threads */
bool vlc_aout_stream_IsDrained(vlc_aout_stream *stream);
/* Called from output.c */
+void vlc_aout_stream_NotifyTiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts);
void vlc_aout_stream_NotifyDrained(vlc_aout_stream *stream);
void vlc_aout_stream_NotifyGain(vlc_aout_stream *stream, float gain);
void vlc_aout_stream_RequestRestart(vlc_aout_stream *stream, unsigned);
+void vlc_aout_stream_RequestRetiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts);
void aout_InputRequestRestart(audio_output_t *aout);
=====================================
src/audio_output/dec.c
=====================================
@@ -39,6 +39,13 @@
#include "clock/clock.h"
#include "libvlc.h"
+#define MAX_TIMING_POINT 16
+struct timing_point
+{
+ vlc_tick_t system_ts;
+ vlc_tick_t audio_ts;
+};
+
struct vlc_aout_stream
{
aout_instance_t *instance;
@@ -61,6 +68,29 @@ struct vlc_aout_stream
} sync;
vlc_tick_t original_pts;
+ struct
+ {
+ vlc_mutex_t lock; /* Guard data, count and head */
+
+ /* Circular array */
+ struct timing_point data[MAX_TIMING_POINT];
+ /* Number of points in the array */
+ size_t count;
+ /* Index of the next point to write */
+ size_t head;
+
+ bool running;
+
+ vlc_tick_t first_pts;
+ vlc_tick_t last_pts; /* Used for stream_TimeGet() emulation */
+
+ struct timing_point rate_point;
+ float rate;
+
+ void (*notify_latency_cb)(void *data);
+ void *notify_latency_data;
+ } timing_points;
+
const char *str_id;
/* Original input format and profile, won't change for the lifetime of a
@@ -99,6 +129,43 @@ static inline struct vlc_tracer *aout_stream_tracer(vlc_aout_stream *stream)
vlc_object_get_tracer(VLC_OBJECT(aout_stream_aout(stream)));
}
+static int stream_TimeGet(vlc_aout_stream *stream, vlc_tick_t *delay)
+{
+ audio_output_t *aout = aout_stream_aout(stream);
+
+ if (aout->time_get == NULL)
+ {
+ if (stream->timing_points.last_pts == VLC_TICK_INVALID)
+ return -1;
+
+ /* Interpolate the last updated point. */
+ vlc_tick_t system_now = vlc_tick_now();
+
+ vlc_tick_t play_date =
+ vlc_clock_ConvertToSystem(stream->sync.clock, system_now,
+ stream->timing_points.last_pts,
+ stream->sync.rate);
+ *delay = play_date - system_now;
+ return 0;
+ }
+ return aout->time_get(aout, delay);
+}
+
+static void stream_Discontinuity(vlc_aout_stream *stream)
+{
+ stream->sync.discontinuity = true;
+ stream->original_pts = VLC_TICK_INVALID;
+
+ vlc_mutex_lock(&stream->timing_points.lock);
+ stream->timing_points.head = stream->timing_points.count = 0;
+ vlc_mutex_unlock(&stream->timing_points.lock);
+
+ stream->timing_points.first_pts =
+ stream->timing_points.last_pts = VLC_TICK_INVALID;
+
+ stream->timing_points.running = false;
+}
+
static void stream_Reset(vlc_aout_stream *stream)
{
aout_owner_t *owner = aout_stream_owner(stream);
@@ -130,12 +197,14 @@ static void stream_Reset(vlc_aout_stream *stream)
}
}
+ stream->timing_points.rate_point.audio_ts = VLC_TICK_INVALID;
+ stream->timing_points.rate = 1.0;
+
atomic_store_explicit(&stream->drained, false, memory_order_relaxed);
atomic_store_explicit(&stream->drain_deadline, VLC_TICK_INVALID,
memory_order_relaxed);
- stream->sync.discontinuity = true;
- stream->original_pts = VLC_TICK_INVALID;
+ stream_Discontinuity(stream);
}
/**
@@ -193,6 +262,25 @@ vlc_aout_stream * vlc_aout_stream_New(audio_output_t *p_aout,
stream->sync.clock = cfg->clock;
stream->str_id = cfg->str_id;
+ stream->timing_points.rate_point.audio_ts = VLC_TICK_INVALID;
+ stream->timing_points.rate = 1.f;
+
+ vlc_mutex_init(&stream->timing_points.lock);
+ stream->timing_points.notify_latency_cb = cfg->notify_latency_cb;
+ stream->timing_points.notify_latency_data = cfg->notify_latency_data;
+
+ stream->sync.rate = 1.f;
+ stream->sync.resamp_type = AOUT_RESAMPLING_NONE;
+ stream->sync.delay = stream->sync.request_delay = 0;
+ stream_Discontinuity(stream);
+
+ atomic_init (&stream->buffers_lost, 0);
+ atomic_init (&stream->buffers_played, 0);
+ atomic_store_explicit(&owner->vp.update, true, memory_order_relaxed);
+
+ atomic_init(&stream->drained, false);
+ atomic_init(&stream->drain_deadline, VLC_TICK_INVALID);
+
stream->filters = NULL;
stream->filters_cfg = AOUT_FILTERS_CFG_INIT;
if (aout_OutputNew(p_aout, stream, &stream->mixer_format, stream->input_profile,
@@ -224,19 +312,6 @@ error:
}
}
- stream->sync.rate = 1.f;
- stream->sync.resamp_type = AOUT_RESAMPLING_NONE;
- stream->sync.discontinuity = true;
- stream->sync.delay = stream->sync.request_delay = 0;
- stream->original_pts = VLC_TICK_INVALID;
-
- atomic_init (&stream->buffers_lost, 0);
- atomic_init (&stream->buffers_played, 0);
- atomic_store_explicit(&owner->vp.update, true, memory_order_relaxed);
-
- atomic_init(&stream->drained, false);
- atomic_init(&stream->drain_deadline, VLC_TICK_INVALID);
-
return stream;
}
@@ -258,6 +333,7 @@ void vlc_aout_stream_Delete (vlc_aout_stream *stream)
}
if (stream->volume != NULL)
aout_volume_Delete(stream->volume);
+
free(stream);
}
@@ -382,15 +458,13 @@ static void stream_Silence (vlc_aout_stream *stream, vlc_tick_t length, vlc_tick
aout->play(aout, block, system_pts);
}
-static void stream_RequestRetiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
- vlc_tick_t audio_ts)
+static void stream_HandleDrift(vlc_aout_stream *stream, vlc_tick_t drift,
+ vlc_tick_t audio_ts)
{
aout_owner_t *owner = aout_stream_owner(stream);
audio_output_t *aout = aout_stream_aout(stream);
float rate = stream->sync.rate;
- vlc_tick_t drift =
- vlc_clock_Update(stream->sync.clock, system_ts, audio_ts, rate);
if (unlikely(drift == VLC_TICK_MAX) || owner->bitexact)
return; /* cf. VLC_TICK_MAX comment in vlc_aout_stream_Play() */
@@ -527,9 +601,8 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
* pts = vlc_tick_now() + delay
*/
vlc_tick_t delay;
- audio_output_t *aout = aout_stream_aout(stream);
- if (aout_TimeGet(aout, &delay) != 0)
+ if (stream_TimeGet(stream, &delay) != 0)
return; /* nothing can be done if timing is unknown */
if (stream->sync.discontinuity)
@@ -537,7 +610,7 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
/* Chicken-egg situation for most aout modules that can't be started
* deferred (all except PulseAudio). These modules will start to play
* data immediately and ignore the given play_date (that take the clock
- * jitter into account). We don't want to let stream_RequestRetiming()
+ * jitter into account). We don't want to let stream_HandleDrift()
* handle the first silence (from the "Early audio output" case) since
* this function will first update the clock without taking the jitter
* into account. Therefore, we manually insert silence that correspond
@@ -550,12 +623,113 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
if (jitter > 0)
{
stream_Silence(stream, jitter, dec_pts - delay);
- if (aout_TimeGet(aout, &delay) != 0)
+ if (stream_TimeGet(stream, &delay) != 0)
return;
}
}
- stream_RequestRetiming(stream, system_now + delay, dec_pts);
+ vlc_tick_t drift = vlc_clock_Update(stream->sync.clock, system_now + delay,
+ dec_pts, stream->sync.rate);
+
+ stream_HandleDrift(stream, drift, dec_pts);
+}
+
+void vlc_aout_stream_NotifyTiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts)
+{
+ /* This function might be called from high priority audio threads (so, no
+ * mutexes, allocation, IO, debug, wait...). That is why we use a circular
+ * buffer of points. The vlc_aout_stream user will read these points and
+ * update the clock from vlc_aout_stream_Play() and
+ * vlc_aout_stream_UpdateLatency(). */
+
+ /* VLC mutexes use atomic and the reader will only do very fast
+ * operations (copy of the timing_point data). */
+ vlc_mutex_lock(&stream->timing_points.lock);
+
+ size_t write_idx;
+ if (stream->timing_points.count == MAX_TIMING_POINT)
+ {
+ write_idx = stream->timing_points.head;
+ stream->timing_points.head = (stream->timing_points.head + 1)
+ % MAX_TIMING_POINT;
+ }
+ else
+ write_idx = stream->timing_points.count++;
+
+ struct timing_point *p = &stream->timing_points.data[write_idx];
+ p->system_ts = system_ts;
+ p->audio_ts = audio_ts;
+
+ vlc_mutex_unlock(&stream->timing_points.lock);
+
+ if (stream->timing_points.notify_latency_cb != NULL)
+ stream->timing_points.notify_latency_cb(
+ stream->timing_points.notify_latency_data);
+}
+
+static vlc_tick_t
+stream_ReadTimingPoints(vlc_aout_stream *stream)
+{
+ struct timing_point points[MAX_TIMING_POINT];
+ size_t count = 0;
+
+ vlc_mutex_lock(&stream->timing_points.lock);
+ assert(stream->timing_points.count <= MAX_TIMING_POINT
+ && stream->timing_points.head < MAX_TIMING_POINT);
+
+ size_t initial_read = stream->timing_points.head;
+ for (size_t read = stream->timing_points.head;
+ count < stream->timing_points.count; ++read, ++count)
+ {
+ points[count] = stream->timing_points.data[read % MAX_TIMING_POINT];
+ points[count].audio_ts += stream->timing_points.first_pts;
+ }
+
+ stream->timing_points.count = stream->timing_points.head = 0;
+ vlc_mutex_unlock(&stream->timing_points.lock);
+
+ if (count == 0)
+ return 0;
+
+ if (initial_read > 0)
+ {
+ /* This is not critical to miss timing points, the more important is
+ * to get the last ones. Log it anyway since it can help identifying
+ * buggy audio outputs spamming timing points. */
+
+ audio_output_t *aout = aout_stream_aout(stream);
+ msg_Dbg(aout, "Missed %zu timing points", initial_read);
+ }
+ vlc_tick_t drift = 0; /* Use only the last updated drift */
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ struct timing_point *tp = &points[i];
+ struct timing_point *rp = &stream->timing_points.rate_point;
+
+ if (rp->audio_ts != VLC_TICK_INVALID)
+ {
+ /* Drop timing updates that comes before the rate change */
+ if (tp->system_ts < rp->system_ts)
+ continue;
+
+ /* Fix the audio timestamp with the rate */
+ tp->audio_ts = rp->audio_ts + (tp->system_ts - rp->system_ts)
+ * stream->timing_points.rate;
+ }
+
+ drift = vlc_clock_Update(stream->sync.clock, points[i].system_ts,
+ points[i].audio_ts, stream->timing_points.rate);
+ }
+
+ return drift;
+}
+
+void vlc_aout_stream_UpdateLatency(vlc_aout_stream *stream)
+{
+ if (stream->timing_points.running)
+ stream_ReadTimingPoints(stream);
}
/*****************************************************************************
@@ -576,10 +750,7 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
goto drop; /* Pipeline is unrecoverably broken :-( */
if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
- {
- stream->sync.discontinuity = true;
- stream->original_pts = VLC_TICK_INVALID;
- }
+ stream_Discontinuity(stream);
if (stream->original_pts == VLC_TICK_INVALID)
{
@@ -625,7 +796,13 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
/* Drift correction */
vlc_tick_t system_now = vlc_tick_now();
- stream_Synchronize(stream, system_now, original_pts);
+ if (aout->time_get != NULL)
+ stream_Synchronize(stream, system_now, original_pts);
+ else
+ {
+ vlc_tick_t drift = stream_ReadTimingPoints(stream);
+ stream_HandleDrift(stream, drift, original_pts);
+ }
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, system_now, original_pts,
@@ -640,15 +817,46 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
vlc_audio_meter_Process(&owner->meter, block, play_date);
+ if (!stream->timing_points.running)
+ {
+ stream->timing_points.running = true;
+
+ /* Assert that no timing points are updated between flush and play */
+#ifndef NDEBUG
+ vlc_mutex_lock(&stream->timing_points.lock);
+ assert(stream->timing_points.count == 0);
+ vlc_mutex_unlock(&stream->timing_points.lock);
+#endif
+ }
+
+ if (aout->time_get == NULL
+ && stream->sync.rate != stream->timing_points.rate)
+ {
+ /* Save the first timing point seeing a rate change */
+ stream->timing_points.rate_point = (struct timing_point) {
+ .system_ts = play_date,
+ .audio_ts = original_pts,
+ };
+ stream->timing_points.rate = stream->sync.rate;
+
+ /* Update the clock immediately with the new rate, instead of waiting
+ * for a timing update that could come too late (after 1second). */
+ vlc_clock_Update(stream->sync.clock, play_date,
+ original_pts, stream->sync.rate);
+ }
+
/* Output */
stream->sync.discontinuity = false;
aout->play(aout, block, play_date);
+ if (stream->timing_points.first_pts == VLC_TICK_INVALID)
+ stream->timing_points.first_pts = original_pts;
+ stream->timing_points.last_pts = original_pts;
+
atomic_fetch_add_explicit(&stream->buffers_played, 1, memory_order_relaxed);
return ret;
drop:
- stream->sync.discontinuity = true;
- stream->original_pts = VLC_TICK_INVALID;
+ stream_Discontinuity(stream);
block_Release (block);
atomic_fetch_add_explicit(&stream->buffers_lost, 1, memory_order_relaxed);
return ret;
@@ -678,6 +886,17 @@ void vlc_aout_stream_ChangePause(vlc_aout_stream *stream, bool paused, vlc_tick_
aout->pause(aout, paused, date);
else if (paused)
aout->flush(aout);
+
+ /* Update the rate point after the pause */
+ if (aout->time_get == NULL && !paused
+ && stream->timing_points.rate_point.audio_ts != VLC_TICK_INVALID)
+ {
+ vlc_tick_t play_date =
+ vlc_clock_ConvertToSystem(stream->sync.clock, date,
+ stream->timing_points.rate_point.audio_ts,
+ stream->sync.rate);
+ stream->timing_points.rate_point.system_ts = play_date;
+ }
}
}
@@ -699,9 +918,9 @@ void vlc_aout_stream_Flush(vlc_aout_stream *stream)
if (tracer != NULL)
vlc_tracer_TraceEvent(tracer, "RENDER", stream->str_id, "flushed");
- stream_Reset(stream);
if (stream->mixer_format.i_format)
aout->flush(aout);
+ stream_Reset(stream);
}
void vlc_aout_stream_NotifyGain(vlc_aout_stream *stream, float gain)
@@ -763,7 +982,7 @@ void vlc_aout_stream_Drain(vlc_aout_stream *stream)
vlc_tick_t drain_deadline = vlc_tick_now();
vlc_tick_t delay;
- if (aout_TimeGet(aout, &delay) == 0)
+ if (stream_TimeGet(stream, &delay) == 0)
drain_deadline += delay;
/* else the deadline is now, and vlc_aout_stream_IsDrained() will
* return true on the first call. */
@@ -776,6 +995,5 @@ void vlc_aout_stream_Drain(vlc_aout_stream *stream)
if (stream->filters)
aout_FiltersResetClock(stream->filters);
- stream->sync.discontinuity = true;
- stream->original_pts = VLC_TICK_INVALID;
+ stream_Discontinuity(stream);
}
=====================================
src/audio_output/output.c
=====================================
@@ -63,6 +63,14 @@ static int var_CopyDevice (vlc_object_t *src, const char *name,
return var_Set (dst, "audio-device", value);
}
+static void aout_TimingNotify(audio_output_t *aout, vlc_tick_t system_ts,
+ vlc_tick_t audio_ts)
+{
+ aout_owner_t *owner = aout_owner (aout);
+ assert(owner->main_stream);
+ vlc_aout_stream_NotifyTiming(owner->main_stream, system_ts, audio_ts);
+}
+
static void aout_DrainedNotify(audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
@@ -168,6 +176,7 @@ static int aout_GainNotify (audio_output_t *aout, float gain)
}
static const struct vlc_audio_output_events aout_events = {
+ aout_TimingNotify,
aout_DrainedNotify,
aout_VolumeNotify,
aout_MuteNotify,
@@ -758,9 +767,10 @@ int aout_OutputNew(audio_output_t *aout, vlc_aout_stream *stream,
for (size_t i = 0; formats[i] != 0 && ret != VLC_SUCCESS; ++i)
{
filter_fmt->i_format = fmt->i_format = formats[i];
+ owner->main_stream = stream;
ret = aout->start(aout, fmt);
- if (ret == 0)
- owner->main_stream = stream;
+ if (ret != 0)
+ owner->main_stream = NULL;
}
vlc_mutex_unlock(&owner->lock);
if (ret)
@@ -772,7 +782,7 @@ int aout_OutputNew(audio_output_t *aout, vlc_aout_stream *stream,
"failing back to linear format");
return -1;
}
- assert(aout->flush && aout->play && aout->time_get && aout->pause);
+ assert(aout->flush && aout->play && aout->pause);
/* Autoselect the headphones mode if available and if the user didn't
* request any mode */
=====================================
src/clock/clock.c
=====================================
@@ -161,6 +161,12 @@ static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock,
main_clock->offset =
system_now - ((vlc_tick_t) (ts * main_clock->coeff / rate));
+ if (main_clock->tracer != NULL && clock->track_str_id)
+ vlc_tracer_Trace(main_clock->tracer, VLC_TRACE("type", "RENDER"),
+ VLC_TRACE("id", clock->track_str_id),
+ VLC_TRACE("offset", main_clock->offset),
+ VLC_TRACE_END);
+
main_clock->last = clock_point_Create(system_now, ts);
main_clock->rate = rate;
@@ -605,7 +611,7 @@ vlc_clock_t *vlc_clock_main_CreateMaster(vlc_clock_main_t *main_clock,
vlc_clock_t *vlc_clock_main_CreateInputMaster(vlc_clock_main_t *main_clock)
{
/* The master has always the 0 priority */
- vlc_clock_t *clock = vlc_clock_main_Create(main_clock, NULL, 0, NULL, NULL);
+ vlc_clock_t *clock = vlc_clock_main_Create(main_clock, "input", 0, NULL, NULL);
if (!clock)
return NULL;
=====================================
src/input/decoder.c
=====================================
@@ -339,6 +339,13 @@ static bool aout_replaygain_changed( const audio_replay_gain_t *a,
return false;
}
+static void aout_stream_NotifyLatency(void *data)
+{
+ /* Wake the DecoderThread in order to update the audio latency via
+ * vlc_aout_stream_UpdateLatency() */
+ vlc_fifo_Signal(data);
+}
+
static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec )
{
vlc_input_decoder_t *p_owner = dec_get_owner( p_dec );
@@ -401,7 +408,9 @@ static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec )
.profile = p_dec->fmt_out.i_profile,
.clock = p_owner->p_clock,
.str_id = p_owner->psz_id,
- .replay_gain = &p_dec->fmt_out.audio_replay_gain
+ .replay_gain = &p_dec->fmt_out.audio_replay_gain,
+ .notify_latency_cb = aout_stream_NotifyLatency,
+ .notify_latency_data = p_owner->p_fifo,
};
p_astream = vlc_aout_stream_New( p_aout, &cfg );
if( p_astream == NULL )
@@ -1758,6 +1767,13 @@ static void *DecoderThread( void *p_data )
p_owner->b_idle = true;
vlc_cond_signal( &p_owner->wait_acknowledge );
vlc_fifo_Wait( p_owner->p_fifo );
+
+ /* Update the audio latency after a possible long wait in order
+ * to update the audio clock as soon as possible. */
+ if( p_owner->dec.fmt_in.i_cat == AUDIO_ES &&
+ p_owner->p_astream != NULL )
+ vlc_aout_stream_UpdateLatency( p_owner->p_astream );
+
p_owner->b_idle = false;
continue;
}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0718770a5c56a9b0ee36bc6621a08f367aa049a6...9eabb2821e83037efadb5b53f66d8316cc33ef28
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0718770a5c56a9b0ee36bc6621a08f367aa049a6...9eabb2821e83037efadb5b53f66d8316cc33ef28
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list