From 49cbf166c6a3e34ff0b69e9d1525520e83a4d538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Mon, 13 Aug 2018 17:56:01 +0200 Subject: [PATCH] core: medialibrary: Add an event API --- include/vlc_media_library.h | 195 ++++++++++++++++++++++++++++++++++++ src/libvlccore.sym | 2 + src/misc/medialibrary.c | 63 ++++++++++++ 3 files changed, 260 insertions(+) diff --git a/include/vlc_media_library.h b/include/vlc_media_library.h index e1e08203f0..b5f08cedbe 100644 --- a/include/vlc_media_library.h +++ b/include/vlc_media_library.h @@ -280,6 +280,8 @@ struct vlc_ml_entrypoint_t typedef struct vlc_medialibrary_t vlc_medialibrary_t; /* "Private" medialibrary pointer, to be used by the core & medialibrary modules */ typedef struct vlc_medialibrary_module_t vlc_medialibrary_module_t; +/* Opaque event callback type */ +typedef struct vlc_ml_event_callback_t vlc_ml_event_callback_t; typedef enum vlc_ml_sorting_criteria_t { @@ -460,6 +462,181 @@ enum vlc_ml_playback_pref VLC_ML_PLAYBACK_PREF_APP_SPECIFIC, }; +enum vlc_ml_event_type +{ + /** + * Entity modification callbacks. The affected entity will be passed: + * - As a vlc_ml__t, depending on the type of the modified/inserted + * entity, in vlc_ml_event_t::modification::p_ + * for ADDED and UPDATED variants. + * - as an id, in vlc_ml_event_t::deletion::i_entity_id + * When _DELETED callbacks get invoked, the entity will already have been + * deleted from the database, and cannot be retrieved anymore + */ + VLC_ML_EVENT_MEDIA_ADDED, + VLC_ML_EVENT_MEDIA_UPDATED, + VLC_ML_EVENT_MEDIA_DELETED, + VLC_ML_EVENT_ARTIST_ADDED, + VLC_ML_EVENT_ARTIST_UPDATED, + VLC_ML_EVENT_ARTIST_DELETED, + VLC_ML_EVENT_ALBUM_ADDED, + VLC_ML_EVENT_ALBUM_UPDATED, + VLC_ML_EVENT_ALBUM_DELETED, + VLC_ML_EVENT_PLAYLIST_ADDED, + VLC_ML_EVENT_PLAYLIST_UPDATED, + VLC_ML_EVENT_PLAYLIST_DELETED, + VLC_ML_EVENT_GENRE_ADDED, + VLC_ML_EVENT_GENRE_UPDATED, + VLC_ML_EVENT_GENRE_DELETED, + /** + * A discovery started. + * For each VLC_ML_EVENT_DISCOVERY_STARTED event, there will be + * 1 VLC_ML_EVENT_DISCOVERY_COMPLETED event, and N + * VLC_ML_EVENT_DISCOVERY_COMPLETED events. + * The entry point being discovered is stored in + * vlc_ml_event_t::discovery_started::psz_entry_point. + */ + VLC_ML_EVENT_DISCOVERY_STARTED, + /** + * Sent when a discovery or reload operation starts analyzing a new folder. + * The discovered entry point is stored in + * vlc_ml_event_t::discovery_progress::psz_entry_point. + */ + VLC_ML_EVENT_DISCOVERY_PROGRESS, + /** + * Sent when an entry point discovery is completed. + * The entry point that was being discovered is stored in + * vlc_ml_event_t::discovery_completed::psz_entry_point. + * The success or failure state is stored in + * vlc_ml_event_t::discovery_completed::b_success + */ + VLC_ML_EVENT_DISCOVERY_COMPLETED, + /** + * An entry point reload operation started. + * For all the entry points being reloaded, N VLC_EVENT_DISCOVERY_PROGRESS + * and 1 VLC_EVENT_RELOAD_COMPLETED event will be sent. + * The entry point being reloaded is stored in + * vlc_ml_event_t::reload_started::psz_entry_point. + */ + VLC_ML_EVENT_RELOAD_STARTED, + /** + * Sent when an entry point reload is completed. + * The entry point that was being reloaded is stored in + * vlc_ml_event_t::reload_completed::psz_entry_point. + * The success or failure state is stored in + * vlc_ml_event_t::reload_completed::b_success + */ + VLC_ML_EVENT_RELOAD_COMPLETED, + /** + * Sent when an entry point removal request has been processed. + * The removed entry point is stored in + * vlc_ml_event_t::entry_point_removed::psz_entry_point and the success or failure + * state is stored in vlc_ml_event_t::entry_point_removed::b_success + */ + VLC_ML_EVENT_ENTRY_POINT_REMOVED, + /** + * Sent when an entry point ban request has been processed. + * The banned entry point is stored in + * vlc_ml_event_t::entry_point_banned::psz_entry_point and the operation success + * state is stored in vlc_ml_event_t::entry_point_banned::b_success + */ + VLC_ML_EVENT_ENTRY_POINT_BANNED, + /** + * Sent when an entry point unban request has been processed. + * The unbanned entry point is stored in + * vlc_ml_event_t::entry_point_unbanned::psz_entry_point and the operation success + * state is stored in vlc_ml_event_t::entry_point_unbanned::b_success + */ + VLC_ML_EVENT_ENTRY_POINT_UNBANNED, + /** + * Sent when a discoverer or parser threads changes its idle state. + * The idle state is stored in vlc_ml_event_t::background_idle_changed.b_idle. + * False means at least one background thread is in running, true means + * both discoverer & parser threads are paused. + */ + VLC_ML_EVENT_BACKGROUND_IDLE_CHANGED, + /** + * Sent when the parsing progress percentage gets updated. + * The percentage is stored as a [0;100] integer, in + * vlc_ml_event_t::parsing_progress::i_percent + * This value might decrease as more media get discovered, but it will only + * increase once all discovery operations are completed. + */ + VLC_ML_EVENT_PARSING_PROGRESS_UPDATED, +}; + +typedef struct vlc_ml_event_t +{ + int i_type; + union + { + struct + { + const char* psz_entry_point; + } discovery_started; + struct + { + const char* psz_entry_point; + } discovery_progress; + struct + { + const char* psz_entry_point; + bool b_success; + } discovery_completed; + struct + { + const char* psz_entry_point; + } reload_started; + struct + { + const char* psz_entry_point; + bool b_success; + } reload_completed; + struct + { + const char* psz_entry_point; + bool b_success; + } entry_point_removed; + struct + { + const char* psz_entry_point; + bool b_success; + } entry_point_banned; + struct + { + const char* psz_entry_point; + bool b_success; + } entry_point_unbanned; + struct + { + uint8_t i_percent; + } parsing_progress; + union + { + const vlc_ml_media_t* p_media; + const vlc_ml_artist_t* p_artist; + const vlc_ml_album_t* p_album; + const vlc_ml_playlist_t* p_playlist; + const vlc_ml_genre_t* p_genre; + } modification; + struct + { + int64_t i_entity_id; + } deletion; + struct + { + bool b_idle; + } background_idle_changed; + }; +} vlc_ml_event_t; + +typedef void (*vlc_ml_callback_t)( void* p_data, const vlc_ml_event_t* p_event ); + +typedef struct vlc_medialibrary_callbacks_t +{ + void (*pf_send_event)( vlc_medialibrary_module_t* p_ml, const vlc_ml_event_t* p_event ); +} vlc_medialibrary_callbacks_t; + struct vlc_medialibrary_module_t { struct vlc_common_members obj; @@ -493,6 +670,8 @@ struct vlc_medialibrary_module_t * Refer to the list of queries for the specific return type */ void* (*pf_get)( struct vlc_medialibrary_module_t* p_ml, int i_query, int64_t i_id ); + + const vlc_medialibrary_callbacks_t* cbs; }; vlc_medialibrary_t* libvlc_MlCreate( libvlc_int_t* p_libvlc ); @@ -505,6 +684,22 @@ VLC_API void* vlc_ml_get( vlc_medialibrary_t* p_ml, int i_query, int64_t i_id ) VLC_API int vlc_ml_control( vlc_medialibrary_t* p_ml, int i_query, ... ) VLC_USED; VLC_API int vlc_ml_list( vlc_medialibrary_t* p_ml, int i_query, const vlc_ml_query_params_t* p_params, ... ); + +/** + * \brief Registers a medialibrary callback. + * \returns A handle to the callback, to be passed to vlc_ml_event_unregister_callback + */ +VLC_API vlc_ml_event_callback_t* +vlc_ml_event_register_callback( vlc_medialibrary_t* p_ml, vlc_ml_callback_t cb, void* p_data ); + +/** + * \brief Unregisters a medialibrary callback + * \param p_handle The handled returned by vlc_ml_register_callback + */ +VLC_API void vlc_ml_event_unregister_callback( vlc_medialibrary_t* p_ml, + vlc_ml_event_callback_t* p_callback ); + + VLC_API void vlc_ml_entrypoints_release( vlc_ml_entrypoint_t* p_list, size_t i_nb_items ); VLC_API void vlc_ml_show_release( vlc_ml_show_t* p_show ); diff --git a/src/libvlccore.sym b/src/libvlccore.sym index 63293849d6..19fb6d492e 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -596,6 +596,8 @@ vlc_ml_instance_get vlc_ml_get vlc_ml_control vlc_ml_list +vlc_ml_event_register_callback +vlc_ml_event_unregister_callback vlc_ml_entrypoints_release vlc_ml_show_release vlc_ml_artist_release diff --git a/src/misc/medialibrary.c b/src/misc/medialibrary.c index 78ba661034..4fb9d786db 100644 --- a/src/misc/medialibrary.c +++ b/src/misc/medialibrary.c @@ -25,15 +25,32 @@ #include #include #include +#include +#include #include #include +struct vlc_ml_event_callback_t +{ + vlc_ml_callback_t pf_cb; + void* p_data; + struct vlc_list node; +}; + struct vlc_medialibrary_t { vlc_medialibrary_module_t m; + + vlc_mutex_t lock; + struct vlc_list cbs; }; +static vlc_medialibrary_t* ml_priv( vlc_medialibrary_module_t* p_ml ) +{ + return container_of( p_ml, struct vlc_medialibrary_t, m ); +} + void vlc_ml_entrypoints_release( vlc_ml_entrypoint_t* p_list, size_t i_nb_items ) { for ( size_t i = 0; i < i_nb_items; ++i ) @@ -43,16 +60,60 @@ void vlc_ml_entrypoints_release( vlc_ml_entrypoint_t* p_list, size_t i_nb_items free( p_list ); } +static void vlc_ml_event_send( vlc_medialibrary_module_t* p_ml, const vlc_ml_event_t* p_event ) +{ + vlc_medialibrary_t* p_priv = ml_priv( p_ml ); + vlc_mutex_lock( &p_priv->lock ); + struct vlc_ml_event_callback_t* p_cb; + vlc_list_foreach( p_cb, &p_priv->cbs, node ) + { + p_cb->pf_cb( p_cb->p_data, p_event ); + } + vlc_mutex_unlock( &p_priv->lock ); +} + +vlc_ml_event_callback_t* +vlc_ml_event_register_callback( vlc_medialibrary_t* p_ml, vlc_ml_callback_t cb, + void* p_data ) +{ + struct vlc_ml_event_callback_t* p_cb = malloc( sizeof( *p_cb ) ); + if ( unlikely( p_cb == NULL ) ) + return NULL; + p_cb->pf_cb = cb; + p_cb->p_data = p_data; + vlc_mutex_lock( &p_ml->lock ); + vlc_list_append( &p_cb->node, &p_ml->cbs ); + vlc_mutex_unlock( &p_ml->lock ); + return p_cb; +} + +void vlc_ml_event_unregister_callback( vlc_medialibrary_t* p_ml, + vlc_ml_event_callback_t* p_cb ) +{ + vlc_mutex_lock( &p_ml->lock ); + vlc_list_remove( &p_cb->node ); + vlc_mutex_unlock( &p_ml->lock ); + free( p_cb ); +} + +static const vlc_medialibrary_callbacks_t callbacks = { + .pf_send_event = &vlc_ml_event_send +}; + vlc_medialibrary_t* libvlc_MlCreate( libvlc_int_t* p_libvlc ) { vlc_medialibrary_t *p_ml = vlc_custom_create( VLC_OBJECT( p_libvlc ), sizeof( *p_ml ), "medialibrary" ); if ( unlikely( p_ml == NULL ) ) return NULL; + vlc_mutex_init( &p_ml->lock ); + vlc_list_init( &p_ml->cbs ); + p_ml->m.cbs = &callbacks; p_ml->m.p_module = module_need( &p_ml->m, "medialibrary", NULL, false ); if ( p_ml->m.p_module == NULL ) { vlc_object_release( &p_ml->m ); + vlc_mutex_destroy( &p_ml->lock ); return NULL; } return p_ml; @@ -62,6 +123,8 @@ void libvlc_MlRelease( vlc_medialibrary_t* p_ml ) { assert( p_ml != NULL ); module_unneed( &p_ml->m, p_ml->m.p_module ); + assert( vlc_list_is_empty( &p_ml->cbs ) ); + vlc_mutex_destroy( &p_ml->lock ); vlc_object_release( &p_ml->m ); }