[vlc-commits] player: add a metadata listener API
Thomas Guillem
git at videolan.org
Wed Aug 26 16:01:20 CEST 2020
vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Thu Aug 13 15:56:04 2020 +0200| [e8f17308146f446bc46f6a3b787aed2406ce8d31] | committer: Thomas Guillem
player: add a metadata listener API
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.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e8f17308146f446bc46f6a3b787aed2406ce8d31
---
include/vlc_player.h | 112 +++++++++++++++++++++++++
src/Makefile.am | 1 +
src/libvlccore.sym | 2 +
src/player/metadata.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/player/player.c | 9 +-
src/player/player.h | 15 ++++
6 files changed, 359 insertions(+), 2 deletions(-)
diff --git a/include/vlc_player.h b/include/vlc_player.h
index ac5991982d..bec0dea9fe 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 159bb9e5c9..6dc46ab3d6 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 a9f2799f4f..2b750873fc 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 0000000000..aafdcbed7e
--- /dev/null
+++ b/src/player/metadata.c
@@ -0,0 +1,222 @@
+/*****************************************************************************
+ * 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_cbs meter_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);
+
+ const struct vlc_audio_meter_plugin_owner meter_plugin_owner =
+ {
+ .cbs = &meter_cbs,
+ .sys = player,
+ };
+
+ listener_id->audio_meter = aout_AddMeterPlugin(aout, chain, &meter_plugin_owner);
+ 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 fd072e414b..6ad82acd7e 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 077086199a..363f294e12 100644
--- a/src/player/player.h
+++ b/src/player/player.h
@@ -127,6 +127,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;
@@ -210,6 +223,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;
@@ -224,6 +238,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;
More information about the vlc-commits
mailing list