[vlc-devel] [RFC PATCHv2 1/6] add a new type of module: vlc_renderer

Rémi Denis-Courmont remi at remlab.net
Thu Mar 3 21:18:47 CET 2016


Le 2016-03-02 16:58, Thomas Guillem a écrit :
> This new type of module will be used by renderers (chromecast, UPnP 
> Renderer,
> miracast, airport, DIAL, ConeCast) to configure a VLC input_thread 
> for
> rendering, like configuring a sout according to the renderer ip and
> capabilities.
>
> This API is experimental and has been tested only for chromecast, it 
> may move
> in the future when implementing others renderers.
>
> Also-by: Steve Lhomme <robux4 at videolabs.io>
> ---
>  include/vlc_common.h   |   2 +
>  include/vlc_renderer.h | 203 +++++++++++++++++++++++++++++++
>  src/Makefile.am        |   2 +
>  src/libvlccore.sym     |  13 ++
>  src/misc/renderer.c    | 323
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 543 insertions(+)
>  create mode 100644 include/vlc_renderer.h
>  create mode 100644 src/misc/renderer.c
>
> diff --git a/include/vlc_common.h b/include/vlc_common.h
> index 296439e..431fbb4 100644
> --- a/include/vlc_common.h
> +++ b/include/vlc_common.h
> @@ -208,6 +208,8 @@ typedef struct playlist_item_t playlist_item_t;
>  typedef struct services_discovery_t services_discovery_t;
>  typedef struct services_discovery_sys_t services_discovery_sys_t;
>  typedef struct playlist_add_t playlist_add_t;
> +typedef struct vlc_renderer_item vlc_renderer_item;
> +typedef struct vlc_renderer vlc_renderer;
>
>  /* Modules */
>  typedef struct module_t module_t;
> diff --git a/include/vlc_renderer.h b/include/vlc_renderer.h
> new file mode 100644
> index 0000000..c084dcb
> --- /dev/null
> +++ b/include/vlc_renderer.h
> @@ -0,0 +1,203 @@
> 
> +/*****************************************************************************
> + * vlc_renderer.h: VLC renderer
> +
> 
> *****************************************************************************
> + * Copyright (C) 2016 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.
> +
> 
> *****************************************************************************/
> +
> +#ifndef VLC_RENDERER_H
> +#define VLC_RENDERER_H 1
> +
> +/**
> + * @defgroup vlc_renderer VLC renderer
> + * @{
> + * @file
> + * This file declares VLC renderer structures and functions
> + * @defgroup vlc_renderer_item VLC renderer items
> + * @{
> + */
> +
> +typedef struct vlc_renderer_item vlc_renderer_item;
> +
> +/** Renderer flags */
> +typedef enum {
> +    VLC_RENDERER_CAN_AUDIO  = 0x0001,  /**< Renderer can render 
> audio */
> +    VLC_RENDERER_CAN_VIDEO  = 0x0002,  /**< Renderer can render 
> video */
> +} vlc_renderer_flags;

Flags are not enumerations.

C++ compilers don't like them that way.

> +
> +/**
> + * Create a new renderer item
> + *
> + * @param psz_module name of the module to use with this renderer,
> must be valid
> + * @param psz_host Host or IP of the item, must be valid
> + * @param i_port TCP/UDP port of the item, 0 for unknown port
> + * @param psz_name name of the item
> + * @param e_flags flags for the item
> + * @return a renderer item or NULL in case of error
> + */
> +VLC_API vlc_renderer_item *
> +vlc_renderer_item_new(const char *psz_module, const char *psz_host,
> +                      uint16_t i_port, const char *psz_name,
> +                      vlc_renderer_flags e_flags) VLC_USED;
> +
> +/**
> + * Hold a renderer item, i.e. creates a new reference
> + */
> +VLC_API vlc_renderer_item *
> +vlc_renderer_item_hold(vlc_renderer_item *p_item);
> +
> +/**
> + * Releases a renderer item, i.e. decrements its reference counter
> + */
> +VLC_API void
> +vlc_renderer_item_release(vlc_renderer_item *p_item);
> +
> +/**
> + *  Returns true if the renderer item has the same parameters
> + */
> +VLC_API bool
> +vlc_renderer_item_equals(const vlc_renderer_item *p_item,
> +                         const char *psz_module, const char 
> *psz_host,
> +                         uint16_t i_port, vlc_renderer_flags 
> e_flags);
> +/**
> + * Get the name of a renderer item
> + */
> +VLC_API const char *
> +vlc_renderer_item_name(const vlc_renderer_item *p_item);
> +

> +/**
> + * Get the host of a renderer item
> + */
> +VLC_API const char *
> +vlc_renderer_item_host(const vlc_renderer_item *p_item);
> +
> +/**
> + * Get the port of a renderer item
> + */
> +VLC_API uint16_t
> +vlc_renderer_item_port(const vlc_renderer_item *p_item);

For the umpteenth time, use URIs to identify resources.

And don't rely on ever being able to compare them for (in)equality. 
We've tried and failed already with SD items.

> +
> +/**
> + * Get the flags of a renderer item
> + */
> +VLC_API vlc_renderer_flags
> +vlc_renderer_item_flags(const vlc_renderer_item *p_item);
> +
> +/**
> + * Get the VLC option used to run this renderer
> + */
> +VLC_API const char *
> +vlc_renderer_item_option(const vlc_renderer_item *p_item);
> +
> +/**
> + * @}
> + * @defgroup vlc_renderer_service VLC renderer functions
> + * @{
> + */
> +
> +/**
> + * Get the volume of the loaded renderer
> + *
> + * @param pf_volume a pointer to the volume (0.0 to 1.0)
> + * @return VLC_SUCCESS on success, VLC_ENOOBJ if no module is 
> loaded, or an
> + * other VLC error code
> + */
> +VLC_API int
> +vlc_renderer_volume_get(vlc_renderer *p_renderer, float *pf_volume);

That and the rest below seems completely out of place.

On the one hand, it's not a consistent/sufficient interface for volume 
control. On the other hand, it seems to be limited to volume control in 
a completely arbitrary way: there are indeed plenty of properties that a 
render target can potentially have, and yet we don't want to end up with 
The Renderer as a God Object.

And also, audio volume is a property of the render target (what you 
called "vlc_renderer_item"), not the render target enumerator (what you 
called "vlc_renderer").

> +
> +/**
> + * Set the volume of the loaded renderer
> + *
> + * @param f_volume the volume (0.0 to 1.0)
> + * @return VLC_SUCCESS on success, VLC_ENOOBJ if no module is 
> loaded, or an
> + * other VLC error code
> + */
> +VLC_API int
> +vlc_renderer_volume_set(vlc_renderer *p_renderer, float f_volume);
> +
> +/**
> + * Get the mute state of the loaded renderer
> + *
> + * @return VLC_SUCCESS on success, VLC_ENOOBJ if no module is 
> loaded, or an
> + * other VLC error code
> + */
> +VLC_API int
> +vlc_renderer_mute_get(vlc_renderer *p_renderer, bool *b_mute);
> +
> +/**
> + * Get the mute state of the loaded renderer
> + *
> + * @return VLC_SUCCESS on success, VLC_ENOOBJ if no module is 
> loaded, or an
> + * other VLC error code
> + */
> +VLC_API int
> +vlc_renderer_mute_set(vlc_renderer *p_renderer, bool b_mute);
> +
> +/**
> + * @}
> + * @defgroup vlc_renderer_module VLC renderer module
> + * @{
> + */
> +
> +typedef struct vlc_renderer vlc_renderer;
> +typedef struct vlc_renderer_sys vlc_renderer_sys;
> +struct vlc_renderer
> +{
> +    VLC_COMMON_MEMBERS
> +    /**
> +     */
> +    module_t            *p_module;
> +    vlc_renderer_sys    *p_sys;
> +
> +    const vlc_renderer_item *p_item;
> +
> +    /**
> +     * Called on vlc_renderer_set_input()
> +     */
> +    int     (*pf_set_input)(vlc_renderer *p_renderer, input_thread_t
> *p_input);
> +    /**
> +     * Called on vlc_renderer_volume_get()
> +     */
> +    int     (*pf_volume_get)(vlc_renderer *p_renderer, float 
> *pf_volume);
> +    /**
> +     * Called on vlc_renderer_volume_set()
> +     */
> +    int     (*pf_volume_set)(vlc_renderer *p_renderer, float 
> f_volume);
> +    /**
> +     * Called on vlc_renderer_mute_get()
> +     */
> +    int     (*pf_mute_get)(vlc_renderer *p_renderer, bool *pb_mute);
> +    /**
> +     * Called on vlc_renderer_mute_set()
> +     */
> +    int     (*pf_mute_set)(vlc_renderer *p_renderer, bool b_mute);
> +};
> +
> +/** @} @} */
> +
> +/* Release with vlc_object_release */
> +vlc_renderer *
> +vlc_renderer_new(vlc_object_t *p_obj, const char *psz_renderer);
> +#define vlc_renderer_new(a, b) vlc_renderer_new(VLC_OBJECT(a), b)
> +
> +/* Returns true if the renderer is created from this string option 
> */
> +bool
> +vlc_renderer_equals(const vlc_renderer *p_renderer, const char
> *psz_renderer);
> +
> +int
> +vlc_renderer_set_input(vlc_renderer *p_renderer, input_thread_t 
> *p_input);
> +
> +#endif
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 5ee185f..4d5fd6c 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -82,6 +82,7 @@ pluginsinclude_HEADERS = \
>  	../include/vlc_services_discovery.h \
>  	../include/vlc_fingerprinter.h \
>  	../include/vlc_interrupt.h \
> +	../include/vlc_renderer.h \
>  	../include/vlc_sout.h \
>  	../include/vlc_spu.h \
>  	../include/vlc_stream.h \
> @@ -444,6 +445,7 @@ SOURCES_libvlc_common = \
>  	misc/interrupt.h \
>  	misc/interrupt.c \
>  	misc/keystore.c \
> +	misc/renderer.c \
>  	modules/modules.h \
>  	modules/modules.c \
>  	modules/bank.c \
> diff --git a/src/libvlccore.sym b/src/libvlccore.sym
> index 0d69d14..572e293 100644
> --- a/src/libvlccore.sym
> +++ b/src/libvlccore.sym
> @@ -734,3 +734,16 @@ addons_manager_Remove
>  addon_entry_New
>  addon_entry_Hold
>  addon_entry_Release
> +vlc_renderer_item_new
> +vlc_renderer_item_hold
> +vlc_renderer_item_release
> +vlc_renderer_item_equals
> +vlc_renderer_item_name
> +vlc_renderer_item_host
> +vlc_renderer_item_port
> +vlc_renderer_item_flags
> +vlc_renderer_item_option
> +vlc_renderer_volume_get
> +vlc_renderer_volume_set
> +vlc_renderer_mute_get
> +vlc_renderer_mute_set
> diff --git a/src/misc/renderer.c b/src/misc/renderer.c
> new file mode 100644
> index 0000000..f6ffbd9
> --- /dev/null
> +++ b/src/misc/renderer.c
> @@ -0,0 +1,323 @@
> 
> +/*****************************************************************************
> + * renderer.c: Renderers
> +
> 
> *****************************************************************************
> + * Copyright (C) 2016 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 <stdint.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_atomic.h>
> +#include <vlc_renderer.h>
> +#include <vlc_modules.h>
> +#include <libvlc.h>
> +#include "../lib/libvlc_internal.h"
> +
> +struct vlc_renderer_item
> +{
> +    char *psz_module;
> +    char *psz_host;
> +    uint16_t i_port;
> +    vlc_renderer_flags e_flags;
> +    char *psz_name;
> +    char *psz_option;
> +    atomic_uint refs;
> +};
> +
> +struct renderer_priv
> +{
> +    vlc_renderer        s;
> +    bool                b_has_input;
> +    vlc_renderer_item * p_item;
> +};
> +
> +vlc_renderer_item *
> +vlc_renderer_item_new(const char *psz_module, const char *psz_host,
> +                      uint16_t i_port, const char *psz_name,
> +                      vlc_renderer_flags e_flags)
> +{
> +    assert(psz_module != NULL && psz_host != NULL);
> +
> +    vlc_renderer_item *p_item = calloc(1, 
> sizeof(vlc_renderer_item));
> +    if (p_item == NULL)
> +        return NULL;
> +
> +    if (psz_name == NULL)
> +        psz_name = "";
> +    if ((p_item->psz_module = strdup(psz_module)) == NULL
> +     || (p_item->psz_host = strdup(psz_host)) == NULL
> +     || (p_item->psz_name = strdup(psz_name)) == NULL
> +     || asprintf(&p_item->psz_option,
> "%s{host=%s,port=%u,name=%s,flags=%d}",
> +                 psz_module, psz_host, i_port, psz_name, e_flags) == 
> -1)
> +    {
> +        free(p_item->psz_module);
> +        free(p_item->psz_host);
> +        free(p_item->psz_name);
> +        free(p_item);
> +        return NULL;
> +    }
> +    p_item->i_port = i_port;
> +    p_item->e_flags = e_flags;
> +    atomic_init(&p_item->refs, 1);
> +    return p_item;
> +}
> +
> +static vlc_renderer_item *
> +renderer_item_new_from_option(const char *psz_renderer)
> +{
> +    config_chain_t *p_cfg = NULL;
> +    char *psz_module, *psz_host = NULL, *psz_name = NULL;
> +    vlc_renderer_flags e_flags = 0;
> +    uint16_t i_port = 0;
> +    free(config_ChainCreate(&psz_module, &p_cfg, psz_renderer));
> +
> +    config_chain_t *p_read_cfg = p_cfg;
> +    while (p_read_cfg != NULL)
> +    {
> +        if (!strcmp(p_cfg->psz_name, "host"))
> +            psz_host = p_cfg->psz_value;
> +        else if (!strcmp(p_cfg->psz_name, "name"))
> +            psz_name = p_cfg->psz_value;
> +        else if (!strcmp(p_cfg->psz_name, "port"))
> +        {
> +            int i_val = atoi(p_cfg->psz_value);
> +            if (i_val >= 0 && i_val <= UINT16_MAX)
> +                i_port = i_val;
> +        }
> +        else if (!strcmp(p_cfg->psz_name, "flags"))
> +            e_flags = atoi(p_cfg->psz_value);
> +        p_read_cfg = p_read_cfg->p_next;
> +    }
> +
> +    vlc_renderer_item *p_item = NULL;
> +    if (psz_module != NULL && psz_host != NULL)
> +        p_item = vlc_renderer_item_new(psz_module, psz_host, i_port,
> psz_name,
> +                                       e_flags);
> +    free(psz_module);
> +    config_ChainDestroy( p_cfg );
> +
> +    return p_item;
> +}
> +
> +const char *
> +vlc_renderer_item_name(const vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    return p_item->psz_name != NULL ? p_item->psz_name : 
> p_item->psz_module;
> +}
> +
> +bool
> +vlc_renderer_item_equals(const vlc_renderer_item *p_item,
> +                         const char *psz_module, const char 
> *psz_host,
> +                         uint16_t i_port, vlc_renderer_flags 
> e_flags)
> +{
> +    assert(p_item != NULL);
> +    return (p_item->i_port == i_port || !p_item->i_port || !i_port)
> +            && !strcmp(p_item->psz_host, psz_host)
> +            && !strcmp(p_item->psz_module, psz_module)
> +            && p_item->e_flags == e_flags;
> +}
> +
> +const char *
> +vlc_renderer_item_host(const vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    return p_item->psz_host;
> +}
> +
> +uint16_t
> +vlc_renderer_item_port(const vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    return p_item->i_port;
> +}
> +
> +vlc_renderer_flags
> +vlc_renderer_item_flags(const vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    return p_item->e_flags;
> +}
> +
> +const char *
> +vlc_renderer_item_option(const vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    return p_item->psz_option;
> +}
> +
> +vlc_renderer_item *
> +vlc_renderer_item_hold(vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    atomic_fetch_add(&p_item->refs, 1);
> +    return p_item;
> +}
> +
> +void
> +vlc_renderer_item_release(vlc_renderer_item *p_item)
> +{
> +    assert(p_item != NULL);
> +
> +    if (atomic_fetch_sub(&p_item->refs, 1) != 1)
> +        return;
> +    free(p_item->psz_module);
> +    free(p_item->psz_host);
> +    free(p_item->psz_name);
> +    free(p_item->psz_option);
> +    free(p_item);
> +}
> +
> +static inline struct renderer_priv *
> +renderer_priv(vlc_renderer *p_renderer)
> +{
> +    return (struct renderer_priv *)p_renderer;
> +}
> +
> +static void
> +renderer_destructor(vlc_object_t *p_obj)
> +{
> +    vlc_renderer *p_renderer = (vlc_renderer *)p_obj;
> +    struct renderer_priv *p_priv = renderer_priv(p_renderer);
> +
> +    if (p_priv->b_has_input)
> +        vlc_renderer_set_input(p_renderer, NULL);
> +
> +    module_unneed(p_renderer, p_renderer->p_module);
> +
> +    vlc_renderer_item_release(p_priv->p_item);
> +}
> +
> +#undef vlc_renderer_new
> +vlc_renderer *
> +vlc_renderer_new(vlc_object_t *p_obj, const char *psz_renderer)
> +{
> +    assert(p_obj != NULL && psz_renderer != NULL);
> +    struct renderer_priv *p_priv =
> +        vlc_custom_create(p_obj, sizeof (*p_priv), "renderer");
> +    if (p_priv == NULL)
> +        return NULL;
> +    vlc_renderer *p_renderer = &p_priv->s;
> +
> +    p_priv->p_item = renderer_item_new_from_option(psz_renderer);
> +    if (p_priv->p_item == NULL)
> +    {
> +        vlc_object_release(p_obj);
> +        return NULL;
> +    }
> +    p_renderer->p_item = p_priv->p_item;
> +
> +    p_renderer->p_module = module_need(p_renderer, "renderer",
> +                                       p_priv->p_item->psz_module, 
> true);
> +    if (p_renderer->p_module == NULL)
> +    {
> +        vlc_renderer_item_release(p_priv->p_item);
> +        vlc_object_release(p_obj);
> +        return NULL;
> +    }
> +    assert(p_renderer->pf_set_input);
> +    vlc_object_set_destructor(p_renderer, renderer_destructor);
> +
> +    return p_renderer;
> +}
> +
> +bool vlc_renderer_equals(const vlc_renderer *p_renderer,
> +                         const char *psz_renderer)
> +{
> +    vlc_renderer_item *p_item = 
> renderer_item_new_from_option(psz_renderer);
> +    if (p_item == NULL)
> +        return NULL;
> +    else
> +    {
> +        bool b_ret = !strcmp(p_renderer->p_item->psz_option,
> p_item->psz_option);
> +        vlc_renderer_item_release(p_item);
> +        return b_ret;
> +    }
> +}
> +
> +int
> +vlc_renderer_set_input(vlc_renderer *p_renderer, input_thread_t 
> *p_input)
> +{
> +    assert(p_renderer != NULL);
> +    struct renderer_priv *p_priv = renderer_priv(p_renderer);
> +
> +    int i_ret = p_renderer->pf_set_input(p_renderer, p_input);
> +    if (i_ret == VLC_SUCCESS)
> +        p_priv->b_has_input = p_input != NULL;
> +    else
> +        p_priv->b_has_input = false;
> +
> +    return i_ret;
> +}
> +
> +int
> +vlc_renderer_volume_get(vlc_renderer *p_renderer, float *pf_volume)
> +{
> +    assert(p_renderer != NULL);
> +
> +    if (!pf_volume)
> +        return VLC_EGENERIC;
> +
> +    if (p_renderer->pf_volume_get == NULL)
> +        return VLC_ENOOBJ;
> +
> +    return p_renderer->pf_volume_get(p_renderer, pf_volume);
> +}
> +
> +int
> +vlc_renderer_volume_set(vlc_renderer *p_renderer, float f_volume)
> +{
> +    assert(p_renderer != NULL);
> +
> +    if (p_renderer->pf_volume_set == NULL)
> +        return VLC_ENOOBJ;
> +
> +    return p_renderer->pf_volume_set(p_renderer, f_volume);
> +}
> +
> +int
> +vlc_renderer_mute_get(vlc_renderer *p_renderer, bool *pb_mute)
> +{
> +    assert(p_renderer != NULL && pb_mute != NULL);
> +
> +    if (p_renderer->pf_mute_get == NULL)
> +        return VLC_ENOOBJ;
> +
> +    return p_renderer->pf_mute_get(p_renderer, pb_mute);
> +}
> +
> +int
> +vlc_renderer_mute_set(vlc_renderer *p_renderer, bool b_mute)
> +{
> +    assert(p_renderer != NULL);
> +
> +    if (p_renderer->pf_mute_set == NULL)
> +        return VLC_ENOOBJ;
> +
> +    return p_renderer->pf_mute_set(p_renderer, b_mute);
> +}

-- 
Rémi Denis-Courmont
http://www.remlab.net/


More information about the vlc-devel mailing list