[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