[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