[vlc-commits] [Git][videolan/vlc][master] 2 commits: media_list_player: Extract find_next_media_locked()

François Cartegnie (@fcartegnie) gitlab at videolan.org
Tue Sep 29 06:20:25 UTC 2026



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
96faf044 by Diogo Simao Marques at 2026-09-29T08:01:59+02:00
media_list_player: Extract find_next_media_locked()

Move the lookup of the media to play after the current one, according
to the playback mode, into its own helper so that it can be reused.

No functional change.

- - - - -
7e0e881a by Diogo Simao Marques at 2026-09-29T08:01:59+02:00
media_list_player: Keep position and next media in sync on set_media_list()

libvlc_media_list_player_set_media_list() unconditionally stopped the
media player and jumped to playing the next item whenever a new list
was set while playback was ongoing, even when the media currently
playing was still part of the new list.

This is hit by any client that swaps in a shuffled copy of the
same media list during playback, e.g. toggling shuffle mode, which
made the currently playing track restart from the beginning on every
toggle. The natural end-of-track would pick the wrong media to play
next as well or, if the stale position ran past the end of the new
list, the playback would silently stop even though the shuffled list
was not empty.

So this drops the unconditional stop() call interrupting the playback,
and locates the currently playing media_t in the new list by identity
only when the media list player itself still has a position.
When found, update current_playing_item_path and re-queue the correct
next media accordingly.

If the currently playing media isn't part of the new list anymore, we
switch to the first media that followed it in the previous list (wrapping
around in loop mode) and is part of the new one, like vlc_playlist does
when the current item is removed. If there is none (for example: the list
was rebuilt with different media_t objects for the same tracks), the
playback stops and the position is cleared instead of leaving it stale.

This also adds regression tests for set_media_list() position handling,
covering the following behaviours:
- Replacing the list while playing, current media still present
  (the shuffle case set_media_list() is meant to handle)
- A completed stop followed by set_media_list() starts over at item 0
  instead of resuming the stopped position
- The currently playing media removed from the list, continuing with the
  media that followed it, including in loop mode, or stopping when there
  is none
- The currently playing media absent from the new list, including
  repeat mode and a list containing a node, doesn't crash or get stuck
  on a stale position

The tests don't depend on timing: they use media long enough to never
reach EOS during a test, check the queued next media with
libvlc_media_player_get_next_media() instead of waiting for EOS, and
only wait for media changes caused by the test itself.

- - - - -


3 changed files:

- include/vlc/libvlc_media_list_player.h
- lib/media_list_player.c
- test/libvlc/media_list_player.c


Changes:

=====================================
include/vlc/libvlc_media_list_player.h
=====================================
@@ -111,8 +111,16 @@ LIBVLC_API libvlc_media_player_t *
 /**
  * Set the media list associated with the player
  *
+ * If the current media is part of the new list, it keeps playing and the
+ * playback continues from its position in the new list. Otherwise, the
+ * playback switches to the first media that followed it in the previous
+ * list and is part of the new one, or stops if there is none.
+ *
  * \param p_mlp media list player instance
  * \param p_mlist list of media
+ *
+ * \note the caller must not hold p_mlist's lock
+ *       (libvlc_media_list_lock()) when calling this function.
  */
 LIBVLC_API void
     libvlc_media_list_player_set_media_list(


=====================================
lib/media_list_player.c
=====================================
@@ -37,6 +37,7 @@
 
 #include <vlc_common.h>
 #include <vlc_atomic.h>
+#include <vlc_vector.h>
 
 #include "libvlc_internal.h"
 
@@ -314,13 +315,110 @@ set_current_playing_item(libvlc_media_list_player_t * p_mlp, libvlc_media_list_p
     return libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
 }
 
+/**************************************************************************
+ *       find_next_media_locked (private)
+ *
+ * Find the media that should play after p_mlp->current_playing_item_path,
+ * according to the current playback mode.
+ *
+ * Playlist lock must be held.
+ **************************************************************************/
+static libvlc_media_t *
+find_next_media_locked(libvlc_media_list_player_t * p_mlp)
+{
+    libvlc_media_list_path_t path;
+    if (p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
+    {
+        bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
+        path = get_next_path(p_mlp, b_loop);
+    }
+    else
+        path = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
+
+    libvlc_media_t *md = NULL;
+    if (path != NULL)
+    {
+        md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
+        free(path);
+    }
+    return md;
+}
+
+static bool
+path_equals(const libvlc_media_list_path_t a, const libvlc_media_list_path_t b)
+{
+    int depth = libvlc_media_list_path_depth(a);
+    return depth == libvlc_media_list_path_depth(b)
+        && memcmp(a, b, depth * sizeof(*a)) == 0;
+}
+
+typedef struct VLC_VECTOR(libvlc_media_t *) media_vector_t;
+
+/**************************************************************************
+ *       append_next_media_locked (private)
+ *
+ * Append the media following p_mlp->current_playing_item_path to
+ * following, in playback order, until the end of the list or until
+ * reaching stop_path. current_playing_item_path is used as the cursor and
+ * is left NULL or pointing to stop_path.
+ *
+ * Playlist lock must be held.
+ **************************************************************************/
+static void
+append_next_media_locked(libvlc_media_list_player_t * p_mlp,
+                         media_vector_t *following,
+                         const libvlc_media_list_path_t stop_path)
+{
+    for (;;)
+    {
+        libvlc_media_list_path_t path = get_next_path(p_mlp, false);
+        free(p_mlp->current_playing_item_path);
+        p_mlp->current_playing_item_path = path;
+
+        if (path == NULL || (stop_path != NULL && path_equals(path, stop_path)))
+            return;
+
+        libvlc_media_t *md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
+        if (md != NULL && !vlc_vector_push(following, md))
+            libvlc_media_release(md);
+    }
+}
+
+/**************************************************************************
+ *       get_following_media_locked (private)
+ *
+ * Collect the media that would play after p_mlp->current_playing_item_path,
+ * in playback order, wrapping around in loop mode.
+ *
+ * Playlist lock must be held.
+ **************************************************************************/
+static void
+get_following_media_locked(libvlc_media_list_player_t * p_mlp,
+                           media_vector_t *following)
+{
+    libvlc_media_list_path_t current = p_mlp->current_playing_item_path;
+
+    p_mlp->current_playing_item_path = libvlc_media_list_path_copy(current);
+    append_next_media_locked(p_mlp, following, NULL);
+
+    if (p_mlp->e_playback_mode == libvlc_playback_mode_loop)
+    {
+        /* Start over from the first item, up to the current one */
+        free(p_mlp->current_playing_item_path);
+        p_mlp->current_playing_item_path = NULL;
+        append_next_media_locked(p_mlp, following, current);
+    }
+
+    free(p_mlp->current_playing_item_path);
+    p_mlp->current_playing_item_path = current;
+}
+
 static void
 internal_player_media_changed(vlc_player_t *player, input_item_t *new_media,
                               void *opaque)
 {
     (void) player;
     libvlc_media_list_player_t *p_mlp = opaque;
-    libvlc_media_t *md = NULL;
 
     libvlc_media_list_lock(p_mlp->p_mlist);
 
@@ -335,20 +433,7 @@ internal_player_media_changed(vlc_player_t *player, input_item_t *new_media,
     }
 
     /* Find and set the next media */
-    if (p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
-    {
-        bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
-        path = get_next_path(p_mlp, b_loop);
-    }
-    else
-        path = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
-
-
-    if (path != NULL)
-    {
-        md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
-        free(path);
-    }
+    libvlc_media_t *md = find_next_media_locked(p_mlp);
 
     libvlc_media_list_unlock(p_mlp->p_mlist);
 
@@ -466,17 +551,88 @@ void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp,
     assert (p_mlist);
 
     lock(p_mlp);
+
+    libvlc_media_t *p_current_media = p_mlp->current_playing_item_path
+        ? libvlc_media_player_get_media(p_mlp->p_mi) : NULL;
+
+    /* If the current media isn't part of the new list, playback continues
+     * with the first media that followed it in the old list and is part of
+     * the new one: collect them before the old list goes away. */
+    media_vector_t following = VLC_VECTOR_INITIALIZER;
+    if (p_current_media)
+    {
+        libvlc_media_list_lock(p_mlist);
+        libvlc_media_list_path_t path =
+            libvlc_media_list_path_of_item(p_mlist, p_current_media);
+        libvlc_media_list_unlock(p_mlist);
+
+        if (path == NULL)
+        {
+            libvlc_media_list_lock(p_mlp->p_mlist);
+            get_following_media_locked(p_mlp, &following);
+            libvlc_media_list_unlock(p_mlp->p_mlist);
+        }
+        free(path);
+    }
+
     if (p_mlp->p_mlist)
         libvlc_media_list_release(p_mlp->p_mlist);
     libvlc_media_list_retain(p_mlist);
 
     p_mlp->p_mlist = p_mlist;
-    if (libvlc_media_player_is_playing(p_mlp->p_mi))
+
+    if (p_current_media)
     {
-        stop(p_mlp);
-        set_relative_playlist_position_and_play(p_mlp, true);
+        libvlc_media_list_lock(p_mlp->p_mlist);
+
+        libvlc_media_t *p_following_media = NULL;
+        libvlc_media_list_path_t path =
+            libvlc_media_list_path_of_item(p_mlp->p_mlist, p_current_media);
+        for (size_t i = 0; path == NULL && i < following.size; i++)
+        {
+            p_following_media = following.data[i];
+            path = libvlc_media_list_path_of_item(p_mlp->p_mlist,
+                                                  p_following_media);
+        }
+
+        free(p_mlp->current_playing_item_path);
+        p_mlp->current_playing_item_path = path;
+
+        if (path == NULL)
+        {
+            /* Nothing left to play from the new list */
+            libvlc_media_list_unlock(p_mlp->p_mlist);
+            stop(p_mlp);
+        }
+        else if (p_following_media == NULL)
+        {
+            /* The current media keeps playing, queue its new next media */
+            libvlc_media_t *md = find_next_media_locked(p_mlp);
+
+            libvlc_media_list_unlock(p_mlp->p_mlist);
+
+            libvlc_media_player_set_next_media(p_mlp->p_mi, md);
+            libvlc_media_release(md);
+        }
+        else
+        {
+            /* The current media was removed, switch to its follower */
+            libvlc_media_list_unlock(p_mlp->p_mlist);
+
+            bool b_playing = libvlc_media_player_is_playing(p_mlp->p_mi);
+            libvlc_media_player_set_media(p_mlp->p_mi, p_following_media);
+            if (b_playing)
+                libvlc_media_player_play(p_mlp->p_mi);
+        }
+
+        libvlc_media_release(p_current_media);
     }
 
+    libvlc_media_t *md;
+    vlc_vector_foreach(md, &following)
+        libvlc_media_release(md);
+    vlc_vector_destroy(&following);
+
     unlock(p_mlp);
 }
 


=====================================
test/libvlc/media_list_player.c
=====================================
@@ -51,6 +51,7 @@ static void check_data_init(struct check_items_order_data *check)
 {
     vlc_mutex_init(&check->lock);
     vlc_cond_init(&check->wait);
+    check->state = libvlc_NothingSpecial;
     check->current_item = NULL;
     check->item_count = 0;
 }
@@ -398,17 +399,503 @@ static void test_media_list_player_play_item_at_index(const char** argv, int arg
     libvlc_release (vlc);
 }
 
+/* Long enough to never reach EOS on its own during a test: the tests below
+ * must control when the current media changes or ends, whatever the load. */
+#define LONG_SAMPLE "mock://length=100000000"
+
+static void test_media_list_player_set_media_list_while_playing(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml_shuffled;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+
+    test_log ("Testing set_media_list() while playing, current media "
+              "still present (shuffle case)\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    void *id0 = media_list_add_file_path(ml, file);
+    void *id1 = media_list_add_file_path(ml, file);
+    void *id2 = media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+
+    int ret = libvlc_media_list_player_play_item (mlp, id1);
+    assert(ret == 0);
+
+    wait_item (&check, id1);
+
+    vlc_mutex_lock(&check.lock);
+    unsigned item_count_before = check.item_count;
+    vlc_mutex_unlock(&check.lock);
+
+    /* A shuffled copy of the same list: same media_t identities as ml,
+     * but reordered, with id1 (currently playing) moved to the middle. */
+    ml_shuffled = libvlc_media_list_new ();
+    assert (ml_shuffled != NULL);
+    libvlc_media_list_add_media (ml_shuffled, id2);
+    libvlc_media_list_add_media (ml_shuffled, id1);
+    libvlc_media_list_add_media (ml_shuffled, id0);
+
+    libvlc_media_list_player_set_media_list (mlp, ml_shuffled);
+
+    /* Playback must not have been interrupted/restarted by the swap */
+    libvlc_media_player_t *mp = libvlc_media_list_player_get_media_player (mlp);
+    libvlc_media_t *md = libvlc_media_player_get_media (mp);
+    assert(md == id1);
+    libvlc_media_release (md);
+
+    /* The next media must follow ml_shuffled's order (id0), not ml's
+     * stale order (which would have been id2) */
+    md = libvlc_media_player_get_next_media (mp);
+    assert(md == id0);
+    libvlc_media_release (md);
+    libvlc_media_player_release (mp);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.current_item == id1);
+    assert(check.item_count == item_count_before);
+    vlc_mutex_unlock(&check.lock);
+
+    /* So must next(): it is the only media change that can happen from
+     * now on. */
+    ret = libvlc_media_list_player_next (mlp);
+    assert(ret == 0);
+    wait_item_count (&check, item_count_before + 1);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.current_item == id0);
+    vlc_mutex_unlock(&check.lock);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml_shuffled);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_after_stop(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+
+    test_log ("Testing set_media_list() after an explicit stop doesn't "
+              "resurrect the stopped position\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    void *id0 = media_list_add_file_path(ml, file);
+    media_list_add_file_path(ml, file);
+    media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+    libvlc_media_list_player_play (mlp);
+
+    wait_playing (&check);
+    wait_item (&check, id0);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    /* Forget about the first playback, so that the waits below can only
+     * be satisfied by the playback started by next() */
+    vlc_mutex_lock(&check.lock);
+    check.state = libvlc_NothingSpecial;
+    check.current_item = NULL;
+    unsigned item_count_before = check.item_count;
+    vlc_mutex_unlock(&check.lock);
+
+    /* Re-installing the list after a completed stop must not carry the
+     * stopped position or its queued next media over. */
+    libvlc_media_list_player_set_media_list (mlp, ml);
+
+    int ret = libvlc_media_list_player_next (mlp);
+    assert(ret == 0);
+    wait_playing (&check);
+    wait_item_count (&check, item_count_before + 1);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.current_item == id0);
+    vlc_mutex_unlock(&check.lock);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_not_found(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml2;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+
+    test_log ("Testing set_media_list() when the currently playing media "
+              "can't be identified in the new list\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    void *id0 = media_list_add_file_path(ml, file);
+    media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+    int ret = libvlc_media_list_player_play_item_at_index (mlp, 0);
+    assert(ret == 0);
+
+    wait_item (&check, id0);
+
+    /* A brand new list built from different media_t objects: id0 can't
+     * be identified in it by identity. */
+    ml2 = libvlc_media_list_new ();
+    assert (ml2 != NULL);
+    void *id2_0 = media_list_add_file_path(ml2, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml2);
+
+    /* Nothing that followed id0 is part of the new list: playback stops */
+    wait_stopped (&check);
+
+    /* Must not get stuck on the stale index: play() falls back to the
+     * start of the new list. */
+    libvlc_media_list_player_play (mlp);
+    wait_item (&check, id2_0);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml2);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_not_found_repeat(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml2;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+
+    test_log ("Testing set_media_list() in repeat mode when the currently "
+              "playing media can't be identified in the new list\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    libvlc_media_list_player_set_playback_mode (mlp, libvlc_playback_mode_repeat);
+
+    void *id0 = media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+    int ret = libvlc_media_list_player_play_item_at_index (mlp, 0);
+    assert(ret == 0);
+
+    wait_playing (&check);
+    wait_item (&check, id0);
+
+    /* In repeat mode, id0 is queued as its own next media */
+    libvlc_media_player_t *mp = libvlc_media_list_player_get_media_player (mlp);
+    libvlc_media_t *md = libvlc_media_player_get_next_media (mp);
+    assert(md == id0);
+    libvlc_media_release (md);
+    libvlc_media_player_release (mp);
+
+    vlc_mutex_lock(&check.lock);
+    unsigned item_count_before = check.item_count;
+    vlc_mutex_unlock(&check.lock);
+
+    /* Unrelated new list: id0 can't be identified in it, so playback must
+     * stop instead of resurrecting id0 via the queued next media. */
+    ml2 = libvlc_media_list_new ();
+    assert (ml2 != NULL);
+    media_list_add_file_path(ml2, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml2);
+    wait_stopped (&check);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.item_count == item_count_before);
+    vlc_mutex_unlock(&check.lock);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml2);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_not_found_hierarchical(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml2;
+    libvlc_media_list_player_t *mlp;
+
+    static const char * file = LONG_SAMPLE;
+    static const char * file_node = "mock://node_count=3;length=100000000";
+
+    test_log ("Testing set_media_list() when the current position is a "
+              "multi-level path into a node, and the new list doesn't "
+              "contain the currently playing media\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    /* item 0: flat media, item 1: a node with 3 sub-media */
+    media_list_add_file_path(ml, file);
+    media_list_add_file_path(ml, file_node);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+
+    /* Play the node directly: it is expanded, then its first sub-media
+     * plays, so that current_playing_item_path is a multi-level path */
+    int ret = libvlc_media_list_player_play_item_at_index (mlp, 1);
+    assert(ret == 0);
+    wait_item_count (&check, 2);
+
+    /* An unrelated, shorter list: the currently playing sub-item can't
+     * be found in it, and its top-level index is out of range. */
+    ml2 = libvlc_media_list_new ();
+    assert (ml2 != NULL);
+    void *id2_0 = media_list_add_file_path(ml2, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml2);
+    wait_stopped (&check);
+
+    /* Must not crash, and must recover cleanly */
+    ret = libvlc_media_list_player_next (mlp);
+    assert(ret == 0);
+    wait_item (&check, id2_0);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml2);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_current_removed(const char** argv, int argc)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml2;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+
+    test_log ("Testing set_media_list() when the currently playing media "
+              "was removed from the list\n");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    void *id0 = media_list_add_file_path(ml, file);
+    void *id1 = media_list_add_file_path(ml, file);
+    void *id2 = media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+
+    int ret = libvlc_media_list_player_play_item (mlp, id1);
+    assert(ret == 0);
+    wait_item (&check, id1);
+
+    vlc_mutex_lock(&check.lock);
+    unsigned item_count_before = check.item_count;
+    vlc_mutex_unlock(&check.lock);
+
+    /* [id0, id1, id2] -> [id0, id2] while id1 is playing */
+    ml2 = libvlc_media_list_new ();
+    assert (ml2 != NULL);
+    libvlc_media_list_add_media (ml2, id0);
+    libvlc_media_list_add_media (ml2, id2);
+
+    libvlc_media_list_player_set_media_list (mlp, ml2);
+
+    /* id1 must be stopped right away, continuing with the media that
+     * followed it: id2 */
+    wait_item_count (&check, item_count_before + 1);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.current_item == id2);
+    vlc_mutex_unlock(&check.lock);
+
+    libvlc_media_list_player_stop_async (mlp);
+    wait_stopped (&check);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml2);
+    libvlc_release (vlc);
+}
+
+static void test_media_list_player_set_media_list_last_removed(const char** argv, int argc,
+                                                               libvlc_playback_mode_t mode)
+{
+    libvlc_instance_t *vlc;
+    libvlc_media_list_t *ml;
+    libvlc_media_list_t *ml2;
+    libvlc_media_list_player_t *mlp;
+
+    const char * file = LONG_SAMPLE;
+    bool b_loop = mode == libvlc_playback_mode_loop;
+
+    test_log ("Testing set_media_list() when the currently playing media "
+              "was the last one and was removed from the list (%s)\n",
+              b_loop ? "loop" : "default");
+
+    vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    ml = libvlc_media_list_new ();
+    assert (ml != NULL);
+
+    struct check_items_order_data check;
+    check_data_init(&check);
+
+    mlp = libvlc_media_list_player_new (vlc, &cbs, &check);
+    assert(mlp);
+
+    libvlc_media_list_player_set_playback_mode (mlp, mode);
+
+    void *id0 = media_list_add_file_path(ml, file);
+    void *id1 = media_list_add_file_path(ml, file);
+    void *id2 = media_list_add_file_path(ml, file);
+
+    libvlc_media_list_player_set_media_list (mlp, ml);
+
+    int ret = libvlc_media_list_player_play_item (mlp, id2);
+    assert(ret == 0);
+    wait_item (&check, id2);
+
+    vlc_mutex_lock(&check.lock);
+    unsigned item_count_before = check.item_count;
+    vlc_mutex_unlock(&check.lock);
+
+    /* [id0, id1, id2] -> [id1, id0] while id2 is playing */
+    ml2 = libvlc_media_list_new ();
+    assert (ml2 != NULL);
+    libvlc_media_list_add_media (ml2, id1);
+    libvlc_media_list_add_media (ml2, id0);
+
+    libvlc_media_list_player_set_media_list (mlp, ml2);
+
+    if (b_loop)
+    {
+        /* Wrapping around the old list, id0 followed id2 */
+        wait_item_count (&check, item_count_before + 1);
+
+        vlc_mutex_lock(&check.lock);
+        assert(check.current_item == id0);
+        vlc_mutex_unlock(&check.lock);
+
+        libvlc_media_list_player_stop_async (mlp);
+    }
+
+    /* Without looping, nothing followed id2: playback stops */
+    wait_stopped (&check);
+
+    vlc_mutex_lock(&check.lock);
+    assert(check.item_count == item_count_before + b_loop);
+    vlc_mutex_unlock(&check.lock);
+
+    libvlc_media_list_player_release (mlp);
+    libvlc_media_list_release (ml);
+    libvlc_media_list_release (ml2);
+    libvlc_release (vlc);
+}
+
 int main (void)
 {
     test_init();
 
-    // There are 6 tests. And they take some times.
-    alarm(6 * 5);
+    // There are 13 tests. And they take some times.
+    alarm(13 * 5);
 
     test_media_list_player_pause_stop (test_defaults_args, test_defaults_nargs);
     test_media_list_player_play_item_at_index (test_defaults_args, test_defaults_nargs);
     test_media_list_player_previous (test_defaults_args, test_defaults_nargs);
     test_media_list_player_next (test_defaults_args, test_defaults_nargs);
     test_media_list_player_items_queue (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_while_playing (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_after_stop (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_not_found (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_not_found_repeat (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_not_found_hierarchical (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_current_removed (test_defaults_args, test_defaults_nargs);
+    test_media_list_player_set_media_list_last_removed (test_defaults_args, test_defaults_nargs, libvlc_playback_mode_default);
+    test_media_list_player_set_media_list_last_removed (test_defaults_args, test_defaults_nargs, libvlc_playback_mode_loop);
     return 0;
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/80f5f6084f06a9e1e1e9d0b4282034fb1ee7d66e...7e0e881a198330e36865bb15418c30b5a61f38c9

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/80f5f6084f06a9e1e1e9d0b4282034fb1ee7d66e...7e0e881a198330e36865bb15418c30b5a61f38c9
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list