[vlc-devel] [PATCHv2 6/9] player: add a metadata listener API

Thomas Guillem thomas at gllm.fr
Wed Aug 19 21:26:00 CEST 2020


And use the loudness measurement as a first use case.

The main difference between metadata listeners the player listeners are:

 - The information returned by metadata events is mainly useful for the UI, it
   should not be used to control the player.

 - It's not possible to call or lock the player from metadata events

 - Registering a metadata could cost some CPU cycle since it may spawn a
   measurement filter to get the requested metadata. Such cost should be
   explained in the comment of vlc_player_metadata_option enum.

Some player events could be moved to metadata events, like the statistics one.
---
 include/vlc_player.h  | 112 ++++++++++++++++++++++
 src/Makefile.am       |   1 +
 src/libvlccore.sym    |   2 +
 src/player/metadata.c | 217 ++++++++++++++++++++++++++++++++++++++++++
 src/player/player.c   |   9 +-
 src/player/player.h   |  15 +++
 6 files changed, 354 insertions(+), 2 deletions(-)
 create mode 100644 src/player/metadata.c

diff --git a/include/vlc_player.h b/include/vlc_player.h
index ac5991982df..bec0dea9fef 100644
--- a/include/vlc_player.h
+++ b/include/vlc_player.h
@@ -2141,6 +2141,118 @@ vlc_player_GetRenderer(vlc_player_t *player);
 
 /** @} vlc_player__renderer */
 
+/**
+ * @defgroup vlc_player__metadata Metadata callbacks
+ * @{
+ */
+
+/**
+ * Player metadata listener opaque structure.
+ *
+ * This opaque structure is returned by vlc_player_AddMetadataListener() and
+ * can be used to remove the listener via
+ * vlc_player_RemoveMetadataListener().
+ */
+typedef struct vlc_player_metadata_listener_id vlc_player_metadata_listener_id;
+
+/**
+ * Player metadata option
+ */
+enum vlc_player_metadata_option
+{
+    /**
+     * Ask for momentary loudness measurement
+     *
+     * Very low CPU usage.
+     * @see vlc_player_metadata_cbs.on_momentary_loudness_changed
+     */
+    VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY,
+
+    /**
+     * Ask for all loudness measurements
+     *
+     * High CPU usage.
+     * @see vlc_player_metadata_cbs.on_loudness_changed
+     */
+    VLC_PLAYER_METADATA_LOUDNESS_FULL,
+};
+
+/**
+ * Player metadata callbacks
+ *
+ * Can be registered with vlc_player_AddMetadataListener().
+ *
+ * @warning To avoid deadlocks, users should never call vlc_player_t functions
+ * from these callbacks.
+ */
+union vlc_player_metadata_cbs
+{
+    /**
+     * Called when the momentary loudness measurement have changed
+     *
+     * @see VLC_PLAYER_METADATA_LOUDNESS_MOMEMTARY
+     *
+     * Only sent when audio is playing, approximately every 400ms (but can be
+     * higher, depending on the input sample size).
+     *
+     * @param date Absolute date of the measurement. It is most likely in the
+     * future (0 to 2seconds) depending on the audio output buffer size.
+     * @param momentary_loudness Momentary loudness
+     * @param data opaque pointer set by vlc_player_AddMetadataListener()
+     */
+    void (*on_momentary_loudness_changed)(vlc_tick_t date,
+                                          double momentary_loudness,
+                                          void *data);
+
+    /**
+     * Called when loudness measurements have changed
+     *
+     * @see VLC_PLAYER_METADATA_LOUDNESS_FULL
+     *
+     * Only sent when audio is playing, approximately every 400ms (but can be
+     * higher, depending on the input sample size).
+     *
+     * @param date Absolute date of the measurement. It is most likely in the
+     * future (0 to 2seconds) depending on the audio output buffer size.
+     * @param loudness loudness measurement
+     * @param data opaque pointer set by vlc_player_AddMetadataListener()
+     */
+    void (*on_loudness_changed)(vlc_tick_t date,
+                                const struct vlc_audio_loudness *loudness,
+                                void *data);
+};
+
+/**
+ * Add a metadata listener
+ *
+ * @note Every registered loudness meter need to be removed by the caller with
+ * vlc_player_RemoveMetadataListener().
+ *
+ * @param player locked player instance
+ * @param cbs pointer to a vlc_player_metadata_cbs union, the
+ * structure must be valid during the lifetime of the player
+ * @param cbs_data opaque pointer used by the callbacks
+ * @return a valid listener id, or NULL in case of error (plugin missing)
+ */
+VLC_API vlc_player_metadata_listener_id *
+vlc_player_AddMetadataListener(vlc_player_t *player,
+                               enum vlc_player_metadata_option option,
+                               const union vlc_player_metadata_cbs *cbs,
+                               void *cbs_data);
+
+/**
+ * Remove a metadata listener
+ *
+ * @param player player instance
+ * @param listener_id listener id returned by vlc_player_AddMetadataListener()
+ */
+VLC_API void
+vlc_player_RemoveMetadataListener(vlc_player_t *player,
+                                  vlc_player_metadata_listener_id *listener_id);
+
+
+/** @} vlc_player__metadata */
+
 /**
  * @defgroup vlc_player__aout Audio output control
  * @{
diff --git a/src/Makefile.am b/src/Makefile.am
index 159bb9e5c90..6dc46ab3d69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -273,6 +273,7 @@ libvlccore_la_SOURCES = \
 	player/vout.c \
 	player/osd.c \
 	player/medialib.c \
+	player/metadata.c \
 	clock/input_clock.h \
 	clock/clock.h \
 	clock/clock_internal.h \
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index a9f2799f4f0..2b750873fcd 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -778,6 +778,7 @@ vlc_thumbnailer_Cancel
 vlc_thumbnailer_Release
 vlc_player_AddAssociatedMedia
 vlc_player_AddListener
+vlc_player_AddMetadataListener
 vlc_player_AddSmpteTimer
 vlc_player_AddTimer
 vlc_player_aout_AddListener
@@ -839,6 +840,7 @@ vlc_player_Pause
 vlc_player_program_Delete
 vlc_player_program_Dup
 vlc_player_RemoveListener
+vlc_player_RemoveMetadataListener
 vlc_player_RemoveTimer
 vlc_player_RestartEsId
 vlc_player_RestorePlaybackPos
diff --git a/src/player/metadata.c b/src/player/metadata.c
new file mode 100644
index 00000000000..15a1364a2ac
--- /dev/null
+++ b/src/player/metadata.c
@@ -0,0 +1,217 @@
+/*****************************************************************************
+ * player_metadata.c: Player metadata listener implementation
+ *****************************************************************************
+ * Copyright © 2020 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_modules.h>
+#include "player.h"
+#include "../audio_output/aout_internal.h"
+
+static void
+vlc_player_OnLoudnessEvent(vlc_tick_t date,
+                           const struct vlc_audio_loudness *loudness,
+                           void *data)
+{
+    vlc_player_t *player = data;
+
+    vlc_mutex_lock(&player->metadata_listeners_lock);
+
+    vlc_player_metadata_listener_id *other_id;
+    vlc_list_foreach(other_id, &player->metadata_listeners, node)
+    {
+        switch (other_id->option)
+        {
+            case VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY:
+                other_id->cbs->on_momentary_loudness_changed(date,
+                    loudness->loudness_momentary, other_id->cbs_data);
+                break;
+            case VLC_PLAYER_METADATA_LOUDNESS_FULL:
+                other_id->cbs->on_loudness_changed(date,
+                    loudness, other_id->cbs_data);
+                break;
+            default: break;
+        }
+    }
+
+    vlc_mutex_unlock(&player->metadata_listeners_lock);
+}
+
+static int
+vlc_player_AddMetadataLoudnessListener(vlc_player_t *player,
+                                       vlc_player_metadata_listener_id *listener_id)
+{
+    static const struct vlc_audio_meter_events audio_cbs = {
+        .on_loudness = vlc_player_OnLoudnessEvent,
+    };
+
+    listener_id->audio_meter = NULL;
+
+    vlc_player_metadata_listener_id *audio_meter_listener_id = NULL;
+    bool has_same_meter_module = false;
+
+    vlc_player_metadata_listener_id *other_id;
+    vlc_list_foreach(other_id, &player->metadata_listeners, node)
+    {
+        if (other_id->option == listener_id->option)
+            has_same_meter_module = true;
+
+        if (other_id->audio_meter != NULL)
+        {
+            assert(audio_meter_listener_id == NULL);
+            audio_meter_listener_id = other_id;
+        }
+    }
+
+    if (audio_meter_listener_id == NULL
+     || (!has_same_meter_module && listener_id->option == VLC_PLAYER_METADATA_LOUDNESS_FULL))
+    {
+        /* There are no audio meter plugins, or the audio meter plugin mode
+         * need to be increased */
+        audio_output_t *aout = vlc_player_aout_Hold(player);
+        if (aout == NULL)
+            return VLC_EGENERIC;
+
+        unsigned mode = listener_id->option == VLC_PLAYER_METADATA_LOUDNESS_FULL ? 4 : 0;
+        char chain[sizeof("ebur128{mode=X}")];
+        sprintf(chain, "ebur128{mode=%1u}", mode);
+
+        listener_id->audio_meter = aout_AddMeterPlugin(aout, chain,
+                                                       &audio_cbs, player);
+        if (listener_id->audio_meter == NULL)
+        {
+            aout_Release(aout);
+            return VLC_EGENERIC;
+        }
+
+        if (audio_meter_listener_id != NULL)
+        {
+            aout_RemoveMeterPlugin(aout, audio_meter_listener_id->audio_meter);
+            audio_meter_listener_id->audio_meter = NULL;
+        }
+        aout_Release(aout);
+    }
+
+    return VLC_SUCCESS;
+
+}
+
+static void
+vlc_player_RemoveMetadataLoudnessListener(vlc_player_t *player,
+                                          vlc_player_metadata_listener_id *listener_id)
+{
+    if (listener_id->audio_meter == NULL)
+        return; /* This listener is not the owner of the audio meter plugin */
+
+    /* Attach the audio meter plugin to an other listener */
+    vlc_player_metadata_listener_id *other_id;
+    vlc_list_foreach(other_id, &player->metadata_listeners, node)
+    {
+        if (other_id == listener_id)
+            continue;
+
+        if (other_id->option == VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY
+         || other_id->option == VLC_PLAYER_METADATA_LOUDNESS_FULL)
+        {
+            other_id->audio_meter = listener_id->audio_meter;
+            listener_id->audio_meter = NULL;
+            return;
+        }
+    }
+
+    /* There are no other listeners, remove the audio meter */
+    audio_output_t *aout = vlc_player_aout_Hold(player);
+    if (aout != NULL)
+    {
+        aout_RemoveMeterPlugin(aout, listener_id->audio_meter);
+        aout_Release(aout);
+    }
+}
+
+vlc_player_metadata_listener_id *
+vlc_player_AddMetadataListener(vlc_player_t *player,
+                               enum vlc_player_metadata_option option,
+                               const union vlc_player_metadata_cbs *cbs,
+                               void *cbs_data)
+{
+    vlc_player_assert_locked(player);
+    assert(cbs);
+
+    vlc_player_metadata_listener_id *listener_id = malloc(sizeof(*listener_id));
+    if (listener_id == NULL)
+        return NULL;
+
+    listener_id->cbs = cbs;
+    listener_id->cbs_data = cbs_data;
+    listener_id->option = option;
+
+    vlc_mutex_lock(&player->metadata_listeners_lock);
+
+    int ret;
+    switch (option)
+    {
+        case VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY:
+        case VLC_PLAYER_METADATA_LOUDNESS_FULL:
+            ret = vlc_player_AddMetadataLoudnessListener(player, listener_id);
+            break;
+        default: vlc_assert_unreachable();
+    }
+
+    if (ret == VLC_EGENERIC)
+    {
+        free(listener_id);
+        vlc_mutex_unlock(&player->metadata_listeners_lock);
+        return NULL;
+    }
+
+    vlc_list_append(&listener_id->node, &player->metadata_listeners);
+
+    vlc_mutex_unlock(&player->metadata_listeners_lock);
+
+    return listener_id;
+}
+
+void
+vlc_player_RemoveMetadataListener(vlc_player_t *player,
+                                    vlc_player_metadata_listener_id *listener_id)
+{
+    vlc_player_assert_locked(player);
+    assert(listener_id);
+
+    vlc_mutex_lock(&player->metadata_listeners_lock);
+
+    switch (listener_id->option)
+    {
+        case VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY:
+        case VLC_PLAYER_METADATA_LOUDNESS_FULL:
+            vlc_player_RemoveMetadataLoudnessListener(player, listener_id);
+            break;
+        default: vlc_assert_unreachable();
+    }
+
+    vlc_list_remove(&listener_id->node);
+    free(listener_id);
+
+    vlc_mutex_unlock(&player->metadata_listeners_lock);
+}
diff --git a/src/player/player.c b/src/player/player.c
index fd072e414b5..6ad82acd7ed 100644
--- a/src/player/player.c
+++ b/src/player/player.c
@@ -951,8 +951,10 @@ vlc_player_SelectPrevChapter(vlc_player_t *player)
 void
 vlc_player_Lock(vlc_player_t *player)
 {
-    /* Vout and aout locks should not be held, cf. vlc_player_vout_cbs and
-     * vlc_player_aout_cbs documentation */
+    /* Metadata, Vout and aout locks should not be held, cf.
+     * vlc_player_metadata_cbs, vlc_player_vout_cbs and vlc_player_aout_cbs
+     * documentation */
+    assert(!vlc_mutex_held(&player->metadata_listeners_lock));
     assert(!vlc_mutex_held(&player->vout_listeners_lock));
     assert(!vlc_mutex_held(&player->aout_listeners_lock));
     /* The timer lock should not be held (possible lock-order-inversion), cf.
@@ -1873,6 +1875,7 @@ vlc_player_InitLocks(vlc_player_t *player, enum vlc_player_lock_type lock_type)
     else
         vlc_mutex_init(&player->lock);
 
+    vlc_mutex_init(&player->metadata_listeners_lock);
     vlc_mutex_init(&player->vout_listeners_lock);
     vlc_mutex_init(&player->aout_listeners_lock);
     vlc_cond_init(&player->start_delay_cond);
@@ -1891,6 +1894,7 @@ vlc_player_Delete(vlc_player_t *player)
     vlc_cond_signal(&player->destructor.wait);
 
     assert(vlc_list_is_empty(&player->listeners));
+    assert(vlc_list_is_empty(&player->metadata_listeners));
     assert(vlc_list_is_empty(&player->vout_listeners));
     assert(vlc_list_is_empty(&player->aout_listeners));
 
@@ -1932,6 +1936,7 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type,
     assert(!media_provider || media_provider->get_next);
 
     vlc_list_init(&player->listeners);
+    vlc_list_init(&player->metadata_listeners);
     vlc_list_init(&player->vout_listeners);
     vlc_list_init(&player->aout_listeners);
     vlc_list_init(&player->destructor.inputs);
diff --git a/src/player/player.h b/src/player/player.h
index 87b9e430330..42d8f1cd075 100644
--- a/src/player/player.h
+++ b/src/player/player.h
@@ -126,6 +126,19 @@ struct vlc_player_listener_id
     struct vlc_list node;
 };
 
+struct vlc_player_metadata_listener_id
+{
+    const union vlc_player_metadata_cbs *cbs;
+    void *cbs_data;
+
+    enum vlc_player_metadata_option option;
+    union
+    {
+        vlc_audio_meter_plugin *audio_meter;
+    };
+    struct vlc_list node;
+};
+
 struct vlc_player_vout_listener_id
 {
     const struct vlc_player_vout_cbs *cbs;
@@ -209,6 +222,7 @@ struct vlc_player_t
 {
     struct vlc_object_t obj;
     vlc_mutex_t lock;
+    vlc_mutex_t metadata_listeners_lock;
     vlc_mutex_t aout_listeners_lock;
     vlc_mutex_t vout_listeners_lock;
     vlc_cond_t start_delay_cond;
@@ -223,6 +237,7 @@ struct vlc_player_t
     bool corked;
 
     struct vlc_list listeners;
+    struct vlc_list metadata_listeners;
     struct vlc_list aout_listeners;
     struct vlc_list vout_listeners;
 
-- 
2.28.0



More information about the vlc-devel mailing list