[vlc-commits] [Git][videolan/vlc][master] 22 commits: clock: move variables used to convert in a new struct
Steve Lhomme (@robUx4)
gitlab at videolan.org
Sat Jul 13 11:57:54 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
34d32f14 by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: move variables used to convert in a new struct
- - - - -
32cadbfc by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: early return
- - - - -
47bde44e by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: add vlc_clock_main_CreateInputSlave
- - - - -
0b21de86 by Thomas Guillem at 2024-07-13T11:29:36+00:00
es_out: use vlc_clock_main_CreateInputSlave()
- - - - -
c18be747 by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: reset the clock on SetFirstPCR()
- - - - -
cf93349b by Thomas Guillem at 2024-07-13T11:29:36+00:00
input_clock: add discontinuity to the update callback
- - - - -
36713cdc by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: create a new context when SetFirstPcr() is called
The previous context will be used by other output clocks in read-only
until all outputs switch to the newly created context (when all outputs
do update using the clock_id of the new context).
The previous contexts are removed when not used anymore.
- - - - -
4ea725d7 by Thomas Guillem at 2024-07-13T11:29:36+00:00
es_out: handle input clock discontinuity
Call vlc_clock_main_SetFirstPcr() in case of PCR discontinuity.
- - - - -
bf5dd04b by Thomas Guillem at 2024-07-13T11:29:36+00:00
clock: expose the clock context used for conversion
Increasing uint32_t, that will be used by output to identify when a
converted PTS discontinued with the previous one.
- - - - -
d253bec5 by Thomas Guillem at 2024-07-13T11:29:36+00:00
vout: always convert the just decoded picture
- - - - -
60c70e2d by Thomas Guillem at 2024-07-13T11:29:36+00:00
vout: merge if conditions
- - - - -
34b1c4a2 by Thomas Guillem at 2024-07-13T11:29:36+00:00
vout: fix invalid PTS when switching clock contexts
When using a deinterlace module (from the static chain).
- - - - -
183bc17c by Thomas Guillem at 2024-07-13T11:29:36+00:00
aout: trigger a discontinuity when switching clock contexts
- - - - -
156fa18c by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: rework ctx_flags
Move them up, and use a define instead.
- - - - -
de054823 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: store creation flags in ctx
- - - - -
37062157 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: add discontinuities option
- - - - -
d3ee6c48 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: forward ctx to submodules
- - - - -
028c6847 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: add AUDIO_INSTANT_DRAIN flag
- - - - -
a769f57a by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: rename REPORT_LIST
- - - - -
1f7bac2b by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: report aout first pts
- - - - -
b2a86a28 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: player: add test_clock_discontinuities
Check that the audio output receive all discontinuities triggered by
clock contexts switch.
- - - - -
f8b6c4e4 by Thomas Guillem at 2024-07-13T11:29:36+00:00
test: clock: test contexts
Check the clock API when using several clock contexts.
- - - - -
11 changed files:
- src/audio_output/dec.c
- src/clock/clock.c
- src/clock/clock.h
- src/clock/input_clock.c
- src/clock/input_clock.h
- src/input/decoder.c
- src/input/es_out.c
- src/video_output/video_output.c
- src/video_output/vout_subpictures.c
- test/src/clock/clock.c
- test/src/player/player.c
Changes:
=====================================
src/audio_output/dec.c
=====================================
@@ -39,6 +39,8 @@
#include "clock/clock.h"
#include "libvlc.h"
+#define BLOCK_FLAG_CORE_PRIVATE_FILTERED (1 << BLOCK_FLAG_CORE_PRIVATE_SHIFT)
+
struct vlc_aout_stream
{
aout_instance_t *instance;
@@ -59,6 +61,7 @@ struct vlc_aout_stream
struct
{
struct vlc_clock_t *clock;
+ uint32_t clock_id;
float rate; /**< Play-out speed rate */
vlc_tick_t resamp_start_drift; /**< Resampler drift absolute value */
int resamp_type; /**< Resampler mode (FIXME: redundant / resampling) */
@@ -367,6 +370,7 @@ vlc_aout_stream * vlc_aout_stream_New(audio_output_t *p_aout,
stream->filter_format = stream->mixer_format = stream->input_format = *p_format;
stream->sync.clock = cfg->clock;
+ stream->sync.clock_id = 0;
stream->str_id = cfg->str_id;
stream->timing.rate_audio_ts = VLC_TICK_INVALID;
@@ -570,7 +574,7 @@ static void stream_Silence (vlc_aout_stream *stream, vlc_tick_t length, vlc_tick
vlc_clock_Lock(stream->sync.clock);
const vlc_tick_t system_pts =
vlc_clock_ConvertToSystem(stream->sync.clock, system_now, pts,
- stream->sync.rate);
+ stream->sync.rate, NULL);
vlc_clock_Unlock(stream->sync.clock);
stream->timing.played_samples += block->i_nb_samples;
aout->play(aout, block, system_pts);
@@ -844,7 +848,7 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
if (unlikely(ret == AOUT_DEC_FAILED))
goto drop; /* Pipeline is unrecoverably broken :-( */
- if (stream->filters)
+ if (stream->filters && (block->i_flags & BLOCK_FLAG_CORE_PRIVATE_FILTERED) == 0)
{
if (atomic_load_explicit(&owner->vp.update, memory_order_relaxed))
{
@@ -879,11 +883,20 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
/* Drift correction */
vlc_tick_t system_now = vlc_tick_now();
+ uint32_t clock_id;
vlc_clock_Lock(stream->sync.clock);
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, system_now, block->i_pts,
- stream->sync.rate);
+ stream->sync.rate, &clock_id);
vlc_clock_Unlock(stream->sync.clock);
+
+ if (clock_id != stream->sync.clock_id)
+ {
+ stream->sync.clock_id = clock_id;
+ block->i_flags |= BLOCK_FLAG_CORE_PRIVATE_FILTERED;
+ return stream_StartDiscontinuity(stream, block);
+ }
+
stream_Synchronize(stream, system_now, play_date, block->i_pts);
vlc_audio_meter_Process(&owner->meter, block, play_date);
@@ -952,7 +965,7 @@ void vlc_aout_stream_ChangePause(vlc_aout_stream *stream, bool paused, vlc_tick_
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, date,
stream->timing.rate_audio_ts,
- stream->sync.rate);
+ stream->sync.rate, NULL);
vlc_clock_Unlock(stream->sync.clock);
stream->timing.rate_system_ts = play_date;
}
=====================================
src/clock/clock.c
=====================================
@@ -45,6 +45,20 @@ struct vlc_clock_listener_id
void *data;
};
+struct vlc_clock_context
+{
+ double rate;
+ double coeff;
+ vlc_tick_t offset;
+ uint32_t clock_id;
+
+ clock_point_t last;
+ clock_point_t wait_sync_ref;
+
+ struct vlc_list node;
+ struct vlc_list using_clocks;
+};
+
struct vlc_clock_main_t
{
struct vlc_logger *logger;
@@ -61,33 +75,30 @@ struct vlc_clock_main_t
* Linear function
* system = ts * coeff / rate + offset
*/
- clock_point_t last;
average_t coeff_avg; /* Moving average to smooth out the instant coeff */
- double rate;
- double coeff;
- vlc_tick_t offset;
vlc_tick_t delay;
+ struct vlc_clock_context *context;
vlc_tick_t pause_date;
unsigned wait_sync_ref_priority;
- clock_point_t wait_sync_ref; /* When the master */
clock_point_t first_pcr;
vlc_tick_t output_dejitter; /* Delay used to absorb the output clock jitter */
vlc_tick_t input_dejitter; /* Delay used to absorb the input jitter */
struct VLC_VECTOR(vlc_clock_listener_id *) listeners;
+ struct vlc_list prev_contexts;
};
struct vlc_clock_ops
{
- vlc_tick_t (*update)(vlc_clock_t *clock, vlc_tick_t system_now,
- vlc_tick_t ts, double rate,
+ vlc_tick_t (*update)(vlc_clock_t *clock, struct vlc_clock_context *ctx,
+ vlc_tick_t system_now, vlc_tick_t ts, double rate,
unsigned frame_rate, unsigned frame_rate_base);
void (*reset)(vlc_clock_t *clock);
vlc_tick_t (*set_delay)(vlc_clock_t *clock, vlc_tick_t delay);
- vlc_tick_t (*to_system)(vlc_clock_t *clock, vlc_tick_t system_now,
- vlc_tick_t ts, double rate);
+ vlc_tick_t (*to_system)(vlc_clock_t *clock, struct vlc_clock_context *ctx,
+ vlc_tick_t system_now, vlc_tick_t ts, double rate);
};
struct vlc_clock_t
@@ -100,6 +111,12 @@ struct vlc_clock_t
const struct vlc_clock_cbs *cbs;
void *cbs_data;
+
+ struct vlc_list node;
+
+ struct vlc_clock_context *context;
+
+ vlc_tick_t last_conversion;
};
vlc_clock_listener_id *
@@ -181,25 +198,156 @@ static inline void TraceRender(struct vlc_tracer *tracer, const char *type,
VLC_TRACE_END);
}
-static vlc_tick_t main_stream_to_system(vlc_clock_main_t *main_clock,
- vlc_tick_t ts)
+static void context_reset(struct vlc_clock_context *ctx)
+{
+ ctx->coeff = 1.0f;
+ ctx->rate = 1.0f;
+ ctx->offset = VLC_TICK_INVALID;
+ ctx->wait_sync_ref = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
+ ctx->last = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
+}
+
+static void context_init(struct vlc_clock_context *ctx)
+{
+ vlc_list_init(&ctx->using_clocks);
+ context_reset(ctx);
+ ctx->clock_id = 0;
+}
+
+static struct vlc_clock_context *context_new(void)
{
- if (main_clock->last.system == VLC_TICK_INVALID)
+ struct vlc_clock_context *ctx = malloc(sizeof(*ctx));
+ if (unlikely(ctx == NULL))
+ return NULL;
+ context_init(ctx);
+ return ctx;
+}
+
+static vlc_tick_t context_stream_to_system(const struct vlc_clock_context *ctx,
+ vlc_tick_t ts)
+{
+ if (ctx->last.system == VLC_TICK_INVALID)
return VLC_TICK_INVALID;
- return ((vlc_tick_t) (ts * main_clock->coeff / main_clock->rate))
- + main_clock->offset;
+ return ((vlc_tick_t) (ts * ctx->coeff / ctx->rate)) + ctx->offset;
+}
+
+static void
+vlc_clock_remove_current_context(vlc_clock_t *clock)
+{
+ vlc_clock_main_t *main_clock = clock->owner;
+ struct vlc_clock_context *ctx = clock->context;
+ assert(ctx != NULL);
+
+ /* Remove the clock from the context using_clocks */
+ vlc_list_remove(&clock->node);
+ uint32_t clock_id = ctx->clock_id;
+ bool removed = false;
+
+ /* Free the context if it is not used anymore. Don't free the main context
+ * that should stay alive even when no clocks are referencing it. */
+ if (vlc_list_is_empty(&ctx->using_clocks) && ctx != main_clock->context)
+ {
+ /* Remove the context from the list of previous contexts */
+ vlc_list_remove(&ctx->node);
+ free(ctx);
+ removed = true;
+ }
+ clock->context = NULL;
+
+ if (main_clock->logger != NULL && clock->track_str_id != NULL
+ && ctx != main_clock->context)
+ vlc_warning(main_clock->logger, "clock(%s): unused clock context(%u)%s",
+ clock->track_str_id, clock_id, removed ? " (removed)" : "");
+}
+
+static void
+vlc_clock_attach_context(vlc_clock_t *clock, struct vlc_clock_context *ctx)
+{
+ vlc_list_append(&clock->node, &ctx->using_clocks);
+ clock->context = ctx;
+}
+
+static void
+vlc_clock_switch_context(vlc_clock_t *clock, struct vlc_clock_context *ctx)
+{
+ vlc_clock_main_t *main_clock = clock->owner;
+
+ vlc_clock_remove_current_context(clock);
+ vlc_clock_attach_context(clock, ctx);
+
+ if (main_clock->logger != NULL && clock->track_str_id != NULL)
+ vlc_warning(main_clock->logger, "clock(%s): using clock context(%u)",
+ clock->track_str_id, ctx->clock_id);
+}
+
+static void
+context_get_closest(vlc_clock_t *clock, struct vlc_clock_context *ctx,
+ vlc_tick_t system_now, vlc_tick_t ts,
+ struct vlc_clock_context **closest_ctx,
+ vlc_tick_t *closest_diff)
+{
+ /* Find the context which has the smallest gap with the last conversion. */
+ vlc_tick_t converted =
+ clock->ops->to_system(clock, ctx, system_now, ts, 1.0);
+
+ vlc_tick_t diff = converted - clock->last_conversion;
+
+ if (diff < 0)
+ diff = -diff;
+
+ if (diff < *closest_diff)
+ {
+ *closest_diff = diff;
+ *closest_ctx = ctx;
+ }
+}
+
+static struct vlc_clock_context *
+vlc_clock_get_context(vlc_clock_t *clock, vlc_tick_t system_now, vlc_tick_t ts,
+ bool update)
+{
+ vlc_clock_main_t *main_clock = clock->owner;
+
+ /* The input clock is always using the last context */
+ if (clock->context == NULL)
+ return main_clock->context;
+
+ if (vlc_list_is_empty(&main_clock->prev_contexts)
+ || clock->context == main_clock->context)
+ return main_clock->context;
+
+ vlc_tick_t closest_diff = VLC_TICK_MAX;
+ struct vlc_clock_context *ctx = NULL;
+
+ /* Iterate through newer contexts, and not past ones */
+ for (struct vlc_clock_context *ctx_it = clock->context; ctx_it != NULL;
+ ctx_it = vlc_list_next_entry_or_null(&main_clock->prev_contexts, ctx_it,
+ struct vlc_clock_context, node))
+ context_get_closest(clock, ctx_it, system_now, ts, &ctx, &closest_diff);
+
+ context_get_closest(clock, main_clock->context, system_now, ts, &ctx,
+ &closest_diff);
+
+ if (clock->context != ctx && update)
+ {
+ /* Switch the clock context to the newer one. This means that contexts
+ * past the new one will never be selected again by this current clock.
+ * Switch context only when updating, since outputs will hold frames
+ * until there are displayed. */
+ vlc_clock_switch_context(clock, ctx);
+ }
+
+ return ctx;
}
static void vlc_clock_main_reset(vlc_clock_main_t *main_clock)
{
- main_clock->coeff = 1.0f;
- main_clock->rate = 1.0f;
- AvgResetAndFill(&main_clock->coeff_avg, main_clock->coeff);
- main_clock->offset = VLC_TICK_INVALID;
+ struct vlc_clock_context *ctx = main_clock->context;
+
+ context_reset(ctx);
+ AvgResetAndFill(&main_clock->coeff_avg, ctx->coeff);
main_clock->wait_sync_ref_priority = UINT_MAX;
- main_clock->wait_sync_ref =
- main_clock->last = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
vlc_cond_broadcast(&main_clock->cond);
}
@@ -224,19 +372,20 @@ static inline void vlc_clock_on_update(vlc_clock_t *clock,
static void vlc_clock_master_update_coeff(
- vlc_clock_t *clock, vlc_tick_t system_now, vlc_tick_t ts, double rate)
+ vlc_clock_t *clock, struct vlc_clock_context *ctx,
+ vlc_tick_t system_now, vlc_tick_t ts, double rate)
{
vlc_clock_main_t *main_clock = clock->owner;
vlc_mutex_assert(&main_clock->lock);
- if (main_clock->last.system != VLC_TICK_INVALID
- && ts != main_clock->last.stream)
+ if (ctx->last.system != VLC_TICK_INVALID
+ && ts != ctx->last.stream)
{
- if (rate == main_clock->rate)
+ if (rate == ctx->rate)
{
/* We have a reference so we can update coeff */
- vlc_tick_t system_diff = system_now - main_clock->last.system;
- vlc_tick_t stream_diff = ts - main_clock->last.stream;
+ vlc_tick_t system_diff = system_now - ctx->last.system;
+ vlc_tick_t stream_diff = ts - ctx->last.stream;
double instant_coeff = system_diff / (double) stream_diff * rate;
@@ -273,35 +422,35 @@ static void vlc_clock_master_update_coeff(
else
{
AvgUpdate(&main_clock->coeff_avg, instant_coeff);
- main_clock->coeff = AvgGet(&main_clock->coeff_avg);
+ ctx->coeff = AvgGet(&main_clock->coeff_avg);
}
}
}
else
{
main_clock->wait_sync_ref_priority = UINT_MAX;
- main_clock->wait_sync_ref =
+ ctx->wait_sync_ref =
clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
vlc_clock_SendEvent(main_clock, discontinuity);
}
- main_clock->offset =
- system_now - ((vlc_tick_t) (ts * main_clock->coeff / rate));
+ ctx->offset = system_now - ((vlc_tick_t) (ts * ctx->coeff / rate));
if (main_clock->tracer != NULL && clock->track_str_id != NULL)
vlc_tracer_Trace(main_clock->tracer,
VLC_TRACE("type", "RENDER"),
VLC_TRACE("id", clock->track_str_id),
- VLC_TRACE_TICK_NS("offset", main_clock->offset),
- VLC_TRACE("coeff", main_clock->coeff),
+ VLC_TRACE_TICK_NS("offset", ctx->offset),
+ VLC_TRACE("coeff", ctx->coeff),
VLC_TRACE_END);
- main_clock->rate = rate;
+ ctx->rate = rate;
vlc_cond_broadcast(&main_clock->cond);
}
static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock,
+ struct vlc_clock_context *ctx,
vlc_tick_t system_now,
vlc_tick_t ts, double rate,
unsigned frame_rate,
@@ -312,12 +461,15 @@ static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock,
if (unlikely(ts == VLC_TICK_INVALID || system_now == VLC_TICK_INVALID))
return VLC_TICK_INVALID;
+ if (ctx != main_clock->context)
+ return VLC_TICK_INVALID; /* Don't update previous contexts */
+
/* If system_now is VLC_TICK_MAX, the update is forced, don't modify
* anything but only notify the new clock point. */
if (system_now != VLC_TICK_MAX)
{
- vlc_clock_master_update_coeff(clock, system_now, ts, rate);
- main_clock->last = clock_point_Create(system_now, ts);
+ vlc_clock_master_update_coeff(clock, ctx, system_now, ts, rate);
+ ctx->last = clock_point_Create(system_now, ts);
}
/* Fix the reported ts if both master and slaves source are delayed. This
@@ -338,6 +490,12 @@ static void vlc_clock_master_reset(vlc_clock_t *clock)
if (main_clock->tracer != NULL && clock->track_str_id != NULL)
vlc_tracer_TraceEvent(main_clock->tracer, "RENDER", clock->track_str_id,
"reset_user");
+
+ /* Attach to the last and main context in case of reset */
+ struct vlc_clock_context *ctx = main_clock->context;
+ if (clock->context != NULL && clock->context != ctx)
+ vlc_clock_switch_context(clock, ctx);
+
vlc_clock_main_reset(main_clock);
assert(main_clock->delay <= 0);
@@ -387,12 +545,13 @@ static vlc_tick_t vlc_clock_master_set_delay(vlc_clock_t *clock, vlc_tick_t dela
}
static vlc_tick_t
-vlc_clock_monotonic_to_system(vlc_clock_t *clock, vlc_tick_t now,
- vlc_tick_t ts, double rate)
+vlc_clock_monotonic_to_system(vlc_clock_t *clock, struct vlc_clock_context *ctx,
+ vlc_tick_t now, vlc_tick_t ts, double rate)
{
vlc_clock_main_t *main_clock = clock->owner;
- if (clock->priority < main_clock->wait_sync_ref_priority)
+ if (clock->priority < main_clock->wait_sync_ref_priority
+ && ctx == main_clock->context)
{
/* XXX: This input_delay calculation is needed until we (finally) get
* ride of the input clock. This code is adapted from input_clock.c and
@@ -416,46 +575,50 @@ vlc_clock_monotonic_to_system(vlc_clock_t *clock, vlc_tick_t now,
__MAX(input_delay, main_clock->output_dejitter);
main_clock->wait_sync_ref_priority = clock->priority;
- main_clock->wait_sync_ref = clock_point_Create(now + delay, ts);
+ ctx->wait_sync_ref = clock_point_Create(now + delay, ts);
}
- return (ts - main_clock->wait_sync_ref.stream) / rate
- + main_clock->wait_sync_ref.system;
+
+ assert(ctx->wait_sync_ref.stream != VLC_TICK_INVALID);
+
+ return (ts - ctx->wait_sync_ref.stream) / rate + ctx->wait_sync_ref.system;
}
static vlc_tick_t vlc_clock_slave_to_system(vlc_clock_t *clock,
+ struct vlc_clock_context *ctx,
vlc_tick_t now, vlc_tick_t ts,
double rate)
{
vlc_clock_main_t *main_clock = clock->owner;
- vlc_tick_t system = main_stream_to_system(main_clock, ts);
+ vlc_tick_t system = context_stream_to_system(ctx, ts);
if (system == VLC_TICK_INVALID)
{
/* We don't have a master sync point, let's fallback to a monotonic ref
* point */
- system = vlc_clock_monotonic_to_system(clock, now, ts, rate);
+ system = vlc_clock_monotonic_to_system(clock, ctx, now, ts, rate);
}
return system + (clock->delay - main_clock->delay) * rate;
}
static vlc_tick_t vlc_clock_master_to_system(vlc_clock_t *clock,
+ struct vlc_clock_context *ctx,
vlc_tick_t now, vlc_tick_t ts,
double rate)
{
- vlc_clock_main_t *main_clock = clock->owner;
- vlc_tick_t system = main_stream_to_system(main_clock, ts);
+ vlc_tick_t system = context_stream_to_system(ctx, ts);
if (system == VLC_TICK_INVALID)
{
/* We don't have a master sync point, let's fallback to a monotonic ref
* point */
- system = vlc_clock_monotonic_to_system(clock, now, ts, rate);
+ system = vlc_clock_monotonic_to_system(clock, ctx, now, ts, rate);
}
return system;
}
static vlc_tick_t vlc_clock_slave_update(vlc_clock_t *clock,
+ struct vlc_clock_context *ctx,
vlc_tick_t system_now,
vlc_tick_t ts, double rate,
unsigned frame_rate,
@@ -470,7 +633,7 @@ static vlc_tick_t vlc_clock_slave_update(vlc_clock_t *clock,
return VLC_TICK_MAX;
}
- vlc_tick_t computed = clock->ops->to_system(clock, system_now, ts, rate);
+ vlc_tick_t computed = clock->ops->to_system(clock, ctx, system_now, ts, rate);
vlc_tick_t drift = computed - system_now;
vlc_clock_on_update(clock, computed, ts, drift, rate,
@@ -481,9 +644,14 @@ static vlc_tick_t vlc_clock_slave_update(vlc_clock_t *clock,
static void vlc_clock_slave_reset(vlc_clock_t *clock)
{
vlc_clock_main_t *main_clock = clock->owner;
+
+ /* Attach to the last and main context in case of reset */
+ struct vlc_clock_context *ctx = main_clock->context;
+ if (clock->context != NULL && clock->context != ctx)
+ vlc_clock_switch_context(clock, ctx);
+
main_clock->wait_sync_ref_priority = UINT_MAX;
- main_clock->wait_sync_ref =
- clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
+ ctx->wait_sync_ref = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
vlc_clock_on_update(clock, VLC_TICK_INVALID, VLC_TICK_INVALID,
VLC_TICK_INVALID, 1.0f, 0, 0);
@@ -548,6 +716,13 @@ vlc_clock_main_t *vlc_clock_main_New(struct vlc_logger *parent_logger, struct vl
if (main_clock == NULL)
return NULL;
+ struct vlc_clock_context *ctx = main_clock->context = context_new();
+ if (unlikely(ctx == NULL))
+ {
+ free(main_clock);
+ return NULL;
+ }
+
main_clock->logger = vlc_LogHeaderCreate(parent_logger, "clock");
main_clock->tracer = parent_tracer;
@@ -556,25 +731,21 @@ vlc_clock_main_t *vlc_clock_main_New(struct vlc_logger *parent_logger, struct vl
main_clock->input_master = main_clock->master = NULL;
main_clock->rc = 1;
- main_clock->coeff = 1.0f;
- main_clock->rate = 1.0f;
- main_clock->offset = VLC_TICK_INVALID;
main_clock->delay = 0;
main_clock->first_pcr =
clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
main_clock->wait_sync_ref_priority = UINT_MAX;
- main_clock->wait_sync_ref = main_clock->last =
- clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
main_clock->pause_date = VLC_TICK_INVALID;
main_clock->input_dejitter = DEFAULT_PTS_DELAY;
main_clock->output_dejitter = AOUT_MAX_PTS_ADVANCE * 2;
AvgInit(&main_clock->coeff_avg, 10);
- AvgResetAndFill(&main_clock->coeff_avg, main_clock->coeff);
+ AvgResetAndFill(&main_clock->coeff_avg, ctx->coeff);
vlc_vector_init(&main_clock->listeners);
+ vlc_list_init(&main_clock->prev_contexts);
return main_clock;
}
@@ -584,6 +755,7 @@ void vlc_clock_main_Reset(vlc_clock_main_t *main_clock)
vlc_mutex_assert(&main_clock->lock);
vlc_clock_main_reset(main_clock);
+
main_clock->first_pcr =
clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
}
@@ -592,14 +764,30 @@ void vlc_clock_main_SetFirstPcr(vlc_clock_main_t *main_clock,
vlc_tick_t system_now, vlc_tick_t ts)
{
vlc_mutex_assert(&main_clock->lock);
+ struct vlc_clock_context *ctx = main_clock->context;
- if (main_clock->first_pcr.system == VLC_TICK_INVALID)
+ /* Keep the old context if it allows to convert via master or monotonic */
+ if (main_clock->first_pcr.system != VLC_TICK_INVALID
+ && (ctx->last.system != VLC_TICK_INVALID ||
+ ctx->wait_sync_ref.stream != VLC_TICK_INVALID)
+ && !vlc_list_is_empty(&ctx->using_clocks))
{
- main_clock->first_pcr = clock_point_Create(system_now, ts);
- main_clock->wait_sync_ref_priority = UINT_MAX;
- main_clock->wait_sync_ref =
- clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
+ struct vlc_clock_context *newctx = context_new();
+ if (newctx != NULL)
+ {
+ newctx->clock_id = ctx->clock_id + 1;
+ vlc_list_append(&ctx->node, &main_clock->prev_contexts);
+ main_clock->context = ctx = newctx;
+ }
+ vlc_warning(main_clock->logger, "new clock context(%u) @%"PRId64,
+ ctx->clock_id, ts);
}
+ vlc_clock_main_reset(main_clock);
+
+ main_clock->first_pcr = clock_point_Create(system_now, ts);
+ main_clock->wait_sync_ref_priority = UINT_MAX;
+ ctx->wait_sync_ref =
+ clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
}
void vlc_clock_main_SetInputDejitter(vlc_clock_main_t *main_clock,
@@ -631,20 +819,21 @@ void vlc_clock_main_ChangePause(vlc_clock_main_t *main_clock, vlc_tick_t now,
return;
}
+ struct vlc_clock_context *ctx = main_clock->context;
/**
* Only apply a delay if the clock has a reference point to avoid
* messing up the timings if the stream was paused then seeked
*/
const vlc_tick_t delay = now - main_clock->pause_date;
- if (main_clock->last.system != VLC_TICK_INVALID)
+ if (ctx->last.system != VLC_TICK_INVALID)
{
- main_clock->last.system += delay;
- main_clock->offset += delay;
+ ctx->last.system += delay;
+ ctx->offset += delay;
}
if (main_clock->first_pcr.system != VLC_TICK_INVALID)
main_clock->first_pcr.system += delay;
- if (main_clock->wait_sync_ref.system != VLC_TICK_INVALID)
- main_clock->wait_sync_ref.system += delay;
+ if (ctx->wait_sync_ref.system != VLC_TICK_INVALID)
+ ctx->wait_sync_ref.system += delay;
main_clock->pause_date = VLC_TICK_INVALID;
vlc_cond_broadcast(&main_clock->cond);
}
@@ -658,6 +847,11 @@ void vlc_clock_main_Delete(vlc_clock_main_t *main_clock)
assert(main_clock->listeners.size == 0);
vlc_vector_destroy(&main_clock->listeners);
+ assert(vlc_list_is_empty(&main_clock->prev_contexts));
+
+ assert(vlc_list_is_empty(&main_clock->context->using_clocks));
+ free(main_clock->context);
+
free(main_clock);
}
@@ -676,7 +870,10 @@ vlc_tick_t vlc_clock_Update(vlc_clock_t *clock, vlc_tick_t system_now,
vlc_tick_t ts, double rate)
{
AssertLocked(clock);
- return clock->ops->update(clock, system_now, ts, rate, 0, 0);
+ struct vlc_clock_context *ctx =
+ vlc_clock_get_context(clock, system_now, ts, true);
+
+ return clock->ops->update(clock, ctx, system_now, ts, rate, 0, 0);
}
vlc_tick_t vlc_clock_UpdateVideo(vlc_clock_t *clock, vlc_tick_t system_now,
@@ -684,7 +881,11 @@ vlc_tick_t vlc_clock_UpdateVideo(vlc_clock_t *clock, vlc_tick_t system_now,
unsigned frame_rate, unsigned frame_rate_base)
{
AssertLocked(clock);
- return clock->ops->update(clock, system_now, ts, rate, frame_rate, frame_rate_base);
+ struct vlc_clock_context *ctx =
+ vlc_clock_get_context(clock, system_now, ts, true);
+
+ return clock->ops->update(clock, ctx, system_now, ts, rate,
+ frame_rate, frame_rate_base);
}
void vlc_clock_Reset(vlc_clock_t *clock)
@@ -701,10 +902,17 @@ vlc_tick_t vlc_clock_SetDelay(vlc_clock_t *clock, vlc_tick_t delay)
vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t *clock,
vlc_tick_t system_now, vlc_tick_t ts,
- double rate)
+ double rate, uint32_t *clock_id)
{
AssertLocked(clock);
- return clock->ops->to_system(clock, system_now, ts, rate);
+ struct vlc_clock_context *ctx =
+ vlc_clock_get_context(clock, system_now, ts, false);
+ if (clock_id != NULL)
+ *clock_id = ctx->clock_id;
+
+ clock->last_conversion =
+ clock->ops->to_system(clock, ctx, system_now, ts, rate);
+ return clock->last_conversion;
}
static const struct vlc_clock_ops master_ops = {
@@ -723,7 +931,7 @@ static const struct vlc_clock_ops slave_ops = {
static vlc_clock_t *vlc_clock_main_Create(vlc_clock_main_t *main_clock,
const char* track_str_id,
- unsigned priority,
+ bool input, unsigned priority,
const struct vlc_clock_cbs *cbs,
void *cbs_data)
{
@@ -738,6 +946,20 @@ static vlc_clock_t *vlc_clock_main_Create(vlc_clock_main_t *main_clock,
clock->cbs_data = cbs_data;
clock->priority = priority;
assert(!cbs || cbs->on_update);
+ clock->last_conversion = 0;
+
+ if (input)
+ clock->context = NULL; /* Always use the main one */
+ else
+ {
+ /* Attach the clock to the first context or the main one */
+ struct vlc_clock_context *ctx =
+ vlc_list_first_entry_or_null(&main_clock->prev_contexts,
+ struct vlc_clock_context, node);
+ if (likely(ctx == NULL))
+ ctx = main_clock->context;
+ vlc_clock_attach_context(clock, ctx);
+ }
return clock;
}
@@ -750,7 +972,8 @@ vlc_clock_t *vlc_clock_main_CreateMaster(vlc_clock_main_t *main_clock,
vlc_mutex_assert(&main_clock->lock);
/* The master has always the 0 priority */
- vlc_clock_t *clock = vlc_clock_main_Create(main_clock, track_str_id, 0, cbs, cbs_data);
+ vlc_clock_t *clock = vlc_clock_main_Create(main_clock, track_str_id, false,
+ 0, cbs, cbs_data);
if (!clock)
return NULL;
@@ -770,9 +993,11 @@ 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)
{
vlc_mutex_assert(&main_clock->lock);
+ struct vlc_clock_context *ctx = main_clock->context;
/* The master has always the 0 priority */
- vlc_clock_t *clock = vlc_clock_main_Create(main_clock, "input", 0, NULL, NULL);
+ vlc_clock_t *clock = vlc_clock_main_Create(main_clock, "input", true,
+ 0, NULL, NULL);
if (!clock)
return NULL;
@@ -780,7 +1005,7 @@ vlc_clock_t *vlc_clock_main_CreateInputMaster(vlc_clock_main_t *main_clock)
/* Even if the master ES clock has already been created, it should not
* have updated any points */
- assert(main_clock->offset == VLC_TICK_INVALID);
+ assert(ctx->offset == VLC_TICK_INVALID); (void) ctx;
/* Override the master ES clock if it exists */
if (main_clock->master != NULL)
@@ -793,6 +1018,22 @@ vlc_clock_t *vlc_clock_main_CreateInputMaster(vlc_clock_main_t *main_clock)
return clock;
}
+vlc_clock_t *vlc_clock_main_CreateInputSlave(vlc_clock_main_t *main_clock)
+{
+ vlc_mutex_assert(&main_clock->lock);
+
+ /* The input has always the 0 priority */
+ vlc_clock_t *clock = vlc_clock_main_Create(main_clock, "input", true,
+ 0, NULL, NULL);
+ if (!clock)
+ return NULL;
+
+ clock->ops = &slave_ops;
+ main_clock->rc++;
+
+ return clock;
+}
+
vlc_clock_t *vlc_clock_main_CreateSlave(vlc_clock_main_t *main_clock,
const char* track_str_id,
enum es_format_category_e cat,
@@ -818,8 +1059,8 @@ vlc_clock_t *vlc_clock_main_CreateSlave(vlc_clock_main_t *main_clock,
break;
}
- vlc_clock_t *clock = vlc_clock_main_Create(main_clock, track_str_id, priority, cbs,
- cbs_data);
+ vlc_clock_t *clock = vlc_clock_main_Create(main_clock, track_str_id, false,
+ priority, cbs, cbs_data);
if (!clock)
return NULL;
@@ -839,6 +1080,10 @@ void vlc_clock_Delete(vlc_clock_t *clock)
{
vlc_clock_main_t *main_clock = clock->owner;
vlc_mutex_lock(&main_clock->lock);
+
+ if (clock->context != NULL)
+ vlc_clock_remove_current_context(clock);
+
if (clock == main_clock->input_master)
{
/* The input master must be the last clock to be deleted */
=====================================
src/clock/clock.h
=====================================
@@ -170,6 +170,17 @@ 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);
+/**
+ * This function creates a new input slave vlc_clock_t interface
+ *
+ * @warning There can be only one input, slave or master, at a given time.
+ *
+ * You must use vlc_clock_Delete to free it.
+ *
+ * @param main_clock the locked main_clock
+ */
+vlc_clock_t *vlc_clock_main_CreateInputSlave(vlc_clock_main_t *main_clock);
+
/**
* This function creates a new slave vlc_clock_t interface
*
@@ -312,10 +323,11 @@ vlc_clock_RemoveListener(vlc_clock_t *clock, vlc_clock_listener_id *listener_id)
* This function converts a timestamp from stream to system
*
* @param clock the locked clock used by the source
+ * @param clock_id pointer to the clock id used for conversion. Can be NULL.
* @return the valid system time
*/
vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t *clock,
vlc_tick_t system_now, vlc_tick_t ts,
- double rate);
+ double rate, uint32_t *clock_id);
#endif /*CLOCK_H*/
=====================================
src/clock/input_clock.c
=====================================
@@ -144,7 +144,7 @@ static vlc_tick_t ClockSystemToStream( input_clock_t *, vlc_tick_t i_system );
static vlc_tick_t ClockGetTsOffset( input_clock_t * );
-static void UpdateListener( input_clock_t *cl )
+static void UpdateListener( input_clock_t *cl, bool discontinuity )
{
if (cl->listener.cbs == NULL)
return;
@@ -156,7 +156,7 @@ static void UpdateListener( input_clock_t *cl )
/* The returned drift value is ignored for now since a different
* value is computed by the input_clock. */
cl->listener.cbs->update(cl->listener.opaque, system_expected,
- cl->last.stream, cl->rate);
+ cl->last.stream, cl->rate, discontinuity);
}
/*****************************************************************************
@@ -224,6 +224,7 @@ vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
vlc_tick_t i_ck_stream, vlc_tick_t i_ck_system )
{
bool b_reset_reference = false;
+ bool discontinuity = false;
assert( i_ck_stream != VLC_TICK_INVALID && i_ck_system != VLC_TICK_INVALID );
@@ -255,6 +256,7 @@ vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
msg_Warn(p_log, "feeding synchro with a new reference point trying"
" to recover from clock gap");
b_reset_reference= true;
+ discontinuity = true;
}
}
cl->b_origin_changed = false;
@@ -313,7 +315,7 @@ vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;
}
- UpdateListener( cl );
+ UpdateListener( cl, discontinuity );
return i_late;
}
@@ -345,7 +347,7 @@ void input_clock_ChangeRate( input_clock_t *cl, float rate )
}
cl->rate = rate;
- UpdateListener( cl );
+ UpdateListener( cl, false );
}
/*****************************************************************************
@@ -364,7 +366,7 @@ void input_clock_ChangePause( input_clock_t *cl, bool b_paused, vlc_tick_t i_dat
cl->ref.system += i_duration;
cl->last.system += i_duration;
- UpdateListener( cl );
+ UpdateListener( cl, false );
}
}
cl->i_pause_date = i_date;
@@ -419,7 +421,7 @@ void input_clock_ChangeSystemOrigin( input_clock_t *cl, vlc_tick_t i_system )
cl->ref.system += i_offset;
cl->last.system += i_offset;
- UpdateListener( cl );
+ UpdateListener( cl, false );
}
#warning "input_clock_SetJitter needs more work"
=====================================
src/clock/input_clock.h
=====================================
@@ -49,12 +49,13 @@ struct vlc_input_clock_cbs {
* \param ck_system time reference for the buffering progress
* \param ck_stream progress of the buffering in tick
* \param rate current playback rate for the buffering
+ * \param discontinuity PCR discontinuity with the previous update
*
* \return how much time the playback has drifted from
* the main clock
*/
vlc_tick_t (*update)(void *opaque, vlc_tick_t ck_system,
- vlc_tick_t ck_stream, double rate);
+ vlc_tick_t ck_stream, double rate, bool discontinuity);
/**
* Notify the listener that the buffering needed a reset.
@@ -109,6 +110,7 @@ void input_clock_Delete(input_clock_t *);
vlc_tick_t input_clock_Update( input_clock_t *clock, vlc_object_t *p_log,
bool b_can_pace_control, bool b_buffering_allowed,
vlc_tick_t i_clock, vlc_tick_t i_system );
+
/**
* This function will reset the drift of a input_clock_t.
*
=====================================
src/input/decoder.c
=====================================
@@ -991,7 +991,7 @@ static vlc_tick_t ModuleThread_GetDisplayDate( decoder_t *p_dec,
vlc_clock_Lock( p_owner->p_clock );
vlc_tick_t conv_ts =
- vlc_clock_ConvertToSystem( p_owner->p_clock, system_now, i_ts, rate );
+ vlc_clock_ConvertToSystem( p_owner->p_clock, system_now, i_ts, rate, NULL );
vlc_clock_Unlock( p_owner->p_clock );
return conv_ts;
}
=====================================
src/input/es_out.c
=====================================
@@ -1240,10 +1240,17 @@ static void EsOutSendEsEvent(es_out_sys_t *p_sys, es_out_id_t *es, int action,
static vlc_tick_t
ClockListenerUpdate(void *opaque, vlc_tick_t ck_system,
- vlc_tick_t ck_stream, double rate)
+ vlc_tick_t ck_stream, double rate, bool discontinuity)
{
es_out_pgrm_t *pgrm = opaque;
vlc_clock_Lock(pgrm->clocks.input);
+
+ if (discontinuity)
+ {
+ const vlc_tick_t current_date = vlc_tick_now();
+ vlc_clock_main_SetFirstPcr(pgrm->clocks.main, current_date, ck_stream);
+ }
+
vlc_tick_t drift =
vlc_clock_Update(pgrm->clocks.input, ck_system, ck_stream, rate);
vlc_clock_Unlock(pgrm->clocks.input);
@@ -1259,7 +1266,6 @@ ClockListenerReset(void *opaque)
vlc_clock_Unlock(pgrm->clocks.input);
}
-
static void EsOutProgramHandleClockSource(es_out_sys_t *p_sys, es_out_pgrm_t *p_pgrm)
{
input_thread_t *p_input = p_sys->p_input;
@@ -1327,8 +1333,8 @@ static void EsOutProgramHandleClockSource(es_out_sys_t *p_sys, es_out_pgrm_t *p_
if (p_pgrm->active_clock_source != VLC_CLOCK_MASTER_INPUT)
{
vlc_clock_main_Lock(p_pgrm->clocks.main);
- p_pgrm->clocks.input = vlc_clock_main_CreateSlave(
- p_pgrm->clocks.main, "pcr", UNKNOWN_ES, NULL, NULL);
+ p_pgrm->clocks.input =
+ vlc_clock_main_CreateInputSlave(p_pgrm->clocks.main);
vlc_clock_main_Unlock(p_pgrm->clocks.main);
if (p_pgrm->clocks.input != NULL)
=====================================
src/video_output/video_output.c
=====================================
@@ -85,6 +85,7 @@ typedef struct vout_thread_sys_t
vlc_clock_t *clock;
vlc_clock_listener_id *clock_listener_id;
+ uint32_t clock_id;
float rate;
vlc_tick_t delay;
@@ -982,25 +983,37 @@ static picture_t *PreparePicture(vout_thread_sys_t *vout, bool reuse_decoded,
decoded = picture_fifo_Pop(sys->decoder_fifo);
if (decoded) {
- if (is_late_dropped && !decoded->b_force)
+ const vlc_tick_t system_now = vlc_tick_now();
+ uint32_t clock_id;
+ vlc_clock_Lock(sys->clock);
+ const vlc_tick_t system_pts =
+ vlc_clock_ConvertToSystem(sys->clock, system_now,
+ decoded->date, sys->rate, &clock_id);
+ vlc_clock_Unlock(sys->clock);
+ if (clock_id != sys->clock_id)
{
- const vlc_tick_t system_now = vlc_tick_now();
- vlc_clock_Lock(sys->clock);
- const vlc_tick_t system_pts =
- vlc_clock_ConvertToSystem(sys->clock, system_now,
- decoded->date, sys->rate);
- vlc_clock_Unlock(sys->clock);
-
- if (IsPictureLate(vout, decoded, system_now, system_pts))
- {
- picture_Release(decoded);
- vout_statistic_AddLost(&sys->statistic, 1);
+ sys->clock_id = clock_id;
+ msg_Dbg(&vout->obj, "Using a new clock context (%u), "
+ "flusing static filters", clock_id);
+
+ /* Most deinterlace modules can't handle a PTS
+ * discontinuity, so flush them.
+ *
+ * FIXME: Pass a discontinuity flag and handle it in
+ * deinterlace modules. */
+ filter_chain_VideoFlush(sys->filter.chain_static);
+ }
- /* A picture dropped means discontinuity for the
- * filters and we need to notify eg. deinterlacer. */
- filter_chain_VideoFlush(sys->filter.chain_static);
- continue;
- }
+ if (is_late_dropped && !decoded->b_force
+ && IsPictureLate(vout, decoded, system_now, system_pts))
+ {
+ picture_Release(decoded);
+ vout_statistic_AddLost(&sys->statistic, 1);
+
+ /* A picture dropped means discontinuity for the
+ * filters and we need to notify eg. deinterlacer. */
+ filter_chain_VideoFlush(sys->filter.chain_static);
+ continue;
}
if (!VideoFormatIsCropArEqual(&decoded->format, &sys->filter.src_fmt))
@@ -1152,7 +1165,7 @@ static int PrerenderPicture(vout_thread_sys_t *sys, picture_t *filtered,
vlc_clock_Lock(sys->clock);
render_subtitle_date = filtered->date <= VLC_TICK_0 ? system_now :
vlc_clock_ConvertToSystem(sys->clock, system_now, filtered->date,
- sys->rate);
+ sys->rate, NULL);
vlc_clock_Unlock(sys->clock);
}
@@ -1325,7 +1338,7 @@ static int RenderPicture(vout_thread_sys_t *sys, bool render_now)
const vlc_tick_t pts = todisplay->date;
vlc_clock_Lock(sys->clock);
vlc_tick_t system_pts = render_now ? system_now :
- vlc_clock_ConvertToSystem(sys->clock, system_now, pts, sys->rate);
+ vlc_clock_ConvertToSystem(sys->clock, system_now, pts, sys->rate, NULL);
vlc_clock_Unlock(sys->clock);
const unsigned frame_rate = todisplay->format.i_frame_rate;
@@ -1371,7 +1384,7 @@ static int RenderPicture(vout_thread_sys_t *sys, bool render_now)
{
deadline = vlc_clock_ConvertToSystem(sys->clock,
vlc_tick_now(), pts,
- sys->rate);
+ sys->rate, NULL);
if (deadline > max_deadline)
deadline = max_deadline;
}
@@ -1479,7 +1492,7 @@ static bool UpdateCurrentPicture(vout_thread_sys_t *sys)
vlc_clock_Lock(sys->clock);
const vlc_tick_t system_swap_current =
vlc_clock_ConvertToSystem(sys->clock, system_now,
- sys->displayed.current->date, sys->rate);
+ sys->displayed.current->date, sys->rate, NULL);
vlc_clock_Unlock(sys->clock);
const vlc_tick_t render_delay = vout_chrono_GetHigh(&sys->chrono.render) + VOUT_MWAIT_TOLERANCE;
@@ -1932,6 +1945,7 @@ static void vout_ReleaseDisplay(vout_thread_sys_t *vout)
sys->clock = NULL;
vlc_mutex_unlock(&sys->clock_lock);
sys->str_id = NULL;
+ sys->clock_id = 0;
}
void vout_StopDisplay(vout_thread_t *vout)
@@ -2269,6 +2283,7 @@ int vout_Request(const vout_configuration_t *cfg, vlc_video_context *vctx, input
sys->delay = 0;
sys->rate = 1.f;
sys->str_id = cfg->str_id;
+ sys->clock_id = 0;
vlc_mutex_lock(&sys->clock_lock);
sys->clock = cfg->clock;
=====================================
src/video_output/vout_subpictures.c
=====================================
@@ -776,11 +776,13 @@ static size_t spu_channel_UpdateDates(struct spu_channel *channel,
assert(entry);
entry->start = vlc_clock_ConvertToSystem(channel->clock, system_now,
- entry->orgstart, channel->rate);
+ entry->orgstart, channel->rate,
+ NULL);
entry->stop =
vlc_clock_ConvertToSystem(channel->clock, system_now,
- entry->orgstop, channel->rate);
+ entry->orgstop, channel->rate,
+ NULL);
}
vlc_clock_Unlock(channel->clock);
@@ -2125,10 +2127,10 @@ void spu_PutSubpicture(spu_t *spu, subpicture_t *subpic)
vlc_clock_Lock(channel->clock);
subpic->i_start =
vlc_clock_ConvertToSystem(channel->clock, system_now,
- orgstart, channel->rate);
+ orgstart, channel->rate, NULL);
subpic->i_stop =
vlc_clock_ConvertToSystem(channel->clock, system_now,
- orgstop, channel->rate);
+ orgstop, channel->rate, NULL);
vlc_clock_Unlock(channel->clock);
spu_channel_EarlyRemoveLate(sys, channel, system_now);
=====================================
test/src/clock/clock.c
=====================================
@@ -398,7 +398,7 @@ static void play_scenario(libvlc_int_t *vlc, struct vlc_tracer *tracer,
vlc_clock_Lock(ctx.slave);
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(ctx.slave, video_system, video_ts,
- 1.0f);
+ 1.0f, NULL);
vlc_clock_Update(ctx.slave, play_date, video_ts, 1.0f);
vlc_clock_Unlock(ctx.slave);
video_system += video_increment;
@@ -561,7 +561,7 @@ static void normal_check(const struct clock_ctx *ctx, size_t update_count,
vlc_clock_Lock(ctx->slave);
vlc_tick_t converted =
vlc_clock_ConvertToSystem(ctx->slave, expected_system_end,
- stream_end, 1.0f);
+ stream_end, 1.0f, NULL);
vlc_clock_Unlock(ctx->slave);
assert(converted == expected_system_end);
}
@@ -643,7 +643,7 @@ static void drift_check(const struct clock_ctx *ctx, size_t update_count,
vlc_clock_Lock(ctx->slave);
vlc_tick_t converted =
vlc_clock_ConvertToSystem(ctx->slave, expected_system_end,
- stream_end, 1.0f);
+ stream_end, 1.0f, NULL);
vlc_clock_Unlock(ctx->slave);
assert(converted - expected_system_end == scenario->total_drift_duration);
@@ -683,7 +683,7 @@ static void pause_common(const struct clock_ctx *ctx, vlc_clock_t *updater)
{
vlc_clock_Lock(ctx->slave);
- vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f);
+ vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f, NULL);
assert(converted == system);
vlc_clock_Unlock(ctx->slave);
}
@@ -702,7 +702,7 @@ static void pause_common(const struct clock_ctx *ctx, vlc_clock_t *updater)
system += 1;
vlc_clock_Lock(ctx->slave);
- vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f);
+ vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f, NULL);
vlc_clock_Unlock(ctx->slave);
assert(converted == system_start + pause_duration);
}
@@ -734,7 +734,7 @@ static void convert_paused_common(const struct clock_ctx *ctx, vlc_clock_t *upda
system += 1;
vlc_clock_Lock(ctx->slave);
- vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f);
+ vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f, NULL);
vlc_clock_Unlock(ctx->slave);
assert(converted == system_start);
}
@@ -749,6 +749,72 @@ static void monotonic_convert_paused_run(const struct clock_ctx *ctx)
convert_paused_common(ctx, ctx->slave);
}
+static void contexts_run(const struct clock_ctx *ctx)
+{
+ vlc_tick_t converted;
+ vlc_tick_t system = ctx->system_start;
+ vlc_tick_t stream_context0 = 1;
+ uint32_t clock_id;
+
+ vlc_clock_main_Lock(ctx->mainclk);
+
+ /* Initial SetFirstPcr, that will initialise the default and main context */
+ vlc_clock_main_SetFirstPcr(ctx->mainclk, system, stream_context0);
+
+ /* Check that the converted point is valid */
+ converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context0,
+ 1.0f, &clock_id);
+ assert(clock_id == 0);
+ assert(converted == system);
+ vlc_clock_Update(ctx->slave, system, stream_context0, 1.0f);
+
+ /* Discontinuity from 1us to 30 sec */
+ vlc_tick_t system_context0 = system;
+ vlc_tick_t stream_context1 = VLC_TICK_FROM_SEC(30);
+ system += VLC_TICK_FROM_MS(100);
+ vlc_clock_main_SetFirstPcr(ctx->mainclk, system, stream_context1);
+
+ /* Check that we can use the new context (or new origin) */
+ converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context1,
+ 1.0f, &clock_id);
+ assert(clock_id == 1);
+ assert(converted == system);
+
+ /* Check that we can still use the old context when converting a point
+ * closer to the original context */
+ converted = vlc_clock_ConvertToSystem(ctx->slave, system,
+ VLC_TICK_FROM_MS(10) + stream_context0,
+ 1.0f, &clock_id);
+ assert(clock_id == 0);
+ assert(converted == system_context0 + VLC_TICK_FROM_MS(10));
+
+ /* Update on the newest context will cause previous contexts to be removed */
+ vlc_clock_Update(ctx->slave, system, stream_context1, 1.0f);
+
+ /* Discontinuity back to 1us */
+ system += VLC_TICK_FROM_MS(100);
+ vlc_tick_t stream_context2 = 1;
+ vlc_clock_main_SetFirstPcr(ctx->mainclk, system, stream_context2);
+
+ /* Check that we can use the new context (or new origin) */
+ converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context2,
+ 1.0f, &clock_id);
+ assert(clock_id == 2);
+ assert(converted == system);
+ /* Update on the newest context will cause previous contexts to be removed */
+ vlc_clock_Update(ctx->slave, system, stream_context2, 1.0f);
+
+ /* Check that the same conversion will output a different result now that
+ * the old contexts are removed */
+ system += VLC_TICK_FROM_MS(100);
+ converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context1, 1.0f,
+ &clock_id);
+ assert(clock_id == 2);
+ assert(converted != system_context0);
+
+ vlc_clock_main_Unlock(ctx->mainclk);
+}
+
#define VLC_TICK_12H VLC_TICK_FROM_SEC(12 * 60 * 60)
#define VLC_TICK_2H VLC_TICK_FROM_SEC(2 * 60 * 60)
#define DEFAULT_STREAM_INCREMENT VLC_TICK_FROM_MS(100)
@@ -849,6 +915,13 @@ static struct clock_scenario clock_scenarios[] = {
.run = monotonic_convert_paused_run,
.disable_jitter = true,
},
+{
+ .name = "contexts",
+ .desc = "switching contexts is handled",
+ .type = CLOCK_SCENARIO_RUN,
+ .run = contexts_run,
+ .disable_jitter = true,
+},
};
int main(int argc, const char *argv[])
=====================================
test/src/player/player.c
=====================================
@@ -124,7 +124,7 @@ struct report_media_attachments
size_t count;
};
-#define REPORT_LIST \
+#define PLAYER_REPORT_LIST \
X(input_item_t *, on_current_media_changed) \
X(enum vlc_player_state, on_state_changed) \
X(enum vlc_player_error, on_error_changed) \
@@ -154,6 +154,14 @@ struct report_media_attachments
X(struct report_media_subitems, on_media_subitems_changed) \
X(struct report_media_attachments, on_media_attachments_added) \
+struct report_aout_first_pts
+{
+ vlc_tick_t first_pts;
+};
+
+#define REPORT_LIST \
+ X(vlc_tick_t, on_aout_first_pts) \
+
struct report_timer
{
enum
@@ -179,12 +187,14 @@ struct timer_state
};
#define X(type, name) typedef struct VLC_VECTOR(type) vec_##name;
+PLAYER_REPORT_LIST
REPORT_LIST
#undef X
#define X(type, name) vec_##name name;
struct reports
{
+PLAYER_REPORT_LIST
REPORT_LIST
};
#undef X
@@ -193,6 +203,7 @@ static inline void
reports_init(struct reports *report)
{
#define X(type, name) vlc_vector_init(&report->name);
+PLAYER_REPORT_LIST
REPORT_LIST
#undef X
}
@@ -220,6 +231,7 @@ struct media_params
vlc_tick_t pts_delay;
const char *config;
+ const char *discontinuities;
};
#define DEFAULT_MEDIA_PARAMS(param_length) { \
@@ -243,10 +255,19 @@ struct media_params
.null_names = false, \
.pts_delay = DEFAULT_PTS_DELAY, \
.config = NULL, \
+ .discontinuities = NULL, \
}
+#define DISABLE_VIDEO_OUTPUT (1 << 0)
+#define DISABLE_AUDIO_OUTPUT (1 << 1)
+#define DISABLE_VIDEO (1 << 2)
+#define DISABLE_AUDIO (1 << 3)
+#define AUDIO_INSTANT_DRAIN (1 << 4)
+
struct ctx
{
+ int flags;
+
libvlc_instance_t *vlc;
vlc_player_t *player;
vlc_player_listener_id *listener;
@@ -748,6 +769,7 @@ ctx_reset(struct ctx *ctx)
#undef FOREACH_VEC
#define X(type, name) vlc_vector_clear(&ctx->report.name);
+PLAYER_REPORT_LIST
REPORT_LIST
#undef X
@@ -786,7 +808,7 @@ create_mock_media(const char *name, const struct media_params *params)
"video_frame_rate=%u;video_frame_rate_base=%u;"
"title_count=%zu;chapter_count=%zu;"
"can_seek=%d;can_pause=%d;error=%d;null_names=%d;pts_delay=%"PRId64";"
- "config=%s;attachment_count=%zu",
+ "config=%s;discontinuities=%s;attachment_count=%zu",
params->track_count[VIDEO_ES], params->track_count[AUDIO_ES],
params->track_count[SPU_ES], params->program_count,
params->video_packetized, params->audio_packetized,
@@ -795,7 +817,9 @@ create_mock_media(const char *name, const struct media_params *params)
params->title_count, params->chapter_count,
params->can_seek, params->can_pause, params->error, params->null_names,
params->pts_delay,
- params->config ? params->config : "", params->attachment_count);
+ params->config ? params->config : "",
+ params->discontinuities ? params->discontinuities : "",
+ params->attachment_count);
assert(ret != -1);
input_item_t *item = input_item_New(url, name);
assert(item);
@@ -2234,6 +2258,7 @@ static void
ctx_destroy(struct ctx *ctx)
{
#define X(type, name) vlc_vector_destroy(&ctx->report.name);
+PLAYER_REPORT_LIST
REPORT_LIST
#undef X
vlc_player_RemoveListener(ctx->player, ctx->listener);
@@ -2243,16 +2268,8 @@ REPORT_LIST
libvlc_release(ctx->vlc);
}
-enum ctx_flags
-{
- DISABLE_VIDEO_OUTPUT = 1 << 0,
- DISABLE_AUDIO_OUTPUT = 1 << 1,
- DISABLE_VIDEO = 1 << 2,
- DISABLE_AUDIO = 1 << 3,
-};
-
static void
-ctx_init(struct ctx *ctx, enum ctx_flags flags)
+ctx_init(struct ctx *ctx, int flags)
{
const char * argv[] = {
"-v",
@@ -2277,11 +2294,12 @@ ctx_init(struct ctx *ctx, enum ctx_flags flags)
#define X(type, name) .name = player_##name,
static const struct vlc_player_cbs cbs = {
-REPORT_LIST
+PLAYER_REPORT_LIST
};
#undef X
*ctx = (struct ctx) {
+ .flags = flags,
.vlc = vlc,
.next_medias = VLC_VECTOR_INITIALIZER,
.added_medias = VLC_VECTOR_INITIALIZER,
@@ -2297,6 +2315,11 @@ REPORT_LIST
int ret = var_SetString(vlc->p_libvlc_int, "window", "wdummy");
assert(ret == VLC_SUCCESS);
+ ret = var_Create(vlc->p_libvlc_int, "test-ctx", VLC_VAR_ADDRESS);
+ assert(ret == VLC_SUCCESS);
+ ret = var_SetAddress(vlc->p_libvlc_int, "test-ctx", ctx);
+ assert(ret == VLC_SUCCESS);
+
ctx->player = vlc_player_New(VLC_OBJECT(vlc->p_libvlc_int),
VLC_PLAYER_LOCK_NORMAL);
assert(ctx->player);
@@ -2964,6 +2987,32 @@ test_attachments(struct ctx *ctx)
test_end(ctx);
}
+static void
+test_clock_discontinuities(struct ctx *ctx)
+{
+ test_log("discontinuities\n");
+
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(20));
+ params.pts_delay = VLC_TICK_FROM_MS(50);
+ params.discontinuities = "(400000,2)(400000,2500000)(3000000,10000000)";
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ vec_on_aout_first_pts *vec = &ctx->report.on_aout_first_pts;
+ while (vec->size != 4)
+ vlc_player_CondWait(player, &ctx->wait);
+
+ assert(vec->data[0] == VLC_TICK_0); /* Initial PTS */
+ assert(vec->data[1] == 2); /* 1st discontinuity */
+ assert(vec->data[2] == 2500000); /* 2nd discontinuity */
+ assert(vec->data[3] == 10000000); /* 3rd discontinuity */
+
+ test_end(ctx);
+}
+
static void
test_audio_loudness_meter_cb(vlc_tick_t date, double momentary_loudness,
void *data)
@@ -3122,6 +3171,12 @@ main(void)
test_delete_while_playback(VLC_OBJECT(ctx.vlc->p_libvlc_int), false);
ctx_destroy(&ctx);
+
+ /* Test with instantaneous audio drain */
+ ctx_init(&ctx, AUDIO_INSTANT_DRAIN);
+ test_clock_discontinuities(&ctx);
+ ctx_destroy(&ctx);
+
/* Test with --no-video */
ctx_init(&ctx, DISABLE_VIDEO);
test_es_selection_override(&ctx);
@@ -3144,6 +3199,8 @@ struct aout_sys
vlc_tick_t first_pts;
vlc_tick_t first_play_date;
vlc_tick_t pos;
+
+ struct ctx *ctx;
};
static void aout_Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
@@ -3155,6 +3212,9 @@ static void aout_Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
assert(sys->first_pts == VLC_TICK_INVALID);
sys->first_play_date = date;
sys->first_pts = block->i_pts;
+
+ struct ctx *ctx = sys->ctx;
+ VEC_PUSH(on_aout_first_pts, sys->first_pts);
}
aout_TimingReport(aout, sys->first_play_date + sys->pos - VLC_TICK_0,
@@ -3170,6 +3230,11 @@ static void aout_Flush(audio_output_t *aout)
sys->first_pts = sys->first_play_date = VLC_TICK_INVALID;
}
+static void aout_InstantDrain(audio_output_t *aout)
+{
+ aout_DrainedReport(aout);
+}
+
static int aout_Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
{
(void) aout;
@@ -3197,6 +3262,13 @@ static int aout_Open(vlc_object_t *obj)
struct aout_sys *sys = aout->sys = malloc(sizeof(*sys));
assert(sys != NULL);
+
+ sys->ctx = var_InheritAddress(aout, "test-ctx");
+ assert(sys->ctx != NULL);
+
+ if (sys->ctx->flags & AUDIO_INSTANT_DRAIN)
+ aout->drain = aout_InstantDrain;
+
aout_Flush(aout);
return VLC_SUCCESS;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6e5d01d1e20259227033e9efeef79f95cd0145d8...f8b6c4e45147f3fd4a653bd1b2ed27e8595f9b63
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6e5d01d1e20259227033e9efeef79f95cd0145d8...f8b6c4e45147f3fd4a653bd1b2ed27e8595f9b63
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