[vlc-devel] [PATCH 06/12] core: playlist: implement shuffle
Romain Vimont
rom1v at videolabs.io
Thu Oct 11 23:14:44 CEST 2018
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.
---
include/vlc_playlist_new.h | 8 +++
src/libvlccore.sym | 1 +
src/playlist/playlist.c | 127 +++++++++++++++++++++++++++++++++++++
3 files changed, 136 insertions(+)
diff --git a/include/vlc_playlist_new.h b/include/vlc_playlist_new.h
index 0b32a0909e..03956a48b4 100644
--- a/include/vlc_playlist_new.h
+++ b/include/vlc_playlist_new.h
@@ -571,6 +571,14 @@ vlc_playlist_RequestRemove(vlc_playlist_t *playlist,
vlc_playlist_item_t *const items[], size_t count,
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.
*
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 12c938d4c2..77a87e7f05 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -876,6 +876,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/playlist.c b/src/playlist/playlist.c
index eb03c422f5..118e3f8721 100644
--- a/src/playlist/playlist.c
+++ b/src/playlist/playlist.c
@@ -30,6 +30,7 @@
#include <vlc_atomic.h>
#include <vlc_input_item.h>
#include <vlc_list.h>
+#include <vlc_rand.h>
#include <vlc_threads.h>
#include <vlc_vector.h>
#include "input/player.h"
@@ -896,6 +897,48 @@ vlc_playlist_Remove(vlc_playlist_t *playlist, size_t index, size_t count)
PlaylistItemsRemoved(playlist, index, count);
}
+void
+vlc_playlist_Shuffle(vlc_playlist_t *playlist)
+{
+ PlaylistAssertLocked(playlist);
+ if (playlist->items.size < 2)
+ /* we use size_t (unsigned), so the following loop would be incorrect */
+ return;
+
+ input_item_t *current_media = PlaylistGetMedia(playlist, playlist->current);
+
+ /* 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_media)
+ {
+ /* the current position have changed after the shuffle */
+ vlc_playlist_state_Save(playlist, &state);
+ playlist->current = vlc_playlist_IndexOfMedia(playlist, current_media);
+ playlist->has_prev = PlaylistHasPrev(playlist);
+ playlist->has_next = PlaylistHasNext(playlist);
+ }
+
+ PlaylistNotify(playlist, on_items_reset, playlist->items.data,
+ playlist->items.size);
+ if (current_media)
+ vlc_playlist_state_NotifyChanges(playlist, &state);
+}
+
ssize_t
vlc_playlist_IndexOf(vlc_playlist_t *playlist, const vlc_playlist_item_t *item)
{
@@ -3294,6 +3337,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)
@@ -3325,6 +3451,7 @@ int main(void)
test_request_goto_without_hint();
test_request_goto_adapt();
test_random();
+ test_shuffle();
return 0;
}
#endif /* TEST_PLAYLIST */
--
2.19.1
More information about the vlc-devel
mailing list