00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028
00029 #include <vlc/vlc.h>
00030 #include <vlc/intf.h>
00031
00032 #include <avahi-client/client.h>
00033 #ifdef HAVE_AVAHI_06
00034 # include <avahi-client/publish.h>
00035 # include <avahi-client/lookup.h>
00036 #endif
00037 #include <avahi-common/simple-watch.h>
00038 #include <avahi-common/malloc.h>
00039 #include <avahi-common/error.h>
00040
00041
00042
00043
00044
00045
00046 static int Open ( vlc_object_t * );
00047 static void Close( vlc_object_t * );
00048
00049 vlc_module_begin();
00050 set_shortname( "Bonjour" );
00051 set_description( _("Bonjour services") );
00052 set_category( CAT_PLAYLIST );
00053 set_subcategory( SUBCAT_PLAYLIST_SD );
00054 set_capability( "services_discovery", 0 );
00055 set_callbacks( Open, Close );
00056 vlc_module_end();
00057
00058
00059
00060
00061
00062 struct services_discovery_sys_t
00063 {
00064
00065 playlist_item_t *p_node;
00066 playlist_t *p_playlist;
00067
00068 AvahiSimplePoll *simple_poll;
00069 AvahiClient *client;
00070 AvahiServiceBrowser *sb;
00071 };
00072
00073
00074
00075
00076
00077
00078 static void Run ( services_discovery_t *p_intf );
00079
00080
00081
00082
00083 static void client_callback( AvahiClient *c, AvahiClientState state,
00084 void * userdata )
00085 {
00086 services_discovery_t *p_sd = ( services_discovery_t* )userdata;
00087 services_discovery_sys_t *p_sys = p_sd->p_sys;
00088
00089 #ifdef HAVE_AVAHI_06
00090 if( state == AVAHI_CLIENT_FAILURE &&
00091 (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
00092 #else
00093 if( state == AVAHI_CLIENT_DISCONNECTED )
00094 #endif
00095 {
00096 msg_Err( p_sd, "avahi client disconnected" );
00097 avahi_simple_poll_quit( p_sys->simple_poll );
00098 }
00099 }
00100
00101
00102
00103
00104 static void resolve_callback(
00105 AvahiServiceResolver *r,
00106 AvahiIfIndex interface,
00107 AvahiProtocol protocol,
00108 AvahiResolverEvent event,
00109 const char *name,
00110 const char *type,
00111 const char *domain,
00112 const char *host_name,
00113 const AvahiAddress *address,
00114 uint16_t port,
00115 AvahiStringList *txt,
00116 #ifdef HAVE_AVAHI_06
00117 AvahiLookupResultFlags flags,
00118 #endif
00119 void* userdata )
00120 {
00121 services_discovery_t *p_sd = ( services_discovery_t* )userdata;
00122 services_discovery_sys_t *p_sys = p_sd->p_sys;
00123
00124 #ifdef HAVE_AVAHI_06
00125 if( event == AVAHI_RESOLVER_FAILURE )
00126 #else
00127 if( event == AVAHI_RESOLVER_TIMEOUT )
00128 #endif
00129 {
00130 msg_Err( p_sd,
00131 "failed to resolve service '%s' of type '%s' in domain '%s'",
00132 name, type, domain );
00133 }
00134 else if( event == AVAHI_RESOLVER_FOUND )
00135 {
00136 char a[128];
00137 char *psz_uri = NULL;
00138 char *psz_addr = NULL;
00139 AvahiStringList *asl = NULL;
00140 playlist_item_t *p_item = NULL;
00141
00142 msg_Dbg( p_sd, "service '%s' of type '%s' in domain '%s'",
00143 name, type, domain );
00144
00145 avahi_address_snprint(a, (sizeof(a)/sizeof(a[0]))-1, address);
00146 if( protocol == AVAHI_PROTO_INET6 )
00147 asprintf( &psz_addr, "[%s]", a );
00148
00149 if( txt != NULL )
00150 asl = avahi_string_list_find( txt, "path" );
00151 if( asl != NULL )
00152 {
00153 size_t size;
00154 char *key = NULL;
00155 char *value = NULL;
00156 if( avahi_string_list_get_pair( asl, &key, &value, &size ) == 0 &&
00157 value != NULL )
00158 {
00159 asprintf( &psz_uri, "http://%s:%d%s",
00160 psz_addr != NULL ? psz_addr : a, port, value );
00161 }
00162 if( key != NULL )
00163 avahi_free( (void *)key );
00164 if( value != NULL )
00165 avahi_free( (void *)value );
00166 }
00167 else
00168 {
00169 asprintf( &psz_uri, "http://%s:%d",
00170 psz_addr != NULL ? psz_addr : a, port );
00171 }
00172
00173 if( psz_addr != NULL )
00174 free( (void *)psz_addr );
00175
00176 if( psz_uri != NULL )
00177 {
00178 p_item = playlist_ItemNew( p_sd, psz_uri, name );
00179 free( (void *)psz_uri );
00180 }
00181 if( p_item != NULL )
00182 {
00183 p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
00184
00185 playlist_NodeAddItem( p_sys->p_playlist, p_item,
00186 VIEW_CATEGORY, p_sys->p_node,
00187 PLAYLIST_APPEND, PLAYLIST_END );
00188 }
00189 }
00190
00191 avahi_service_resolver_free( r );
00192 }
00193
00194
00195
00196
00197 static void browse_callback(
00198 AvahiServiceBrowser *b,
00199 AvahiIfIndex interface,
00200 AvahiProtocol protocol,
00201 AvahiBrowserEvent event,
00202 const char *name,
00203 const char *type,
00204 const char *domain,
00205 #ifdef HAVE_AVAHI_06
00206 AvahiLookupResultFlags flags,
00207 #endif
00208 void* userdata )
00209 {
00210 services_discovery_t *p_sd = ( services_discovery_t* )userdata;
00211 services_discovery_sys_t *p_sys = p_sd->p_sys;
00212
00213 if( event == AVAHI_BROWSER_NEW )
00214 {
00215 if( avahi_service_resolver_new( p_sys->client, interface, protocol,
00216 name, type, domain, AVAHI_PROTO_UNSPEC,
00217 #ifdef HAVE_AVAHI_06
00218 0,
00219 #endif
00220 resolve_callback, userdata ) == NULL )
00221 {
00222 msg_Err( p_sd, "failed to resolve service '%s': %s", name,
00223 avahi_strerror( avahi_client_errno( p_sys->client ) ) );
00224 }
00225 }
00226 else
00227 {
00228 playlist_item_t *p_item;
00229
00230 p_item = playlist_ChildSearchName( p_sys->p_node, name );
00231 if( p_item == NULL )
00232 {
00233 msg_Err( p_sd, "failed to find service '%s' in playlist", name );
00234 }
00235 else
00236 {
00237 playlist_Delete( p_sys->p_playlist, p_item->input.i_id );
00238 }
00239 }
00240 }
00241
00242
00243
00244
00245 static int Open( vlc_object_t *p_this )
00246 {
00247 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00248 services_discovery_sys_t *p_sys;
00249 playlist_view_t *p_view;
00250 vlc_value_t val;
00251 int err;
00252
00253 p_sd->p_sys = p_sys = (services_discovery_sys_t *)malloc(
00254 sizeof( services_discovery_sys_t ) );
00255 if( p_sd->p_sys == NULL )
00256 {
00257 msg_Err( p_sd, "out of memory" );
00258 return VLC_EGENERIC;
00259 }
00260
00261 memset( p_sys, 0, sizeof(*p_sys) );
00262
00263 p_sys->simple_poll = avahi_simple_poll_new();
00264 if( p_sys->simple_poll == NULL )
00265 {
00266 msg_Err( p_sd, "failed to create avahi simple poll" );
00267 goto error;
00268 }
00269
00270 p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
00271 #ifdef HAVE_AVAHI_06
00272 0,
00273 #endif
00274 client_callback, p_sd, &err );
00275 if( p_sys->client == NULL )
00276 {
00277 msg_Err( p_sd, "failed to create avahi client: %s",
00278 avahi_strerror( err ) );
00279 goto error;
00280 }
00281
00282 p_sys->sb = avahi_service_browser_new( p_sys->client, AVAHI_IF_UNSPEC,
00283 AVAHI_PROTO_UNSPEC,
00284 "_vlc-http._tcp", NULL,
00285 #ifdef HAVE_AVAHI_06
00286 0,
00287 #endif
00288 browse_callback, p_sd );
00289 if( p_sys->sb == NULL )
00290 {
00291 msg_Err( p_sd, "failed to create avahi service browser" );
00292 goto error;
00293 }
00294
00295
00296 p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
00297 VLC_OBJECT_PLAYLIST,
00298 FIND_ANYWHERE );
00299 if( !p_sys->p_playlist )
00300 {
00301 msg_Warn( p_sd, "unable to find playlist, cancelling");
00302 goto error;
00303 }
00304
00305 p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
00306 p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
00307 _("Bonjour"), p_view->p_root );
00308
00309 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
00310 val.b_bool = VLC_TRUE;
00311 var_Set( p_sys->p_playlist, "intf-change", val );
00312
00313 p_sd->pf_run = Run;
00314
00315 return VLC_SUCCESS;
00316
00317 error:
00318 if( p_sys->p_playlist != NULL )
00319 vlc_object_release( p_sys->p_playlist );
00320 if( p_sys->sb != NULL )
00321 avahi_service_browser_free( p_sys->sb );
00322 if( p_sys->client != NULL )
00323 avahi_client_free( p_sys->client );
00324 if( p_sys->simple_poll != NULL )
00325 avahi_simple_poll_free( p_sys->simple_poll );
00326
00327 free( (void *)p_sys );
00328
00329 return VLC_EGENERIC;
00330 }
00331
00332
00333
00334
00335 static void Close( vlc_object_t *p_this )
00336 {
00337 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00338 services_discovery_sys_t *p_sys = p_sd->p_sys;
00339
00340 avahi_service_browser_free( p_sys->sb );
00341 avahi_client_free( p_sys->client );
00342 avahi_simple_poll_free( p_sys->simple_poll );
00343
00344 playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
00345 vlc_object_release( p_sys->p_playlist );
00346
00347 free( p_sys );
00348 }
00349
00350
00351
00352
00353 static void Run( services_discovery_t *p_sd )
00354 {
00355 services_discovery_sys_t *p_sys = p_sd->p_sys;
00356
00357 while( !p_sd->b_die )
00358 {
00359 if( avahi_simple_poll_iterate( p_sys->simple_poll, 100 ) != 0 )
00360 {
00361 msg_Err( p_sd, "poll iterate failed" );
00362 break;
00363 }
00364 }
00365 }