Avahi 0.6 patch by Diego 'Flameeyes' Pettenò flameeyes at gentoo d0t org
[vlc.git] / modules / access_output / bonjour.c
1 /*****************************************************************************
2  * bonjour.c
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon@nanocrew.net>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30
31 #ifdef HAVE_AVAHI_CLIENT
32 #include <vlc/intf.h>
33 #include <vlc/sout.h>
34
35 #include <avahi-client/client.h>
36 #ifdef HAVE_AVAHI_06
37 # include <avahi-client/publish.h>
38 # include <avahi-client/lookup.h>
39 #endif
40 #include <avahi-common/alternative.h>
41 #include <avahi-common/simple-watch.h>
42 #include <avahi-common/malloc.h>
43 #include <avahi-common/error.h>
44
45 /*****************************************************************************
46  * Structures
47  *****************************************************************************/
48 typedef struct poll_thread_t
49 {
50     VLC_COMMON_MEMBERS
51
52     AvahiSimplePoll     *simple_poll;
53 } poll_thread_t;
54
55 typedef struct bonjour_t
56 {
57     vlc_object_t        *p_log;
58
59     poll_thread_t       *poll_thread;
60     AvahiSimplePoll     *simple_poll;
61     AvahiEntryGroup     *group;
62     AvahiClient         *client;
63     char                *psz_stype;
64     char                *psz_name;
65     int                 i_port;
66     char                *psz_txt;
67 } bonjour_t;
68
69 /*****************************************************************************
70  * Prototypes
71  *****************************************************************************/
72 static int create_service( bonjour_t * );
73
74 /*****************************************************************************
75  * entry_group_callback
76  *****************************************************************************/
77 static void entry_group_callback( AvahiEntryGroup *g,
78                                   AvahiEntryGroupState state,
79                                   void *userdata )
80 {
81     bonjour_t *p_sys = (bonjour_t *)userdata;
82
83     if( state == AVAHI_ENTRY_GROUP_ESTABLISHED )
84     {
85         msg_Dbg( p_sys->p_log, "service '%s' successfully established",
86                  p_sys->psz_name );
87     }
88     else if( state == AVAHI_ENTRY_GROUP_COLLISION )
89     {
90         char *n;
91
92         n = avahi_alternative_service_name( p_sys->psz_name );
93         avahi_free( p_sys->psz_name );
94         p_sys->psz_name = n;
95
96         create_service( p_sys );
97     }
98 }
99
100 /*****************************************************************************
101  * create_service
102  *****************************************************************************/
103 static int create_service( bonjour_t *p_sys )
104 {
105     int error;
106
107     if( p_sys->group == NULL )
108     {
109         p_sys->group = avahi_entry_group_new( p_sys->client,
110                                               entry_group_callback,
111                                               p_sys );
112         if( p_sys->group == NULL )
113         {
114             msg_Err( p_sys->p_log, "failed to create avahi entry group: %s",
115                      avahi_strerror( avahi_client_errno( p_sys->client ) ) );
116             return VLC_EGENERIC;
117         }
118     }
119
120     error = avahi_entry_group_add_service( p_sys->group, AVAHI_IF_UNSPEC,
121 #ifdef HAVE_AVAHI_06
122                                            AVAHI_PROTO_UNSPEC, 0, p_sys->psz_name,
123 #else
124                                            AVAHI_PROTO_UNSPEC, p_sys->psz_name,
125 #endif
126                                            p_sys->psz_stype, NULL, NULL,
127                                            p_sys->i_port,
128                                            p_sys->psz_txt, NULL );
129     if( error < 0 )
130     {
131         msg_Err( p_sys->p_log, "failed to add %s service: %s",
132                  p_sys->psz_stype, avahi_strerror( error ) );
133         return VLC_EGENERIC;
134     }
135
136     error = avahi_entry_group_commit( p_sys->group );
137     if( error < 0 )
138     {
139         msg_Err( p_sys->p_log, "failed to commit entry group: %s",
140                  avahi_strerror( error ) );
141         return VLC_EGENERIC;
142     }
143
144     return VLC_SUCCESS;
145 }
146
147 /*****************************************************************************
148  * client_callback
149  *****************************************************************************/
150 static void client_callback( AvahiClient *c,
151                              AvahiClientState state,
152                              void * userdata )
153 {
154     bonjour_t *p_sys = (bonjour_t *)userdata;
155
156     if( state == AVAHI_CLIENT_S_RUNNING )
157     {
158         p_sys->client = c;
159         create_service( p_sys );
160     }
161     else if( state == AVAHI_CLIENT_S_COLLISION )
162     {
163         if( p_sys->group != NULL )
164             avahi_entry_group_reset( p_sys->group );
165     }
166 #ifdef HAVE_AVAHI_06
167     else if( state == AVAHI_CLIENT_FAILURE &&
168               (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
169 #else
170     else if( state == AVAHI_CLIENT_DISCONNECTED )
171 #endif
172     {
173         msg_Err( p_sys->p_log, "avahi client disconnected" );
174         avahi_simple_poll_quit( p_sys->simple_poll );
175     }
176 }
177
178 /*****************************************************************************
179  * poll_iterate_thread
180  *****************************************************************************/
181 static void poll_iterate_thread( poll_thread_t *p_pt )
182 {
183     vlc_thread_ready( p_pt );
184
185     while( !p_pt->b_die )
186         if( avahi_simple_poll_iterate( p_pt->simple_poll, 100 ) != 0 )
187             break;
188 }
189
190 /*****************************************************************************
191  * bonjour_start_service
192  *****************************************************************************/
193 void *bonjour_start_service( vlc_object_t *p_log, char *psz_stype,
194                             char *psz_name, int i_port, char *psz_txt )
195 {
196     int err;
197     bonjour_t *p_sys;
198
199     p_sys = (bonjour_t *)malloc( sizeof(*p_sys) );
200     if( p_sys == NULL )
201     {
202         msg_Err( p_log, "out of memory" );
203         return NULL;
204     }
205
206     memset( p_sys, 0, sizeof(*p_sys) );
207
208     p_sys->p_log = p_log;
209
210     p_sys->i_port = i_port;
211     p_sys->psz_name = avahi_strdup( psz_name );
212     p_sys->psz_stype = avahi_strdup( psz_stype );
213     if( p_sys->psz_name == NULL || p_sys->psz_stype == NULL )
214     {
215         msg_Err( p_sys->p_log, "out of memory" );
216         goto error;
217     }
218
219     if( psz_txt != NULL )
220     {
221         p_sys->psz_txt = avahi_strdup( psz_txt );
222         if( p_sys->psz_txt == NULL )
223         {
224             msg_Err( p_sys->p_log, "out of memory" );
225             goto error;
226         }
227     }
228
229     p_sys->simple_poll = avahi_simple_poll_new();
230     if( p_sys->simple_poll == NULL )
231     {
232         msg_Err( p_sys->p_log, "failed to create avahi simple pool" );
233         goto error;
234     }
235
236     p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
237 #ifdef HAVE_AVAHI_06
238                                       0,
239 #endif
240                                       client_callback, p_sys, &err );
241     if( p_sys->client == NULL )
242     {
243         msg_Err( p_sys->p_log, "failed to create avahi client: %s",
244                  avahi_strerror( err ) );
245         goto error;
246     }
247
248     p_sys->poll_thread = vlc_object_create( p_sys->p_log,
249                                             sizeof(poll_thread_t) );
250     if( p_sys->poll_thread == NULL )
251     {
252         msg_Err( p_sys->p_log, "out of memory" );
253         goto error;
254     }
255     p_sys->poll_thread->simple_poll = p_sys->simple_poll;
256
257     if( vlc_thread_create( p_sys->poll_thread, "Avahi Poll Iterate Thread",
258                            poll_iterate_thread,
259                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
260     {
261         msg_Err( p_sys->p_log, "failed to create poll iterate thread" );
262         goto error;
263     }
264
265     return (void *)p_sys;
266
267 error:
268     if( p_sys->poll_thread != NULL )
269         vlc_object_destroy( p_sys->poll_thread );
270     if( p_sys->client != NULL )
271         avahi_client_free( p_sys->client );
272     if( p_sys->simple_poll != NULL )
273         avahi_simple_poll_free( p_sys->simple_poll );
274     if( p_sys->psz_stype != NULL )
275         avahi_free( p_sys->psz_stype );
276     if( p_sys->psz_name != NULL )
277         avahi_free( p_sys->psz_name );
278     if( p_sys->psz_txt != NULL )
279         avahi_free( p_sys->psz_txt );
280
281     free( (void *)p_sys );
282
283     return NULL;
284 }
285
286 /*****************************************************************************
287  * bonjour_stop_service
288  *****************************************************************************/
289 void bonjour_stop_service( void *_p_sys )
290 {
291     bonjour_t *p_sys = (bonjour_t *)_p_sys;
292
293     if( p_sys->poll_thread->b_thread )
294     {
295         p_sys->poll_thread->b_die = 1;
296         vlc_thread_join( p_sys->poll_thread );
297     }
298
299     vlc_object_destroy( p_sys->poll_thread );
300
301     if( p_sys->group != NULL )
302         avahi_entry_group_free( p_sys->group );
303
304     avahi_client_free( p_sys->client );
305     avahi_simple_poll_free( p_sys->simple_poll );
306
307     if( p_sys->psz_name != NULL )
308         avahi_free( p_sys->psz_name );
309
310     if( p_sys->psz_txt != NULL )
311         avahi_free( p_sys->psz_txt );
312
313     avahi_free( p_sys->psz_stype );
314
315     free( _p_sys );
316 }
317
318 #endif /* HAVE_AVAHI_CLIENT */