libvlc: add renderer_discoverer external API
authorThomas Guillem <thomas@gllm.fr>
Thu, 21 Jul 2016 17:40:22 +0000 (19:40 +0200)
committerThomas Guillem <thomas@gllm.fr>
Tue, 20 Sep 2016 09:06:51 +0000 (11:06 +0200)
include/vlc/libvlc_events.h
include/vlc/libvlc_renderer_discoverer.h [new file with mode: 0644]
include/vlc/vlc.h
lib/Makefile.am
lib/libvlc.sym
lib/renderer_discoverer.c [new file with mode: 0644]
lib/renderer_discoverer_internal.h [new file with mode: 0644]
modules/services_discovery/microdns.c
test/Makefile.am
test/libvlc/renderer_discoverer.c [new file with mode: 0644]

index 462fe7f..b69c5ce 100644 (file)
@@ -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;
 
diff --git a/include/vlc/libvlc_renderer_discoverer.h b/include/vlc/libvlc_renderer_discoverer.h
new file mode 100644 (file)
index 0000000..2e84673
--- /dev/null
@@ -0,0 +1,231 @@
+/*****************************************************************************
+ * 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
index 1e2cf65..6d25cd5 100644 (file)
@@ -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>
index 7a6b7c1..1562ad3 100644 (file)
@@ -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 \
index 733a4dd..84e7189 100644 (file)
@@ -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
diff --git a/lib/renderer_discoverer.c b/lib/renderer_discoverer.c
new file mode 100644 (file)
index 0000000..5b5e7e3
--- /dev/null
@@ -0,0 +1,279 @@
+/*****************************************************************************
+ * 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;
+}
diff --git a/lib/renderer_discoverer_internal.h b/lib/renderer_discoverer_internal.h
new file mode 100644 (file)
index 0000000..880b92e
--- /dev/null
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * 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
index 4701436..19fec74 100644 (file)
@@ -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-- );
         }
     }
index 4e9b1f2..7731782 100644 (file)
@@ -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
diff --git a/test/libvlc/renderer_discoverer.c b/test/libvlc/renderer_discoverer.c
new file mode 100644 (file)
index 0000000..aa60d99
--- /dev/null
@@ -0,0 +1,127 @@
+/*****************************************************************************
+ * 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;
+
+    libvlc_instance_t *p_vlc = libvlc_new(test_defaults_nargs,
+                                          test_defaults_args);
+    assert(p_vlc != NULL);
+
+    if (psz_test_name != NULL)
+    {
+        /* Test a specific service discovery from command line */
+        alarm(0);
+        test_discoverer(p_vlc, psz_test_name);
+        libvlc_release(p_vlc);
+        return 0;
+    }
+
+    log("== getting the list of renderer_discoverer  ==\n");
+
+    libvlc_rd_description_t **pp_services;
+    ssize_t i_count =
+        libvlc_renderer_discoverer_list_get(p_vlc, &pp_services);
+    if (i_count <= 0)
+    {
+        log("warn: no discoverers (not critical)\n");
+        goto end;
+    }
+    assert(pp_services != NULL);
+
+    for (unsigned int i = 0; i < i_count; ++i)
+    {
+        libvlc_rd_description_t *p_service = pp_services[i];
+
+        log("= discoverer: name: '%s', longname: '%s' =\n",
+            p_service->psz_name, p_service->psz_longname);
+    }
+
+    libvlc_renderer_discoverer_list_release(pp_services, i_count);
+
+end:
+    libvlc_release(p_vlc);
+
+    return 0;
+}