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