Commit 5f3abe14 authored by Thomas Guillem's avatar Thomas Guillem

libvlc: add renderer_discoverer external API

parent fa289ee1
......@@ -34,6 +34,8 @@
extern "C" {
# endif
typedef struct libvlc_renderer_item_t libvlc_renderer_item_t;
/**
* \ingroup libvlc_event
* @{
......@@ -111,6 +113,9 @@ enum libvlc_event_e {
*/
libvlc_MediaDiscovererEnded,
libvlc_RendererDiscovererItemAdded,
libvlc_RendererDiscovererItemDeleted,
libvlc_VlmMediaAdded=0x600,
libvlc_VlmMediaRemoved,
libvlc_VlmMediaChanged,
......@@ -269,6 +274,15 @@ typedef struct libvlc_event_t
{
const char *device;
} media_player_audio_device;
struct
{
const libvlc_renderer_item_t *item;
} renderer_discoverer_item_added;
struct
{
const libvlc_renderer_item_t *item;
} renderer_discoverer_item_deleted;
} u; /**< Type-dependent event description */
} libvlc_event_t;
......
/*****************************************************************************
* libvlc_renderer_discoverer.h: libvlc external API
*****************************************************************************
* Copyright © 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_LIBVLC_RENDERER_DISCOVERER_H
#define VLC_LIBVLC_RENDERER_DISCOVERER_H 1
# ifdef __cplusplus
extern "C" {
# endif
/**
* @defgroup libvlc_renderer_discoverer LibVLC renderer discoverer
* @ingroup libvlc
* LibVLC renderer discoverer finds available renderers available on the local
* network
* @{
* @file
* LibVLC renderer discoverer external API
*/
typedef struct libvlc_renderer_discoverer_t libvlc_renderer_discoverer_t;
/**
* Renderer discoverer description
*
* \see libvlc_renderer_discoverer_list_get()
*/
typedef struct libvlc_rd_description_t
{
char *psz_name;
char *psz_longname;
} libvlc_rd_description_t;
/** The renderer can render audio */
#define LIBVLC_RENDERER_CAN_AUDIO 0x0001
/** The renderer can render video */
#define LIBVLC_RENDERER_CAN_VIDEO 0x0002
/**
* Renderer item
*
* This struct is passed by a @ref libvlc_event_t when a new renderer is added
* or deleted.
*
* An item is valid until the @ref libvlc_RendererDiscovererItemDeleted event
* is called with the same pointer.
*
* \see libvlc_renderer_discoverer_event_manager()
*/
typedef struct libvlc_renderer_item_t libvlc_renderer_item_t;
/**
* Get the human readable name of a renderer item
*
* \version LibVLC 3.0.0 or later
*
* \return the name of the item (can't be NULL, must *not* be freed)
*/
LIBVLC_API const char *
libvlc_renderer_item_name(const libvlc_renderer_item_t *p_item);
/**
* Get the type (not translated) of a renderer item. For now, the type can only
* be "chromecast" ("upnp", "airplay" may come later).
*
* \version LibVLC 3.0.0 or later
*
* \return the type of the item (can't be NULL, must *not* be freed)
*/
LIBVLC_API const char *
libvlc_renderer_item_type(const libvlc_renderer_item_t *p_item);
/**
* Get the icon uri of a renderer item
*
* \version LibVLC 3.0.0 or later
*
* \return the uri of the item's icon (can be NULL, must *not* be freed)
*/
LIBVLC_API const char *
libvlc_renderer_item_icon_uri(const libvlc_renderer_item_t *p_item);
/**
* Get the flags of a renderer item
*
* \see LIBVLC_RENDERER_CAN_AUDIO
* \see LIBVLC_RENDERER_CAN_VIDEO
*
* \version LibVLC 3.0.0 or later
*
* \return bitwise flag: capabilities of the renderer, see
*/
LIBVLC_API int
libvlc_renderer_item_flags(const libvlc_renderer_item_t *p_item);
/**
* Create a renderer discoverer object by name
*
* After this object is created, you should attach to events in order to be
* notified of the discoverer events.
*
* You need to call libvlc_renderer_discoverer_start() in order to start the
* discovery.
*
* \see libvlc_renderer_discoverer_event_manager()
* \see libvlc_renderer_discoverer_start()
*
* \version LibVLC 3.0.0 or later
*
* \param p_inst libvlc instance
* \param psz_name service name; use libvlc_renderer_discoverer_list_get() to
* get a list of the discoverer names available in this libVLC instance
* \return media discover object or NULL in case of error
*/
LIBVLC_API libvlc_renderer_discoverer_t *
libvlc_renderer_discoverer_new( libvlc_instance_t *p_inst,
const char *psz_name );
/**
* Release a renderer discoverer object
*
* \version LibVLC 3.0.0 or later
*
* \param p_rd renderer discoverer object
*/
LIBVLC_API void
libvlc_renderer_discoverer_release( libvlc_renderer_discoverer_t *p_rd );
/**
* Start renderer discovery
*
* To stop it, call libvlc_renderer_discoverer_stop() or
* libvlc_renderer_discoverer_release() directly.
*
* \see libvlc_renderer_discoverer_stop()
*
* \version LibVLC 3.0.0 or later
*
* \param p_rd renderer discoverer object
* \return -1 in case of error, 0 otherwise
*/
LIBVLC_API int
libvlc_renderer_discoverer_start( libvlc_renderer_discoverer_t *p_rd );
/**
* Stop renderer discovery.
*
* \see libvlc_renderer_discoverer_start()
*
* \version LibVLC 3.0.0 or later
*
* \param p_rd renderer discoverer object
*/
LIBVLC_API void
libvlc_renderer_discoverer_stop( libvlc_renderer_discoverer_t *p_rd );
/**
* Get the event manager of the renderer discoverer
*
* The possible events to attach are @ref libvlc_RendererDiscovererItemAdded
* and @ref libvlc_RendererDiscovererItemDeleted.
*
* The @ref libvlc_renderer_item_t struct passed to event callbacks is owned by
* VLC, users should take care of copying this struct for their internal usage.
*
* \see libvlc_event_t.u.renderer_discoverer_item_added.item
* \see libvlc_event_t.u.renderer_discoverer_item_removed.item
*
* \version LibVLC 3.0.0 or later
*
* \return a valid event manager (can't fail)
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_renderer_discoverer_event_manager( libvlc_renderer_discoverer_t *p_rd );
/**
* Get media discoverer services
*
* \see libvlc_renderer_list_release()
*
* \version LibVLC 3.0.0 and later
*
* \param p_inst libvlc instance
* \param ppp_services address to store an allocated array of renderer
* discoverer services (must be freed with libvlc_renderer_list_release() by
* the caller) [OUT]
*
* \return the number of media discoverer services or -1 on error
*/
LIBVLC_API ssize_t
libvlc_renderer_discoverer_list_get( libvlc_instance_t *p_inst,
libvlc_rd_description_t ***ppp_services );
/**
* Release an array of media discoverer services
*
* \see libvlc_renderer_discoverer_list_get()
*
* \version LibVLC 3.0.0 and later
*
* \param pp_services array to release
* \param i_count number of elements in the array
*/
LIBVLC_API void
libvlc_renderer_discoverer_list_release( libvlc_rd_description_t **pp_services,
size_t i_count );
/** @} */
# ifdef __cplusplus
}
# endif
#endif
......@@ -38,6 +38,7 @@ extern "C" {
# endif
#include <vlc/libvlc.h>
#include <vlc/libvlc_renderer_discoverer.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_player.h>
#include <vlc/libvlc_media_list.h>
......
......@@ -20,6 +20,7 @@ pkginclude_HEADERS = \
../include/vlc/libvlc_media_list_player.h \
../include/vlc/libvlc_media_player.h \
../include/vlc/libvlc_vlm.h \
../include/vlc/libvlc_renderer_discoverer.h \
../include/vlc/vlc.h
nodist_pkginclude_HEADERS = ../include/vlc/libvlc_version.h
......@@ -37,6 +38,7 @@ libvlc_la_SOURCES = \
media_player_internal.h \
core.c \
dialog.c \
renderer_discoverer.c \
error.c \
log.c \
playlist.c \
......
......@@ -218,6 +218,17 @@ libvlc_media_tracks_release
libvlc_new
libvlc_playlist_play
libvlc_release
libvlc_renderer_item_name
libvlc_renderer_item_type
libvlc_renderer_item_icon_uri
libvlc_renderer_item_flags
libvlc_renderer_discoverer_event_manager
libvlc_renderer_discoverer_list_get
libvlc_renderer_discoverer_list_release
libvlc_renderer_discoverer_new
libvlc_renderer_discoverer_release
libvlc_renderer_discoverer_start
libvlc_renderer_discoverer_stop
libvlc_retain
libvlc_set_fullscreen
libvlc_set_log_verbosity
......
/*****************************************************************************
* renderer_discoverer.c: libvlc renderer API
*****************************************************************************
* Copyright © 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 <vlc/libvlc.h>
#include <vlc/libvlc_renderer_discoverer.h>
#include <vlc_common.h>
#include "libvlc_internal.h"
#include "renderer_discoverer_internal.h"
struct libvlc_renderer_discoverer_t
{
vlc_renderer_discovery_t *p_rd;
libvlc_event_manager_t *p_event_manager;
int i_items;
vlc_renderer_item_t ** pp_items;
};
static_assert( VLC_RENDERER_CAN_AUDIO == LIBVLC_RENDERER_CAN_AUDIO &&
VLC_RENDERER_CAN_VIDEO == LIBVLC_RENDERER_CAN_VIDEO,
"core/libvlc renderer flags mismatch" );
const vlc_renderer_item_t *
libvlc_renderer_item_to_vlc( const libvlc_renderer_item_t *p_item )
{
return (const vlc_renderer_item_t*) p_item;
}
static void
renderer_discovery_item_added( const vlc_event_t *p_event, void *p_user_data )
{
libvlc_renderer_discoverer_t *p_lrd = p_user_data;
vlc_renderer_item_t *p_item =
p_event->u.renderer_discovery_item_added.p_new_item;
vlc_renderer_item_hold( p_item );
TAB_APPEND( p_lrd->i_items, p_lrd->pp_items, p_item );
libvlc_event_t event = {
.type = libvlc_RendererDiscovererItemAdded,
.u.renderer_discoverer_item_added.item =
(libvlc_renderer_item_t*) p_item,
};
libvlc_event_send( p_lrd->p_event_manager, &event );
}
static void
renderer_discovery_item_removed( const vlc_event_t *p_event, void *p_user_data )
{
libvlc_renderer_discoverer_t *p_lrd = p_user_data;
vlc_renderer_item_t *p_item =
p_event->u.renderer_discovery_item_removed.p_item;
int i_idx;
TAB_FIND( p_lrd->i_items, p_lrd->pp_items, p_item, i_idx );
assert( i_idx != -1 );
TAB_ERASE( p_lrd->i_items, p_lrd->pp_items, i_idx );
libvlc_event_t event = {
.type = libvlc_RendererDiscovererItemDeleted,
.u.renderer_discoverer_item_deleted.item =
(libvlc_renderer_item_t*) p_item,
};
libvlc_event_send( p_lrd->p_event_manager, &event );
vlc_renderer_item_release( p_item );
}
const char *
libvlc_renderer_item_name( const libvlc_renderer_item_t *p_item )
{
return vlc_renderer_item_name( (vlc_renderer_item_t *) p_item );
}
const char *
libvlc_renderer_item_type( const libvlc_renderer_item_t *p_item )
{
return vlc_renderer_item_type( (vlc_renderer_item_t *) p_item );
}
const char *
libvlc_renderer_item_icon_uri( const libvlc_renderer_item_t *p_item )
{
return vlc_renderer_item_icon_uri( (vlc_renderer_item_t *) p_item );
}
int
libvlc_renderer_item_flags( const libvlc_renderer_item_t *p_item )
{
return vlc_renderer_item_flags( (vlc_renderer_item_t *) p_item );
}
libvlc_renderer_discoverer_t *
libvlc_renderer_discoverer_new( libvlc_instance_t *p_inst,
const char *psz_name )
{
libvlc_renderer_discoverer_t *p_lrd =
calloc( 1, sizeof(libvlc_renderer_discoverer_t) );
if( unlikely(p_lrd == NULL) )
return NULL;
p_lrd->p_rd = vlc_rd_new( VLC_OBJECT( p_inst->p_libvlc_int ), psz_name );
if( unlikely(p_lrd->p_rd == NULL) )
goto error;
TAB_INIT( p_lrd->i_items, p_lrd->pp_items );
p_lrd->p_event_manager = libvlc_event_manager_new( p_lrd );
if( unlikely(p_lrd->p_event_manager == NULL) )
goto error;
vlc_event_manager_t *p_rd_ev = vlc_rd_event_manager( p_lrd->p_rd );
if( vlc_event_attach( p_rd_ev, vlc_RendererDiscoveryItemAdded,
renderer_discovery_item_added, p_lrd )
!= VLC_SUCCESS )
goto error;
if( vlc_event_attach( p_rd_ev, vlc_RendererDiscoveryItemRemoved,
renderer_discovery_item_removed, p_lrd )
!= VLC_SUCCESS )
goto error;
return p_lrd;
error:
libvlc_renderer_discoverer_release( p_lrd );
return NULL;
}
void
libvlc_renderer_discoverer_release( libvlc_renderer_discoverer_t *p_lrd )
{
if( p_lrd->p_rd != NULL )
vlc_rd_release( p_lrd->p_rd );
if( p_lrd->p_event_manager != NULL )
libvlc_event_manager_release( p_lrd->p_event_manager );
free( p_lrd );
}
int
libvlc_renderer_discoverer_start( libvlc_renderer_discoverer_t *p_lrd )
{
return vlc_rd_start( p_lrd->p_rd );
}
void
libvlc_renderer_discoverer_stop( libvlc_renderer_discoverer_t *p_lrd )
{
vlc_rd_stop( p_lrd->p_rd );
for( int i = 0; i < p_lrd->i_items; ++i )
vlc_renderer_item_release( p_lrd->pp_items[i] );
TAB_CLEAN( p_lrd->i_items, p_lrd->pp_items );
}
libvlc_event_manager_t *
libvlc_renderer_discoverer_event_manager( libvlc_renderer_discoverer_t *p_lrd )
{
return p_lrd->p_event_manager;
}
void
libvlc_renderer_discoverer_list_release( libvlc_rd_description_t **pp_services,
size_t i_count )
{
if( i_count > 0 )
{
for( size_t i = 0; i < i_count; ++i )
{
free( pp_services[i]->psz_name );
free( pp_services[i]->psz_longname );
}
free( *pp_services );
free( pp_services );
}
}
ssize_t
libvlc_renderer_discoverer_list_get( libvlc_instance_t *p_inst,
libvlc_rd_description_t ***ppp_services )
{
assert( p_inst != NULL && ppp_services != NULL );
/* Fetch all rd names, and longnames */
char **ppsz_names, **ppsz_longnames;
int i_ret = vlc_rd_get_names( p_inst->p_libvlc_int, &ppsz_names,
&ppsz_longnames );
if( i_ret != VLC_SUCCESS )
{
*ppp_services = NULL;
return -1;
}
/* Count the number of sd matching our category (i_cat/i_core_cat) */
size_t i_nb_services = 0;
char **ppsz_name = ppsz_names;
for( ; *ppsz_name != NULL; ppsz_name++ )
i_nb_services++;
libvlc_rd_description_t **pp_services = NULL,
*p_services = NULL;
if( i_nb_services > 0 )
{
/* Double alloc here, so that the caller iterates through pointers of
* struct instead of structs. This allows us to modify the struct
* without breaking the API. */
pp_services =
malloc( i_nb_services
* sizeof(libvlc_rd_description_t *) );
p_services =
malloc( i_nb_services
* sizeof(libvlc_rd_description_t) );
if( pp_services == NULL || p_services == NULL )
{
free( pp_services );
free( p_services );
pp_services = NULL;
p_services = NULL;
i_nb_services = -1;
/* Even if alloc fails, the next loop must be run in order to free
* names returned by vlc_sd_GetNames */
}
}
/* Fill output pp_services or free unused name, longname */
char **ppsz_longname = ppsz_longnames;
unsigned int i_service_idx = 0;
libvlc_rd_description_t *p_service = p_services;
for( ppsz_name = ppsz_names; *ppsz_name != NULL; ppsz_name++, ppsz_longname++ )
{
if( pp_services != NULL )
{
p_service->psz_name = *ppsz_name;
p_service->psz_longname = *ppsz_longname;
pp_services[i_service_idx++] = p_service++;
}
else
{
free( *ppsz_name );
free( *ppsz_longname );
}
}
free( ppsz_names );
free( ppsz_longnames );
*ppp_services = pp_services;
return i_nb_services;
}
/*****************************************************************************
* renderer_discoverer_internal.h: libvlc external API
*****************************************************************************
* Copyright © 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 _LIBVLC_RENDERER_DISCOVERER_INTERNAL_H
#define _LIBVLC_RENDERER_DISCOVERER_INTERNAL_H 1
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_renderer_discovery.h>
const vlc_renderer_item_t *
libvlc_renderer_item_to_vlc( const libvlc_renderer_item_t *p_item );
#endif
......@@ -249,6 +249,7 @@ static void
items_timeout( struct discovery_sys *p_sys, services_discovery_t *p_sd,
vlc_renderer_discovery_t *p_rd )
{
assert( p_rd != NULL || p_sd != NULL );
mtime_t i_now = mdate();
/* Remove items that are not seen since TIMEOUT */
......@@ -257,14 +258,11 @@ items_timeout( struct discovery_sys *p_sys, services_discovery_t *p_sd,
struct item *p_item = vlc_array_item_at_index( &p_sys->items, i );
if( i_now - p_item->i_last_seen > TIMEOUT )
{
items_release( p_sys, p_item );
if( p_sd != NULL )
services_discovery_RemoveItem( p_sd, p_item->p_input_item );
else
{
assert( p_rd != NULL );
vlc_rd_remove_item( p_rd, p_item->p_renderer_item );
}
items_release( p_sys, p_item );
vlc_array_remove( &p_sys->items, i-- );
}
}
......
......@@ -19,6 +19,7 @@ check_PROGRAMS = \
test_libvlc_media_list \
test_libvlc_media_player \
test_libvlc_media_discoverer \
test_libvlc_renderer_discoverer \
test_libvlc_slaves \
test_src_config_chain \
test_src_misc_variables \
......@@ -85,6 +86,8 @@ test_libvlc_media_player_SOURCES = libvlc/media_player.c
test_libvlc_media_player_LDADD = $(LIBVLC)
test_libvlc_media_discoverer_SOURCES = libvlc/media_discoverer.c
test_libvlc_media_discoverer_LDADD = $(LIBVLC)
test_libvlc_renderer_discoverer_SOURCES = libvlc/renderer_discoverer.c
test_libvlc_renderer_discoverer_LDADD = $(LIBVLC)
test_libvlc_slaves_SOURCES = libvlc/slaves.c
test_libvlc_slaves_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_libvlc_meta_SOURCES = libvlc/meta.c
......
/*****************************************************************************
* renderer_discoverer.c - libvlc smoke test
*****************************************************************************
* Copyright © 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.
*****************************************************************************/
#include "test.h"
#include <string.h>
static void
item_event(const libvlc_renderer_item_t *p_item, const char *psz_event)
{
log("item %s: name: '%s', type: '%s', flags: 0x%X\n", psz_event,
libvlc_renderer_item_name(p_item), libvlc_renderer_item_type(p_item),
libvlc_renderer_item_flags(p_item));
}
static void
renderer_discoverer_item_added(const struct libvlc_event_t *p_ev, void *p_data)
{
(void) p_data;
item_event(p_ev->u.renderer_discoverer_item_added.item, "added");
}
static void
renderer_discoverer_item_deleted(const struct libvlc_event_t *p_ev, void *p_data)
{
(void) p_data;
item_event(p_ev->u.renderer_discoverer_item_deleted.item, "deleted");
}
static void
test_discoverer(libvlc_instance_t *p_vlc, const char *psz_name)
{
log("creating and starting discoverer %s\n", psz_name);
libvlc_renderer_discoverer_t *p_rd =
libvlc_renderer_discoverer_new(p_vlc, psz_name);
assert(p_rd != NULL);
libvlc_event_manager_t *p_evm = libvlc_renderer_discoverer_event_manager(p_rd);
assert(p_evm);
int i_ret;
i_ret = libvlc_event_attach(p_evm, libvlc_RendererDiscovererItemAdded,
renderer_discoverer_item_added, NULL);
assert(i_ret == 0);
i_ret = libvlc_event_attach(p_evm, libvlc_RendererDiscovererItemDeleted,
renderer_discoverer_item_deleted, NULL);
assert(i_ret == 0);
if (libvlc_renderer_discoverer_start(p_rd) == -1)
{
log("warn: could not start md (not critical)\n");
}
else
{
log("Press any keys to stop\n");
getchar();
libvlc_renderer_discoverer_stop(p_rd);
}
libvlc_renderer_discoverer_release(p_rd);
}
int
main(int i_argc, char *ppsz_argv[])
{
test_init();
char *psz_test_name = i_argc > 1 ? ppsz_argv[1] : NULL;