[vlc-commits] core: playlist: handle random playback order

Romain Vimont git at videolan.org
Thu Nov 15 16:23:37 CET 2018


vlc | branch: master | Romain Vimont <rom1v at videolabs.io> | Tue Oct 16 15:27:35 2018 +0200| [3e5e863ca67c47c8ec7317bcae5e2be985af8c04] | committer: Thomas Guillem

core: playlist: handle random playback order

Use the randomizer in the playlist to implement random playback order.

Signed-off-by: Thomas Guillem <thomas at gllm.fr>

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=3e5e863ca67c47c8ec7317bcae5e2be985af8c04
---

 src/Makefile.am         |   1 +
 src/playlist/content.c  |  17 ++++++
 src/playlist/control.c  |  68 +++++++++++++++++++----
 src/playlist/playlist.c |   2 +
 src/playlist/playlist.h |   2 +
 src/playlist/test.c     | 142 ++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 220 insertions(+), 12 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index eefeab25cd..cb47fb463e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -592,6 +592,7 @@ test_playlist_SOURCES = playlist/test.c \
 	playlist/player.c \
 	playlist/playlist.c \
 	playlist/preparse.c \
+	playlist/randomizer.c \
 	playlist/request.c
 test_playlist_CFLAGS = -DTEST_PLAYLIST
 test_randomizer_SOURCES = playlist/randomizer.c
diff --git a/src/playlist/content.c b/src/playlist/content.c
index cf9f53aa79..86de05ccf9 100644
--- a/src/playlist/content.c
+++ b/src/playlist/content.c
@@ -41,6 +41,9 @@ vlc_playlist_ClearItems(vlc_playlist_t *playlist)
 static void
 vlc_playlist_ItemsReset(vlc_playlist_t *playlist)
 {
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+        randomizer_Clear(&playlist->randomizer);
+
     struct vlc_playlist_state state;
     vlc_playlist_state_Save(playlist, &state);
 
@@ -57,6 +60,10 @@ vlc_playlist_ItemsReset(vlc_playlist_t *playlist)
 void
 vlc_playlist_ItemsInserted(vlc_playlist_t *playlist, size_t index, size_t count)
 {
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+        randomizer_Add(&playlist->randomizer,
+                       &playlist->items.data[index], count);
+
     struct vlc_playlist_state state;
     vlc_playlist_state_Save(playlist, &state);
 
@@ -113,6 +120,14 @@ vlc_playlist_ItemsMoved(vlc_playlist_t *playlist, size_t index, size_t count,
 }
 
 static void
+vlc_playlist_ItemsRemoving(vlc_playlist_t *playlist, size_t index, size_t count)
+{
+    if (playlist->order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
+        randomizer_Remove(&playlist->randomizer,
+                          &playlist->items.data[index], count);
+}
+
+static void
 vlc_playlist_ItemsRemoved(vlc_playlist_t *playlist, size_t index, size_t count)
 {
     struct vlc_playlist_state state;
@@ -262,6 +277,8 @@ vlc_playlist_Remove(vlc_playlist_t *playlist, size_t index, size_t count)
     vlc_playlist_AssertLocked(playlist);
     assert(index < playlist->items.size);
 
+    vlc_playlist_ItemsRemoving(playlist, index, count);
+
     for (size_t i = 0; i < count; ++i)
         vlc_playlist_item_Release(playlist->items.data[index + i]);
 
diff --git a/src/playlist/control.c b/src/playlist/control.c
index f921e0d933..99c975bf64 100644
--- a/src/playlist/control.c
+++ b/src/playlist/control.c
@@ -31,6 +31,20 @@
 static void
 vlc_playlist_PlaybackOrderChanged(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);
 
@@ -44,6 +58,12 @@ vlc_playlist_PlaybackOrderChanged(vlc_playlist_t *playlist)
 static void
 vlc_playlist_PlaybackRepeatChanged(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);
 
@@ -166,33 +186,35 @@ vlc_playlist_NormalOrderGetNextIndex(vlc_playlist_t *playlist)
 static inline bool
 vlc_playlist_RandomOrderHasPrev(vlc_playlist_t *playlist)
 {
-    VLC_UNUSED(playlist);
-    /* TODO */
-    return false;
+    return randomizer_HasPrev(&playlist->randomizer);
 }
 
 static inline size_t
 vlc_playlist_RandomOrderGetPrevIndex(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
 vlc_playlist_RandomOrderHasNext(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
 vlc_playlist_RandomOrderGetNextIndex(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
@@ -304,6 +326,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);
+    }
+
     vlc_playlist_SetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
@@ -323,6 +353,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);
+    }
+
     vlc_playlist_SetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
@@ -337,6 +375,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);
+    }
+
     vlc_playlist_SetCurrentIndex(playlist, index);
     return VLC_SUCCESS;
 }
diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c
index 0fbac27d73..618433c9e3 100644
--- a/src/playlist/playlist.c
+++ b/src/playlist/playlist.c
@@ -45,6 +45,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;
@@ -61,6 +62,7 @@ vlc_playlist_Delete(vlc_playlist_t *playlist)
     assert(vlc_list_is_empty(&playlist->listeners));
 
     vlc_playlist_PlayerDestroy(playlist);
+    randomizer_Destroy(&playlist->randomizer);
     vlc_playlist_ClearItems(playlist);
     free(playlist);
 }
diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h
index adbcf506ed..7af25b65bd 100644
--- a/src/playlist/playlist.h
+++ b/src/playlist/playlist.h
@@ -25,6 +25,7 @@
 #include <vlc_playlist.h>
 #include <vlc_vector.h>
 #include "../input/player.h"
+#include "randomizer.h"
 
 typedef struct input_item_t input_item_t;
 
@@ -48,6 +49,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;
diff --git a/src/playlist/test.c b/src/playlist/test.c
index a41d007e2c..8bbcbe5c66 100644
--- a/src/playlist/test.c
+++ b/src/playlist/test.c
@@ -1853,6 +1853,147 @@ 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[6];
+    CreateDummyMediaArray(media, 6);
+
+    /* initial playlist with 5 items (1 is not added immediately) */
+    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));
+
+    /* add a new item, it must be taken into account */
+    ret = vlc_playlist_AppendOne(playlist, media[5]);
+    assert(ret == VLC_SUCCESS);
+    assert(vlc_playlist_HasNext(playlist));
+
+    ret = vlc_playlist_Next(playlist);
+    assert(ret == VLC_SUCCESS);
+
+    assert(vlc_playlist_GetCurrentIndex(playlist) == 5);
+    assert(!vlc_playlist_HasNext(playlist));
+
+    vlc_playlist_RemoveOne(playlist, 5);
+
+    /* 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, 6);
+    vlc_playlist_Delete(playlist);
+}
+
 #undef EXPECT_AT
 
 int main(void)
@@ -1883,5 +2024,6 @@ int main(void)
     test_request_goto_with_matching_hint();
     test_request_goto_without_hint();
     test_request_goto_adapt();
+    test_random();
     return 0;
 }



More information about the vlc-commits mailing list