VLC_ADD_BUILTINS([mux_ts])
fi
VLC_ADD_CPPFLAGS([mux_ts ts dvb],[-I${real_dvbpsi_tree}/src])
- VLC_ADD_LDFLAGS([mux_ts ts dvb],[-L${real_dvbpsi_tree}/src/.libs -ldvbpsi])
+ VLC_ADD_LDFLAGS([mux_ts ts dvb],[${real_dvbpsi_tree}/src/.libs/libdvbpsi.a])
else
dnl The given libdvbpsi wasn't built
AC_MSG_RESULT(no)
[AC_MSG_WARN(DAAP library not found)])
fi
+dnl
+dnl Bonjour services discovery
+dnl
+AC_ARG_ENABLE(bonjour,
+ [ --enable-bonjour Bonjour services discovery (default enabled)])
+if test "${enable_bonjour}" != "no"
+then
+ PKG_CHECK_MODULES(BONJOUR, avahi-client >= 0.3,
+ [AC_DEFINE(HAVE_AVAHI_CLIENT, [], [Define if you have the avahi-client library])
+ VLC_ADD_LDFLAGS([bonjour access_output_http],[$BONJOUR_LIBS])
+ VLC_ADD_CFLAGS([bonjour access_output_http],[$BONJOUR_CFLAGS])
+ VLC_ADD_PLUGINS([bonjour]) ],
+ [AC_MSG_WARN(avahi-client library not found)])
+fi
+
dnl
dnl Lirc plugin
dnl
SOURCES_access_output_dummy = dummy.c
SOURCES_access_output_file = file.c
SOURCES_access_output_udp = udp.c
-SOURCES_access_output_http = http.c
+SOURCES_access_output_http = http.c bonjour.c bonjour.h
SOURCES_access_output_shout = shout.c
--- /dev/null
+/*****************************************************************************
+ * bonjour.c
+ *****************************************************************************
+ * Copyright (C) 2005 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jon Lech Johansen <jon@nanocrew.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>
+
+#include <vlc/vlc.h>
+
+#ifdef HAVE_AVAHI_CLIENT
+#include <vlc/intf.h>
+#include <vlc/sout.h>
+
+#include <avahi-client/client.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+/*****************************************************************************
+ * Structures
+ *****************************************************************************/
+typedef struct poll_thread_t
+{
+ VLC_COMMON_MEMBERS
+
+ AvahiSimplePoll *simple_poll;
+} poll_thread_t;
+
+typedef struct bonjour_t
+{
+ vlc_object_t *p_log;
+
+ poll_thread_t *poll_thread;
+ AvahiSimplePoll *simple_poll;
+ AvahiEntryGroup *group;
+ AvahiClient *client;
+ char *psz_stype;
+ char *psz_name;
+ int i_port;
+ char *psz_txt;
+} bonjour_t;
+
+/*****************************************************************************
+ * Prototypes
+ *****************************************************************************/
+static int create_service( bonjour_t * );
+
+/*****************************************************************************
+ * entry_group_callback
+ *****************************************************************************/
+static void entry_group_callback( AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ void *userdata )
+{
+ bonjour_t *p_sys = (bonjour_t *)userdata;
+
+ if( state == AVAHI_ENTRY_GROUP_ESTABLISHED )
+ {
+ msg_Dbg( p_sys->p_log, "service '%s' successfully established",
+ p_sys->psz_name );
+ }
+ else if( state == AVAHI_ENTRY_GROUP_COLLISION )
+ {
+ char *n;
+
+ n = avahi_alternative_service_name( p_sys->psz_name );
+ avahi_free( p_sys->psz_name );
+ p_sys->psz_name = n;
+
+ create_service( p_sys );
+ }
+}
+
+/*****************************************************************************
+ * create_service
+ *****************************************************************************/
+static int create_service( bonjour_t *p_sys )
+{
+ int error;
+
+ if( p_sys->group == NULL )
+ {
+ p_sys->group = avahi_entry_group_new( p_sys->client,
+ entry_group_callback,
+ p_sys );
+ if( p_sys->group == NULL )
+ {
+ msg_Err( p_sys->p_log, "failed to create avahi entry group: %s",
+ avahi_strerror( avahi_client_errno( p_sys->client ) ) );
+ return VLC_EGENERIC;
+ }
+ }
+
+ error = avahi_entry_group_add_service( p_sys->group, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, p_sys->psz_name,
+ p_sys->psz_stype, NULL, NULL,
+ p_sys->i_port,
+ p_sys->psz_txt, NULL );
+ if( error < 0 )
+ {
+ msg_Err( p_sys->p_log, "failed to add %s service: %s",
+ p_sys->psz_stype, avahi_strerror( error ) );
+ return VLC_EGENERIC;
+ }
+
+ error = avahi_entry_group_commit( p_sys->group );
+ if( error < 0 )
+ {
+ msg_Err( p_sys->p_log, "failed to commit entry group: %s",
+ avahi_strerror( error ) );
+ return VLC_EGENERIC;
+ }
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * client_callback
+ *****************************************************************************/
+static void client_callback( AvahiClient *c,
+ AvahiClientState state,
+ void * userdata )
+{
+ bonjour_t *p_sys = (bonjour_t *)userdata;
+
+ if( state == AVAHI_CLIENT_S_RUNNING )
+ {
+ p_sys->client = c;
+ create_service( p_sys );
+ }
+ else if( state == AVAHI_CLIENT_S_COLLISION )
+ {
+ if( p_sys->group != NULL )
+ avahi_entry_group_reset( p_sys->group );
+ }
+ else if( state == AVAHI_CLIENT_DISCONNECTED )
+ {
+ msg_Err( p_sys->p_log, "avahi client disconnected" );
+ avahi_simple_poll_quit( p_sys->simple_poll );
+ }
+}
+
+/*****************************************************************************
+ * poll_iterate_thread
+ *****************************************************************************/
+static void poll_iterate_thread( poll_thread_t *p_pt )
+{
+ vlc_thread_ready( p_pt );
+
+ while( !p_pt->b_die )
+ if( avahi_simple_poll_iterate( p_pt->simple_poll, 100 ) != 0 )
+ break;
+}
+
+/*****************************************************************************
+ * bonjour_start_service
+ *****************************************************************************/
+void *bonjour_start_service( vlc_object_t *p_log, char *psz_stype,
+ char *psz_name, int i_port, char *psz_txt )
+{
+ int err;
+ bonjour_t *p_sys;
+
+ p_sys = (bonjour_t *)malloc( sizeof(*p_sys) );
+ if( p_sys == NULL )
+ {
+ msg_Err( p_log, "out of memory" );
+ return NULL;
+ }
+
+ memset( p_sys, 0, sizeof(*p_sys) );
+
+ p_sys->p_log = p_log;
+
+ p_sys->i_port = i_port;
+ p_sys->psz_name = avahi_strdup( psz_name );
+ p_sys->psz_stype = avahi_strdup( psz_stype );
+ if( p_sys->psz_name == NULL || p_sys->psz_stype == NULL )
+ {
+ msg_Err( p_sys->p_log, "out of memory" );
+ goto error;
+ }
+
+ if( psz_txt != NULL )
+ {
+ p_sys->psz_txt = avahi_strdup( psz_txt );
+ if( p_sys->psz_txt == NULL )
+ {
+ msg_Err( p_sys->p_log, "out of memory" );
+ goto error;
+ }
+ }
+
+ p_sys->simple_poll = avahi_simple_poll_new();
+ if( p_sys->simple_poll == NULL )
+ {
+ msg_Err( p_sys->p_log, "failed to create avahi simple pool" );
+ goto error;
+ }
+
+ p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
+ client_callback, p_sys, &err );
+ if( p_sys->client == NULL )
+ {
+ msg_Err( p_sys->p_log, "failed to create avahi client: %s",
+ avahi_strerror( err ) );
+ goto error;
+ }
+
+ p_sys->poll_thread = vlc_object_create( p_sys->p_log,
+ sizeof(poll_thread_t) );
+ if( p_sys->poll_thread == NULL )
+ {
+ msg_Err( p_sys->p_log, "out of memory" );
+ goto error;
+ }
+ p_sys->poll_thread->simple_poll = p_sys->simple_poll;
+
+ if( vlc_thread_create( p_sys->poll_thread, "Avahi Poll Iterate Thread",
+ poll_iterate_thread,
+ VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
+ {
+ msg_Err( p_sys->p_log, "failed to create poll iterate thread" );
+ goto error;
+ }
+
+ return (void *)p_sys;
+
+error:
+ if( p_sys->poll_thread != NULL )
+ vlc_object_destroy( p_sys->poll_thread );
+ if( p_sys->client != NULL )
+ avahi_client_free( p_sys->client );
+ if( p_sys->simple_poll != NULL )
+ avahi_simple_poll_free( p_sys->simple_poll );
+ if( p_sys->psz_stype != NULL )
+ avahi_free( p_sys->psz_stype );
+ if( p_sys->psz_name != NULL )
+ avahi_free( p_sys->psz_name );
+ if( p_sys->psz_txt != NULL )
+ avahi_free( p_sys->psz_txt );
+
+ free( (void *)p_sys );
+
+ return NULL;
+}
+
+/*****************************************************************************
+ * bonjour_stop_service
+ *****************************************************************************/
+void bonjour_stop_service( void *_p_sys )
+{
+ bonjour_t *p_sys = (bonjour_t *)_p_sys;
+
+ if( p_sys->poll_thread->b_thread )
+ {
+ p_sys->poll_thread->b_die = 1;
+ vlc_thread_join( p_sys->poll_thread );
+ }
+
+ vlc_object_destroy( p_sys->poll_thread );
+
+ if( p_sys->group != NULL )
+ avahi_entry_group_free( p_sys->group );
+
+ avahi_client_free( p_sys->client );
+ avahi_simple_poll_free( p_sys->simple_poll );
+
+ if( p_sys->psz_name != NULL )
+ avahi_free( p_sys->psz_name );
+
+ if( p_sys->psz_txt != NULL )
+ avahi_free( p_sys->psz_txt );
+
+ avahi_free( p_sys->psz_stype );
+
+ free( _p_sys );
+}
+
+#endif /* HAVE_AVAHI_CLIENT */
--- /dev/null
+/*****************************************************************************
+ * bonjour.h
+ *****************************************************************************
+ * Copyright (C) 2005 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jon Lech Johansen <jon@nanocrew.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+void *bonjour_start_service( vlc_object_t *, char*, char *, int, char * );
+void bonjour_stop_service( void * );
/*****************************************************************************
* http.c
*****************************************************************************
- * Copyright (C) 2001-2003 the VideoLAN team
+ * Copyright (C) 2001-2005 the VideoLAN team
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
+ * Jon Lech Johansen <jon@nanocrew.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <vlc/vlc.h>
#include <vlc/sout.h>
+#ifdef HAVE_AVAHI_CLIENT
+ #include <vlc/intf.h>
+
+ #include "bonjour.h"
+
+ #if defined( WIN32 )
+ #define DIRECTORY_SEPARATOR '\\'
+ #else
+ #define DIRECTORY_SEPARATOR '/'
+ #endif
+#endif
+
#include "vlc_httpd.h"
#define FREE( p ) if( p ) { free( p); (p) = NULL; }
int i_header_size;
uint8_t *p_header;
vlc_bool_t b_header_complete;
+
+#ifdef HAVE_AVAHI_CLIENT
+ void *p_bonjour;
+#endif
};
/*****************************************************************************
*psz_crl = NULL;
vlc_value_t val;
+#ifdef HAVE_AVAHI_CLIENT
+ playlist_t *p_playlist;
+ char *psz_txt;
+#endif
+
if( !( p_sys = p_access->p_sys =
malloc( sizeof( sout_access_out_sys_t ) ) ) )
{
return VLC_EGENERIC;
}
+#ifdef HAVE_AVAHI_CLIENT
+ asprintf( &psz_txt, "path=%s", psz_file_name );
+#endif
+
free( psz_file_name );
free( psz_name );
+#ifdef HAVE_AVAHI_CLIENT
+ p_playlist = (playlist_t *)vlc_object_find( p_access, VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+ if( p_playlist == NULL )
+ {
+ msg_Err( p_access, "unable to find playlist" );
+ httpd_HostDelete( p_sys->p_httpd_host );
+ free( (void *)psz_txt );
+ free( (void *)p_sys );
+ return VLC_EGENERIC;
+ }
+
+ psz_name = strrchr( p_playlist->status.p_item->input.psz_uri,
+ DIRECTORY_SEPARATOR );
+ if( psz_name != NULL ) psz_name++;
+ else psz_name = p_playlist->status.p_item->input.psz_uri;
+
+ p_sys->p_bonjour = bonjour_start_service( (vlc_object_t *)p_access,
+ "_vlc-http._tcp",
+ psz_name, i_bind_port, psz_txt );
+ free( (void *)psz_txt );
+ if( p_sys->p_bonjour == NULL )
+ {
+ vlc_object_release( p_playlist );
+ httpd_HostDelete( p_sys->p_httpd_host );
+ free( (void *)p_sys );
+ return VLC_EGENERIC;
+ }
+
+ vlc_object_release( p_playlist );
+#endif
+
p_sys->i_header_allocated = 1024;
p_sys->i_header_size = 0;
p_sys->p_header = malloc( p_sys->i_header_allocated );
sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys = p_access->p_sys;
+#ifdef HAVE_AVAHI_CLIENT
+ bonjour_stop_service( p_sys->p_bonjour );
+#endif
+
/* update p_sout->i_out_pace_nocontrol */
p_access->p_sout->i_out_pace_nocontrol--;
SOURCES_daap = daap.c
SOURCES_shout = shout.c
SOURCES_upnp = upnp.cpp
+SOURCES_bonjour = bonjour.c
--- /dev/null
+/*****************************************************************************
+ * bonjour.c: Bonjour services discovery module
+ *****************************************************************************
+ * Copyright (C) 2005 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jon Lech Johansen <jon@nanocrew.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Includes
+ *****************************************************************************/
+#include <stdlib.h> /* malloc(), free() */
+
+#include <vlc/vlc.h>
+#include <vlc/intf.h>
+
+#include <avahi-client/client.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+/* Callbacks */
+ static int Open ( vlc_object_t * );
+ static void Close( vlc_object_t * );
+
+vlc_module_begin();
+ set_shortname( "Bonjour" );
+ set_description( _("Bonjour services") );
+ set_category( CAT_PLAYLIST );
+ set_subcategory( SUBCAT_PLAYLIST_SD );
+ set_capability( "services_discovery", 0 );
+ set_callbacks( Open, Close );
+vlc_module_end();
+
+/*****************************************************************************
+ * Local structures
+ *****************************************************************************/
+
+struct services_discovery_sys_t
+{
+ /* playlist node */
+ playlist_item_t *p_node;
+ playlist_t *p_playlist;
+
+ AvahiSimplePoll *simple_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *sb;
+};
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+/* Main functions */
+ static void Run ( services_discovery_t *p_intf );
+
+/*****************************************************************************
+ * client_callback
+ *****************************************************************************/
+static void client_callback( AvahiClient *c, AvahiClientState state,
+ void * userdata )
+{
+ services_discovery_t *p_sd = ( services_discovery_t* )userdata;
+ services_discovery_sys_t *p_sys = p_sd->p_sys;
+
+ if( state == AVAHI_CLIENT_DISCONNECTED )
+ {
+ msg_Err( p_sd, "avahi client disconnected" );
+ avahi_simple_poll_quit( p_sys->simple_poll );
+ }
+}
+
+/*****************************************************************************
+ * resolve_callback
+ *****************************************************************************/
+static void resolve_callback(
+ AvahiServiceResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ void* userdata )
+{
+ services_discovery_t *p_sd = ( services_discovery_t* )userdata;
+ services_discovery_sys_t *p_sys = p_sd->p_sys;
+
+ if( event == AVAHI_RESOLVER_TIMEOUT )
+ {
+ msg_Err( p_sd,
+ "failed to resolve service '%s' of type '%s' in domain '%s'",
+ name, type, domain );
+ }
+ else if( event == AVAHI_RESOLVER_FOUND )
+ {
+ char a[128];
+ char *psz_uri = NULL;
+ AvahiStringList *asl;
+ playlist_item_t *p_item = NULL;
+
+ msg_Dbg( p_sd, "service '%s' of type '%s' in domain '%s'",
+ name, type, domain );
+
+ avahi_address_snprint(a, (sizeof(a)/sizeof(a[0]))-1, address);
+
+ asl = avahi_string_list_find( txt, "path" );
+ if( asl != NULL )
+ {
+ size_t size;
+ char *key = NULL;
+ char *value = NULL;
+ if( avahi_string_list_get_pair( asl, &key, &value, &size ) == 0 )
+ asprintf( &psz_uri, "http://%s:%d%s", a, port, value );
+ }
+ else
+ {
+ asprintf( &psz_uri, "http://%s:%d", a, port );
+ }
+
+ if( psz_uri != NULL )
+ p_item = playlist_ItemNew( p_sd, psz_uri, name );
+ if( p_item != NULL )
+ {
+ p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
+
+ playlist_NodeAddItem( p_sys->p_playlist, p_item,
+ VIEW_CATEGORY, p_sys->p_node,
+ PLAYLIST_APPEND, PLAYLIST_END );
+ }
+ }
+
+ avahi_service_resolver_free( r );
+}
+
+/*****************************************************************************
+ * browser_callback
+ *****************************************************************************/
+static void browse_callback(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ void* userdata )
+{
+ services_discovery_t *p_sd = ( services_discovery_t* )userdata;
+ services_discovery_sys_t *p_sys = p_sd->p_sys;
+
+ if( event == AVAHI_BROWSER_NEW )
+ {
+ if( avahi_service_resolver_new( p_sys->client, interface, protocol,
+ name, type, domain, AVAHI_PROTO_UNSPEC,
+ resolve_callback, userdata ) == NULL )
+ {
+ msg_Err( p_sd, "failed to resolve service '%s': %s", name,
+ avahi_strerror( avahi_client_errno( p_sys->client ) ) );
+ }
+ }
+ else
+ {
+ playlist_item_t *p_item;
+
+ p_item = playlist_ChildSearchName( p_sys->p_node, name );
+ if( p_item == NULL )
+ {
+ msg_Err( p_sd, "failed to find service '%s' in playlist", name );
+ }
+ else
+ {
+ playlist_Delete( p_sys->p_playlist, p_item->input.i_id );
+ }
+ }
+}
+
+/*****************************************************************************
+ * Open: initialize and create stuff
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+ services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+ services_discovery_sys_t *p_sys;
+ playlist_view_t *p_view;
+ vlc_value_t val;
+ int err;
+
+ p_sd->p_sys = p_sys = (services_discovery_sys_t *)malloc(
+ sizeof( services_discovery_sys_t ) );
+ if( p_sd->p_sys == NULL )
+ {
+ msg_Err( p_sd, "out of memory" );
+ return VLC_EGENERIC;
+ }
+
+ memset( p_sys, 0, sizeof(*p_sys) );
+
+ p_sys->simple_poll = avahi_simple_poll_new();
+ if( p_sys->simple_poll == NULL )
+ {
+ msg_Err( p_sd, "failed to create avahi simple poll" );
+ goto error;
+ }
+
+ p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
+ client_callback, p_sd, &err );
+ if( p_sys->client == NULL )
+ {
+ msg_Err( p_sd, "failed to create avahi client: %s",
+ avahi_strerror( err ) );
+ goto error;
+ }
+
+ p_sys->sb = avahi_service_browser_new( p_sys->client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ "_vlc-http._tcp", NULL,
+ browse_callback, p_sd );
+ if( p_sys->sb == NULL )
+ {
+ msg_Err( p_sd, "failed to create avahi service browser" );
+ goto error;
+ }
+
+ /* Create our playlist node */
+ p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
+ VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
+ if( !p_sys->p_playlist )
+ {
+ msg_Warn( p_sd, "unable to find playlist, cancelling");
+ goto error;
+ }
+
+ p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
+ p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
+ _("Bonjour"), p_view->p_root );
+
+ p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
+ val.b_bool = VLC_TRUE;
+ var_Set( p_sys->p_playlist, "intf-change", val );
+
+ p_sd->pf_run = Run;
+
+ return VLC_SUCCESS;
+
+error:
+ if( p_sys->p_playlist != NULL )
+ vlc_object_release( p_sys->p_playlist );
+ if( p_sys->sb != NULL )
+ avahi_service_browser_free( p_sys->sb );
+ if( p_sys->client != NULL )
+ avahi_client_free( p_sys->client );
+ if( p_sys->simple_poll != NULL )
+ avahi_simple_poll_free( p_sys->simple_poll );
+
+ free( (void *)p_sys );
+
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Close: cleanup
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+ services_discovery_sys_t *p_sys = p_sd->p_sys;
+
+ avahi_service_browser_free( p_sys->sb );
+ avahi_client_free( p_sys->client );
+ avahi_simple_poll_free( p_sys->simple_poll );
+
+ playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
+ vlc_object_release( p_sys->p_playlist );
+
+ free( p_sys );
+}
+
+/*****************************************************************************
+ * Run: main thread
+ *****************************************************************************/
+static void Run( services_discovery_t *p_sd )
+{
+ services_discovery_sys_t *p_sys = p_sd->p_sys;
+
+ while( !p_sd->b_die )
+ {
+ if( avahi_simple_poll_iterate( p_sys->simple_poll, 100 ) != 0 )
+ {
+ msg_Err( p_sd, "poll iterate failed" );
+ break;
+ }
+ }
+}