[vlc-commits] [Git][videolan/vlc][master] 15 commits: player: rename destructor to mainloop

Thomas Guillem (@tguillem) gitlab at videolan.org
Tue Dec 2 15:32:41 UTC 2025



Thomas Guillem pushed to branch master at VideoLAN / VLC


Commits:
84c6f631 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: rename destructor to mainloop

This thread could be used for something else than stopping inputs.

- - - - -
e60ef1ad by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: refactor, merge vlc_player_input_HandleAtoBLoop()

No functional changes.

- - - - -
bece5b79 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: expose vlc_player_input_HandleAtoBLoop()

- - - - -
02adcc43 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: reorder calls in vlc_player_input_HandleAtoBLoop()

Avoid initializing variables if not needed.

- - - - -
993ae382 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: return the seeking state from GetTimerPoint()

- - - - -
1c1cfcf5 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: rework vlc_player_input_HandleAtoBLoop()

Can be forced to force the seek to the a position (not used for now)
Returns true if it caused a seek.

- - - - -
8b71cb69 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: check can_seek later on AtoBLoop requests

So that vlc_player_SetAtoBLoop() can be called when not started.

- - - - -
624afb64 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: finer AtoBLoop handling

Use the player timer to wait for the accurate B deadline.

- - - - -
f893fa24 by Thomas Guillem at 2025-12-02T14:57:10+00:00
player: also handle AtoBLoop on EOF

The deadline (from the previous commit) can still be missed if the B
position is very close to the end (or past the end).

This also fixes AtoBLoop when the length is not known, and near EOF
(when only using positions).

- - - - -
5534270f by Thomas Guillem at 2025-12-02T14:57:10+00:00
mock: add report_length

Can be used to test a stream without a known (or exposed) length.

- - - - -
f6234960 by Thomas Guillem at 2025-12-02T14:57:10+00:00
test: player: handle timer on_seek events

- - - - -
d7d8e95e by Thomas Guillem at 2025-12-02T14:57:10+00:00
test: player: common: handle report_length

- - - - -
07b397ea by Thomas Guillem at 2025-12-02T14:57:10+00:00
test: player: add more timers helpers

Helper to create and listen to timer events.

- - - - -
34795f64 by Thomas Guillem at 2025-12-02T14:57:10+00:00
test: player: timers: use helpers

- - - - -
170bd5d7 by Thomas Guillem at 2025-12-02T14:57:10+00:00
test: player: check AtoBLoop

- - - - -


11 changed files:

- modules/demux/mock.c
- src/player/input.c
- src/player/player.c
- src/player/player.h
- src/player/timer.c
- test/Makefile.am
- test/src/meson.build
- + test/src/player/abloop.c
- test/src/player/common.h
- test/src/player/timers.c
- test/src/player/timers.h


Changes:

=====================================
modules/demux/mock.c
=====================================
@@ -219,6 +219,7 @@ var_Read_float(const char *psz)
 #define OPTIONS_GLOBAL(X) \
     X(node_count, ssize_t, add_integer, Ssize, 0, NO_FREE) \
     X(length, vlc_tick_t, add_integer, Integer, VLC_TICK_FROM_MS(5000), NO_FREE) \
+    X(report_length, bool, add_bool, Bool, true, NO_FREE) \
     X(audio_track_count, ssize_t, add_integer, Ssize, 0, NO_FREE) \
     X(video_track_count, ssize_t, add_integer, Ssize, 0, NO_FREE) \
     X(sub_track_count, ssize_t, add_integer, Ssize, 0, NO_FREE) \
@@ -557,6 +558,8 @@ Control(demux_t *demux, int query, va_list args)
                 VLC_TICK_0 + sys->pts_offset + va_arg(args, double) * sys->length;
             return VLC_SUCCESS;
         case DEMUX_GET_LENGTH:
+            if (!sys->report_length)
+                return VLC_EGENERIC;
             *va_arg(args, vlc_tick_t *) = sys->length;
             return VLC_SUCCESS;
         case DEMUX_GET_NORMAL_TIME:


=====================================
src/player/input.c
=====================================
@@ -35,28 +35,6 @@ vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id,
     return vec ? vlc_player_track_vector_FindById(vec, id, idx) : NULL;
 }
 
-static void
-vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, vlc_tick_t time,
-                                double pos)
-{
-    vlc_player_t *player = input->player;
-
-    if (player->input != input)
-        return;
-
-    assert(input->abloop_state[0].set && input->abloop_state[1].set);
-
-    if (time != VLC_TICK_INVALID
-     && input->abloop_state[0].time != VLC_TICK_INVALID
-     && input->abloop_state[1].time != VLC_TICK_INVALID)
-    {
-        if (time >= input->abloop_state[1].time)
-            vlc_player_SetTime(player, input->abloop_state[0].time);
-    }
-    else if (pos >= input->abloop_state[1].pos)
-        vlc_player_SetPosition(player, input->abloop_state[0].pos);
-}
-
 vlc_tick_t
 vlc_player_input_GetTime(struct vlc_player_input *input, bool seeking,
                          vlc_tick_t system_now)
@@ -65,7 +43,7 @@ vlc_player_input_GetTime(struct vlc_player_input *input, bool seeking,
     vlc_tick_t ts;
 
     if (input == player->input
-     && vlc_player_GetTimerPoint(player, seeking, system_now, &ts, NULL) == 0)
+     && vlc_player_GetTimerPoint(player, &seeking, system_now, &ts, NULL) == 0)
         return ts;
     return input->time;
 }
@@ -78,21 +56,42 @@ vlc_player_input_GetPos(struct vlc_player_input *input, bool seeking,
     double pos;
 
     if (input == player->input
-     && vlc_player_GetTimerPoint(player, seeking, system_now, NULL, &pos) == 0)
+     && vlc_player_GetTimerPoint(player, &seeking, system_now, NULL, &pos) == 0)
         return pos;
     return input->position;
 }
 
-static void
-vlc_player_input_UpdateTime(struct vlc_player_input *input)
+bool
+vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, bool forced)
 {
-    if (input->abloop_state[0].set && input->abloop_state[1].set)
+    if (!input->abloop_state[0].set || !input->abloop_state[1].set
+     || (input->capabilities & VLC_PLAYER_CAP_SEEK) == 0)
+        return false;
+
+    vlc_player_t *player = input->player;
+
+    if (player->input != input)
+        return false;
+
+    vlc_tick_t now = vlc_tick_now();
+    if (input->abloop_state[0].time != VLC_TICK_INVALID
+     && input->abloop_state[1].time != VLC_TICK_INVALID)
     {
-        vlc_tick_t now = vlc_tick_now();
-        vlc_player_input_HandleAtoBLoop(input,
-                                        vlc_player_input_GetTime(input, false, now),
-                                        vlc_player_input_GetPos(input, false, now));
+        vlc_tick_t time = vlc_player_input_GetTime(input, false, now);
+        if (forced
+         || (time != VLC_TICK_INVALID && time >= input->abloop_state[1].time))
+            vlc_player_SetTime(player, input->abloop_state[0].time);
+        return true;
     }
+
+    double pos = vlc_player_input_GetPos(input, false, now);
+    if (forced || pos >= input->abloop_state[1].pos)
+    {
+        vlc_player_SetPosition(player, input->abloop_state[0].pos);
+        return true;
+    }
+
+    return false;
 }
 
 int
@@ -927,6 +926,9 @@ input_thread_Events(input_thread_t *input_thread,
                                               event->state.date);
             break;
         case INPUT_EVENT_EOF:
+            handled = vlc_player_input_HandleAtoBLoop(input, true);
+            if (handled)
+                break;
             if (player->play_and_pause)
             {
                 vlc_player_Pause(player);
@@ -944,6 +946,7 @@ input_thread_Events(input_thread_t *input_thread,
             break;
         case INPUT_EVENT_RATE:
             input->rate = event->rate;
+            vlc_player_SignalAtoBLoop(player);
             vlc_player_SendEvent(player, on_rate_changed, input->rate);
             break;
         case INPUT_EVENT_CAPABILITIES:
@@ -971,7 +974,7 @@ input_thread_Events(input_thread_t *input_thread,
                 vlc_player_SendEvent(player, on_position_changed,
                                      input->time, input->position);
 
-                vlc_player_input_UpdateTime(input);
+                vlc_player_input_HandleAtoBLoop(input, false);
             }
             if (input->length != duration)
             {
@@ -1005,6 +1008,7 @@ input_thread_Events(input_thread_t *input_thread,
                 };
                 vlc_player_UpdateTimer(player, NULL, false, &point,
                                        input->normal_time, 0, 0, priv->i_start);
+                vlc_player_SignalAtoBLoop(player);
             }
             break;
         }


=====================================
src/player/player.c
=====================================
@@ -131,20 +131,20 @@ vlc_player_destructor_AddInput(vlc_player_t *player,
         input->started = false;
         /* Add this input to the stop list: it will be stopped by the
          * destructor thread */
-        assert(!vlc_list_HasInput(&player->destructor.stopping_inputs, input));
-        assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, input));
-        vlc_list_append(&input->node, &player->destructor.inputs);
+        assert(!vlc_list_HasInput(&player->mainloop.stopping_inputs, input));
+        assert(!vlc_list_HasInput(&player->mainloop.joinable_inputs, input));
+        vlc_list_append(&input->node, &player->mainloop.stop_inputs);
     }
     else
     {
         /* Add this input to the joinable list: it will be deleted by the
          * destructor thread */
-        assert(!vlc_list_HasInput(&player->destructor.inputs, input));
-        assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, input));
-        vlc_list_append(&input->node, &player->destructor.joinable_inputs);
+        assert(!vlc_list_HasInput(&player->mainloop.stop_inputs, input));
+        assert(!vlc_list_HasInput(&player->mainloop.joinable_inputs, input));
+        vlc_list_append(&input->node, &player->mainloop.joinable_inputs);
     }
 
-    vlc_cond_signal(&input->player->destructor.wait);
+    vlc_cond_signal(&input->player->mainloop.wait);
 }
 
 void
@@ -152,12 +152,12 @@ vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
                                        struct vlc_player_input *input)
 {
     /* Add this input to the stopping list */
-    if (vlc_list_HasInput(&player->destructor.inputs, input))
+    if (vlc_list_HasInput(&player->mainloop.stop_inputs, input))
         vlc_list_remove(&input->node);
-    if (!vlc_list_HasInput(&player->destructor.stopping_inputs, input))
+    if (!vlc_list_HasInput(&player->mainloop.stopping_inputs, input))
     {
-        vlc_list_append(&input->node, &player->destructor.stopping_inputs);
-        vlc_cond_signal(&input->player->destructor.wait);
+        vlc_list_append(&input->node, &player->mainloop.stopping_inputs);
+        vlc_cond_signal(&input->player->mainloop.wait);
     }
 }
 
@@ -165,7 +165,7 @@ void
 vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
                                        struct vlc_player_input *input)
 {
-    if (vlc_list_HasInput(&player->destructor.stopping_inputs, input))
+    if (vlc_list_HasInput(&player->mainloop.stopping_inputs, input))
         vlc_list_remove(&input->node);
 
     assert(!input->started);
@@ -174,13 +174,50 @@ vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
 
 static bool vlc_player_destructor_IsEmpty(vlc_player_t *player)
 {
-    return vlc_list_is_empty(&player->destructor.inputs)
-        && vlc_list_is_empty(&player->destructor.stopping_inputs)
-        && vlc_list_is_empty(&player->destructor.joinable_inputs);
+    return vlc_list_is_empty(&player->mainloop.stop_inputs)
+        && vlc_list_is_empty(&player->mainloop.stopping_inputs)
+        && vlc_list_is_empty(&player->mainloop.joinable_inputs);
+}
+
+void
+vlc_player_SignalAtoBLoop(vlc_player_t *player)
+{
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
+
+    if (!input || !input->abloop_state[0].set || !input->abloop_state[1].set)
+        return;
+
+    vlc_cond_signal(&player->mainloop.wait);
+}
+
+static vlc_tick_t
+vlc_player_GetAtoBLoopDeadline(vlc_player_t *player)
+{
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
+
+    if (!input || !input->abloop_state[0].set || !input->abloop_state[1].set)
+        return VLC_TICK_MIN;
+
+    vlc_tick_t now = vlc_tick_now();
+    vlc_tick_t ts;
+    bool seeking = false;
+    if (vlc_player_GetTimerPoint(player, &seeking, now, &ts, NULL) != 0)
+        return VLC_TICK_MIN;
+    if (seeking)
+        return VLC_TICK_MIN;
+
+    vlc_tick_t b_time = input->abloop_state[1].time;
+    if (b_time == VLC_TICK_INVALID)
+    {
+        if (input->length == VLC_TICK_INVALID)
+            return VLC_TICK_MIN;
+        b_time = input->abloop_state[1].pos * input->length;
+    }
+    return now + (b_time - ts) * input->rate;
 }
 
 static void *
-vlc_player_destructor_Thread(void *data)
+vlc_player_mainloop_Thread(void *data)
 {
     vlc_player_t *player = data;
 
@@ -195,12 +232,23 @@ vlc_player_destructor_Thread(void *data)
     {
         /* Wait for an input to stop or close. No while loop here since we want
          * to leave this code path when the player is deleting. */
-        if (vlc_list_is_empty(&player->destructor.inputs)
-         && vlc_list_is_empty(&player->destructor.joinable_inputs))
-            vlc_cond_wait(&player->destructor.wait, &player->lock);
+        vlc_tick_t deadline = vlc_player_GetAtoBLoopDeadline(player);
+        bool timeout = false;
+        if (vlc_list_is_empty(&player->mainloop.stop_inputs)
+         && vlc_list_is_empty(&player->mainloop.joinable_inputs))
+        {
+            if (deadline == VLC_TICK_MIN)
+                vlc_cond_wait(&player->mainloop.wait, &player->lock);
+            else
+                timeout = vlc_cond_timedwait(&player->mainloop.wait,
+                                             &player->lock, deadline) != 0;
+        }
+
+        if (timeout && player->input != NULL)
+            vlc_player_input_HandleAtoBLoop(player->input, true);
 
         struct vlc_player_input *input;
-        vlc_list_foreach(input, &player->destructor.inputs, node)
+        vlc_list_foreach(input, &player->mainloop.stop_inputs, node)
         {
             input_Stop(input->thread);
             input->stopping_reason = VLC_PLAYER_MEDIA_STOPPING_USER;
@@ -211,8 +259,8 @@ vlc_player_destructor_Thread(void *data)
 
         bool keep_sout = true;
         const bool inputs_changed =
-            !vlc_list_is_empty(&player->destructor.joinable_inputs);
-        vlc_list_foreach(input, &player->destructor.joinable_inputs, node)
+            !vlc_list_is_empty(&player->mainloop.joinable_inputs);
+        vlc_list_foreach(input, &player->mainloop.joinable_inputs, node)
         {
             vlc_player_UpdateMLStates(player, input);
 
@@ -1471,8 +1519,8 @@ vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop)
 {
     struct vlc_player_input *input = vlc_player_get_input_locked(player);
 
-    if (!input || !vlc_player_CanSeek(player))
-        return VLC_EGENERIC;
+    if (!input )
+        return VLC_EINVAL;
 
     vlc_tick_t time = vlc_player_GetTime(player);
     float pos = vlc_player_GetPosition(player);
@@ -1487,6 +1535,12 @@ vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop)
             input->abloop_state[0].set = true;
             break;
         case VLC_PLAYER_ABLOOP_B:
+            if (!vlc_player_CanSeek(player))
+            {
+                input->abloop_state[0].set = false;
+                return VLC_EGENERIC;
+            }
+
             if (!input->abloop_state[0].set)
                 return VLC_EGENERIC;
             input->abloop_state[1].time = time;
@@ -1537,8 +1591,8 @@ vlc_player_SetAtoBLoopTime(vlc_player_t *player, vlc_tick_t a_time, vlc_tick_t b
 
     struct vlc_player_input *input = vlc_player_get_input_locked(player);
 
-    if (!input || !vlc_player_CanSeek(player))
-        return VLC_EGENERIC;
+    if (!input)
+        return VLC_EINVAL;
 
     input->abloop_state[0].time = a_time;
     input->abloop_state[0].pos = 0;
@@ -1565,8 +1619,8 @@ vlc_player_SetAtoBLoopPosition(vlc_player_t *player, double a_pos, double b_pos)
 
     struct vlc_player_input *input = vlc_player_get_input_locked(player);
 
-    if (!input || !vlc_player_CanSeek(player))
-        return VLC_EGENERIC;
+    if (!input)
+        return VLC_EINVAL;
 
     input->abloop_state[0].time = VLC_TICK_INVALID;
     input->abloop_state[0].pos = a_pos;
@@ -1940,7 +1994,7 @@ vlc_player_InitLocks(vlc_player_t *player, enum vlc_player_lock_type lock_type)
     vlc_mutex_init(&player->vout_listeners_lock);
     vlc_mutex_init(&player->aout_listeners_lock);
     vlc_cond_init(&player->start_delay_cond);
-    vlc_cond_init(&player->destructor.wait);
+    vlc_cond_init(&player->mainloop.wait);
 }
 
 void
@@ -1955,7 +2009,7 @@ vlc_player_Delete(vlc_player_t *player)
     }
 
     player->deleting = true;
-    vlc_cond_signal(&player->destructor.wait);
+    vlc_cond_signal(&player->mainloop.wait);
 
     assert(vlc_list_is_empty(&player->listeners));
     assert(vlc_list_is_empty(&player->metadata_listeners));
@@ -1964,7 +2018,7 @@ vlc_player_Delete(vlc_player_t *player)
 
     vlc_mutex_unlock(&player->lock);
 
-    vlc_join(player->destructor.thread, NULL);
+    vlc_join(player->mainloop.thread, NULL);
 
     if (player->media)
         input_item_Release(player->media);
@@ -2008,9 +2062,9 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type)
     vlc_list_init(&player->metadata_listeners);
     vlc_list_init(&player->vout_listeners);
     vlc_list_init(&player->aout_listeners);
-    vlc_list_init(&player->destructor.inputs);
-    vlc_list_init(&player->destructor.stopping_inputs);
-    vlc_list_init(&player->destructor.joinable_inputs);
+    vlc_list_init(&player->mainloop.stop_inputs);
+    vlc_list_init(&player->mainloop.stopping_inputs);
+    vlc_list_init(&player->mainloop.joinable_inputs);
     player->start_paused = false;
     player->pause_on_cork = false;
     player->corked = false;
@@ -2086,7 +2140,7 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type)
     vlc_player_InitLocks(player, lock_type);
     vlc_player_InitTimer(player);
 
-    if (vlc_clone(&player->destructor.thread, vlc_player_destructor_Thread,
+    if (vlc_clone(&player->mainloop.thread, vlc_player_mainloop_Thread,
                   player) != 0)
     {
         vlc_player_DestroyTimer(player);


=====================================
src/player/player.h
=====================================
@@ -290,10 +290,10 @@ struct vlc_player_t
         vlc_thread_t thread;
         vlc_cond_t wait;
         vlc_cond_t notify;
-        struct vlc_list inputs;
+        struct vlc_list stop_inputs;
         struct vlc_list stopping_inputs;
         struct vlc_list joinable_inputs;
-    } destructor;
+    } mainloop;
 
     struct vlc_player_timer timer;
 };
@@ -361,6 +361,9 @@ void
 vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
                                        struct vlc_player_input *input);
 
+void
+vlc_player_SignalAtoBLoop(vlc_player_t *player);
+
 /*
  * player_track.c
  */
@@ -471,6 +474,9 @@ void
 vlc_player_input_HandleState(struct vlc_player_input *, enum vlc_player_state,
                              vlc_tick_t state_date);
 
+bool
+vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, bool forced);
+
 /*
  * player_timer.c
 */
@@ -505,7 +511,7 @@ void
 vlc_player_RemoveTimerSource(vlc_player_t *player, vlc_es_id_t *es_source);
 
 int
-vlc_player_GetTimerPoint(vlc_player_t *player, bool seeking,
+vlc_player_GetTimerPoint(vlc_player_t *player, bool *seeking,
                          vlc_tick_t system_now,
                          vlc_tick_t *out_ts, double *out_pos);
 


=====================================
src/player/timer.c
=====================================
@@ -527,14 +527,15 @@ vlc_player_RemoveTimerSource(vlc_player_t *player, vlc_es_id_t *es_source)
 }
 
 int
-vlc_player_GetTimerPoint(vlc_player_t *player, bool seeking,
+vlc_player_GetTimerPoint(vlc_player_t *player, bool *seeking,
                          vlc_tick_t system_now,
                          vlc_tick_t *out_ts, double *out_pos)
 {
     int ret = VLC_EGENERIC;
     vlc_mutex_lock(&player->timer.lock);
-    if (seeking
-     && (player->timer.seek_ts != VLC_TICK_INVALID || player->timer.seek_position >= 0.0f))
+    bool timer_seeking = player->timer.seek_ts != VLC_TICK_INVALID
+                      || player->timer.seek_position >= 0.0f;
+    if (*seeking && timer_seeking)
     {
         if (out_ts != NULL)
         {
@@ -569,6 +570,8 @@ vlc_player_GetTimerPoint(vlc_player_t *player, bool seeking,
         ret = VLC_SUCCESS;
     }
 
+    if (ret == VLC_SUCCESS)
+        *seeking = timer_seeking;
 end:
     vlc_mutex_unlock(&player->timer.lock);
     return ret;


=====================================
test/Makefile.am
=====================================
@@ -8,6 +8,7 @@ AUTOMAKE_OPTIONS = subdir-objects
 # Unit/regression test
 ###############################################################################
 player_programs = \
+	test_src_player_abloop \
 	test_src_player_attachments \
 	test_src_player_capabilities \
 	test_src_player_discontinuities \
@@ -216,6 +217,9 @@ test_src_preparser_thumbnail_SOURCES = src/preparser/thumbnail.c
 test_src_preparser_thumbnail_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_preparser_thumbnail_to_files_SOURCES = src/preparser/thumbnail_to_files.c
 test_src_preparser_thumbnail_to_files_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_player_abloop_SOURCES = src/player/common.h src/player/modules.c \
+	src/player/abloop.c src/player/timers.h
+test_src_player_abloop_LDADD = $(LIBVLCCORE) $(LIBVLC) $(LIBM)
 test_src_player_attachments_SOURCES = src/player/common.h src/player/modules.c \
 	src/player/attachments.c
 test_src_player_attachments_LDADD = $(LIBVLCCORE) $(LIBVLC) $(LIBM)


=====================================
test/src/meson.build
=====================================
@@ -105,6 +105,18 @@ vlc_tests += {
     'module_depends' : ['demux_mock', 'rawvideo']
 }
 
+vlc_tests += {
+    'name' : 'test_src_player_abloop',
+    'sources' : files(
+        'player/common.h',
+        'player/modules.c',
+        'player/timers.h',
+        'player/abloop.c'),
+    'suite' : ['src', 'test_src'],
+    'link_with' : [libvlc, libvlccore],
+    'module_depends' : vlc_plugins_targets.keys()
+}
+
 vlc_tests += {
     'name' : 'test_src_player_attachments',
     'sources' : files(


=====================================
test/src/player/abloop.c
=====================================
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*****************************************************************************
+ * abloop.c: abloop player test
+ *****************************************************************************
+ * Copyright (C) 2018-2025 VLC authors and VideoLAN
+ *****************************************************************************/
+
+#include "common.h"
+#include "timers.h"
+
+struct abloop_scenario
+{
+    bool can_seek;
+    bool after_1st_buferring;
+    bool nolength_report;
+    bool wait_stopped;
+    bool check_prev_ts;
+    vlc_tick_t length;
+    vlc_tick_t a_time;
+    vlc_tick_t b_time;
+    double a_pos;
+    double b_pos;
+    size_t seek_count;
+};
+
+static void
+test_abloop_scenario(struct ctx *ctx, const struct abloop_scenario *scenario)
+{
+    vlc_player_t *player = ctx->player;
+
+    struct media_params params = DEFAULT_MEDIA_PARAMS(scenario->length);
+    params.can_seek = scenario->can_seek;
+    params.track_count[AUDIO_ES] = 1;
+    params.track_count[VIDEO_ES] = 0;
+    params.track_count[SPU_ES] = 0;
+    params.report_length = !scenario->nolength_report;
+    player_set_next_mock_media(ctx, "media1", &params);
+
+    struct timer_state timer;
+    player_add_timer(player, &timer, false, VLC_TICK_INVALID);
+
+    player_start(ctx);
+
+    size_t expected_buffering_count = scenario->seek_count;
+    if (scenario->after_1st_buferring)
+    {
+        /* Wait for 1st buffering */
+        while (get_buffering_count(ctx) < 1)
+            vlc_player_CondWait(ctx->player, &ctx->wait);
+        expected_buffering_count++;
+    }
+
+    if (scenario->a_time != VLC_TICK_INVALID)
+    {
+        assert(scenario->b_time != VLC_TICK_INVALID);
+        int ret = vlc_player_SetAtoBLoopTime(ctx->player, scenario->a_time,
+                                             scenario->b_time);
+        assert(ret == VLC_SUCCESS);
+    }
+    else
+    {
+        int ret = vlc_player_SetAtoBLoopPosition(ctx->player, scenario->a_pos,
+                                                 scenario->b_pos);
+        assert(ret == VLC_SUCCESS);
+    }
+
+    if (!scenario->wait_stopped)
+    {
+        /* Wait all seek request */
+        while (get_buffering_count(ctx) < expected_buffering_count)
+            vlc_player_CondWait(ctx->player, &ctx->wait);
+
+        vlc_player_Stop(player);
+    }
+    wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+    vec_report_timer *vec = &timer.vec;
+    size_t seek_count = 0;
+    for (size_t i = 0; i < vec->size; ++i)
+    {
+        struct report_timer *report = &vec->data[i];
+        if (report->type != REPORT_TIMER_SEEK)
+            continue;
+        if (report->seek.finished)
+            continue;
+        struct vlc_player_timer_point *point = &report->seek.point;
+
+        if (scenario->a_time != VLC_TICK_INVALID)
+            assert(point->ts == scenario->a_time);
+        else
+            assert(point->position == scenario->a_pos);
+
+        if (seek_count > 0 && scenario->check_prev_ts)
+        {
+            assert(scenario->a_time != VLC_TICK_INVALID);
+            assert(i > 0);
+            bool checked = false;
+            for (size_t j = i - 1; j > 0; --j)
+            {
+                struct report_timer *time_report = &vec->data[j];
+                if (time_report->type != REPORT_TIMER_POINT)
+                    continue;
+                struct vlc_player_timer_point *time_point = &time_report->point;
+
+                vlc_tick_t end_ts = scenario->b_time;
+                if (end_ts > params.length)
+                    end_ts = params.length;
+                assert(time_point->ts >= scenario->a_time);
+                assert(time_point->ts <= end_ts);
+                assert(time_point->ts + params.audio_sample_length >= end_ts);
+                checked = true;
+                break;
+            }
+            assert(checked);
+        }
+        seek_count++;
+    }
+    assert(seek_count == scenario->seek_count);
+
+    test_end(ctx);
+    player_remove_timer(player, &timer);
+}
+
+static void
+test_abloop(struct ctx *ctx)
+{
+    const struct abloop_scenario scenarios[] =
+    {
+        {
+            /* Check that b_time past length is handled */
+            .can_seek = true, .after_1st_buferring = false,
+            .check_prev_ts = true,
+            .length = VLC_TICK_FROM_MS(20000),
+            .a_time = VLC_TICK_FROM_MS(19800), .b_time = VLC_TICK_FROM_MS(30000),
+            .seek_count = 2,
+        },
+        {
+            /* Check we have the same result if called after buffering */
+            .can_seek = true, .after_1st_buferring = true,
+            .check_prev_ts = true,
+            .length = VLC_TICK_FROM_MS(20000),
+            .a_time = VLC_TICK_FROM_MS(19800), .b_time = VLC_TICK_FROM_MS(30000),
+            .seek_count = 2,
+        },
+        {
+            /* Check small A->B loop values */
+            .can_seek = true, .after_1st_buferring = true,
+            .length = VLC_TICK_FROM_MS(3000),
+            .a_time = VLC_TICK_FROM_MS(1), .b_time = VLC_TICK_FROM_MS(2),
+            .seek_count = 4,
+        },
+        {
+            /* Check with positions */
+            .can_seek = true, .after_1st_buferring = true,
+            .length = VLC_TICK_FROM_MS(3000),
+            .a_pos = 0.9, .b_pos = 1.0f,
+            .seek_count = 1,
+        },
+        {
+            /* Check we have the same result if called after buffering */
+            .can_seek = true, .after_1st_buferring = false,
+            .length = VLC_TICK_FROM_MS(3000),
+            .a_pos = 0.9, .b_pos = 1.0f,
+            .seek_count = 2,
+        },
+        {
+            /* Check that seek is triggered by EOF (no reported length) */
+            .can_seek = true, .after_1st_buferring = false,
+            .nolength_report = true,
+            .length = VLC_TICK_FROM_MS(1000),
+            .a_pos = 0.9, .b_pos = 1.0f,
+            .seek_count = 2,
+        },
+        {
+            /* Check that A->B loop is not triggered */
+            .can_seek = false, .after_1st_buferring = false,
+            .nolength_report = true, .wait_stopped = true,
+            .length = VLC_TICK_FROM_MS(100),
+            .a_pos = 0.1, .b_pos = 1.0f,
+            .seek_count = 1, /* Caused by the A seek request that fails */
+        },
+    };
+
+    for (size_t i = 0; i < ARRAY_SIZE(scenarios); ++i)
+    {
+        test_log("abloop[%zu]\n", i);
+        test_abloop_scenario(ctx, &scenarios[i]);
+    }
+}
+
+int
+main(void)
+{
+    struct ctx ctx;
+    ctx_init(&ctx, 0);
+    test_abloop(&ctx);
+    ctx_destroy(&ctx);
+    return 0;
+}


=====================================
test/src/player/common.h
=====================================
@@ -189,6 +189,7 @@ struct media_params
     bool can_pause;
     bool error;
     bool null_names;
+    bool report_length;
     vlc_tick_t pts_delay;
 
     const char *config;
@@ -214,6 +215,7 @@ struct media_params
     .can_pause = true, \
     .error = false, \
     .null_names = false, \
+    .report_length = true, \
     .pts_delay = DEFAULT_PTS_DELAY, \
     .config = NULL, \
     .discontinuities = NULL, \
@@ -781,7 +783,8 @@ create_mock_media(const char *name, const struct media_params *params)
         "sub_packetized=%d;length=%"PRId64";audio_sample_length=%"PRId64";"
         "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";"
+        "can_seek=%d;can_pause=%d;error=%d;null_names=%d;"
+        "report_length=%d;ts_delay=%"PRId64";"
         "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,
@@ -790,7 +793,7 @@ create_mock_media(const char *name, const struct media_params *params)
         params->video_frame_rate, params->video_frame_rate_base,
         params->title_count, params->chapter_count,
         params->can_seek, params->can_pause, params->error, params->null_names,
-        params->pts_delay,
+        params->report_length, params->pts_delay,
         params->config ? params->config : "",
         params->discontinuities ? params->discontinuities : "",
         params->attachment_count);


=====================================
test/src/player/timers.c
=====================================
@@ -251,16 +251,6 @@ test_timers(struct ctx *ctx)
 
     vlc_player_t *player = ctx->player;
 
-    static const struct vlc_player_timer_cbs cbs =
-    {
-        .on_update = timers_on_update,
-        .on_paused = timers_on_paused,
-    };
-    static const struct vlc_player_timer_smpte_cbs smpte_cbs =
-    {
-        .on_update = timers_smpte_on_update,
-    };
-
     /* Configure timers */
     struct timer_state timers[TIMER_COUNT];
 
@@ -269,18 +259,14 @@ test_timers(struct ctx *ctx)
 
     /* Filter some points in order to not be flooded */
     timers[REGULAR_DELAY_TIMER_IDX].delay = SOURCE_DELAY_TIMER_VALUE;
+    timers[SMPTE_TIMER_IDX].delay = VLC_TICK_INVALID;
 
     /* Create all timers */
     for (size_t i = 0; i < ARRAY_SIZE(timers); ++i)
     {
         vlc_vector_init(&timers[i].vec);
-        if (i == SMPTE_TIMER_IDX)
-            timers[i].id = vlc_player_AddSmpteTimer(player, &smpte_cbs,
-                                                    &timers[i]);
-        else
-            timers[i].id = vlc_player_AddTimer(player, timers[i].delay, &cbs,
-                                               &timers[i]);
-        assert(timers[i].id);
+        bool smpte = i == SMPTE_TIMER_IDX;
+        player_add_timer(player, &timers[i], smpte, timers[i].delay);
     }
 
     /* Test all timers using valid tracks */
@@ -337,8 +323,7 @@ test_timers(struct ctx *ctx)
     for (size_t i = 0; i < ARRAY_SIZE(timers); ++i)
     {
         struct timer_state *timer = &timers[i];
-        vlc_vector_clear(&timer->vec);
-        vlc_player_RemoveTimer(player, timer->id);
+        player_remove_timer(player, timer);
     }
 }
 


=====================================
test/src/player/timers.h
=====================================
@@ -15,12 +15,17 @@ struct report_timer
         REPORT_TIMER_POINT,
         REPORT_TIMER_TC,
         REPORT_TIMER_PAUSED,
+        REPORT_TIMER_SEEK,
     } type;
     union
     {
         struct vlc_player_timer_point point;
         struct vlc_player_timer_smpte_timecode tc;
         vlc_tick_t paused_date;
+        struct {
+            struct vlc_player_timer_point point;
+            bool finished;
+        } seek;
     };
 };
 typedef struct VLC_VECTOR(struct report_timer) vec_report_timer;
@@ -58,6 +63,21 @@ timers_on_paused(vlc_tick_t system_date, void *data)
     assert(success);
 }
 
+static inline void
+timers_on_seek(const struct vlc_player_timer_point *point, void *data)
+{
+    struct timer_state *timer = data;
+    struct report_timer report =
+    {
+        .type = REPORT_TIMER_SEEK,
+    };
+    report.seek.finished = point == NULL;
+    if (!report.seek.finished)
+        report.seek.point = *point;
+    bool success = vlc_vector_push(&timer->vec, report);
+    assert(success);
+}
+
 static inline void
 timers_smpte_on_update(const struct vlc_player_timer_smpte_timecode *tc,
                        void *data)
@@ -70,4 +90,36 @@ timers_smpte_on_update(const struct vlc_player_timer_smpte_timecode *tc,
     };
     bool success = vlc_vector_push(&timer->vec, report);
     assert(success);
+}
+
+static inline void
+player_add_timer(vlc_player_t *player, struct timer_state *timer, bool smpte,
+                 vlc_tick_t delay)
+{
+    static const struct vlc_player_timer_cbs cbs =
+    {
+        .on_update = timers_on_update,
+        .on_paused = timers_on_paused,
+        .on_seek = timers_on_seek,
+    };
+
+    static const struct vlc_player_timer_smpte_cbs smpte_cbs =
+    {
+        .on_update = timers_smpte_on_update,
+    };
+
+    vlc_vector_init(&timer->vec);
+    timer->delay = delay;
+    if (smpte)
+        timer->id = vlc_player_AddSmpteTimer(player, &smpte_cbs, timer);
+    else
+        timer->id = vlc_player_AddTimer(player, timer->delay, &cbs, timer);
+    assert(timer->id);
+}
+
+static inline void
+player_remove_timer(vlc_player_t *player, struct timer_state *timer)
+{
+    vlc_vector_clear(&timer->vec);
+    vlc_player_RemoveTimer(player, timer->id);
 }
\ No newline at end of file



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e48a61a6fb74322db8e841a8321bff298f00a0bf...170bd5d7850ffde19511a8b948c84a6b6a5a45cb

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e48a61a6fb74322db8e841a8321bff298f00a0bf...170bd5d7850ffde19511a8b948c84a6b6a5a45cb
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