Commit 0d870796 authored by Thomas Guillem's avatar Thomas Guillem
Browse files

aout: add the vlc_audio_meter API

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.
parent a2dc3a87
......@@ -24,6 +24,7 @@
#define VLC_AOUT_H 1
#include <assert.h>
#include <vlc_list.h>
/**
* \defgroup audio_output Audio output
......@@ -537,4 +538,163 @@ 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 callback
*
* Triggered from vlc_audio_meter_Process() and vlc_audio_meter_Flush().
*/
struct vlc_audio_meter_cbs
{
/**
* 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 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 plugin owner structure
*
* Used to setup callbacks and private data
*
* Can be registered with vlc_audio_meter_AddPlugin().
*/
struct vlc_audio_meter_plugin_owner
{
const struct vlc_audio_meter_cbs *cbs;
void *sys;
};
/**
* 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_plugin_owner *owner);
/**
* 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 */
......@@ -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;
};
......@@ -234,6 +245,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
......
......@@ -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 \
......
/*****************************************************************************
* 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;
struct vlc_audio_meter_plugin_owner owner;
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->owner.cbs->on_loudness != NULL)
plugin->owner.cbs->on_loudness(plugin->last_date, loudness, plugin->owner.sys);
}
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_plugin_owner *owner)
{
assert(owner != NULL && owner->cbs != NULL);
vlc_audio_meter_plugin *plugin = malloc(sizeof(*plugin));
if (plugin == NULL)
return NULL;
plugin->owner = *owner;
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);
}
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment