diff --git a/src/clock/clock.c b/src/clock/clock.c
index c1fdc02d5ae59eb8b36a3d743d02f8eb23383a89..891634401eb01347997377669081996f2eb6d778 100644
--- a/src/clock/clock.c
+++ b/src/clock/clock.c
@@ -55,6 +55,12 @@ struct vlc_clock_context
     clock_point_t last;
     clock_point_t wait_sync_ref;
 
+    /**
+     * Start point emitted by the buffering to indicate when we supposedly
+     * started the playback. It does not account for latency of the output
+     * when one of them is driving the main_clock. */
+    clock_point_t start_time;
+
     struct vlc_list node;
     struct vlc_list using_clocks;
 };
@@ -83,6 +89,7 @@ struct vlc_clock_main_t
 
     unsigned wait_sync_ref_priority;
     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 */
 
@@ -99,6 +106,21 @@ struct vlc_clock_ops
     vlc_tick_t (*set_delay)(vlc_clock_t *clock, vlc_tick_t delay);
     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);
+
+    /**
+     * Signal the clock bus that the given clock will start a new timeline.
+     *
+     * When starting an input clock, a whole new context is created to track
+     * the parameters for the new timeline.
+     *
+     * When starting an output clock, the given clock will track the new context
+     * to register the progress and convert the timestamps.
+     *
+     * \param clock         the clock which is ready to start
+     * \param system_now    the system time for the start point
+     * \param ts            the time for the start point
+     **/
+    void (*start)(vlc_clock_t *clock, vlc_tick_t system_now, vlc_tick_t ts);
 };
 
 struct vlc_clock_t
@@ -201,13 +223,30 @@ static inline void TraceRender(struct vlc_tracer *tracer, const char *type,
                                  VLC_TRACE_END);
 }
 
+static vlc_tick_t ComputeOffset(vlc_clock_main_t *main,
+                          struct vlc_clock_context *ctx,
+                          vlc_tick_t system_now,
+                          vlc_tick_t ts,
+                          double rate)
+{
+    vlc_tick_t start_stream = ctx->start_time.stream - VLC_TICK_0;
+    vlc_tick_t start_system = ctx->start_time.system - VLC_TICK_0;
+    if (main->first_pcr.system != VLC_TICK_INVALID)
+        start_stream = start_system = 0;
+
+    return system_now
+        - ((vlc_tick_t) ((ts - start_stream) * ctx->coeff / rate))
+        - start_system;
+}
+
 static void context_reset(struct vlc_clock_context *ctx)
 {
     ctx->coeff = 1.0f;
     ctx->rate = 1.0f;
-    ctx->offset = VLC_TICK_INVALID;
+    ctx->offset = 0;
     ctx->wait_sync_ref = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
     ctx->last = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
+    ctx->start_time = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
 }
 
 static void context_init(struct vlc_clock_context *ctx)
@@ -231,7 +270,10 @@ static vlc_tick_t context_stream_to_system(const struct vlc_clock_context *ctx,
 {
     if (ctx->last.system == VLC_TICK_INVALID)
         return VLC_TICK_INVALID;
-    return ((vlc_tick_t) (ts * ctx->coeff / ctx->rate)) + ctx->offset;
+
+    return ((vlc_tick_t) ((ts - ctx->start_time.stream) * ctx->coeff / ctx->rate))
+            + ctx->offset
+            + ctx->start_time.system;
 }
 
 static void
@@ -373,7 +415,6 @@ static inline void vlc_clock_on_update(vlc_clock_t *clock,
                     system_now, ts, drift, clock->context ? clock->context->clock_id : 0);
 }
 
-
 static void vlc_clock_master_update_coeff(
     vlc_clock_t *clock, struct vlc_clock_context *ctx,
     vlc_tick_t system_now, vlc_tick_t ts, double rate)
@@ -420,7 +461,9 @@ static void vlc_clock_master_update_coeff(
 
                 /* Reset and continue (calculate the offset from the
                  * current point) */
-                vlc_clock_main_reset(main_clock);
+                ctx->coeff = 1.f;
+                AvgResetAndFill(&main_clock->coeff_avg, 1.);
+                ctx->offset = ComputeOffset(main_clock, ctx, system_now, ts, rate);
             }
             else
             {
@@ -438,7 +481,7 @@ static void vlc_clock_master_update_coeff(
         vlc_clock_SendEvent(main_clock, discontinuity);
     }
 
-    ctx->offset = system_now - ((vlc_tick_t) (ts * ctx->coeff / rate));
+    ctx->offset = ComputeOffset(main_clock, ctx, system_now, ts, rate);
 
     if (main_clock->tracer != NULL && clock->track_str_id != NULL)
         vlc_tracer_Trace(main_clock->tracer,
@@ -480,7 +523,7 @@ static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock,
     if (clock->delay > 0 && main_clock->delay < 0 && ts > -main_clock->delay)
         ts += main_clock->delay;
 
-    vlc_tick_t drift = VLC_TICK_INVALID;
+    vlc_tick_t drift = 0;
     vlc_clock_on_update(clock, system_now, ts, drift,
                         rate, frame_rate, frame_rate_base);
     return drift;
@@ -499,7 +542,6 @@ static void vlc_clock_master_reset(vlc_clock_t *clock)
     if (clock->context != NULL && clock->context != ctx)
         vlc_clock_switch_context(clock, ctx);
 
-    vlc_clock_main_reset(main_clock);
 
     assert(main_clock->delay <= 0);
     assert(clock->delay >= 0);
@@ -547,6 +589,79 @@ static vlc_tick_t vlc_clock_master_set_delay(vlc_clock_t *clock, vlc_tick_t dela
     return delta;
 }
 
+static void
+vlc_clock_input_start(vlc_clock_t *clock,
+                      vlc_tick_t start_date,
+                      vlc_tick_t first_ts)
+{
+    vlc_clock_main_t *main_clock = clock->owner;
+    vlc_mutex_assert(&main_clock->lock);
+    assert(main_clock->context != NULL);
+
+    if (main_clock->first_pcr.system != VLC_TICK_INVALID)
+        return;
+
+    struct vlc_clock_context *context
+        = main_clock->context;
+
+    struct vlc_clock_context *last_context = 
+        vlc_list_last_entry_or_null(&main_clock->prev_contexts, struct vlc_clock_context, node);
+
+    if (last_context != NULL)
+        context->clock_id = last_context->clock_id + 1;
+
+    // Note: should not trigger when SetFirstPCR is called
+    context->start_time = clock_point_Create(start_date, first_ts);
+    context->offset = ComputeOffset(main_clock, context, start_date, first_ts, 1.f);
+    main_clock->wait_sync_ref_priority = UINT_MAX;
+
+    vlc_clock_switch_context(clock, context);
+}
+
+static void vlc_clock_slave_reset(vlc_clock_t *clock);
+static void vlc_clock_input_reset(vlc_clock_t *clock)
+{
+    vlc_clock_main_t *main_clock = clock->owner;
+
+    if (main_clock->tracer != NULL && clock->track_str_id != NULL)
+        vlc_tracer_TraceEvent(main_clock->tracer, "RENDER", clock->track_str_id,
+                              "reset_user");
+
+    if (main_clock->first_pcr.system != VLC_TICK_INVALID)
+    {
+        (main_clock->master == clock ? vlc_clock_master_reset : vlc_clock_slave_reset)(clock);
+        return;
+    }
+
+    if (clock->context == NULL || clock->context->start_time.system == VLC_TICK_INVALID)
+        return;
+
+    struct vlc_clock_context *context = main_clock->context;
+
+    bool has_other_clock = false;
+    vlc_clock_t *clock_iterator;
+    vlc_list_foreach(clock_iterator, &context->using_clocks, node)
+    {
+        if (clock_iterator == clock)
+            continue;
+
+        has_other_clock = true;
+    }
+
+    if (!has_other_clock)
+    {
+        context_reset(context);
+        return;
+    }
+
+    assert(context->start_time.stream != VLC_TICK_INVALID);
+    vlc_list_append(&context->node, &main_clock->prev_contexts);
+    main_clock->context = context_new();
+
+    if (main_clock->context == NULL)
+        main_clock->context = context; /* TODO: It fallbacks to previous context */
+}
+
 static vlc_tick_t
 vlc_clock_monotonic_to_system(vlc_clock_t *clock, struct vlc_clock_context *ctx,
                               vlc_tick_t now, vlc_tick_t ts, double rate)
@@ -554,16 +669,20 @@ vlc_clock_monotonic_to_system(vlc_clock_t *clock, struct vlc_clock_context *ctx,
     vlc_clock_main_t *main_clock = clock->owner;
 
     if (clock->priority < main_clock->wait_sync_ref_priority
-     && ctx == main_clock->context)
+     && ctx == main_clock->context
+     && main_clock->first_pcr.system != VLC_TICK_INVALID)
     {
         /* 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
          * is used to introduce the same delay than the input clock (first PTS
          * - first PCR). */
-        vlc_tick_t pcr_delay =
-            main_clock->first_pcr.system == VLC_TICK_INVALID ? 0 :
-            (ts - main_clock->first_pcr.stream) / rate +
-            main_clock->first_pcr.system - now;
+        vlc_tick_t pcr_delay = 0;
+        if (ctx->start_time.system != VLC_TICK_INVALID)
+            pcr_delay = (ts - ctx->start_time.stream) / rate +
+                ctx->start_time.system - now;
+        else if (main_clock->first_pcr.system != VLC_TICK_INVALID)
+            pcr_delay = (ts - main_clock->first_pcr.stream) / rate +
+                main_clock->first_pcr.system - now;
 
         if (pcr_delay > MAX_PCR_DELAY)
         {
@@ -581,7 +700,6 @@ vlc_clock_monotonic_to_system(vlc_clock_t *clock, struct vlc_clock_context *ctx,
         ctx->wait_sync_ref = clock_point_Create(now + delay, ts);
     }
 
-    assert(ctx->wait_sync_ref.stream != VLC_TICK_INVALID);
 
     return (ts - ctx->wait_sync_ref.stream) / rate + ctx->wait_sync_ref.system;
 }
@@ -627,6 +745,8 @@ static vlc_tick_t vlc_clock_slave_update(vlc_clock_t *clock,
                                          unsigned frame_rate,
                                          unsigned frame_rate_base)
 {
+    vlc_clock_main_t *main_clock = clock->owner;
+
     if (system_now == VLC_TICK_MAX)
     {
         /* If system_now is VLC_TICK_MAX, the update is forced, don't modify
@@ -636,9 +756,19 @@ 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, ctx, system_now, ts, rate);
+    vlc_tick_t drift;
+    vlc_tick_t computed;
+    if (main_clock->first_pcr.system != VLC_TICK_INVALID || ctx->wait_sync_ref.stream != VLC_TICK_INVALID)
+    {
+        computed = clock->ops->to_system(clock, ctx, system_now, ts, rate);
+        drift = computed - system_now;
+    }
+    else
+    {
+        computed = system_now;
+        drift = 0;
+    }
 
-    vlc_tick_t drift = computed - system_now;
     vlc_clock_on_update(clock, computed, ts, drift, rate,
                         frame_rate, frame_rate_base);
     return drift;
@@ -653,9 +783,6 @@ static void vlc_clock_slave_reset(vlc_clock_t *clock)
     if (clock->context != NULL && clock->context != ctx)
         vlc_clock_switch_context(clock, ctx);
 
-    main_clock->wait_sync_ref_priority = UINT_MAX;
-    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);
 }
@@ -670,6 +797,111 @@ static vlc_tick_t vlc_clock_slave_set_delay(vlc_clock_t *clock, vlc_tick_t delay
     return 0;
 }
 
+static void
+vlc_clock_output_start(vlc_clock_t *clock,
+                       vlc_tick_t start_date,
+                       vlc_tick_t first_ts)
+{
+    (void)start_date; (void)first_ts;
+
+    vlc_clock_main_t *main_clock = clock->owner;
+    vlc_mutex_assert(&main_clock->lock);
+
+    if (clock->last_conversion == VLC_TICK_INVALID)
+        clock->last_conversion = start_date;
+
+    /* Attach to the correct context in case of reset */
+    struct vlc_clock_context *context
+        = vlc_clock_get_context(clock, start_date, first_ts, true);
+
+#if 0
+    /* Disabled for now, the handling will be done later. */
+    /* vlc_clock_Start must have already been called. */
+    assert(context->start_time.system != VLC_TICK_INVALID);
+    assert(context->start_time.stream != VLC_TICK_INVALID);
+#endif
+    if (context->start_time.system == VLC_TICK_INVALID)
+        return;
+
+    /* Attach to the correct context in case of reset */
+    clock->context = context;
+
+    if (clock->priority >= main_clock->wait_sync_ref_priority)
+        return;
+
+    /**
+     * The clock should have already been started, so we have a valid
+     * start_time which links a PCR value to a time where the PCR is being
+     * "used". Any PTS from the output will necessarily be converted after
+     * this start_time, and will necessarily have bigger PTS than the
+     * registered PCR, so we can use the difference to find how much delta
+     * there is between the first PTS and the first PCR.
+     *
+     * When setting up the outputs, some amount of wait will already be
+     * consumed, for instance when playing live stream, we consume some time
+     * between the first decoded packet and the end of the buffering. The
+     * core currently handles this by substracting the buffering duration to
+     * the start time, and we re-inject this duration through the pts-delay,
+     * called input_dejitter in the clock.
+     *
+     *    |  Start                   End of      Decoders ready
+     *    |  buffering               buffering   (DecoderWaitUnblock)
+     *    |     v                       v           v
+     *    |     x-----------------------x-----------x---> system time
+     *    |
+     *    |                               Preroll and
+     *    |             pts delay         decoder delay
+     *    |     |----------------------->|---------->
+     *    |                 <-----------------------|
+     *    |                     Buffering duration
+     *    |                       (media time)
+     *    |
+     *    | Fig.1: System time representation of the clock startup
+     *
+     * The defined start_time from the input is set according to the PCR
+     * but the first PTS received by an output is likely later than this
+     * clock time, so the real output start_time must be shifted to keep
+     * intra-media synchronization in acceptable levels.
+     *
+     *    |    Start     First audio    First video
+     *    |    time      packet PTS     packet PTS
+     *    |      v            v            v
+     *    |      x------------x------------x----------> media time
+     *    |
+     *    |      |------------>
+     *    |      Audio PCR delay
+     *    |
+     *    |      |------------------------->
+     *    |      Video PCR delay
+     *    |
+     *    | Fig.2: Media time representation of the clock startup
+     *
+     * If the pts-delay is very low, and if the PCR delay is very small,
+     * an additional output_dejitter is used to offset the beginning of
+     * the playback in a uniform manner, to let some time for the outputs
+     * to start. It only makes sense when one of the output will drive
+     * the bus clock.
+     **/
+
+    vlc_tick_t pcr_delay = (first_ts - context->start_time.stream) / context->rate
+                         + context->start_time.system - start_date;
+
+    if (pcr_delay > MAX_PCR_DELAY)
+    {
+        if (main_clock->logger != NULL)
+            vlc_error(main_clock->logger, "Invalid PCR delay ! Ignoring it...");
+        pcr_delay = 0;
+    }
+
+    const vlc_tick_t input_delay = main_clock->input_dejitter + pcr_delay;
+
+    const vlc_tick_t delay =
+        __MAX(input_delay, main_clock->output_dejitter);
+
+    main_clock->wait_sync_ref_priority = clock->priority;
+    context->wait_sync_ref = clock_point_Create(start_date + delay, first_ts);
+}
+
 void vlc_clock_Lock(vlc_clock_t *clock)
 {
     vlc_clock_main_t *main_clock = clock->owner;
@@ -740,6 +972,9 @@ vlc_clock_main_t *vlc_clock_main_New(struct vlc_logger *parent_logger, struct vl
         clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID);
     main_clock->wait_sync_ref_priority = UINT_MAX;
 
+    ctx->start_time =
+        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;
@@ -831,10 +1066,13 @@ void vlc_clock_main_ChangePause(vlc_clock_main_t *main_clock, vlc_tick_t now,
     if (ctx->last.system != VLC_TICK_INVALID)
     {
         ctx->last.system += delay;
-        ctx->offset += delay;
+        if (ctx->start_time.system == VLC_TICK_INVALID)
+            ctx->offset += delay;
     }
     if (main_clock->first_pcr.system != VLC_TICK_INVALID)
         main_clock->first_pcr.system += delay;
+    if (ctx->start_time.system != VLC_TICK_INVALID)
+        ctx->start_time.system += delay;
     if (ctx->wait_sync_ref.system != VLC_TICK_INVALID)
         ctx->wait_sync_ref.system += delay;
     main_clock->pause_date = VLC_TICK_INVALID;
@@ -873,8 +1111,14 @@ vlc_tick_t vlc_clock_Update(vlc_clock_t *clock, vlc_tick_t system_now,
                             vlc_tick_t ts, double rate)
 {
     AssertLocked(clock);
-    struct vlc_clock_context *ctx =
-        vlc_clock_get_context(clock, system_now, ts, true);
+
+    vlc_clock_main_t *main_clock = clock->owner;
+    struct vlc_clock_context *ctx;
+
+    if (main_clock->first_pcr.system != VLC_TICK_INVALID)
+        ctx = vlc_clock_get_context(clock, system_now, ts, true);
+    else
+        ctx = clock->context;
 
     return clock->ops->update(clock, ctx, system_now, ts, rate, 0, 0);
 }
@@ -884,8 +1128,13 @@ vlc_tick_t vlc_clock_UpdateVideo(vlc_clock_t *clock, vlc_tick_t system_now,
                                  unsigned frame_rate, unsigned frame_rate_base)
 {
     AssertLocked(clock);
-    struct vlc_clock_context *ctx =
-        vlc_clock_get_context(clock, system_now, ts, true);
+    vlc_clock_main_t *main_clock = clock->owner;
+    struct vlc_clock_context *ctx;
+
+    if (main_clock->first_pcr.system != VLC_TICK_INVALID)
+        ctx = vlc_clock_get_context(clock, system_now, ts, true);
+    else
+        ctx = clock->context;
 
     return clock->ops->update(clock, ctx, system_now, ts, rate,
                               frame_rate, frame_rate_base);
@@ -923,6 +1172,7 @@ static const struct vlc_clock_ops master_ops = {
     .reset = vlc_clock_master_reset,
     .set_delay = vlc_clock_master_set_delay,
     .to_system = vlc_clock_master_to_system,
+    .start = vlc_clock_output_start,
 };
 
 static const struct vlc_clock_ops slave_ops = {
@@ -930,6 +1180,23 @@ static const struct vlc_clock_ops slave_ops = {
     .reset = vlc_clock_slave_reset,
     .set_delay = vlc_clock_slave_set_delay,
     .to_system = vlc_clock_slave_to_system,
+    .start = vlc_clock_output_start,
+};
+
+static const struct vlc_clock_ops input_master_ops = {
+    .update = vlc_clock_master_update,
+    .reset = vlc_clock_input_reset,
+    .set_delay = vlc_clock_master_set_delay,
+    .to_system = vlc_clock_master_to_system,
+    .start = vlc_clock_input_start,
+};
+
+static const struct vlc_clock_ops input_slave_ops = {
+    .update = vlc_clock_slave_update,
+    .reset = vlc_clock_input_reset,
+    .set_delay = vlc_clock_slave_set_delay,
+    .to_system = vlc_clock_slave_to_system,
+    .start = vlc_clock_input_start,
 };
 
 static vlc_clock_t *vlc_clock_main_Create(vlc_clock_main_t *main_clock,
@@ -949,21 +1216,22 @@ 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;
+    clock->last_conversion = VLC_TICK_INVALID;
 
     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);
+        /* Always use the main one */
+        vlc_clock_attach_context(clock, main_clock->context);
+        return clock;
     }
 
+    /* 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;
 }
 
@@ -1008,13 +1276,13 @@ 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(ctx->offset == VLC_TICK_INVALID); (void) ctx;
+    assert(ctx->offset == 0); (void) ctx;
 
     /* Override the master ES clock if it exists */
     if (main_clock->master != NULL)
         main_clock->master->ops = &slave_ops;
 
-    clock->ops = &master_ops;
+    clock->ops = &input_master_ops;
     main_clock->input_master = clock;
     main_clock->rc++;
 
@@ -1031,7 +1299,7 @@ vlc_clock_t *vlc_clock_main_CreateInputSlave(vlc_clock_main_t *main_clock)
     if (!clock)
         return NULL;
 
-    clock->ops = &slave_ops;
+    clock->ops = &input_slave_ops;
     main_clock->rc++;
 
     return clock;
@@ -1104,3 +1372,10 @@ void vlc_clock_Delete(vlc_clock_t *clock)
     vlc_mutex_unlock(&main_clock->lock);
     free(clock);
 }
+
+void vlc_clock_Start(vlc_clock_t *clock,
+                     vlc_tick_t start_date,
+                     vlc_tick_t first_ts)
+{
+    clock->ops->start(clock, start_date, first_ts);
+}
diff --git a/src/clock/clock.h b/src/clock/clock.h
index ddc9c0e8b29f2217f18af8928de1cc48e17dd0fa..7b0557096fde9704e99aa6a611e15e00cc3cbb38 100644
--- a/src/clock/clock.h
+++ b/src/clock/clock.h
@@ -330,4 +330,16 @@ vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t *clock,
                                      vlc_tick_t system_now, vlc_tick_t ts,
                                      double rate, uint32_t *clock_id);
 
+/**
+ * Starts a new clock based on the given clock point, accounting for
+ * previous updates.
+ *
+ * @param clock       an input or output clock that is being started
+ * @param start_date  the system time at which the first update starts
+ * @param first_ts    the media time origin
+ **/
+void vlc_clock_Start(vlc_clock_t *clock,
+                     vlc_tick_t start_date,
+                     vlc_tick_t first_ts);
+
 #endif /*CLOCK_H*/
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 9ba55bed3fa64d826efd59dc5a7be8ddea7fd050..beb714f5ae8f209ec60563d09eddb9f756eee445 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -209,6 +209,7 @@ struct vlc_input_decoder_t
     bool b_waiting;
     bool b_first;
     bool b_has_data;
+    bool out_started;
 
     /* Flushing */
     bool flushing;
@@ -1023,7 +1024,7 @@ static void RequestReload( vlc_input_decoder_t *p_owner )
     atomic_compare_exchange_strong( &p_owner->reload, &expected, RELOAD_DECODER );
 }
 
-static int DecoderWaitUnblock( vlc_input_decoder_t *p_owner )
+static int DecoderWaitUnblock(vlc_input_decoder_t *p_owner, vlc_tick_t date)
 {
     struct vlc_tracer *tracer = vlc_object_get_tracer(VLC_OBJECT(&p_owner->dec));
     vlc_fifo_Assert(p_owner->p_fifo);
@@ -1036,16 +1037,19 @@ static int DecoderWaitUnblock( vlc_input_decoder_t *p_owner )
         vlc_cond_signal( &p_owner->wait_acknowledge );
     }
 
-    bool did_wait = false;
     while (p_owner->b_waiting && p_owner->b_has_data && !p_owner->flushing)
-    {
         vlc_fifo_WaitCond(p_owner->p_fifo, &p_owner->wait_request);
-        /* We should not start the clock if we ended up flushing. */
-        did_wait = !p_owner->flushing;
-    }
 
-    if (tracer != NULL && did_wait)
-        vlc_tracer_TraceEvent(tracer, "DEC", p_owner->psz_id, "stop wait");
+    if (!p_owner->out_started)
+    {
+        p_owner->out_started = true;
+        if (tracer != NULL)
+            vlc_tracer_TraceEvent(tracer, "DEC", p_owner->psz_id, "stop wait");
+
+        vlc_clock_Lock(p_owner->p_clock);
+        vlc_clock_Start(p_owner->p_clock, vlc_tick_now(), date);
+        vlc_clock_Unlock(p_owner->p_clock);
+    }
 
     if (p_owner->flushing)
     {
@@ -1364,12 +1368,13 @@ static int ModuleThread_PlayVideo( vlc_input_decoder_t *p_owner, picture_t *p_pi
     }
     else
     {
-        int ret = DecoderWaitUnblock(p_owner);
+        int ret = DecoderWaitUnblock(p_owner, p_picture->date);
         if (ret != VLC_SUCCESS)
         {
             picture_Release(p_picture);
             return ret;
         }
+
     }
 
     if( unlikely(p_owner->paused) && likely(p_owner->frames_countdown > 0) )
@@ -1506,7 +1511,7 @@ static int ModuleThread_PlayAudio( vlc_input_decoder_t *p_owner, vlc_frame_t *p_
         vlc_aout_stream_Flush( p_astream );
     }
 
-    int ret = DecoderWaitUnblock(p_owner);
+    int ret = DecoderWaitUnblock(p_owner, p_audio->i_pts);
     if (ret != VLC_SUCCESS)
     {
         block_Release(p_audio);
@@ -1573,7 +1578,7 @@ static void ModuleThread_PlaySpu( vlc_input_decoder_t *p_owner, subpicture_t *p_
     }
 
     /* */
-    int ret = DecoderWaitUnblock(p_owner);
+    int ret = DecoderWaitUnblock(p_owner, p_subpic->i_start);
 
     if (ret != VLC_SUCCESS || p_subpic->i_start == VLC_TICK_INVALID)
     {
@@ -1813,6 +1818,7 @@ static void *DecoderThread( void *p_data )
              * is called again. This will avoid a second useless flush (but
              * harmless). */
             p_owner->flushing = false;
+            p_owner->out_started = false;
             p_owner->i_preroll_end = PREROLL_NONE;
             continue;
         }
@@ -1976,6 +1982,7 @@ CreateDecoder( vlc_object_t *p_parent, const struct vlc_input_decoder_cfg *cfg )
     p_owner->b_waiting = false;
     p_owner->b_first = true;
     p_owner->b_has_data = false;
+    p_owner->out_started = false;
 
     p_owner->error = false;
 
diff --git a/src/input/es_out.c b/src/input/es_out.c
index 6ab2433d1889fc1b4c89b68ee24db210995eace3..cdf56ce699aac755b68a2076c4cc6acd8ddb8b39 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -960,7 +960,10 @@ static void EsOutChangePosition(es_out_sys_t *p_sys, bool b_flush,
     vlc_list_foreach(pgrm, &p_sys->programs, node)
     {
         input_clock_Reset(pgrm->p_input_clock);
+        vlc_clock_main_Lock(pgrm->clocks.main);
         pgrm->i_last_pcr = VLC_TICK_INVALID;
+        vlc_clock_Reset(pgrm->clocks.input);
+        vlc_clock_main_Unlock(pgrm->clocks.main);
     }
 
     p_sys->b_buffering = true;
@@ -1070,10 +1073,9 @@ static void EsOutDecodersStopBuffering(es_out_sys_t *p_sys, bool b_forced)
     EsOutStopFreeVout(p_sys);
 
     /* */
-    const vlc_tick_t i_wakeup_delay = VLC_TICK_FROM_MS(10); /* FIXME CLEANUP thread wake up time*/
     const vlc_tick_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : vlc_tick_now();
 
-    const vlc_tick_t update = i_current_date + i_wakeup_delay - i_buffering_duration;
+    const vlc_tick_t update = i_current_date - i_buffering_duration;
 
     /* The call order of these 3 input_clock_t/vlc_clock_main_t functions is
      * important:
diff --git a/test/Makefile.am b/test/Makefile.am
index 0ae5a58814b12aa809ec67fb9bf408d0ad5f4f69..17fbb3a3fdbc02bcad7b2af1405074f514f5d8a8 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -25,6 +25,7 @@ check_PROGRAMS = \
 	test_libvlc_slaves \
 	test_src_config_chain \
 	test_src_clock_clock \
+	test_src_clock_start \
 	test_src_misc_ancillary \
 	test_src_misc_variables \
 	test_src_input_stream \
@@ -166,6 +167,11 @@ test_src_clock_clock_SOURCES = src/clock/clock.c \
 	../src/clock/clock.c \
 	../src/clock/clock_internal.c
 test_src_clock_clock_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_clock_start_SOURCES = src/clock/clock_start.c \
+	../src/clock/clock.c \
+	../src/clock/clock_internal.c
+test_src_clock_start_LDADD = $(LIBVLCCORE) $(LIBVLC)
+
 test_src_misc_ancillary_SOURCES = src/misc/ancillary.c
 test_src_misc_ancillary_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_misc_variables_SOURCES = src/misc/variables.c
diff --git a/test/libvlc/test.h b/test/libvlc/test.h
index 7ffadf4ba867e14ac39afbb91fe1a968938c7560..aba7521773247d9538826f9a994f18a271007b94 100644
--- a/test/libvlc/test.h
+++ b/test/libvlc/test.h
@@ -61,7 +61,7 @@ static const char test_default_sample[] = "mock://";
  * Some useful common functions
  */
 
-#define test_log( ... ) printf( "testapi: " __VA_ARGS__ );
+#define test_log( ... ) fprintf(stderr, "testapi: " __VA_ARGS__);
 
 static inline void test_setup(void)
 {
diff --git a/test/src/clock/clock.c b/test/src/clock/clock.c
index 349ff02f9a93115180f9de25a0c0527d71be1d2f..d1c9644bcd6bd1225e72f74a736ba3c4d2b90069 100644
--- a/test/src/clock/clock.c
+++ b/test/src/clock/clock.c
@@ -57,10 +57,11 @@ struct clock_scenario
         CLOCK_SCENARIO_RUN,
     } type;
 
+    vlc_tick_t stream_start;
+    vlc_tick_t system_start; /* VLC_TICK_INVALID for vlc_tick_now() */
+
     union {
         struct {
-            vlc_tick_t stream_start;
-            vlc_tick_t system_start; /* VLC_TICK_INVALID for vlc_tick_now() */
             vlc_tick_t duration;
             vlc_tick_t stream_increment; /* VLC_TICK_INVALID for manual increment */
             unsigned video_fps;
@@ -84,6 +85,7 @@ struct clock_ctx
     vlc_clock_main_t *mainclk;
     vlc_clock_t *master;
     vlc_clock_t *slave;
+    vlc_clock_t *input;
 
     vlc_tick_t system_start;
     vlc_tick_t stream_start;
@@ -328,6 +330,9 @@ static void play_scenario(libvlc_int_t *vlc, struct vlc_tracer *tracer,
     assert(mainclk != NULL);
 
     vlc_clock_main_Lock(mainclk);
+    vlc_clock_t *input = vlc_clock_main_CreateInputSlave(mainclk);
+    assert(input != NULL);
+
     vlc_clock_t *master = vlc_clock_main_CreateMaster(mainclk, scenario->name,
                                                       NULL, NULL);
     assert(master != NULL);
@@ -352,10 +357,16 @@ static void play_scenario(libvlc_int_t *vlc, struct vlc_tracer *tracer,
         system_start = scenario->system_start;
         stream_start = scenario->stream_start;
     }
+
+    vlc_clock_Start(input, system_start, stream_start);
+
+    if (scenario->type == CLOCK_SCENARIO_UPDATE)
+        vlc_clock_Start(master, system_start, stream_start);
     vlc_clock_main_Unlock(mainclk);
 
     const struct clock_ctx ctx = {
         .mainclk = mainclk,
+        .input = input,
         .master = master,
         .slave = slave,
         .scenario = scenario,
@@ -420,6 +431,7 @@ static void play_scenario(libvlc_int_t *vlc, struct vlc_tracer *tracer,
 end:
     vlc_clock_Delete(master);
     vlc_clock_Delete(slave);
+    vlc_clock_Delete(input);
     free(slave_name);
     vlc_clock_main_Delete(mainclk);
 }
@@ -545,8 +557,7 @@ static void normal_check(const struct clock_ctx *ctx, size_t update_count,
         {
             case TRACER_EVENT_TYPE_UPDATE:
                 assert(event.update.coeff == 1.0f);
-                assert(event.update.offset ==
-                       scenario->system_start - scenario->stream_start);
+                assert(event.update.offset == 0);
                 break;
             case TRACER_EVENT_TYPE_RENDER_VIDEO:
                 if (last_video_date != VLC_TICK_INVALID)
@@ -563,6 +574,8 @@ static void normal_check(const struct clock_ctx *ctx, size_t update_count,
         vlc_clock_ConvertToSystem(ctx->slave, expected_system_end,
                                   stream_end, 1.0f, NULL);
     vlc_clock_Unlock(ctx->slave);
+    fprintf(stderr, "%s:%d: converted=%" PRId64 " == expected_system_end=%" PRId64 "\n",
+            __func__, __LINE__, converted, expected_system_end);
     assert(converted == expected_system_end);
 }
 
@@ -678,6 +691,7 @@ static void pause_common(const struct clock_ctx *ctx, vlc_clock_t *updater)
     vlc_tick_t system = system_start;
 
     vlc_clock_Lock(updater);
+    vlc_clock_Start(updater, ctx->system_start, ctx->stream_start);
     vlc_clock_Update(updater, system, ctx->stream_start, 1.0f);
     vlc_clock_Unlock(updater);
 
@@ -722,7 +736,12 @@ static void convert_paused_common(const struct clock_ctx *ctx, vlc_clock_t *upda
     const vlc_tick_t system_start = ctx->system_start;
     vlc_tick_t system = system_start;
 
+    vlc_clock_main_Lock(ctx->mainclk);
+    vlc_clock_main_SetFirstPcr(ctx->mainclk, ctx->system_start, ctx->stream_start);
+    vlc_clock_main_Unlock(ctx->mainclk);
+
     vlc_clock_Lock(updater);
+    vlc_clock_Start(updater, ctx->system_start, ctx->stream_start);
     vlc_clock_Update(updater, ctx->system_start, ctx->stream_start, 1.0f);
     vlc_clock_Unlock(updater);
 
@@ -731,11 +750,14 @@ static void convert_paused_common(const struct clock_ctx *ctx, vlc_clock_t *upda
     vlc_clock_main_Lock(ctx->mainclk);
     vlc_clock_main_ChangePause(ctx->mainclk, system, true);
     vlc_clock_main_Unlock(ctx->mainclk);
-    system += 1;
+    system += 100;
 
     vlc_clock_Lock(ctx->slave);
     vlc_tick_t converted = vlc_clock_ConvertToSystem(ctx->slave, system, ctx->stream_start, 1.0f, NULL);
     vlc_clock_Unlock(ctx->slave);
+
+    fprintf(stderr, "%s:%d converted=%" PRId64 " == system_start=%" PRId64 "\n",
+            __func__, __LINE__, converted, system_start);
     assert(converted == system_start);
 }
 
@@ -764,7 +786,10 @@ static void contexts_run(const struct clock_ctx *ctx)
     /* Check that the converted point is valid */
     converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context0,
                                           1.0f, &clock_id);
+    fprintf(stderr, "%s:%d: clock_id=%" PRIu32 " == 0\n", __func__, __LINE__, clock_id);
     assert(clock_id == 0);
+    fprintf(stderr, "%s:%d: converted=%"PRId64 " == system=%" PRId64 "\n",
+            __func__, __LINE__, converted, system);
     assert(converted == system);
     vlc_clock_Update(ctx->slave, system, stream_context0, 1.0f);
 
@@ -777,7 +802,9 @@ static void contexts_run(const struct clock_ctx *ctx)
     /* Check that we can use the new context (or new origin) */
     converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context1,
                                           1.0f, &clock_id);
+    fprintf(stderr, "%s:%d: clock_id=%" PRIu32 " == 1\n", __func__, __LINE__, clock_id);
     assert(clock_id == 1);
+    fprintf(stderr, "%s:%d: converted=%"PRId64 " == system=%" PRId64 "\n", __func__, __LINE__, converted, system);
     assert(converted == system);
 
     /* Check that we can still use the old context when converting a point
@@ -785,7 +812,10 @@ static void contexts_run(const struct clock_ctx *ctx)
     converted = vlc_clock_ConvertToSystem(ctx->slave, system,
                                           VLC_TICK_FROM_MS(10) + stream_context0,
                                           1.0f, &clock_id);
+    fprintf(stderr, "%s:%d: clock_id=%" PRIu32 " == 0\n", __func__, __LINE__, clock_id);
     assert(clock_id == 0);
+    fprintf(stderr, "%s:%d: converted=%"PRId64 " == system=%" PRId64 "\n",
+            __func__, __LINE__, converted, system_context0 + VLC_TICK_FROM_MS(10));
     assert(converted == system_context0 + VLC_TICK_FROM_MS(10));
 
     /* Update on the newest context will cause previous contexts to be removed */
@@ -799,7 +829,10 @@ static void contexts_run(const struct clock_ctx *ctx)
     /* Check that we can use the new context (or new origin) */
     converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context2,
                                           1.0f, &clock_id);
+    fprintf(stderr, "%s:%d: clock_id=%" PRIu32 " == 2\n", __func__, __LINE__, clock_id);
     assert(clock_id == 2);
+    fprintf(stderr, "%s:%d: converted=%"PRId64 " == system=%" PRId64 "\n",
+            __func__, __LINE__, converted, system);
     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);
@@ -809,7 +842,10 @@ static void contexts_run(const struct clock_ctx *ctx)
     system += VLC_TICK_FROM_MS(100);
     converted = vlc_clock_ConvertToSystem(ctx->slave, system, stream_context1, 1.0f,
                                           &clock_id);
+    fprintf(stderr, "%s:%d: clock_id=%" PRIu32 " == 2\n", __func__, __LINE__, clock_id);
     assert(clock_id == 2);
+    fprintf(stderr, "%s:%d: converted=%"PRId64 " == system=%" PRId64 "\n",
+            __func__, __LINE__, converted, system_context0);
     assert(converted != system_context0);
 
     vlc_clock_main_Unlock(ctx->mainclk);
@@ -894,6 +930,8 @@ static struct clock_scenario clock_scenarios[] = {
     .desc = "pause + resume is delaying the next conversion",
     .type = CLOCK_SCENARIO_RUN,
     .run = master_pause_run,
+    .system_start = VLC_TICK_FROM_MS(100),
+    .stream_start = VLC_TICK_FROM_MS(1000),
 },
 {
     .name = "monotonic_pause",
@@ -901,12 +939,16 @@ static struct clock_scenario clock_scenarios[] = {
     .type = CLOCK_SCENARIO_RUN,
     .run = monotonic_pause_run,
     .disable_jitter = true,
+    .system_start = VLC_TICK_FROM_MS(100),
+    .stream_start = VLC_TICK_FROM_MS(1000),
 },
 {
     .name = "master_convert_paused",
     .desc = "it is possible to convert ts while paused",
     .type = CLOCK_SCENARIO_RUN,
     .run = master_convert_paused_run,
+    .system_start = VLC_TICK_FROM_MS(100),
+    .stream_start = VLC_TICK_FROM_MS(1000),
 },
 {
     .name = "monotonic_convert_paused",
@@ -914,6 +956,8 @@ static struct clock_scenario clock_scenarios[] = {
     .type = CLOCK_SCENARIO_RUN,
     .run = monotonic_convert_paused_run,
     .disable_jitter = true,
+    .system_start = VLC_TICK_FROM_MS(100),
+    .stream_start = VLC_TICK_FROM_MS(1000),
 },
 {
     .name = "contexts",
@@ -921,6 +965,8 @@ static struct clock_scenario clock_scenarios[] = {
     .type = CLOCK_SCENARIO_RUN,
     .run = contexts_run,
     .disable_jitter = true,
+    .system_start = VLC_TICK_FROM_MS(100),
+    .stream_start = VLC_TICK_FROM_MS(1000),
 },
 };
 
diff --git a/test/src/clock/clock_start.c b/test/src/clock/clock_start.c
new file mode 100644
index 0000000000000000000000000000000000000000..efdd39c31366ec79d1fe6bbc2f8c17de49c7f677
--- /dev/null
+++ b/test/src/clock/clock_start.c
@@ -0,0 +1,462 @@
+/*****************************************************************************
+ * clock/clock_start.c: test for the vlc clock
+ *****************************************************************************
+ * Copyright (C) 2024 Videolabs
+ *
+ * Authors: Alexandre Janniaux <ajanni@videolabs.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_tick.h>
+#include <vlc_es.h>
+#include <vlc_tracer.h>
+
+#include "../../../src/clock/clock.h"
+
+#include <vlc/vlc.h>
+#include "../../libvlc/test.h"
+#include "../../../lib/libvlc_internal.h"
+
+#define MODULE_NAME test_clock_clock
+#undef VLC_DYNAMIC_PLUGIN
+
+#include <vlc_plugin.h>
+#include <vlc_vector.h>
+
+/* Define a builtin module for mocked parts */
+const char vlc_module_name[] = MODULE_STRING;
+
+/**
+ * Fixture structure containing the clocks used for each test.
+ */
+struct clock_fixture_simple {
+    vlc_clock_main_t *main;
+    vlc_clock_t *input;
+    vlc_clock_t *master;
+    vlc_clock_t *slave;
+};
+
+struct clock_point
+{
+    vlc_tick_t system;
+    vlc_tick_t stream;
+};
+
+/**
+ * Initialize the fixture.
+ *
+ * It must be released using \ref destroy_fixture_simple.
+ */
+static struct clock_fixture_simple
+init_fixture_simple(struct vlc_logger *logger,
+                    bool is_input_master,
+                    vlc_tick_t input_dejitter,
+                    vlc_tick_t output_dejitter)
+{
+    struct clock_fixture_simple f = {
+        .main = vlc_clock_main_New(logger, NULL),
+    };
+    assert(f.main != NULL);
+
+    vlc_clock_main_Lock(f.main);
+    vlc_clock_main_SetInputDejitter(f.main, input_dejitter);
+    vlc_clock_main_SetDejitter(f.main, output_dejitter);
+
+    if (is_input_master)
+    {
+        f.input = vlc_clock_main_CreateInputMaster(f.main);
+        f.master = f.input;
+    }
+    else
+    {
+        f.input = vlc_clock_main_CreateInputSlave(f.main);
+        f.master = vlc_clock_main_CreateMaster(f.main, "Driving", NULL, NULL);
+    }
+    assert(f.input != NULL);
+    assert(f.master != NULL);
+
+    f.slave = vlc_clock_main_CreateSlave(f.main, "Output", AUDIO_ES,
+                                         NULL, NULL);
+    assert(f.slave != NULL);
+    vlc_clock_main_Unlock(f.main);
+
+    return f;
+}
+
+static void destroy_fixture_simple(struct clock_fixture_simple *f)
+{
+    vlc_clock_Delete(f->slave);
+    if (f->master != f->input)
+        vlc_clock_Delete(f->master);
+    vlc_clock_Delete(f->input);
+    vlc_clock_main_Delete(f->main);
+}
+
+/**
+ * Check that conversion doesn't work as long as clock has not
+ * been started.
+ **/
+#if 0
+static void test_clock_without_start(struct vlc_logger *logger)
+{
+    struct clock_fixture_simple f = init_fixture_simple(logger, true, 0, 0);
+
+    vlc_clock_main_Lock(f.main);
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    {
+        vlc_clock_Update(f.master, now, now * 1000, 1.);
+        vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+        //assert(result == VLC_TICK_INVALID);
+    }
+
+    vlc_clock_Start(f.slave, now, now * 1000);
+
+    {
+        vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+        assert(result == VLC_TICK_INVALID);
+    }
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+#endif
+
+/**
+ * Check that conversion works and is valid after the clock start.
+ **/
+static void test_clock_with_start(struct vlc_logger *logger)
+{
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, true, 0, 0);
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+
+    vlc_clock_main_Lock(f.main);
+    vlc_clock_Update(f.master, now, now * 1000, 1.);
+    vlc_clock_Start(f.input, now, now * 1000);
+    vlc_clock_Start(f.slave, now, now * 1000);
+
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+    assert(result != VLC_TICK_INVALID);
+    assert(result == now);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+
+/**
+ * Check that master clock can start.
+ **/
+static void test_clock_with_start_master(struct vlc_logger *logger)
+{
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+
+    vlc_clock_main_Lock(f.main);
+    vlc_clock_Start(f.input, now, now * 1000);
+    vlc_clock_Start(f.master, now, now * 1000);
+    vlc_clock_Start(f.slave, now, now * 1000);
+    vlc_clock_Update(f.master, now, now * 1000, 1.);
+
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+    assert(result != VLC_TICK_INVALID);
+    assert(result == now);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+
+/**
+ * Check that conversion works and is valid after the clock start,
+ * when start is done later.
+ **/
+static void test_clock_with_output_dejitter(struct vlc_logger *logger)
+{
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, true, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    vlc_clock_Update(f.master, now, now * 1000, 1.);
+    vlc_clock_Start(f.input, 2 * now, now * 1000);
+    vlc_clock_Start(f.slave, 2 * now, now * 1000);
+
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+    fprintf(stderr, "%s:%d: now=%" PRId64 " converted=%" PRId64 "\n",
+            __func__, __LINE__, now, result);
+    assert(result != VLC_TICK_INVALID);
+    assert(result == 2 * now);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+
+/**
+ * Check that clock start is working correctly with an output driving
+ * the clock bus.
+ **/
+static void test_clock_with_output_clock_start(struct vlc_logger *logger)
+{
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    vlc_clock_Start(f.input, now, now * 1000);
+    vlc_clock_Start(f.slave, now, now * 1000);
+
+    /* We provide the clock point to the clock bus. The system time should
+     * approximately match the start date. */
+    vlc_clock_Update(f.master, now, now * 1000, 1.);
+
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+    assert(result != VLC_TICK_INVALID);
+    assert(result == now);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+
+/**
+ * Check that monotonic clock start is working correctly.
+ **/
+static void test_clock_with_monotonic_clock(struct vlc_logger *logger)
+{
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    vlc_clock_Start(f.input, now, now * 1000);
+
+    /* The output wants to start before a reference point is found. */
+    vlc_clock_Start(f.slave, now, now * 1000);
+
+    // TODO
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, now, now * 1000, 1., NULL);
+    assert(result != VLC_TICK_INVALID);
+    assert(result == now);
+    vlc_clock_main_Unlock(f.main);
+
+
+    destroy_fixture_simple(&f);
+}
+
+/**
+ * Check that monotonic clock start accounts for the start date.
+ **/
+static vlc_tick_t test_clock_with_monotonic_clock_start(
+    struct vlc_logger *logger,
+    vlc_tick_t input_dejitter,
+    vlc_tick_t output_dejitter,
+    struct clock_point start_point,
+    struct clock_point output_point,
+    struct clock_point convert_point
+){
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, input_dejitter, output_dejitter);
+    vlc_clock_main_Lock(f.main);
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    vlc_clock_Start(f.input, start_point.system, start_point.stream);
+
+    /* The output wants to start before a reference point is found. */
+    vlc_clock_Start(f.slave, output_point.system, output_point.stream);
+
+    vlc_tick_t result = vlc_clock_ConvertToSystem(f.slave, convert_point.system, convert_point.stream, 1., NULL);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+    return result;
+}
+
+static vlc_tick_t test_clock_slave_update(
+    struct vlc_logger *logger,
+    struct clock_point start_point,
+    struct clock_point output_point
+){
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    vlc_clock_Start(f.input, start_point.system, start_point.stream);
+    vlc_clock_Update(f.master, start_point.system, start_point.stream, 1.);
+
+    /* The output wants to start before a reference point is found. */
+    vlc_clock_Start(f.slave, output_point.system, output_point.stream);
+
+    vlc_tick_t result = vlc_clock_Update(f.slave, output_point.system, output_point.stream, 1.);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+    return result;
+}
+
+static void test_clock_start_reset(
+    struct vlc_logger *logger
+){
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    struct clock_point start_point;
+    start_point = (struct clock_point){ now, now * 1000 };
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    vlc_clock_Start(f.input, start_point.system, start_point.stream);
+    vlc_clock_Update(f.master, start_point.system, start_point.stream, 1.);
+    vlc_clock_main_Reset(f.main);
+
+    vlc_tick_t result;
+    result = vlc_clock_ConvertToSystem(f.slave, start_point.system, start_point.stream, 1., NULL);
+    //assert(result == VLC_TICK_INVALID);
+
+    start_point.system *= 2;
+    vlc_clock_Start(f.input, start_point.system, start_point.stream);
+    vlc_clock_Update(f.master, start_point.system, start_point.stream, 1.);
+    result = vlc_clock_ConvertToSystem(f.slave, start_point.system, start_point.stream, 1., NULL);
+    assert(result == start_point.system);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+
+static void test_clock_slave_only(
+    struct vlc_logger *logger
+){
+    fprintf(stderr, "%s:\n", __func__);
+    struct clock_fixture_simple f = init_fixture_simple(logger, false, 0, 0);
+    vlc_clock_main_Lock(f.main);
+
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+    struct clock_point start_point = { now, now * 1000 };
+
+    /* When the input is not driving the clock bus, we should be able to start
+     * without any reference point since the output will provide it later. */
+    vlc_clock_Start(f.input, start_point.system, start_point.stream);
+
+    vlc_clock_Start(f.slave, start_point.system, start_point.stream);
+    vlc_tick_t result;
+    result = vlc_clock_ConvertToSystem(f.slave, start_point.system, start_point.stream, 1., NULL);
+    assert(result == start_point.system);
+
+    result = vlc_clock_ConvertToSystem(f.slave, start_point.system, start_point.stream + 1000, 1., NULL);
+    assert(result == start_point.system + 1000);
+    vlc_clock_main_Unlock(f.main);
+
+    destroy_fixture_simple(&f);
+}
+int main(void)
+{
+    libvlc_instance_t *libvlc = libvlc_new(0, NULL);
+    assert(libvlc != NULL);
+
+    libvlc_int_t *vlc = libvlc->p_libvlc_int;
+    struct vlc_logger *logger = vlc->obj.logger;
+
+    const vlc_tick_t now = VLC_TICK_FROM_MS(1000);
+
+    //test_clock_without_start(logger);
+    test_clock_with_start(logger);
+    test_clock_with_start_master(logger);
+    test_clock_with_output_dejitter(logger);
+    test_clock_with_output_clock_start(logger);
+    test_clock_with_monotonic_clock(logger);
+
+    struct clock_point start_point, output_point;
+    start_point = (struct clock_point){ now, now * 1000 };
+    output_point = (struct clock_point){ now, now * 1000 };
+
+    vlc_tick_t result = test_clock_with_monotonic_clock_start(
+        logger, 0, 0, start_point, start_point, start_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system);
+    assert(result == start_point.system);
+
+    /* Monotonic clock must start at the start point. */
+    start_point = (struct clock_point){ 2 * now, now * 1000 };
+    result = test_clock_with_monotonic_clock_start(
+        logger, 0, 0, start_point, output_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system);
+    assert(result == start_point.system);
+
+    /* A point later in the future will be started later also. */
+    start_point = (struct clock_point){ now, now * 1000 };
+    output_point = (struct clock_point){ now, now * 1000 + 100};
+    result = test_clock_with_monotonic_clock_start(
+        logger, 0, 0, start_point, output_point, output_point);
+    fprintf(stderr, "result(%" PRId64 ") = start_point.system(%" PRId64 ")\n", result, start_point.system + 100);
+    assert(result == start_point.system + 100);
+
+    /* Input dejitter and PCR delay are accounted together. */
+    result = test_clock_with_monotonic_clock_start(
+        logger, 30, 0, start_point, output_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system + 130);
+    assert(result == start_point.system + 130);
+
+    /* Output dejitter will not account the current PCR delay. */
+    result = test_clock_with_monotonic_clock_start(
+        logger, 0, 100, start_point, output_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system + 100);
+    assert(result == start_point.system + 100);
+
+    /* The highest dejitter value is used. */
+    result = test_clock_with_monotonic_clock_start(
+        logger, 33, 55, start_point, output_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system + 133);
+    assert(result == start_point.system + 133);
+
+    /* The highest dejitter value is used. */
+    result = test_clock_with_monotonic_clock_start(
+        logger, 33, 555, start_point, output_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, start_point.system + 555);
+    assert(result == start_point.system + 555);
+
+    start_point = (struct clock_point){ now, now * 1000 };
+    output_point = (struct clock_point){ now, now * 1000 };
+    result = test_clock_slave_update(logger, start_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, (vlc_tick_t)0);
+    assert(result == 0);
+
+    output_point = (struct clock_point){ now + 10, now * 1000 };
+    result = test_clock_slave_update(logger, start_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, (vlc_tick_t)-10);
+    assert(result == -10);
+
+    output_point = (struct clock_point){ now, now * 1000 + 10 };
+    result = test_clock_slave_update(logger, start_point, output_point);
+    fprintf(stderr, "%" PRId64 " = %" PRId64 "\n", result, (vlc_tick_t)10);
+    assert(result == 10);
+
+    test_clock_start_reset(logger);
+    test_clock_slave_only(logger);
+
+    libvlc_release(libvlc);
+}
+
diff --git a/test/src/meson.build b/test/src/meson.build
index 5eac3cab553dba11037a4f8afc915d8bb8ac387e..ecf5c825a1aa8970f2a493616793d117deb17457 100644
--- a/test/src/meson.build
+++ b/test/src/meson.build
@@ -35,6 +35,16 @@ vlc_tests += {
     'link_with' : [libvlc, libvlccore],
 }
 
+vlc_tests += {
+    'name' : 'test_src_clock_start',
+    'sources' : files(
+        'clock/clock_start.c',
+        '../../src/clock/clock.c',
+        '../../src/clock/clock_internal.c'),
+    'suite' : ['src', 'test_src'],
+    'link_with' : [libvlc, libvlccore],
+}
+
 vlc_tests += {
     'name' : 'test_src_misc_variables',
     'sources' : files('misc/variables.c'),