[vlc-devel] [PATCH 05/12] core: playlist: handle random playback order

Romain Vimont rom1v at videolabs.io
Thu Oct 11 23:14:43 CEST 2018


Use the randomizer in the playlist to implement random playback order.
---
 src/Makefile.am         |   2 +-
 src/playlist/playlist.c | 201 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 190 insertions(+), 13 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index ac1ca1ef65..6980320cfd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -567,7 +567,7 @@ test_mrl_helpers_SOURCES = test/mrl_helpers.c
 test_arrays_SOURCES = test/arrays.c
 test_vector_SOURCES = test/vector.c
 test_shared_data_ptr_SOURCES = test/shared_data_ptr.cpp
-test_playlist_SOURCES = playlist/playlist.c
+test_playlist_SOURCES = playlist/playlist.c playlist/randomizer.c
 test_playlist_CFLAGS = -DTEST_PLAYLIST
 test_randomizer_SOURCES = playlist/randomizer.c
 test_randomizer_CFLAGS = -DTEST_RANDOMIZER
diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c
index c0baca824a..eb03c422f5 100644
--- a/src/playlist/playlist.c
+++ b/src/playlist/playlist.c
@@ -34,6 +34,7 @@
 #include <vlc_vector.h>
 #include "input/player.h"
 #include "libvlc.h" // for vlc_MetadataRequest()
+#include "randomizer.h"
 
 #ifdef TEST_PLAYLIST
 /* disable vlc_assert_locked in tests since the symbol is not exported */
@@ -109,6 +110,7 @@ struct vlc_playlist
     /* all remaining fields are protected by the lock of the player */
     struct vlc_player_listener_id *player_listener;
     playlist_item_vector_t items;
+    struct randomizer randomizer;
     ssize_t current;
     bool has_prev;
     bool has_next;
@@ -302,33 +304,35 @@ PlaylistNormalOrderGetNextIndex(vlc_playlist_t *playlist)
 static inline bool
 PlaylistRandomOrderHasPrev(vlc_playlist_t *playlist)
 {
-    VLC_UNUSED(playlist);
-    /* TODO */
-    return false;
+    return randomizer_HasPrev(&playlist->randomizer);
 }
 
 static inline size_t
 PlaylistRandomOrderGetPrevIndex(vlc_playlist_t *playlist)
 {
-    VLC_UNUSED(playlist);
-    /* TODO */
-    return -0;
+    vlc_playlist_item_t *prev = randomizer_PeekPrev(&playlist->randomizer);
+    assert(prev);
+    ssize_t index = vlc_playlist_IndexOf(playlist, prev);
+    assert(index != -1);
+    return (size_t) index;
 }
 
 static inline bool
 PlaylistRandomOrderHasNext(vlc_playlist_t *playlist)
 {
-    VLC_UNUSED(playlist);
-    /* TODO */
-    return false;
+    if (playlist->repeat == VLC_PLAYLIST_PLAYBACK_REPEAT_ALL)
+        return playlist->items.size > 0;
+    return randomizer_HasNext(&playlist->randomizer);
 }
 
 static inline size_t
 PlaylistRandomOrderGetNextIndex(vlc_playlist_t *playlist)
 {
-    VLC_UNUSED(playlist);
-    /* TODO */
-    return 0;
+    vlc_playlist_item_t *next = randomizer_PeekNext(&playlist->randomizer);
+    assert(next);
+    ssize_t index = vlc_playlist_IndexOf(playlist, next);
+    assert(index != -1);
+    return (size_t) index;
 }
 
 static size_t
@@ -394,6 +398,20 @@ PlaylistHasNext(vlc_playlist_t *playlist)
 static void
 PlaylistPlaybackOrderChanged(vlc_playlist_t *playlist)
 {
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+    {
+        /* randomizer is expected to be empty at this point */
+        assert(randomizer_Count(&playlist->randomizer) == 0);
+        randomizer_Add(&playlist->randomizer, playlist->items.data,
+                       playlist->items.size);
+
+        bool loop = playlist->repeat == VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
+        randomizer_SetLoop(&playlist->randomizer, loop);
+    }
+    else
+        /* we don't use the randomizer anymore */
+        randomizer_Clear(&playlist->randomizer);
+
     struct vlc_playlist_state state;
     vlc_playlist_state_Save(playlist, &state);
 
@@ -407,6 +425,12 @@ PlaylistPlaybackOrderChanged(vlc_playlist_t *playlist)
 static void
 PlaylistPlaybackRepeatChanged(vlc_playlist_t *playlist)
 {
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+    {
+        bool loop = playlist->repeat == VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
+        randomizer_SetLoop(&playlist->randomizer, loop);
+    }
+
     struct vlc_playlist_state state;
     vlc_playlist_state_Save(playlist, &state);
 
@@ -705,6 +729,7 @@ vlc_playlist_New(vlc_object_t *parent)
     }
 
     vlc_vector_init(&playlist->items);
+    randomizer_Init(&playlist->randomizer);
     playlist->current = -1;
     playlist->has_prev = false;
     playlist->has_next = false;
@@ -725,6 +750,7 @@ vlc_playlist_Delete(vlc_playlist_t *playlist)
     vlc_player_Unlock(playlist->player);
 
     vlc_player_Delete(playlist->player);
+    randomizer_Destroy(&playlist->randomizer);
 
     PlaylistClear(playlist);
 
@@ -968,6 +994,14 @@ vlc_playlist_Prev(vlc_playlist_t *playlist)
     if (ret != VLC_SUCCESS)
         return ret;
 
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+    {
+        /* mark the item as selected in the randomizer */
+        vlc_playlist_item_t *selected = randomizer_Prev(&playlist->randomizer);
+        assert(selected == playlist->items.data[index]);
+        VLC_UNUSED(selected);
+    }
+
     PlaylistSetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
@@ -987,6 +1021,14 @@ vlc_playlist_Next(vlc_playlist_t *playlist)
     if (ret != VLC_SUCCESS)
         return ret;
 
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+    {
+        /* mark the item as selected in the randomizer */
+        vlc_playlist_item_t *selected = randomizer_Next(&playlist->randomizer);
+        assert(selected == playlist->items.data[index]);
+        VLC_UNUSED(selected);
+    }
+
     PlaylistSetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
@@ -1001,6 +1043,12 @@ vlc_playlist_GoTo(vlc_playlist_t *playlist, ssize_t index)
     if (ret != VLC_SUCCESS)
         return ret;
 
+    if (index != -1 && playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+    {
+        vlc_playlist_item_t *item = playlist->items.data[index];
+        randomizer_Select(&playlist->randomizer, item);
+    }
+
     PlaylistSetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
@@ -3118,6 +3166,134 @@ test_request_goto_adapt(void)
     vlc_playlist_Delete(playlist);
 }
 
+/* this only tests that the randomizer is correctly managed by the playlist,
+ * for further tests on randomization properties, see randomizer tests. */
+static void
+test_random(void)
+{
+    vlc_playlist_t *playlist = vlc_playlist_New(NULL);
+    assert(playlist);
+
+    input_item_t *media[5];
+    CreateDummyMediaArray(media, 5);
+
+    /* initial playlist with 5 items */
+    int ret = vlc_playlist_Append(playlist, media, 5);
+    assert(ret == VLC_SUCCESS);
+
+    struct vlc_playlist_callbacks cbs = {
+        .on_current_index_changed = callback_on_current_index_changed,
+        .on_has_prev_changed = callback_on_has_prev_changed,
+        .on_has_next_changed = callback_on_has_next_changed,
+    };
+
+    struct callback_ctx ctx = CALLBACK_CTX_INITIALIZER;
+    vlc_playlist_listener_id *listener =
+            vlc_playlist_AddListener(playlist, &cbs, &ctx);
+    assert(listener);
+
+    assert(!vlc_playlist_HasPrev(playlist));
+    assert(vlc_playlist_HasNext(playlist));
+
+    for (int i = 0; i < 3; ++i)
+    {
+        assert(vlc_playlist_HasNext(playlist));
+        ret = vlc_playlist_Next(playlist);
+        assert(ret == VLC_SUCCESS);
+    }
+
+    assert(vlc_playlist_HasPrev(playlist));
+    vlc_playlist_SetPlaybackOrder(playlist, VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM);
+
+    /* in random order, previous uses the history of randomly selected items */
+    assert(!vlc_playlist_HasPrev(playlist));
+
+    bool selected[5] = {};
+    for (int i = 0; i < 5; ++i)
+    {
+        assert(vlc_playlist_HasNext(playlist));
+        ret = vlc_playlist_Next(playlist);
+        assert(ret == VLC_SUCCESS);
+        ssize_t index = vlc_playlist_GetCurrentIndex(playlist);
+        assert(index != -1);
+        assert(!selected[index]); /* not selected twice */
+        selected[index] = true;
+    }
+
+    assert(!vlc_playlist_HasNext(playlist));
+
+    /* enable repeat */
+    vlc_playlist_SetPlaybackRepeat(playlist, VLC_PLAYLIST_PLAYBACK_REPEAT_ALL);
+
+    /* now there are more items */
+    assert(vlc_playlist_HasNext(playlist));
+
+    /* once again */
+    memset(selected, 0, sizeof(selected));
+    for (int i = 0; i < 5; ++i)
+    {
+        assert(vlc_playlist_HasNext(playlist));
+        ret = vlc_playlist_Next(playlist);
+        assert(ret == VLC_SUCCESS);
+        ssize_t index = vlc_playlist_GetCurrentIndex(playlist);
+        assert(index != -1);
+        assert(!selected[index]); /* not selected twice */
+        selected[index] = true;
+    }
+
+    /* there are always more items */
+    assert(vlc_playlist_HasNext(playlist));
+
+    /* move to the middle of the random array */
+    for (int i = 0; i < 3; ++i)
+    {
+        assert(vlc_playlist_HasNext(playlist));
+        ret = vlc_playlist_Next(playlist);
+        assert(ret == VLC_SUCCESS);
+    }
+
+    memset(selected, 0, sizeof(selected));
+    int actual[5]; /* store the selected items (by their index) */
+
+    ssize_t current = vlc_playlist_GetCurrentIndex(playlist);
+    assert(current != -1);
+    actual[4] = current;
+
+    for (int i = 3; i >= 0; --i)
+    {
+        assert(vlc_playlist_HasPrev(playlist));
+        ret = vlc_playlist_Prev(playlist);
+        assert(ret == VLC_SUCCESS);
+        ssize_t index = vlc_playlist_GetCurrentIndex(playlist);
+        assert(index != -1);
+        actual[i] = index;
+        assert(!selected[index]); /* not selected twice */
+        selected[index] = true;
+    }
+
+    /* no more previous, the history may only contain each item once */
+    assert(!vlc_playlist_HasPrev(playlist));
+
+    /* we should get the same items in the reverse order going forward */
+    for (int i = 1; i < 5; ++i)
+    {
+        assert(vlc_playlist_HasNext(playlist));
+        ret = vlc_playlist_Next(playlist);
+        assert(ret == VLC_SUCCESS);
+        ssize_t index = vlc_playlist_GetCurrentIndex(playlist);
+        assert(index != -1);
+        assert(index == actual[i]);
+    }
+
+    /* there are always more items */
+    assert(vlc_playlist_HasNext(playlist));
+
+    callback_ctx_destroy(&ctx);
+    vlc_playlist_RemoveListener(playlist, listener);
+    DestroyMediaArray(media, 5);
+    vlc_playlist_Delete(playlist);
+}
+
 #undef EXPECT_AT
 
 int main(void)
@@ -3148,6 +3324,7 @@ int main(void)
     test_request_goto_with_matching_hint();
     test_request_goto_without_hint();
     test_request_goto_adapt();
+    test_random();
     return 0;
 }
 #endif /* TEST_PLAYLIST */
-- 
2.19.1



More information about the vlc-devel mailing list