[vlc-devel] [PATCH 3/4] playlist: add multi-playlist support

Steve Lhomme robux4 at ycbcr.xyz
Mon Aug 24 08:20:21 CEST 2020


On 2020-08-20 17:44, Romain Vimont wrote:
> 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.

This documentation is confusing.
I would assume the list of playlists you receive has each of them 
"locked" so it's valid as long as you want. Otherwise the playlist(s) 
may not be valid anymore until you receive them.

Also is it a lock or a hold on the pointer ? What would a lock lock 
against ?

> + * \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
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
> 


More information about the vlc-devel mailing list