[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