[vlc-commits] core: playlist: implement shuffle

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


vlc | branch: master | Romain Vimont <rom1v at videolabs.io> | Tue Oct 16 15:37:41 2018 +0200| [6b110b648d71504231b84788033e41f1627fb907] | committer: Thomas Guillem

core: playlist: implement shuffle

Expose a function to shuffle the items in the playlist. Contrary to the
random playback mode, the location of the items within the playlist
changes.

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

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

 include/vlc_playlist.h |  8 +++++
 src/Makefile.am        |  4 ++-
 src/libvlccore.sym     |  1 +
 src/playlist/shuffle.c | 74 ++++++++++++++++++++++++++++++++++++++++++++
 src/playlist/test.c    | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 170 insertions(+), 1 deletion(-)

diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h
index 3659c2e2b4..c9074912b3 100644
--- a/include/vlc_playlist.h
+++ b/include/vlc_playlist.h
@@ -582,6 +582,14 @@ vlc_playlist_RequestRemove(vlc_playlist_t *playlist,
                            ssize_t index_hint);
 
 /**
+ * Shuffle the playlist.
+ *
+ * \param playlist the playlist, locked
+ */
+VLC_API void
+vlc_playlist_Shuffle(vlc_playlist_t *playlist);
+
+/**
  * Return the index of a given item.
  *
  * \param playlist the playlist, locked
diff --git a/src/Makefile.am b/src/Makefile.am
index cb47fb463e..58cc5a3319 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -244,6 +244,7 @@ libvlccore_la_SOURCES = \
 	playlist/randomizer.c \
 	playlist/randomizer.h \
 	playlist/request.c \
+	playlist/shuffle.c \
 	preparser/art.c \
 	preparser/art.h \
 	preparser/fetcher.c \
@@ -593,7 +594,8 @@ test_playlist_SOURCES = playlist/test.c \
 	playlist/playlist.c \
 	playlist/preparse.c \
 	playlist/randomizer.c \
-	playlist/request.c
+	playlist/request.c \
+	playlist/shuffle.c
 test_playlist_CFLAGS = -DTEST_PLAYLIST
 test_randomizer_SOURCES = playlist/randomizer.c
 test_randomizer_CFLAGS = -DTEST_RANDOMIZER
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index d1d63a9550..52c8017a3a 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -925,6 +925,7 @@ vlc_playlist_Remove
 vlc_playlist_RequestInsert
 vlc_playlist_RequestMove
 vlc_playlist_RequestRemove
+vlc_playlist_Shuffle
 vlc_playlist_IndexOf
 vlc_playlist_IndexOfMedia
 vlc_playlist_GetPlaybackRepeat
diff --git a/src/playlist/shuffle.c b/src/playlist/shuffle.c
new file mode 100644
index 0000000000..efc644ff58
--- /dev/null
+++ b/src/playlist/shuffle.c
@@ -0,0 +1,74 @@
+/*****************************************************************************
+ * playlist/shuffle.c
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_rand.h>
+#include "control.h"
+#include "item.h"
+#include "notify.h"
+#include "playlist.h"
+
+void
+vlc_playlist_Shuffle(vlc_playlist_t *playlist)
+{
+    vlc_playlist_AssertLocked(playlist);
+    if (playlist->items.size < 2)
+        /* we use size_t (unsigned), so the following loop would be incorrect */
+        return;
+
+    vlc_playlist_item_t *current = playlist->current != -1
+                                 ? playlist->items.data[playlist->current]
+                                 : NULL;
+
+    /* initialize separately instead of using vlc_lrand48() to avoid locking the
+     * mutex once for each item */
+    unsigned short xsubi[3];
+    vlc_rand_bytes(xsubi, sizeof(xsubi));
+
+    /* Fisher-Yates shuffle */
+    for (size_t i = playlist->items.size - 1; i != 0; --i)
+    {
+        size_t selected = (size_t) (nrand48(xsubi) % (i + 1));
+
+        /* swap items i and selected */
+        vlc_playlist_item_t *tmp = playlist->items.data[i];
+        playlist->items.data[i] = playlist->items.data[selected];
+        playlist->items.data[selected] = tmp;
+    }
+
+    struct vlc_playlist_state state;
+    if (current)
+    {
+        /* the current position have changed after the shuffle */
+        vlc_playlist_state_Save(playlist, &state);
+        playlist->current = vlc_playlist_IndexOf(playlist, current);
+        playlist->has_prev = vlc_playlist_ComputeHasPrev(playlist);
+        playlist->has_next = vlc_playlist_ComputeHasNext(playlist);
+    }
+
+    vlc_playlist_Notify(playlist, on_items_reset, playlist->items.data,
+                        playlist->items.size);
+    if (current)
+        vlc_playlist_state_NotifyChanges(playlist, &state);
+}
diff --git a/src/playlist/test.c b/src/playlist/test.c
index 8bbcbe5c66..888e76efa9 100644
--- a/src/playlist/test.c
+++ b/src/playlist/test.c
@@ -1994,6 +1994,89 @@ test_random(void)
     vlc_playlist_Delete(playlist);
 }
 
+static void
+test_shuffle(void)
+{
+    vlc_playlist_t *playlist = vlc_playlist_New(NULL);
+    assert(playlist);
+
+    input_item_t *media[10];
+    CreateDummyMediaArray(media, 10);
+
+    /* initial playlist with 10 items */
+    int ret = vlc_playlist_Append(playlist, media, 10);
+    assert(ret == VLC_SUCCESS);
+
+    struct vlc_playlist_callbacks cbs = {
+        .on_items_reset = callback_on_items_reset,
+        .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);
+
+    /* on_items_reset is called once during AddListener() */
+    callback_ctx_reset(&ctx);
+
+    playlist->current = 4;
+    playlist->has_prev = true;
+    playlist->has_next = true;
+
+    vlc_playlist_Shuffle(playlist);
+
+    ssize_t index = vlc_playlist_IndexOfMedia(playlist, media[4]);
+    assert(index != -1);
+    assert(index == playlist->current);
+
+    assert(ctx.vec_items_reset.size == 1);
+    assert(ctx.vec_items_reset.data[0].count == 10);
+    assert(ctx.vec_items_reset.data[0].state.playlist_size == 10);
+    assert(ctx.vec_items_reset.data[0].state.current == index);
+    assert(ctx.vec_items_reset.data[0].state.has_prev == (index > 0));
+    assert(ctx.vec_items_reset.data[0].state.has_next == (index < 9));
+
+    if (index == 4)
+        assert(ctx.vec_current_index_changed.size == 0);
+    else
+    {
+        assert(ctx.vec_current_index_changed.size == 1);
+        assert(ctx.vec_current_index_changed.data[0].current == index);
+    }
+
+    if (index == 0)
+    {
+        assert(!playlist->has_prev);
+        assert(ctx.vec_has_prev_changed.size == 1);
+        assert(!ctx.vec_has_prev_changed.data[0].has_prev);
+    }
+    else
+    {
+        assert(playlist->has_prev);
+        assert(ctx.vec_has_prev_changed.size == 0);
+    }
+
+    if (index == 9)
+    {
+        assert(!playlist->has_next);
+        assert(ctx.vec_has_next_changed.size == 1);
+        assert(!ctx.vec_has_next_changed.data[0].has_next);
+    }
+    else
+    {
+        assert(playlist->has_next);
+        assert(ctx.vec_has_next_changed.size == 0);
+    }
+
+    callback_ctx_destroy(&ctx);
+    vlc_playlist_RemoveListener(playlist, listener);
+    DestroyMediaArray(media, 10);
+    vlc_playlist_Delete(playlist);
+}
+
 #undef EXPECT_AT
 
 int main(void)
@@ -2025,5 +2108,6 @@ int main(void)
     test_request_goto_without_hint();
     test_request_goto_adapt();
     test_random();
+    test_shuffle();
     return 0;
 }



More information about the vlc-commits mailing list