[vlc-devel] [PATCHv2 2/9] aout: add the vlc_audio_meter API

Thomas Guillem thomas at gllm.fr
Wed Aug 19 21:25:56 CEST 2020


This API will be used by the aout, and could be used by any visualisation
module needing audio measurements.

This API allows to create "audio meter" filters plugins. This new type of
filter is measuring audio blocks without modifying them and send the
measurement via audio filters callbacks. These events are then propagated to
vlc_audio_meter events.
---
 include/vlc_aout.h       | 148 +++++++++++++++++++++++++
 include/vlc_filter.h     |  18 ++++
 src/Makefile.am          |   1 +
 src/audio_output/meter.c | 225 +++++++++++++++++++++++++++++++++++++++
 src/libvlccore.sym       |   7 ++
 5 files changed, 399 insertions(+)
 create mode 100644 src/audio_output/meter.c

diff --git a/include/vlc_aout.h b/include/vlc_aout.h
index 5baa460f6de..1c35bba16eb 100644
--- a/include/vlc_aout.h
+++ b/include/vlc_aout.h
@@ -24,6 +24,7 @@
 #define VLC_AOUT_H 1
 
 #include <assert.h>
+#include <vlc_list.h>
 
 /**
  * \defgroup audio_output Audio output
@@ -537,4 +538,151 @@ static inline int aout_TimeGet(audio_output_t *aout, vlc_tick_t *delay)
 
 /** @} */
 
+/**
+ * @defgroup audio_output__meter Audio meter API
+ */
+
+/**
+ * Audio loudness measurement
+ */
+struct vlc_audio_loudness
+{
+    /** Momentary loudness (last 400 ms), in LUFS */
+    double loudness_momentary;
+    /** Short term loudness (last 3seconds), in LUFS */
+    double loudness_shortterm;
+    /** Integrated loudness (global), in LUFS */
+    double loudness_integrated;
+    /** Loudness range, in LU */
+    double loudness_range;
+    /** True Peak, in dBTP */
+    double truepeak;
+};
+
+/**
+ * Audio meter plugin opaque structure
+ *
+ * This opaque structure is returned by vlc_audio_meter_AddPlugin().
+ */
+typedef struct vlc_audio_meter_plugin vlc_audio_meter_plugin;
+
+/**
+ * Audio meter events
+ *
+ * Triggered from vlc_audio_meter_Process() and vlc_audio_meter_Flush().
+ * Can be registered with vlc_audio_meter_AddPlugin().
+ */
+struct vlc_audio_meter_events
+{
+    /**
+     * Called when new loudness measurements are available
+     *
+     * @param date absolute date (likely in the future) of this measurement
+     * @param loudness pointer to the loudness measurement
+     * @param opaque pointer set by vlc_audio_meter_AddPlugin().
+     */
+    void (*on_loudness)(vlc_tick_t date, const struct vlc_audio_loudness *loudness, void *data);
+};
+
+/**
+ * Audio meter structure
+ *
+ * Initialise with vlc_audio_meter_Init()
+ *
+ * @warning variables of this struct should not be used directly
+ */
+struct vlc_audio_meter
+{
+    vlc_mutex_t lock;
+    vlc_object_t *parent;
+    const audio_sample_format_t *fmt;
+
+    struct vlc_list plugins;
+};
+
+/**
+ * Initialize the audio meter structure
+ *
+ * @param meter allocated audio meter structure
+ * @param parent object that will be used to create audio filters
+ */
+VLC_API void
+vlc_audio_meter_Init(struct vlc_audio_meter *meter, vlc_object_t *parent);
+#define vlc_audio_meter_Init(a,b) vlc_audio_meter_Init(a, VLC_OBJECT(b))
+
+/**
+ * Free allocated resource from the audio meter structure
+ *
+ * @param meter allocated audio meter structure
+ */
+VLC_API void
+vlc_audio_meter_Destroy(struct vlc_audio_meter *meter);
+
+/**
+ * Set or reset the audio format
+ *
+ * This will reload all plugins added with vlc_audio_meter_AddPlugin()
+ *
+ * @param meter audio meter structure
+ * @param fmt NULL to unload all plugins or a valid pointer to an audio format,
+ * must stay valid during the lifetime of the audio meter (until
+ * vlc_audio_meter_Reset() or vlc_audio_meter_Destroy() are called)
+ *
+ * @return VLC_SUCCESS on success, VLC_EGENERIC if a plugin failed to load
+ */
+VLC_API int
+vlc_audio_meter_Reset(struct vlc_audio_meter *meter, const audio_sample_format_t *fmt);
+
+/**
+ * Add an "audio meter" plugin
+ *
+ * The module to be loaded if meter->fmt is valid, otherwise, the module
+ * will be loaded from a next call to vlc_audio_meter_Reset()
+ *
+ * @param meter audio meter structure
+ * @param chain name of the module, can contain specific module options using
+ * the following chain convention:"name{option1=a,option2=b}"
+ * @param cbs pointer to a vlc_audio_meter_events structure, the
+ * structure must stay valid during the lifetime of the plugin
+ * @param cbs_data opaque pointer used by the callbacks
+ * @return a valid audio meter plugin, or NULL in case of error
+ */
+VLC_API vlc_audio_meter_plugin *
+vlc_audio_meter_AddPlugin(struct vlc_audio_meter *meter, const char *chain,
+                          const struct vlc_audio_meter_events *cbs, void *cbs_data);
+
+/**
+ * Remove an "audio meter" plugin
+ *
+ * @param meter audio meter structure
+ * @param plugin plugin returned by vlc_audio_meter_AddPlugin()
+ */
+VLC_API void
+vlc_audio_meter_RemovePlugin(struct vlc_audio_meter *meter, vlc_audio_meter_plugin *plugin);
+
+/**
+ * Process an audio block
+ *
+ * vlc_audio_meter_events callbacks can be triggered from this function.
+ *
+ * @param meter audio meter structure
+ * @param block pointer to a block, this block won't be released of modified
+ * from this function
+ * @param date absolute date (likely in the future) when this block should be rendered
+ */
+VLC_API void
+vlc_audio_meter_Process(struct vlc_audio_meter *meter, block_t *block, vlc_tick_t date);
+
+/**
+ * Flush all "audio meter" plugins
+ *
+ * vlc_audio_meter_events callbacks can be triggered from this function.
+ *
+ * @param meter audio meter structure
+ */
+VLC_API void
+vlc_audio_meter_Flush(struct vlc_audio_meter *meter);
+
+/** @} */
+
 #endif /* VLC_AOUT_H */
diff --git a/include/vlc_filter.h b/include/vlc_filter.h
index 9574c318d98..6bcc0c3038e 100644
--- a/include/vlc_filter.h
+++ b/include/vlc_filter.h
@@ -30,6 +30,7 @@
 #include <vlc_codec.h>
 
 typedef struct vlc_video_context  vlc_video_context;
+struct vlc_audio_loudness;
 
 /**
  * \defgroup filter Filters
@@ -46,6 +47,15 @@ struct filter_video_callbacks
     vlc_decoder_device * (*hold_device)(vlc_object_t *, void *sys);
 };
 
+struct filter_audio_callbacks
+{
+    struct
+    {
+        void (*on_changed)(filter_t *,
+                           const struct vlc_audio_loudness *loudness);
+    } meter_loudness;
+};
+
 struct filter_subpicture_callbacks
 {
     subpicture_t *(*buffer_new)(filter_t *);
@@ -56,6 +66,7 @@ typedef struct filter_owner_t
     union
     {
         const struct filter_video_callbacks *video;
+        const struct filter_audio_callbacks *audio;
         const struct filter_subpicture_callbacks *sub;
     };
 
@@ -235,6 +246,13 @@ static inline block_t *filter_DrainAudio( filter_t *p_filter )
         return NULL;
 }
 
+static inline void filter_SendAudioLoudness(filter_t *filter,
+    const struct vlc_audio_loudness *loudness)
+{
+    assert(filter->owner.audio->meter_loudness.on_changed);
+    filter->owner.audio->meter_loudness.on_changed(filter, loudness);
+}
+
 /**
  * This function will return a new subpicture usable by p_filter as an output
  * buffer. You have to release it using subpicture_Delete or by returning it to
diff --git a/src/Makefile.am b/src/Makefile.am
index 9e7c2931d27..159bb9e5c90 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -303,6 +303,7 @@ libvlccore_la_SOURCES = \
 	audio_output/common.c \
 	audio_output/dec.c \
 	audio_output/filters.c \
+	audio_output/meter.c \
 	audio_output/output.c \
 	audio_output/volume.c \
 	video_output/chrono.h \
diff --git a/src/audio_output/meter.c b/src/audio_output/meter.c
new file mode 100644
index 00000000000..81808fe0558
--- /dev/null
+++ b/src/audio_output/meter.c
@@ -0,0 +1,225 @@
+/*****************************************************************************
+ * meter.c : audio meter
+ *****************************************************************************
+ * Copyright (C) 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 <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_modules.h>
+#include <vlc_aout.h>
+#include "aout_internal.h"
+
+struct vlc_audio_meter_plugin
+{
+    char *name;
+    config_chain_t *cfg;
+    filter_t *filter;
+    vlc_tick_t last_date;
+
+    const struct vlc_audio_meter_events *cbs;
+    void *data;
+
+    struct vlc_list node;
+};
+
+void
+(vlc_audio_meter_Init)(struct vlc_audio_meter *meter, vlc_object_t *obj)
+{
+    vlc_mutex_init(&meter->lock);
+    meter->parent = obj;
+    meter->fmt = NULL;
+    vlc_list_init(&meter->plugins);
+}
+
+void
+vlc_audio_meter_Destroy(struct vlc_audio_meter *meter)
+{
+    vlc_audio_meter_plugin *plugin;
+    vlc_list_foreach(plugin, &meter->plugins, node)
+        vlc_audio_meter_RemovePlugin(meter, plugin);
+}
+
+static void
+vlc_audio_meter_OnLoudnessChanged(filter_t *filter,
+                             const struct vlc_audio_loudness *loudness)
+{
+    vlc_audio_meter_plugin *plugin = filter->owner.sys;
+
+    if (plugin->cbs->on_loudness != NULL)
+        plugin->cbs->on_loudness(plugin->last_date, loudness, plugin->data);
+}
+
+static filter_t *
+vlc_audio_meter_CreatePluginFilter(struct vlc_audio_meter *meter, vlc_audio_meter_plugin *plugin)
+{
+    static const struct filter_audio_callbacks audio_cbs = {
+        .meter_loudness = { .on_changed = vlc_audio_meter_OnLoudnessChanged }
+    };
+
+    const filter_owner_t owner = {
+        .audio = &audio_cbs,
+        .sys = plugin,
+    };
+
+    return aout_filter_Create(meter->parent, &owner, "audio meter", plugin->name,
+                       meter->fmt, meter->fmt, plugin->cfg, true);
+}
+
+vlc_audio_meter_plugin *
+vlc_audio_meter_AddPlugin(struct vlc_audio_meter *meter, const char *chain,
+                     const struct vlc_audio_meter_events *cbs, void *data)
+{
+    assert(cbs != NULL);
+
+    vlc_audio_meter_plugin *plugin = malloc(sizeof(*plugin));
+    if (plugin == NULL)
+        return NULL;
+    plugin->cbs = cbs;
+    plugin->data = data;
+    plugin->last_date = VLC_TICK_INVALID;
+    plugin->name = NULL;
+    plugin->cfg = NULL;
+    plugin->filter = NULL;
+
+    free(config_ChainCreate(&plugin->name, &plugin->cfg, chain));
+    if (plugin->name == NULL)
+        goto error;
+
+    if (meter->fmt != NULL)
+    {
+        plugin->filter = vlc_audio_meter_CreatePluginFilter(meter, plugin);
+        if (plugin->filter == NULL)
+            goto error;
+
+        assert(plugin->filter->pf_audio_drain == NULL); /* Not supported */
+    }
+
+    vlc_mutex_lock(&meter->lock);
+    vlc_list_append(&plugin->node, &meter->plugins);
+    vlc_mutex_unlock(&meter->lock);
+
+    return plugin;
+
+error:
+    free(plugin->name);
+    if (plugin->cfg != NULL)
+        config_ChainDestroy(plugin->cfg);
+    free(plugin);
+    return NULL;
+}
+
+void
+vlc_audio_meter_RemovePlugin(struct vlc_audio_meter *meter, vlc_audio_meter_plugin *plugin)
+{
+    vlc_mutex_lock(&meter->lock);
+
+    if (plugin->filter != NULL)
+    {
+        module_unneed(plugin->filter, plugin->filter->p_module);
+        vlc_object_delete(plugin->filter);
+    }
+
+    if (plugin->cfg != NULL)
+        config_ChainDestroy(plugin->cfg);
+    free(plugin->name);
+
+    vlc_list_remove(&plugin->node);
+    free(plugin);
+
+    vlc_mutex_unlock(&meter->lock);
+}
+
+int
+vlc_audio_meter_Reset(struct vlc_audio_meter *meter, const audio_sample_format_t *fmt)
+{
+    int ret = VLC_SUCCESS;
+
+    meter->fmt = fmt;
+
+    vlc_mutex_lock(&meter->lock);
+
+    /* Reload every plugins using the new fmt */
+    vlc_audio_meter_plugin *plugin;
+    vlc_list_foreach(plugin, &meter->plugins, node)
+    {
+        if (plugin->filter != NULL)
+        {
+            module_unneed(plugin->filter, plugin->filter->p_module);
+            vlc_object_delete(plugin->filter);
+            plugin->filter = NULL;
+        }
+        plugin->last_date = VLC_TICK_INVALID;
+
+        if (meter->fmt != NULL)
+        {
+            plugin->filter = vlc_audio_meter_CreatePluginFilter(meter, plugin);
+            if (plugin->filter == NULL)
+            {
+                ret = VLC_EGENERIC;
+                break;
+            }
+        }
+    }
+
+    vlc_mutex_unlock(&meter->lock);
+
+    return ret;
+}
+
+void
+vlc_audio_meter_Process(struct vlc_audio_meter *meter, block_t *block, vlc_tick_t date)
+{
+    vlc_mutex_lock(&meter->lock);
+
+    vlc_audio_meter_plugin *plugin;
+    vlc_list_foreach(plugin, &meter->plugins, node)
+    {
+        filter_t *filter = plugin->filter;
+
+        if (filter != NULL)
+        {
+            plugin->last_date = date + block->i_length;
+
+            block_t *same_block = filter->pf_audio_filter(filter, block);
+            assert(same_block == block); (void) same_block;
+        }
+    }
+
+    vlc_mutex_unlock(&meter->lock);
+}
+
+void
+vlc_audio_meter_Flush(struct vlc_audio_meter *meter)
+{
+    vlc_mutex_lock(&meter->lock);
+
+    vlc_audio_meter_plugin *plugin;
+    vlc_list_foreach(plugin, &meter->plugins, node)
+    {
+        filter_t *filter = plugin->filter;
+        if (filter != NULL && filter->pf_flush != NULL)
+            filter->pf_flush(filter);
+    }
+
+    vlc_mutex_unlock(&meter->lock);
+}
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index ed43c17715c..a9f2799f4f0 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -29,6 +29,13 @@ aout_FiltersPlay
 aout_FiltersAdjustResampling
 aout_Hold
 aout_Release
+vlc_audio_meter_Init
+vlc_audio_meter_Destroy
+vlc_audio_meter_Reset
+vlc_audio_meter_AddPlugin
+vlc_audio_meter_RemovePlugin
+vlc_audio_meter_Process
+vlc_audio_meter_Flush
 block_Alloc
 block_FifoGet
 block_FifoNew
-- 
2.28.0



More information about the vlc-devel mailing list