[vlc-devel] [PATCH 13/17] input: send input clock points to the player

Thomas Guillem thomas at gllm.fr
Mon Feb 15 10:15:08 UTC 2021


player/timer: there is now always a common master source (when es_source
== NULL), and there is no need to handle ES source priorities anymore.

vlc_player_UpdateTimer() still need a bool clock_source to identify the
source. There are 4 possible sources:

 - The input source (from demux GET_TIME/POSITION/LENGTH) (clock_source
   == false, es_source == NULL). It is only used at startup or if
   the demux doesn't have any programs or ES.

 - The input PCR points from the demux (clock_source == false, es_source
   == NULL)

 - The output points from the master, override the input PCR point.
   There can be only one source between input and master. (clock_source
   == false, es_source == NULL)

 - The Output points from the video (for SMPTE timer (clock_source ==
   false, es_source == video_es)

One test from test/player was removed: the assertions for the regular
timer check. Indeed, we can't ensure that we only receive outputs points
anymore.
---
 src/input/es_out.c         |  45 ++++++++---
 src/input/event.h          |   4 +-
 src/input/input_internal.h |   1 -
 src/player/input.c         |   7 +-
 src/player/player.h        |   5 +-
 src/player/timer.c         | 157 ++++++++++++++++++++-----------------
 test/src/player/player.c   |  26 ------
 7 files changed, 126 insertions(+), 119 deletions(-)

diff --git a/src/input/es_out.c b/src/input/es_out.c
index 0d95e0f8c8a..259a4a52a11 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -1349,6 +1349,20 @@ static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
     }
 }
 
+static void MasterClockUpdate(vlc_tick_t system_ts, vlc_tick_t ts, double rate,
+                             unsigned frame_rate, unsigned frame_rate_base,
+                             void *data)
+{
+    es_out_sys_t *p_sys = data;
+
+    input_SendEventOutputClock(p_sys->p_input, NULL, system_ts,
+                               ts, rate, frame_rate, frame_rate_base);
+}
+
+static const struct vlc_clock_cbs master_clock_cbs = {
+    .on_update = MasterClockUpdate
+};
+
 /* EsOutAddProgram:
  *  Add a program
  */
@@ -1384,7 +1398,7 @@ static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, input_source_t *source, in
     }
 
     vlc_clock_t *clock_listener =
-        vlc_clock_main_CreateInput( p_pgrm->p_main_clock, NULL, NULL );
+        vlc_clock_main_CreateInput( p_pgrm->p_main_clock, &master_clock_cbs, p_sys );
     if( clock_listener == NULL )
     {
         vlc_clock_main_Delete( p_pgrm->p_main_clock );
@@ -2149,14 +2163,14 @@ static bool EsIsSelected( es_out_id_t *es )
     }
 }
 
-static void ClockUpdate(vlc_tick_t system_ts, vlc_tick_t ts, double rate,
-                        unsigned frame_rate, unsigned frame_rate_base,
-                        void *data)
+static void VideoClockUpdate(vlc_tick_t system_ts, vlc_tick_t ts, double rate,
+                             unsigned frame_rate, unsigned frame_rate_base,
+                             void *data)
 {
     es_out_id_t *es = data;
     es_out_sys_t *p_sys = container_of(es->out, es_out_sys_t, out);
 
-    input_SendEventOutputClock(p_sys->p_input, &es->id, es->master, system_ts,
+    input_SendEventOutputClock(p_sys->p_input, &es->id, system_ts,
                                ts, rate, frame_rate, frame_rate_base);
 }
 
@@ -2166,10 +2180,6 @@ static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
     input_thread_t *p_input = p_sys->p_input;
     vlc_input_decoder_t *dec;
 
-    static const struct vlc_clock_cbs clock_cbs = {
-        .on_update = ClockUpdate
-    };
-
     assert( p_es->p_pgrm );
 
     if( p_es->fmt.i_cat != UNKNOWN_ES
@@ -2179,14 +2189,27 @@ static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
         p_es->master = true;
         p_es->p_pgrm->p_master_clock = p_es->p_clock =
             vlc_clock_main_CreateMaster( p_es->p_pgrm->p_main_clock,
-                                         &clock_cbs, p_es );
+                                         &master_clock_cbs, p_sys );
     }
     else
     {
+        const struct vlc_clock_cbs *clock_cbs = NULL;
+        void *clock_cbs_data = NULL;
+
+        if( p_es->fmt.i_cat == VIDEO_ES )
+        {
+            /* Register callbacks for the SMPTE Timer */
+            static const struct vlc_clock_cbs video_clock_cbs = {
+                .on_update = VideoClockUpdate
+            };
+            clock_cbs = &video_clock_cbs;
+            clock_cbs_data = p_es;
+        }
+
         p_es->master = false;
         p_es->p_clock = vlc_clock_main_CreateSlave( p_es->p_pgrm->p_main_clock,
                                                     p_es->fmt.i_cat,
-                                                    &clock_cbs, p_es );
+                                                    clock_cbs, clock_cbs_data );
     }
 
     if( !p_es->p_clock )
diff --git a/src/input/event.h b/src/input/event.h
index dd9aec4a871..3ecd38866a3 100644
--- a/src/input/event.h
+++ b/src/input/event.h
@@ -66,7 +66,7 @@ static inline void input_SendEventTimes(input_thread_t *p_input,
 }
 
 static inline void input_SendEventOutputClock(input_thread_t *p_input,
-                                              vlc_es_id_t *id, bool master,
+                                              vlc_es_id_t *id,
                                               vlc_tick_t system_ts,
                                               vlc_tick_t ts, double rate,
                                               unsigned frame_rate,
@@ -74,7 +74,7 @@ static inline void input_SendEventOutputClock(input_thread_t *p_input,
 {
     input_SendEvent(p_input, &(struct vlc_input_event) {
         .type = INPUT_EVENT_OUTPUT_CLOCK,
-        .output_clock = { id, master, system_ts, ts, rate,
+        .output_clock = { id, system_ts, ts, rate,
                           frame_rate, frame_rate_base }
     });
 }
diff --git a/src/input/input_internal.h b/src/input/input_internal.h
index 7279fb0becc..3e26d95e681 100644
--- a/src/input/input_internal.h
+++ b/src/input/input_internal.h
@@ -169,7 +169,6 @@ struct vlc_input_event_times
 struct vlc_input_event_output_clock
 {
     vlc_es_id_t *id;
-    bool master;
     vlc_tick_t system_ts;
     vlc_tick_t ts;
     double rate;
diff --git a/src/player/input.c b/src/player/input.c
index 535df00e6c4..aa340601cf8 100644
--- a/src/player/input.c
+++ b/src/player/input.c
@@ -709,15 +709,14 @@ input_thread_Events(input_thread_t *input_thread,
         if (event->output_clock.system_ts != VLC_TICK_INVALID)
         {
             const struct vlc_player_timer_point point = {
-                .position = 0,
+                .position = -1,
                 .rate = event->output_clock.rate,
                 .ts = event->output_clock.ts,
                 .length = VLC_TICK_INVALID,
                 .system_date = event->output_clock.system_ts,
             };
-            vlc_player_UpdateTimer(player, event->output_clock.id,
-                                   event->output_clock.master, &point,
-                                   VLC_TICK_INVALID,
+            vlc_player_UpdateTimer(player, event->output_clock.id, true,
+                                   &point, VLC_TICK_INVALID,
                                    event->output_clock.frame_rate,
                                    event->output_clock.frame_rate_base);
         }
diff --git a/src/player/player.h b/src/player/player.h
index ae5a2770d6b..9be85378922 100644
--- a/src/player/player.h
+++ b/src/player/player.h
@@ -170,6 +170,7 @@ struct vlc_player_timer_id
 {
     vlc_tick_t period;
     vlc_tick_t last_update_date;
+    vlc_tick_t last_ts;
 
     union
     {
@@ -214,6 +215,8 @@ struct vlc_player_timer
     enum vlc_player_timer_state state;
     bool seeking;
 
+    bool has_clock_source;
+    vlc_tick_t input_time;
     vlc_tick_t input_length;
     vlc_tick_t input_normal_time;
     vlc_tick_t last_ts;
@@ -462,7 +465,7 @@ vlc_player_UpdateTimerState(vlc_player_t *player, vlc_es_id_t *es_source,
 
 void
 vlc_player_UpdateTimer(vlc_player_t *player, vlc_es_id_t *es_source,
-                       bool es_source_is_master,
+                       bool clock_source,
                        const struct vlc_player_timer_point *point,
                        vlc_tick_t normal_time,
                        unsigned frame_rate, unsigned frame_rate_base);
diff --git a/src/player/timer.c b/src/player/timer.c
index 48005571494..d76126a383c 100644
--- a/src/player/timer.c
+++ b/src/player/timer.c
@@ -32,6 +32,7 @@ vlc_player_ResetTimer(vlc_player_t *player)
     vlc_mutex_lock(&player->timer.lock);
 
     player->timer.state = VLC_PLAYER_TIMER_STATE_DISCONTINUITY;
+    player->timer.has_clock_source = false;
     player->timer.input_length = VLC_TICK_INVALID;
     player->timer.input_normal_time = VLC_TICK_0;
     player->timer.last_ts = VLC_TICK_INVALID;
@@ -52,13 +53,17 @@ vlc_player_SendTimerSourceUpdates(vlc_player_t *player,
 
     vlc_list_foreach(timer, &source->listeners, node)
     {
-        /* Respect refresh delay of the timer */
-        if (force_update || timer->period == VLC_TICK_INVALID
-         || timer->last_update_date == VLC_TICK_INVALID
-         || point->system_date == INT64_MAX /* always update when paused */
-         || point->system_date - timer->last_update_date >= timer->period)
+        /* Respect refresh delay of the timer. Don't update when the ts point
+         * decrease. It can happen for a very short time when switching clock
+         * sources (from input to master ES). */
+        if (point->ts > timer->last_ts
+         && (force_update || timer->period == VLC_TICK_INVALID
+          || timer->last_update_date == VLC_TICK_INVALID
+          || point->system_date == INT64_MAX /* always update when paused */
+          || point->system_date - timer->last_update_date >= timer->period))
         {
             timer->cbs->on_update(point, timer->data);
+            timer->last_ts = point->ts;
             timer->last_update_date = point->system_date == INT64_MAX ?
                                       VLC_TICK_INVALID : point->system_date;
         }
@@ -198,6 +203,7 @@ vlc_player_UpdateTimerState(vlc_player_t *player, vlc_es_id_t *es_source,
                     notify = bestsource->es == es_source;
                 source->point.system_date = VLC_TICK_INVALID;
             }
+            player->timer.has_clock_source = false;
             break;
 
         case VLC_PLAYER_TIMER_STATE_PAUSED:
@@ -221,7 +227,7 @@ vlc_player_UpdateTimerState(vlc_player_t *player, vlc_es_id_t *es_source,
     vlc_player_timer_id *timer;
     vlc_list_foreach(timer, &bestsource->listeners, node)
     {
-        timer->last_update_date = VLC_TICK_INVALID;
+        timer->last_ts = timer->last_update_date = VLC_TICK_INVALID;
         timer->cbs->on_discontinuity(system_date, timer->data);
     }
 
@@ -257,86 +263,84 @@ vlc_player_UpdateTimerSource(vlc_player_t *player,
 
 void
 vlc_player_UpdateTimer(vlc_player_t *player, vlc_es_id_t *es_source,
-                       bool es_source_is_master,
+                       bool clock_source,
                        const struct vlc_player_timer_point *point,
                        vlc_tick_t normal_time,
                        unsigned frame_rate, unsigned frame_rate_base)
 {
     struct vlc_player_timer_source *source;
     assert(point);
-    /* A null source can't be the master */
-    assert(es_source == NULL ? !es_source_is_master : true);
 
     vlc_mutex_lock(&player->timer.lock);
 
+    vlc_tick_t ts = point->ts;
+    vlc_tick_t system_date = point->system_date;
+    if (player->timer.state == VLC_PLAYER_TIMER_STATE_PAUSED)
+        system_date = INT64_MAX;
+
+    if (clock_source)
+        player->timer.has_clock_source = true;
+
     bool force_update = false;
-    if (!es_source) /* input source */
+    if (es_source == NULL) /* clock source or input source */
     {
-        /* Only valid for input sources */
-        if (player->timer.input_normal_time != normal_time)
-        {
-            player->timer.input_normal_time = normal_time;
-            player->timer.last_ts = VLC_TICK_INVALID;
-            force_update = true;
-        }
-        if (player->timer.input_length != point->length
-         && point->length >= VLC_TICK_0)
+        source = &player->timer.best_source;
+
+        if (!clock_source)
         {
-            player->timer.input_length = point->length;
-            player->timer.last_ts = VLC_TICK_INVALID;
-            force_update = true;
+            /* input source */
+            if (player->timer.input_normal_time != normal_time
+             && normal_time != VLC_TICK_INVALID)
+            {
+                player->timer.input_normal_time = normal_time;
+                player->timer.last_ts = VLC_TICK_INVALID;
+                force_update = true;
+            }
+            if (player->timer.input_length != point->length
+             && point->length >= VLC_TICK_0)
+            {
+                player->timer.input_length = point->length;
+                player->timer.last_ts = VLC_TICK_INVALID;
+                force_update = true;
+            }
+
+            /* Will likely be overridden by non input source */
+            if (point->position >= 0)
+                player->timer.input_position = point->position;
+
+            if (player->timer.has_clock_source)
+            {
+                if (!force_update)
+                {
+                    /* Don't override clock points from the input times */
+                    vlc_mutex_unlock(&player->timer.lock);
+                    return;
+                }
+
+                /* Update forced, only update the length, so use the last point
+                 * from the clock */
+                ts = source->point.ts;
+                system_date = source->point.system_date;
+            }
         }
-        /* Will likely be overridden by non input source */
-        player->timer.input_position = point->position;
 
-        if (point->ts == VLC_TICK_INVALID
-         || point->system_date == VLC_TICK_INVALID)
+        if (ts == VLC_TICK_INVALID || system_date == VLC_TICK_INVALID)
         {
-            /* ts can only be invalid from the input source */
             vlc_mutex_unlock(&player->timer.lock);
             return;
         }
-    }
-
-    assert(point->ts != VLC_TICK_INVALID);
 
-    vlc_tick_t system_date = point->system_date;
-    if (player->timer.state == VLC_PLAYER_TIMER_STATE_PAUSED)
-        system_date = INT64_MAX;
+        /* An update after a discontinuity means that the playback is resumed */
+        if (player->timer.state == VLC_PLAYER_TIMER_STATE_DISCONTINUITY)
+            player->timer.state = VLC_PLAYER_TIMER_STATE_PLAYING;
 
-    /* An update after a discontinuity means that the playback is resumed */
-    if (player->timer.state == VLC_PLAYER_TIMER_STATE_DISCONTINUITY)
-        player->timer.state = VLC_PLAYER_TIMER_STATE_PLAYING;
-
-    /* Best source priority:
-     * 1/ es_source != NULL when paused (any ES tracks when paused. Indeed,
-     * there is likely no audio update (master) when paused but only video
-     * ones, via vlc_player_NextVideoFrame() for example)
-     * 2/ es_source != NULL + master (from the master ES track)
-     * 3/ es_source != NULL (from the first ES track updated)
-     * 4/ es_source == NULL (from the input)
-     */
-    source = &player->timer.best_source;
-    if (!source->es || es_source_is_master
-     || (es_source && player->timer.state == VLC_PLAYER_TIMER_STATE_PAUSED))
-        source->es = es_source;
-
-    /* Notify the best source */
-    if (source->es == es_source)
-    {
         if (source->point.rate != point->rate)
-        {
-            player->timer.last_ts = VLC_TICK_INVALID;
             force_update = true;
-        }
 
-        /* When paused (INT64_MAX), the same ts can be send more than one time
-         * from the video source, only send it if different in that case. */
-        if (point->ts != player->timer.last_ts
-          || source->point.system_date != system_date
+        if (source->point.system_date != system_date
           || system_date != INT64_MAX)
         {
-            vlc_player_UpdateTimerSource(player, source, point->rate, point->ts,
+            vlc_player_UpdateTimerSource(player, source, point->rate, ts,
                                          system_date);
 
             if (!vlc_list_is_empty(&source->listeners))
@@ -344,16 +348,23 @@ vlc_player_UpdateTimer(vlc_player_t *player, vlc_es_id_t *es_source,
                                                   &source->point);
         }
     }
+    else
+    {
+        assert(clock_source);
+        source = &player->timer.smpte_source;
 
-    source = &player->timer.smpte_source;
-    /* SMPTE source: only the video source */
-    if (!source->es && es_source && vlc_es_id_GetCat(es_source) == VIDEO_ES)
-        source->es = es_source;
+        /* SMPTE source: only the video source */
+        if (!source->es && vlc_es_id_GetCat(es_source) == VIDEO_ES)
+            source->es = es_source;
 
-    /* Notify the SMPTE source, also notify when the video output was rendered
-     * while the clock was paused */
-    if (source->es == es_source && source->es)
-    {
+        if (source->es != es_source)
+        {
+            vlc_mutex_unlock(&player->timer.lock);
+            return;
+        }
+
+        /* Notify the SMPTE source, also notify when the video output was
+         * rendered while the clock was paused */
         if (frame_rate != 0 && (frame_rate != source->smpte.frame_rate
          || frame_rate_base != source->smpte.frame_rate_base))
         {
@@ -372,10 +383,8 @@ vlc_player_UpdateTimer(vlc_player_t *player, vlc_es_id_t *es_source,
                 vlc_player_SendSmpteTimerSourceUpdates(player, source,
                                                        &source->point);
         }
+        player->timer.last_ts = point->ts;
     }
-
-    player->timer.last_ts = point->ts;
-
     vlc_mutex_unlock(&player->timer.lock);
 }
 
@@ -425,7 +434,7 @@ vlc_player_AddTimer(vlc_player_t *player, vlc_tick_t min_period,
     if (!timer)
         return NULL;
     timer->period = min_period;
-    timer->last_update_date = VLC_TICK_INVALID;
+    timer->last_ts = timer->last_update_date = VLC_TICK_INVALID;
     timer->cbs = cbs;
     timer->data = data;
 
@@ -447,7 +456,7 @@ vlc_player_AddSmpteTimer(vlc_player_t *player,
     if (!timer)
         return NULL;
     timer->period = VLC_TICK_INVALID;
-    timer->last_update_date = VLC_TICK_INVALID;
+    timer->last_ts = timer->last_update_date = VLC_TICK_INVALID;
     timer->smpte_cbs = cbs;
     timer->data = data;
 
diff --git a/test/src/player/player.c b/test/src/player/player.c
index 704b3f8b4d0..30f53ee8cd3 100644
--- a/test/src/player/player.c
+++ b/test/src/player/player.c
@@ -2331,32 +2331,6 @@ test_timers_playback(struct ctx *ctx, struct timer_state timers[],
         }
     }
 
-    /* Assertions for the regular timer that received all update points */
-    if (track_count != 0)
-    {
-        struct timer_state *timer = &timers[REGULAR_TIMER_IDX];
-        vec_report_timer *vec = &timer->vec;
-
-        /* Check that we didn't miss any update points */
-        assert(vec->size > 1);
-        size_t point_count = 1;
-        for (size_t i = 1; i < vec->size - 1; ++i)
-        {
-            struct report_timer *prev_report = &vec->data[i - 1];
-            struct report_timer *report = &vec->data[i];
-
-            /* Don't count forced points */
-            if (report->point.ts != prev_report->point.ts)
-            {
-                assert(report->point.ts == prev_report->point.ts + SAMPLE_LENGTH);
-                point_count++;
-            }
-        }
-        assert(vec->data[vec->size - 2].point.ts
-               == length - SAMPLE_LENGTH + VLC_TICK_0);
-        assert(point_count == MAX_UPDATE_COUNT);
-    }
-
     /* Assertions for the regular filtered timer */
     {
         struct timer_state *timer = &timers[REGULAR_DELAY_TIMER_IDX];
-- 
2.30.0



More information about the vlc-devel mailing list