No need to check b_thread... there is ALWAYS a thread here
[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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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_sout.h>
33
34 #include <avahi-client/client.h>
35 #ifdef HAVE_AVAHI_06
36 # include <avahi-client/publish.h>
37 # include <avahi-client/lookup.h>
38 #endif
39 #include <avahi-common/alternative.h>
40 #include <avahi-common/simple-watch.h>
41 #include <avahi-common/malloc.h>
42 #include <avahi-common/error.h>
43
44 /*****************************************************************************
45  * Structures
46  *****************************************************************************/
47 typedef struct poll_thread_t
48 {
49     VLC_COMMON_MEMBERS
50
51     AvahiSimplePoll     *simple_poll;
52 } poll_thread_t;
53
54 typedef struct bonjour_t
55 {
56     vlc_object_t        *p_log;
57
58     poll_thread_t       *poll_thread;
59     AvahiSimplePoll     *simple_poll;
60     AvahiEntryGroup     *group;
61     AvahiClient         *client;
62     char                *psz_stype;
63     char                *psz_name;
64     int                 i_port;
65     char                *psz_txt;
66 } bonjour_t;
67
68 /*****************************************************************************
69  * Prototypes
70  *****************************************************************************/
71 static int create_service( bonjour_t * );
72
73 /*****************************************************************************
74  * entry_group_callback
75  *****************************************************************************/
76 static void entry_group_callback( AvahiEntryGroup *g,
77                                   AvahiEntryGroupState state,
78                                   void *userdata )
79 {
80     bonjour_t *p_sys = (bonjour_t *)userdata;
81
82     if( state == AVAHI_ENTRY_GROUP_ESTABLISHED )
83     {
84         msg_Dbg( p_sys->p_log, "service '%s' successfully established",
85                  p_sys->psz_name );
86     }
87     else if( state == AVAHI_ENTRY_GROUP_COLLISION )
88     {
89         char *n;
90
91         n = avahi_alternative_service_name( p_sys->psz_name );
92         avahi_free( p_sys->psz_name );
93         p_sys->psz_name = n;
94
95         create_service( p_sys );
96     }
97 }
98
99 /*****************************************************************************
100  * create_service
101  *****************************************************************************/
102 static int create_service( bonjour_t *p_sys )
103 {
104     int error;
105
106     if( p_sys->group == NULL )
107     {
108         p_sys->group = avahi_entry_group_new( p_sys->client,
109                                               entry_group_callback,
110                                               p_sys );
111         if( p_sys->group == NULL )
112         {
113             msg_Err( p_sys->p_log, "failed to create avahi entry group: %s",
114                      avahi_strerror( avahi_client_errno( p_sys->client ) ) );
115             return VLC_EGENERIC;
116         }
117     }
118
119     error = avahi_entry_group_add_service( p_sys->group, AVAHI_IF_UNSPEC,
120 #ifdef HAVE_AVAHI_06
121                                            AVAHI_PROTO_UNSPEC, 0, p_sys->psz_name,
122 #else
123                                            AVAHI_PROTO_UNSPEC, p_sys->psz_name,
124 #endif
125                                            p_sys->psz_stype, NULL, NULL,
126                                            p_sys->i_port,
127                                            p_sys->psz_txt, NULL );
128     if( error < 0 )
129     {
130         msg_Err( p_sys->p_log, "failed to add %s service: %s",
131                  p_sys->psz_stype, avahi_strerror( error ) );
132         return VLC_EGENERIC;
133     }
134
135     error = avahi_entry_group_commit( p_sys->group );
136     if( error < 0 )
137     {
138         msg_Err( p_sys->p_log, "failed to commit entry group: %s",
139                  avahi_strerror( error ) );
140         return VLC_EGENERIC;
141     }
142
143     return VLC_SUCCESS;
144 }
145
146 /*****************************************************************************
147  * client_callback
148  *****************************************************************************/
149 static void client_callback( AvahiClient *c,
150                              AvahiClientState state,
151                              void * userdata )
152 {
153     bonjour_t *p_sys = (bonjour_t *)userdata;
154
155     if( state == AVAHI_CLIENT_S_RUNNING )
156     {
157         p_sys->client = c;
158         create_service( p_sys );
159     }
160     else if( state == AVAHI_CLIENT_S_COLLISION )
161     {
162         if( p_sys->group != NULL )
163             avahi_entry_group_reset( p_sys->group );
164     }
165 #ifdef HAVE_AVAHI_06
166     else if( state == AVAHI_CLIENT_FAILURE &&
167               (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
168 #else
169     else if( state == AVAHI_CLIENT_DISCONNECTED )
170 #endif
171     {
172         msg_Err( p_sys->p_log, "avahi client disconnected" );
173         avahi_simple_poll_quit( p_sys->simple_poll );
174     }
175 }
176
177 /*****************************************************************************
178  * poll_iterate_thread
179  *****************************************************************************/
180 static void poll_iterate_thread( poll_thread_t *p_pt )
181 {
182     vlc_thread_ready( p_pt );
183
184     while( !p_pt->b_die )
185         if( avahi_simple_poll_iterate( p_pt->simple_poll, 100 ) != 0 )
186             break;
187 }
188
189 /*****************************************************************************
190  * bonjour_start_service
191  *****************************************************************************/
192 void *bonjour_start_service( vlc_object_t *p_log, char *psz_stype,
193                             char *psz_name, int i_port, char *psz_txt )
194 {
195     int err;
196     bonjour_t *p_sys;
197
198     p_sys = (bonjour_t *)malloc( sizeof(*p_sys) );
199     if( p_sys == NULL )
200     {
201         msg_Err( p_log, "out of memory" );
202         return NULL;
203     }
204
205     memset( p_sys, 0, sizeof(*p_sys) );
206
207     p_sys->p_log = p_log;
208
209     p_sys->i_port = i_port;
210     p_sys->psz_name = avahi_strdup( psz_name );
211     p_sys->psz_stype = avahi_strdup( psz_stype );
212     if( p_sys->psz_name == NULL || p_sys->psz_stype == NULL )
213     {
214         msg_Err( p_sys->p_log, "out of memory" );
215         goto error;
216     }
217
218     if( psz_txt != NULL )
219     {
220         p_sys->psz_txt = avahi_strdup( psz_txt );
221         if( p_sys->psz_txt == NULL )
222         {
223             msg_Err( p_sys->p_log, "out of memory" );
224             goto error;
225         }
226     }
227
228     p_sys->simple_poll = avahi_simple_poll_new();
229     if( p_sys->simple_poll == NULL )
230     {
231         msg_Err( p_sys->p_log, "failed to create avahi simple pool" );
232         goto error;
233     }
234
235     p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
236 #ifdef HAVE_AVAHI_06
237                                       0,
238 #endif
239                                       client_callback, p_sys, &err );
240     if( p_sys->client == NULL )
241     {
242         msg_Err( p_sys->p_log, "failed to create avahi client: %s",
243                  avahi_strerror( err ) );
244         goto error;
245     }
246
247     p_sys->poll_thread = vlc_object_create( p_sys->p_log,
248                                             sizeof(poll_thread_t) );
249     if( p_sys->poll_thread == NULL )
250     {
251         msg_Err( p_sys->p_log, "out of memory" );
252         goto error;
253     }
254     p_sys->poll_thread->simple_poll = p_sys->simple_poll;
255
256     if( vlc_thread_create( p_sys->poll_thread, "Avahi Poll Iterate Thread",
257                            poll_iterate_thread,
258                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
259     {
260         msg_Err( p_sys->p_log, "failed to create poll iterate thread" );
261         goto error;
262     }
263
264     return (void *)p_sys;
265
266 error:
267     if( p_sys->poll_thread != NULL )
268         vlc_object_destroy( p_sys->poll_thread );
269     if( p_sys->client != NULL )
270         avahi_client_free( p_sys->client );
271     if( p_sys->simple_poll != NULL )
272         avahi_simple_poll_free( p_sys->simple_poll );
273     if( p_sys->psz_stype != NULL )
274         avahi_free( p_sys->psz_stype );
275     if( p_sys->psz_name != NULL )
276         avahi_free( p_sys->psz_name );
277     if( p_sys->psz_txt != NULL )
278         avahi_free( p_sys->psz_txt );
279
280     free( (void *)p_sys );
281
282     return NULL;
283 }
284
285 /*****************************************************************************
286  * bonjour_stop_service
287  *****************************************************************************/
288 void bonjour_stop_service( void *_p_sys )
289 {
290     bonjour_t *p_sys = (bonjour_t *)_p_sys;
291
292     vlc_object_kill( p_sys->poll_thread );
293     vlc_thread_join( p_sys->poll_thread );
294     vlc_object_destroy( p_sys->poll_thread );
295
296     if( p_sys->group != NULL )
297         avahi_entry_group_free( p_sys->group );
298
299     avahi_client_free( p_sys->client );
300     avahi_simple_poll_free( p_sys->simple_poll );
301
302     if( p_sys->psz_name != NULL )
303         avahi_free( p_sys->psz_name );
304
305     if( p_sys->psz_txt != NULL )
306         avahi_free( p_sys->psz_txt );
307
308     avahi_free( p_sys->psz_stype );
309
310     free( _p_sys );
311 }
312
313 #endif /* HAVE_AVAHI_CLIENT */