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