[vlc-devel] [PATCH 3/4] playlist: add multi-playlist support
Romain Vimont
rom1v at videolabs.io
Thu Aug 20 17:44:22 CEST 2020
Add the possibility to manage several playlists, identified by a
string, in the playlist repository.
---
include/vlc_playlist.h | 97 ++++++++++++++++++++++++++-
src/libvlccore.sym | 7 ++
src/playlist/playlist.c | 8 +++
src/playlist/playlist.h | 2 +
src/playlist/repo.c | 142 ++++++++++++++++++++++++++++++++++++++--
5 files changed, 249 insertions(+), 7 deletions(-)
diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h
index 9a1c66e3dc..d773b20ce8 100644
--- a/include/vlc_playlist.h
+++ b/include/vlc_playlist.h
@@ -371,6 +371,15 @@ vlc_playlist_Lock(vlc_playlist_t *);
VLC_API void
vlc_playlist_Unlock(vlc_playlist_t *);
+/**
+ * Get the playlist id.
+ *
+ * If the playlist is added to the playlist repo, this is the id the playlist
+ * is associated to. Otherwise, id is NULL.
+ */
+VLC_API const char *
+vlc_playlist_GetId(vlc_playlist_t *);
+
/**
* Add a playlist listener.
*
@@ -886,6 +895,8 @@ VLC_API int
vlc_playlist_Export(vlc_playlist_t *playlist, const char *filename,
const char *type);
+#define VLC_MAIN_PLAYLIST_ID "main"
+
/** Playlist repository (opaque) */
typedef struct vlc_playlist_repo vlc_playlist_repo_t;
@@ -898,13 +909,95 @@ typedef struct vlc_playlist_repo vlc_playlist_repo_t;
VLC_API vlc_playlist_repo_t *
libvlc_GetPlaylistRepo(libvlc_int_t *libvlc);
+/**
+ * Lock the playlist repo.
+ *
+ * All playlist repo functions must be called with lock held (check their
+ * description).
+
+ * \param repo the playlist repo, unlocked
+ */
+VLC_API void
+vlc_playlist_repo_Lock(vlc_playlist_repo_t *repo);
+
+/**
+ * Unlock the playlist repo.
+ *
+ * \param repo the playlist repo, locked
+ */
+VLC_API void
+vlc_playlist_repo_Unlock(vlc_playlist_repo_t *repo);
+
+/**
+ * Create a new playlist in the repository.
+ *
+ * A playlist with the same id must not already exist in this repo (check with
+ * vlc_playlist_repo_Get() beforehand if necessary).
+ *
+ * \param repo the playlist repo, locked
+ * \param id the id of the playlist to create
+ * \return the new playlist, NULL on error
+ */
+VLC_API vlc_playlist_t *
+vlc_playlist_repo_Create(vlc_playlist_repo_t *repo, const char *id);
+
+/**
+ * Remove a playlist from the repository.
+ *
+ * A playlist having this id must exist in this repo (check with
+ * vlc_playlist_repo_Get() beforehand if necessary).
+ *
+ * It is not possible to remove the main playlist (with id
+ * VLC_MAIN_PLAYLIST_ID).
+ *
+ * \param repo the playlist repo, locked
+ * \param id the id of the playlist to remove
+ */
+VLC_API void
+vlc_playlist_repo_Remove(vlc_playlist_repo_t *repo, const char *id);
+
+/**
+ * Retrieve a playlist from its id.
+ *
+ * If no playlist having is this exist in this repo, this function return NULL.
+ *
+ * \param repo the playlist repo, locked
+ * \param id the id of the playlist to get
+ * \return the associated playlist, NULL if not exist
+ */
+VLC_API vlc_playlist_t *
+vlc_playlist_repo_Get(vlc_playlist_repo_t *repo, const char *id);
+
+/**
+ * Get a list of all playlists in the repo.
+ *
+ * This returns an allocated array of pointers to the playlists. The number of
+ * playlists is written to the output parameter out_len.
+ *
+ * The array must then be released by the caller via free().
+ *
+ * It contains pointers to existing playlists, including the main playlist. The
+ * order is undefined, and may change between successive calls. The id of each
+ * playlist may be retrieved by vlc_playlist_GetId().
+ *
+ * The refcount of the playlists is not incremented. Therefore, they are only
+ * valid until the call vlc_playlist_repo_Unlock(). To keep a reference longer,
+ * it is possible to call vlc_playlist_Hold() before unlocking the repo.
+ *
+ * \param repo the playlist repo, locked
+ * \param out_len output parameter giving the number of playlist
+ * \return the playlists list
+ */
+VLC_API vlc_playlist_t **
+vlc_playlist_repo_List(vlc_playlist_repo_t *repo, size_t *out_len);
+
/**
* Return the main playlist.
*
* This does not increment the playlist refcount. The playlist is guaranteed to
* exist until the libvlc instance is destroyed.
*
- * \param the playlist repo
+ * \param the playlist repo, locked
* \return the main playlist
*/
VLC_API vlc_playlist_t *
@@ -918,6 +1011,8 @@ vlc_playlist_repo_GetMainPlaylist(vlc_playlist_repo_t *repo);
*
* This function is provided for convenience to retrieve the main playlist
* directly from the libvlc_int_t instance, without handling the repo instance.
+ * Therefore, the repo must not be locked, this function locks it internally to
+ * retrieve the main playlist instance.
*
* \param the libvlc instance
* \return the main playlist
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 74f47fd629..33e73267d3 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -902,6 +902,7 @@ vlc_playlist_Hold
vlc_playlist_Release
vlc_playlist_Lock
vlc_playlist_Unlock
+vlc_playlist_GetId
vlc_playlist_AddListener
vlc_playlist_RemoveListener
vlc_playlist_Count
@@ -937,6 +938,12 @@ vlc_playlist_Resume
vlc_playlist_Preparse
vlc_playlist_Export
vlc_playlist_repo_GetMainPlaylist
+vlc_playlist_repo_Lock
+vlc_playlist_repo_Unlock
+vlc_playlist_repo_Create
+vlc_playlist_repo_Remove
+vlc_playlist_repo_Get
+vlc_playlist_repo_List
libvlc_GetPlaylistRepo
libvlc_GetMainPlaylist
vlc_intf_GetMainPlaylist
diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c
index bea773385e..f338b6a3e4 100644
--- a/src/playlist/playlist.c
+++ b/src/playlist/playlist.c
@@ -44,6 +44,7 @@ vlc_playlist_New(vlc_object_t *parent)
return NULL;
}
+ playlist->id = NULL;
vlc_vector_init(&playlist->items);
randomizer_Init(&playlist->randomizer);
playlist->current = -1;
@@ -75,6 +76,7 @@ vlc_playlist_Delete(vlc_playlist_t *playlist)
vlc_playlist_PlayerDestroy(playlist);
randomizer_Destroy(&playlist->randomizer);
vlc_playlist_ClearItems(playlist);
+ free(playlist->id);
free(playlist);
}
@@ -100,3 +102,9 @@ vlc_playlist_Unlock(vlc_playlist_t *playlist)
{
vlc_player_Unlock(playlist->player);
}
+
+const char *
+vlc_playlist_GetId(vlc_playlist_t *playlist)
+{
+ return playlist->id;
+}
diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h
index 7fd34a10c5..838dde7155 100644
--- a/src/playlist/playlist.h
+++ b/src/playlist/playlist.h
@@ -48,6 +48,8 @@ typedef struct VLC_VECTOR(vlc_playlist_item_t *) playlist_item_vector_t;
struct vlc_playlist
{
+ char *id;
+
vlc_player_t *player;
libvlc_int_t *libvlc;
bool auto_preparse;
diff --git a/src/playlist/repo.c b/src/playlist/repo.c
index b9ac59d16a..74ec3ddfd5 100644
--- a/src/playlist/repo.c
+++ b/src/playlist/repo.c
@@ -29,10 +29,13 @@
#include <vlc_threads.h>
#include "libvlc.h"
#include "player/player.h"
+#include "playlist.h"
struct vlc_playlist_repo {
struct vlc_object_t obj;
vlc_mutex_t lock;
+ vlc_dictionary_t dict;
+ /* Reference to the main playlist stored in the dictionary */
vlc_playlist_t *main_playlist;
};
@@ -45,15 +48,22 @@ vlc_playlist_repo_New(vlc_object_t *parent)
return NULL;
vlc_mutex_init(&repo->lock);
+ vlc_dictionary_init(&repo->dict, 1);
repo->main_playlist = NULL;
return repo;
}
+static void ReleasePlaylist(void *data, void *userdata)
+{
+ (void) userdata;
+ vlc_playlist_t *playlist = data;
+ vlc_playlist_Release(playlist);
+}
+
void
vlc_playlist_repo_Delete(vlc_playlist_repo_t *repo)
{
- if (repo->main_playlist)
- vlc_playlist_Release(repo->main_playlist);
+ vlc_dictionary_clear(&repo->dict, ReleasePlaylist, NULL);
vlc_object_delete(repo);
}
@@ -114,9 +124,10 @@ libvlc_GetPlaylistRepo(libvlc_int_t *libvlc)
static void
InitMainPlaylist(vlc_playlist_repo_t *repo)
{
+ vlc_mutex_assert(&repo->lock);
assert(!repo->main_playlist);
- repo->main_playlist = vlc_playlist_New(VLC_OBJECT(repo));
+ repo->main_playlist = vlc_playlist_repo_Create(repo, VLC_MAIN_PLAYLIST_ID);
if (!repo->main_playlist)
return;
@@ -126,10 +137,10 @@ InitMainPlaylist(vlc_playlist_repo_t *repo)
vlc_playlist_t *
vlc_playlist_repo_GetMainPlaylist(vlc_playlist_repo_t *repo)
{
- vlc_mutex_lock(&repo->lock);
+ vlc_mutex_assert(&repo->lock);
+
if (!repo->main_playlist)
InitMainPlaylist(repo);
- vlc_mutex_unlock(&repo->lock);
return repo->main_playlist;
}
@@ -138,5 +149,124 @@ vlc_playlist_t *
libvlc_GetMainPlaylist(libvlc_int_t *libvlc)
{
vlc_playlist_repo_t *repo = libvlc_GetPlaylistRepo(libvlc);
- return vlc_playlist_repo_GetMainPlaylist(repo);
+
+ vlc_playlist_repo_Lock(repo);
+ vlc_playlist_t *playlist = vlc_playlist_repo_GetMainPlaylist(repo);
+ vlc_playlist_repo_Unlock(repo);
+
+ return playlist;
+}
+
+void
+vlc_playlist_repo_Lock(vlc_playlist_repo_t *repo)
+{
+ vlc_mutex_lock(&repo->lock);
+}
+
+void
+vlc_playlist_repo_Unlock(vlc_playlist_repo_t *repo)
+{
+ vlc_mutex_unlock(&repo->lock);
+}
+
+vlc_playlist_t *
+vlc_playlist_repo_Create(vlc_playlist_repo_t *repo, const char *id)
+{
+ vlc_mutex_assert(&repo->lock);
+
+ /* The playlist id must not already exist in the repo */
+ assert(!vlc_playlist_repo_Get(repo, id));
+
+ vlc_playlist_t *playlist = vlc_playlist_New(VLC_OBJECT(repo));
+ if (!playlist)
+ return NULL;
+
+ playlist->id = strdup(id);
+ if (!playlist->id)
+ {
+ vlc_playlist_Release(playlist);
+ return NULL;
+ }
+
+ /* XXX vlc_dictionary_insert "could not" fail :/ */
+ vlc_dictionary_insert(&repo->dict, id, playlist);
+
+ return playlist;
+}
+
+void
+vlc_playlist_repo_Remove(vlc_playlist_repo_t *repo, const char *id)
+{
+ vlc_mutex_assert(&repo->lock);
+
+ /* Could not remove the main playlist */
+ if (!strcmp(id, VLC_MAIN_PLAYLIST_ID))
+ {
+ msg_Warn(repo, "Could not remove the main playlist");
+ return;
+ }
+
+#ifndef NDEBUG
+ /* The playlist id must exist in the repo, and have the expected id */
+ vlc_playlist_t *playlist = vlc_playlist_repo_Get(repo, id);
+ assert(playlist);
+ assert(!strcmp(id, playlist->id));
+#endif
+
+ free(playlist->id);
+ playlist->id = NULL;
+ vlc_dictionary_remove_value_for_key(&repo->dict, id, ReleasePlaylist, NULL);
+}
+
+vlc_playlist_t *
+vlc_playlist_repo_Get(vlc_playlist_repo_t *repo, const char *id)
+{
+ vlc_mutex_assert(&repo->lock);
+
+ vlc_playlist_t *playlist = vlc_dictionary_value_for_key(&repo->dict, id);
+ assert(!playlist || !strcmp(id, playlist->id));
+ return playlist;
+}
+
+static void
+FreeDictionaryKeys(char **keys)
+{
+ char **p = keys;
+ while (*p)
+ free(*p++);
+ free(keys);
+}
+
+vlc_playlist_t **
+vlc_playlist_repo_List(vlc_playlist_repo_t *repo, size_t *out_len)
+{
+ vlc_mutex_assert(&repo->lock);
+
+ /* TODO improve vlc_dictionary_t API to avoid duplicated work */
+ char **keys = vlc_dictionary_all_keys(&repo->dict);
+ if (!keys)
+ return NULL;
+
+ /* The list of keys is NUL-terminated */
+ size_t count = 0;
+ while (keys[count])
+ ++count;
+
+ vlc_playlist_t **playlists = vlc_alloc(count, sizeof(*playlists));
+ if (!playlists)
+ {
+ FreeDictionaryKeys(keys);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ playlists[i] = vlc_dictionary_value_for_key(&repo->dict, keys[i]);
+ assert(playlists[i]);
+ assert(!strcmp(keys[i], playlists[i]->id));
+ }
+
+ FreeDictionaryKeys(keys);
+ *out_len = count;
+ return playlists;
}
--
2.28.0
More information about the vlc-devel
mailing list