[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