/src/vlc/modules/services_discovery/sap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * sap.c : SAP interface module |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2004-2005 the VideoLAN team |
5 | | * Copyright © 2007 Rémi Denis-Courmont |
6 | | * |
7 | | * Authors: Clément Stenac <zorglub@videolan.org> |
8 | | * Rémi Denis-Courmont |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU General Public License as published by |
12 | | * the Free Software Foundation; either version 2 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU General Public License |
21 | | * along with this program; if not, write to the Free Software |
22 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | /***************************************************************************** |
26 | | * Includes |
27 | | *****************************************************************************/ |
28 | | #ifdef HAVE_CONFIG_H |
29 | | # include "config.h" |
30 | | #endif |
31 | | |
32 | | #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS |
33 | | #include <vlc_common.h> |
34 | | #include <vlc_plugin.h> |
35 | | #include <assert.h> |
36 | | |
37 | | #include <vlc_demux.h> |
38 | | #include <vlc_services_discovery.h> |
39 | | |
40 | | #include <vlc_network.h> |
41 | | #include <vlc_charset.h> |
42 | | |
43 | | #include <errno.h> |
44 | | #include <unistd.h> |
45 | | #ifdef HAVE_ARPA_INET_H |
46 | | # include <arpa/inet.h> |
47 | | #endif |
48 | | #ifdef HAVE_POLL_H |
49 | | # include <poll.h> |
50 | | #endif |
51 | | |
52 | | #ifdef HAVE_ZLIB_H |
53 | | # include <zlib.h> |
54 | | #endif |
55 | | |
56 | | #ifdef HAVE_NET_IF_H |
57 | | # include <net/if.h> |
58 | | #endif |
59 | | |
60 | | #include "access/rtp/sdp.h" |
61 | | |
62 | | /************************************************************************ |
63 | | * Macros and definitions |
64 | | ************************************************************************/ |
65 | | |
66 | | #define MAX_LINE_LENGTH 256 |
67 | | |
68 | | /* SAP is always on that port */ |
69 | 0 | #define SAP_PORT 9875 |
70 | | /* Global-scope SAP address */ |
71 | 0 | #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254" |
72 | | /* Organization-local SAP address */ |
73 | 0 | #define SAP_V4_ORG_ADDRESS "239.195.255.255" |
74 | | /* Local (smallest non-link-local scope) SAP address */ |
75 | 0 | #define SAP_V4_LOCAL_ADDRESS "239.255.255.255" |
76 | | /* Link-local SAP address */ |
77 | 0 | #define SAP_V4_LINK_ADDRESS "224.0.0.255" |
78 | | #define ADD_SESSION 1 |
79 | | |
80 | | static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len ) |
81 | 0 | { |
82 | | #ifdef HAVE_ZLIB |
83 | | int i_result, i_dstsize, n = 0; |
84 | | unsigned char *psz_dst = NULL; |
85 | | z_stream d_stream; |
86 | | |
87 | | memset (&d_stream, 0, sizeof (d_stream)); |
88 | | |
89 | | i_result = inflateInit(&d_stream); |
90 | | if( i_result != Z_OK ) |
91 | | return( -1 ); |
92 | | |
93 | | d_stream.next_in = (Bytef *)psz_src; |
94 | | d_stream.avail_in = i_len; |
95 | | |
96 | | do |
97 | | { |
98 | | n++; |
99 | | psz_dst = xrealloc( psz_dst, n * 1000 ); |
100 | | d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000]; |
101 | | d_stream.avail_out = 1000; |
102 | | |
103 | | i_result = inflate(&d_stream, Z_NO_FLUSH); |
104 | | if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) ) |
105 | | { |
106 | | inflateEnd( &d_stream ); |
107 | | free( psz_dst ); |
108 | | return( -1 ); |
109 | | } |
110 | | } |
111 | | while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) && |
112 | | ( i_result != Z_STREAM_END ) ); |
113 | | |
114 | | i_dstsize = d_stream.total_out; |
115 | | inflateEnd( &d_stream ); |
116 | | |
117 | | *_dst = xrealloc( psz_dst, i_dstsize ); |
118 | | |
119 | | return i_dstsize; |
120 | | #else |
121 | 0 | (void)psz_src; |
122 | 0 | (void)_dst; |
123 | 0 | (void)i_len; |
124 | 0 | return -1; |
125 | 0 | #endif |
126 | 0 | } |
127 | | |
128 | | typedef struct sap_announce_t |
129 | | { |
130 | | vlc_tick_t i_last; |
131 | | vlc_tick_t i_period; |
132 | | uint8_t i_period_trust; |
133 | | |
134 | | uint16_t i_hash; |
135 | | uint32_t i_source[4]; |
136 | | |
137 | | input_item_t * p_item; |
138 | | } sap_announce_t; |
139 | | |
140 | | static sap_announce_t *CreateAnnounce(services_discovery_t *p_sd, |
141 | | const uint32_t *i_source, |
142 | | uint16_t i_hash, const char *psz_sdp) |
143 | 0 | { |
144 | | /* Parse SDP info */ |
145 | 0 | struct vlc_sdp *p_sdp = vlc_sdp_parse(psz_sdp, strlen(psz_sdp)); |
146 | 0 | if (p_sdp == NULL) |
147 | 0 | return NULL; |
148 | | |
149 | 0 | char *uri = NULL; |
150 | |
|
151 | 0 | if (asprintf(&uri, "sdp://%s", psz_sdp) == -1) |
152 | 0 | { |
153 | 0 | vlc_sdp_free(p_sdp); |
154 | 0 | return NULL; |
155 | 0 | } |
156 | | |
157 | 0 | input_item_t *p_input; |
158 | 0 | const char *psz_value; |
159 | 0 | sap_announce_t *p_sap = malloc(sizeof (*p_sap)); |
160 | 0 | if( p_sap == NULL ) |
161 | 0 | return NULL; |
162 | | |
163 | 0 | p_sap->i_last = vlc_tick_now(); |
164 | 0 | p_sap->i_period = 0; |
165 | 0 | p_sap->i_period_trust = 0; |
166 | 0 | p_sap->i_hash = i_hash; |
167 | 0 | memcpy (p_sap->i_source, i_source, sizeof(p_sap->i_source)); |
168 | | |
169 | | /* Released in RemoveAnnounce */ |
170 | 0 | p_input = input_item_NewStream(uri, p_sdp->name, |
171 | 0 | INPUT_DURATION_INDEFINITE); |
172 | 0 | if( unlikely(p_input == NULL) ) |
173 | 0 | { |
174 | 0 | free( p_sap ); |
175 | 0 | return NULL; |
176 | 0 | } |
177 | 0 | p_sap->p_item = p_input; |
178 | |
|
179 | 0 | vlc_meta_t *p_meta = vlc_meta_New(); |
180 | 0 | if( likely(p_meta != NULL) ) |
181 | 0 | { |
182 | 0 | vlc_meta_Set(p_meta, vlc_meta_Description, p_sdp->info); |
183 | 0 | p_input->p_meta = p_meta; |
184 | 0 | } |
185 | |
|
186 | 0 | psz_value = vlc_sdp_attr_value(p_sdp, "tool"); |
187 | 0 | if( psz_value != NULL ) |
188 | 0 | { |
189 | 0 | input_item_AddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value ); |
190 | 0 | } |
191 | | |
192 | | /* Handle category */ |
193 | 0 | psz_value = vlc_sdp_attr_value(p_sdp, "cat"); |
194 | 0 | if (psz_value != NULL) |
195 | 0 | { |
196 | | /* a=cat provides a dot-separated hierarchy. |
197 | | * For the time being only replace dots with pipe. TODO: FIXME */ |
198 | 0 | char *str = strdup(psz_value); |
199 | 0 | if (likely(str != NULL)) |
200 | 0 | for (char *p = strchr(str, '.'); p != NULL; p = strchr(p, '.')) |
201 | 0 | *(p++) = '|'; |
202 | 0 | services_discovery_AddItemCat(p_sd, p_input, str ? str : psz_value); |
203 | 0 | free(str); |
204 | 0 | } |
205 | 0 | else |
206 | 0 | { |
207 | | /* backward compatibility with VLC 0.7.3-2.0.0 senders */ |
208 | 0 | psz_value = vlc_sdp_attr_value(p_sdp, "x-plgroup"); |
209 | 0 | services_discovery_AddItemCat(p_sd, p_input, psz_value); |
210 | 0 | } |
211 | |
|
212 | 0 | vlc_sdp_free(p_sdp); |
213 | 0 | return p_sap; |
214 | 0 | } |
215 | | |
216 | | typedef struct |
217 | | { |
218 | | vlc_thread_t thread; |
219 | | |
220 | | /* Socket descriptors */ |
221 | | int i_fd; |
222 | | int *pi_fd; |
223 | | |
224 | | /* Table of announces */ |
225 | | int i_announces; |
226 | | struct sap_announce_t **pp_announces; |
227 | | |
228 | | vlc_tick_t i_timeout; |
229 | | } services_discovery_sys_t; |
230 | | |
231 | | static int RemoveAnnounce( services_discovery_t *p_sd, |
232 | | sap_announce_t *p_announce ) |
233 | 0 | { |
234 | 0 | if( p_announce->p_item ) |
235 | 0 | { |
236 | 0 | services_discovery_RemoveItem( p_sd, p_announce->p_item ); |
237 | 0 | input_item_Release( p_announce->p_item ); |
238 | 0 | p_announce->p_item = NULL; |
239 | 0 | } |
240 | |
|
241 | 0 | services_discovery_sys_t *p_sys = p_sd->p_sys; |
242 | 0 | TAB_REMOVE(p_sys->i_announces, p_sys->pp_announces, p_announce); |
243 | 0 | free( p_announce ); |
244 | |
|
245 | 0 | return VLC_SUCCESS; |
246 | 0 | } |
247 | | |
248 | | static int InitSocket( services_discovery_t *p_sd, const char *psz_address, |
249 | | int i_port ) |
250 | 0 | { |
251 | 0 | int i_fd = net_ListenUDP1 (VLC_OBJECT(p_sd), psz_address, i_port); |
252 | 0 | if (i_fd == -1) |
253 | 0 | return VLC_EGENERIC; |
254 | | |
255 | 0 | shutdown( i_fd, SHUT_WR ); |
256 | 0 | services_discovery_sys_t *p_sys = p_sd->p_sys; |
257 | 0 | TAB_APPEND(p_sys->i_fd, p_sys->pi_fd, i_fd); |
258 | 0 | return VLC_SUCCESS; |
259 | 0 | } |
260 | | |
261 | | /* i_read is at least > 6 */ |
262 | | static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, |
263 | | size_t len ) |
264 | 0 | { |
265 | 0 | services_discovery_sys_t *p_sys = p_sd->p_sys; |
266 | 0 | const char *psz_sdp; |
267 | 0 | const uint8_t *end = buf + len; |
268 | 0 | uint32_t i_source[4]; |
269 | |
|
270 | 0 | assert (buf[len] == '\0'); |
271 | | |
272 | 0 | if (len < 4) |
273 | 0 | return VLC_EGENERIC; |
274 | | |
275 | 0 | uint8_t flags = buf[0]; |
276 | 0 | uint8_t auth_len = buf[1]; |
277 | | |
278 | | /* First, check the sap announce is correct */ |
279 | 0 | if ((flags >> 5) != 1) |
280 | 0 | return VLC_EGENERIC; |
281 | | |
282 | 0 | bool b_ipv6 = (flags & 0x10) != 0; |
283 | 0 | bool b_need_delete = (flags & 0x04) != 0; |
284 | |
|
285 | 0 | if (flags & 0x02) |
286 | 0 | { |
287 | 0 | msg_Dbg( p_sd, "encrypted packet, unsupported" ); |
288 | 0 | return VLC_EGENERIC; |
289 | 0 | } |
290 | | |
291 | 0 | bool b_compressed = (flags & 0x01) != 0; |
292 | |
|
293 | 0 | uint16_t i_hash = U16_AT (buf + 2); |
294 | |
|
295 | 0 | if( i_hash == 0 ) |
296 | 0 | return VLC_EGENERIC; |
297 | | |
298 | 0 | buf += 4; |
299 | 0 | if( b_ipv6 ) |
300 | 0 | { |
301 | 0 | for( int i = 0; i < 4; i++,buf+=4) |
302 | 0 | i_source[i] = U32_AT(buf); |
303 | 0 | } |
304 | 0 | else |
305 | 0 | { |
306 | 0 | memset(i_source, 0, sizeof(i_source)); |
307 | 0 | i_source[3] = U32_AT(buf); |
308 | 0 | buf+=4; |
309 | 0 | } |
310 | | // Skips auth data |
311 | 0 | buf += auth_len; |
312 | 0 | if (buf > end) |
313 | 0 | return VLC_EGENERIC; |
314 | | |
315 | 0 | uint8_t *decomp = NULL; |
316 | 0 | if( b_compressed ) |
317 | 0 | { |
318 | 0 | int newsize = Decompress (buf, &decomp, end - buf); |
319 | 0 | if (newsize < 0) |
320 | 0 | { |
321 | 0 | msg_Dbg( p_sd, "decompression of SAP packet failed" ); |
322 | 0 | return VLC_EGENERIC; |
323 | 0 | } |
324 | | |
325 | 0 | decomp = xrealloc (decomp, newsize + 1); |
326 | 0 | decomp[newsize] = '\0'; |
327 | 0 | psz_sdp = (const char *)decomp; |
328 | 0 | len = newsize; |
329 | 0 | } |
330 | 0 | else |
331 | 0 | { |
332 | 0 | psz_sdp = (const char *)buf; |
333 | 0 | len = end - buf; |
334 | 0 | } |
335 | | |
336 | | /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */ |
337 | 0 | assert( psz_sdp[len] == '\0'); |
338 | | |
339 | | /* Skip payload type */ |
340 | | /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */ |
341 | 0 | if (strncmp (psz_sdp, "v=0", 3)) |
342 | 0 | { |
343 | 0 | size_t clen = strlen (psz_sdp) + 1; |
344 | |
|
345 | 0 | if (strcmp (psz_sdp, "application/sdp")) |
346 | 0 | { |
347 | 0 | msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp); |
348 | 0 | goto error; |
349 | 0 | } |
350 | | |
351 | | // skips content type |
352 | 0 | if (len <= clen) |
353 | 0 | goto error; |
354 | | |
355 | 0 | len -= clen; |
356 | 0 | psz_sdp += clen; |
357 | 0 | } |
358 | | |
359 | 0 | for( int i = 0 ; i < p_sys->i_announces ; i++ ) |
360 | 0 | { |
361 | 0 | sap_announce_t * p_announce = p_sys->pp_announces[i]; |
362 | | /* FIXME: slow */ |
363 | 0 | if (p_announce->i_hash == i_hash |
364 | 0 | && memcmp(p_announce->i_source, i_source, sizeof (i_source)) == 0) |
365 | 0 | { |
366 | | /* We don't support delete announcement as they can easily |
367 | | * Be used to hijack an announcement by a third party. |
368 | | * Instead we cleverly implement Implicit Announcement removal. |
369 | | * |
370 | | * if( b_need_delete ) |
371 | | * RemoveAnnounce( p_sd, p_sys->pp_announces[i]); |
372 | | * else |
373 | | */ |
374 | |
|
375 | 0 | if( !b_need_delete ) |
376 | 0 | { |
377 | | /* No need to go after six, as we start to trust the |
378 | | * average period at six */ |
379 | 0 | if( p_announce->i_period_trust <= 5 ) |
380 | 0 | p_announce->i_period_trust++; |
381 | | |
382 | | /* Compute the average period */ |
383 | 0 | vlc_tick_t now = vlc_tick_now(); |
384 | 0 | p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust; |
385 | 0 | p_announce->i_last = now; |
386 | 0 | } |
387 | 0 | free (decomp); |
388 | 0 | return VLC_SUCCESS; |
389 | 0 | } |
390 | 0 | } |
391 | | |
392 | 0 | sap_announce_t *sap = CreateAnnounce(p_sd, i_source, i_hash, psz_sdp); |
393 | 0 | if (sap != NULL) |
394 | 0 | TAB_APPEND(p_sys->i_announces, p_sys->pp_announces, sap); |
395 | | |
396 | 0 | free (decomp); |
397 | 0 | return VLC_SUCCESS; |
398 | 0 | error: |
399 | 0 | free (decomp); |
400 | 0 | return VLC_EGENERIC; |
401 | 0 | } |
402 | | |
403 | | /***************************************************************************** |
404 | | * Run: main SAP thread |
405 | | ***************************************************************************** |
406 | | * Listens to SAP packets, and sends them to packet_handle |
407 | | *****************************************************************************/ |
408 | 0 | #define MAX_SAP_BUFFER 5000 |
409 | | |
410 | | static void *Run( void *data ) |
411 | 0 | { |
412 | 0 | vlc_thread_set_name("vlc-sap"); |
413 | |
|
414 | 0 | services_discovery_t *p_sd = data; |
415 | 0 | services_discovery_sys_t *p_sys = p_sd->p_sys; |
416 | 0 | char *psz_addr; |
417 | 0 | int timeout = -1; |
418 | 0 | int canc = vlc_savecancel (); |
419 | | |
420 | | /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed |
421 | | * DNS queries, even if the DNS server returns an error with milliseconds. |
422 | | * You don't want to know why the bug (as of XP SP2) wasn't fixed since |
423 | | * Winsock 1.1 from Windows 95, if not Windows 3.1. |
424 | | * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation, |
425 | | * we have to open sockets in Run() rather than Open(). */ |
426 | 0 | InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT ); |
427 | 0 | InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT ); |
428 | 0 | InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT ); |
429 | 0 | InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT ); |
430 | |
|
431 | 0 | char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%"; |
432 | 0 | #ifndef _WIN32 |
433 | 0 | struct if_nameindex *l = if_nameindex (); |
434 | 0 | if (l != NULL) |
435 | 0 | { |
436 | 0 | char *ptr = strchr (psz_address, '%') + 1; |
437 | 0 | for (unsigned i = 0; l[i].if_index; i++) |
438 | 0 | { |
439 | 0 | strcpy (ptr, l[i].if_name); |
440 | 0 | InitSocket (p_sd, psz_address, SAP_PORT); |
441 | 0 | } |
442 | 0 | if_freenameindex (l); |
443 | 0 | } |
444 | | #else |
445 | | /* this is the Winsock2 equivalent of SIOCGIFCONF on BSD stacks, |
446 | | which if_nameindex uses internally anyway */ |
447 | | |
448 | | // first create a dummy socket to pin down the protocol family |
449 | | SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
450 | | if( s != INVALID_SOCKET ) |
451 | | { |
452 | | INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces |
453 | | DWORD len = sizeof(ifaces); |
454 | | |
455 | | if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) ) |
456 | | { |
457 | | unsigned ifcount = len/sizeof(INTERFACE_INFO); |
458 | | char *ptr = strchr (psz_address, '%') + 1; |
459 | | for(unsigned i = 1; i<=ifcount; ++i ) |
460 | | { |
461 | | // append link-local zone identifier |
462 | | sprintf(ptr, "%d", i); |
463 | | } |
464 | | } |
465 | | closesocket(s); |
466 | | } |
467 | | #endif |
468 | 0 | *strchr (psz_address, '%') = '\0'; |
469 | |
|
470 | 0 | static const char ipv6_scopes[] = "1456789ABCDE"; |
471 | 0 | for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++) |
472 | 0 | { |
473 | 0 | psz_address[3] = *c_scope; |
474 | 0 | InitSocket( p_sd, psz_address, SAP_PORT ); |
475 | 0 | } |
476 | |
|
477 | 0 | psz_addr = var_CreateGetString( p_sd, "sap-addr" ); |
478 | 0 | if( psz_addr && *psz_addr ) |
479 | 0 | InitSocket( p_sd, psz_addr, SAP_PORT ); |
480 | 0 | free( psz_addr ); |
481 | |
|
482 | 0 | if( p_sys->i_fd == 0 ) |
483 | 0 | { |
484 | 0 | msg_Err( p_sd, "unable to listen on any address" ); |
485 | 0 | return NULL; |
486 | 0 | } |
487 | | |
488 | | /* read SAP packets */ |
489 | 0 | for (;;) |
490 | 0 | { |
491 | 0 | vlc_restorecancel (canc); |
492 | 0 | unsigned n = p_sys->i_fd; |
493 | 0 | struct pollfd ufd[n]; |
494 | |
|
495 | 0 | for (unsigned i = 0; i < n; i++) |
496 | 0 | { |
497 | 0 | ufd[i].fd = p_sys->pi_fd[i]; |
498 | 0 | ufd[i].events = POLLIN; |
499 | 0 | ufd[i].revents = 0; |
500 | 0 | } |
501 | |
|
502 | 0 | int val = poll (ufd, n, timeout); |
503 | 0 | canc = vlc_savecancel (); |
504 | 0 | if (val > 0) |
505 | 0 | { |
506 | 0 | for (unsigned i = 0; i < n; i++) |
507 | 0 | { |
508 | 0 | if (ufd[i].revents) |
509 | 0 | { |
510 | 0 | uint8_t p_buffer[MAX_SAP_BUFFER+1]; |
511 | 0 | ssize_t i_read; |
512 | |
|
513 | 0 | i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0); |
514 | 0 | if (i_read < 0) |
515 | 0 | msg_Warn (p_sd, "receive error: %s", |
516 | 0 | vlc_strerror_c(errno)); |
517 | 0 | if (i_read > 6) |
518 | 0 | { |
519 | | /* Parse the packet */ |
520 | 0 | p_buffer[i_read] = '\0'; |
521 | 0 | ParseSAP (p_sd, p_buffer, i_read); |
522 | 0 | } |
523 | 0 | } |
524 | 0 | } |
525 | 0 | } |
526 | |
|
527 | 0 | vlc_tick_t now = vlc_tick_now(); |
528 | | |
529 | | /* A 1 hour timeout correspond to the RFC Implicit timeout. |
530 | | * This timeout is tuned in the following loop. */ |
531 | 0 | timeout = 1000 * 60 * 60; |
532 | | |
533 | | /* Check for items that need deletion */ |
534 | 0 | for( int i = 0; i < p_sys->i_announces; i++ ) |
535 | 0 | { |
536 | 0 | sap_announce_t * p_announce = p_sys->pp_announces[i]; |
537 | 0 | vlc_tick_t i_last_period = now - p_announce->i_last; |
538 | | |
539 | | /* Remove the announcement, if the last announcement was 1 hour ago |
540 | | * or if the last packet emitted was 10 times the average time |
541 | | * between two packets */ |
542 | 0 | if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) || |
543 | 0 | i_last_period > p_sys->i_timeout ) |
544 | 0 | { |
545 | 0 | RemoveAnnounce( p_sd, p_announce ); |
546 | 0 | } |
547 | 0 | else |
548 | 0 | { |
549 | | /* Compute next timeout */ |
550 | 0 | if( p_announce->i_period_trust > 5 ) |
551 | 0 | timeout = __MIN(MS_FROM_VLC_TICK(10 * p_announce->i_period - i_last_period), timeout); |
552 | 0 | timeout = __MIN(MS_FROM_VLC_TICK(p_sys->i_timeout - i_last_period), timeout); |
553 | 0 | } |
554 | 0 | } |
555 | |
|
556 | 0 | if( !p_sys->i_announces ) |
557 | 0 | timeout = -1; /* We can safely poll indefinitely. */ |
558 | 0 | else if( timeout < 200 ) |
559 | 0 | timeout = 200; /* Don't wakeup too fast. */ |
560 | 0 | } |
561 | 0 | vlc_assert_unreachable (); |
562 | 0 | } |
563 | | |
564 | | /***************************************************************************** |
565 | | * Open: initialize and create stuff |
566 | | *****************************************************************************/ |
567 | | static int Open( vlc_object_t *p_this ) |
568 | 0 | { |
569 | 0 | services_discovery_t *p_sd = ( services_discovery_t* )p_this; |
570 | 0 | services_discovery_sys_t *p_sys = (services_discovery_sys_t *) |
571 | 0 | malloc( sizeof( services_discovery_sys_t ) ); |
572 | 0 | if( !p_sys ) |
573 | 0 | return VLC_ENOMEM; |
574 | | |
575 | 0 | p_sys->i_timeout = vlc_tick_from_sec(var_CreateGetInteger( p_sd, "sap-timeout" )); |
576 | |
|
577 | 0 | p_sd->p_sys = p_sys; |
578 | 0 | p_sd->description = _("Network streams (SAP)"); |
579 | |
|
580 | 0 | p_sys->pi_fd = NULL; |
581 | 0 | p_sys->i_fd = 0; |
582 | |
|
583 | 0 | p_sys->i_announces = 0; |
584 | 0 | p_sys->pp_announces = NULL; |
585 | | /* TODO: create sockets here, and fix racy sockets table */ |
586 | 0 | if (vlc_clone (&p_sys->thread, Run, p_sd)) |
587 | 0 | { |
588 | 0 | free (p_sys); |
589 | 0 | return VLC_EGENERIC; |
590 | 0 | } |
591 | | |
592 | 0 | return VLC_SUCCESS; |
593 | 0 | } |
594 | | |
595 | | /***************************************************************************** |
596 | | * Close: |
597 | | *****************************************************************************/ |
598 | | static void Close( vlc_object_t *p_this ) |
599 | 0 | { |
600 | 0 | services_discovery_t *p_sd = ( services_discovery_t* )p_this; |
601 | 0 | services_discovery_sys_t *p_sys = p_sd->p_sys; |
602 | 0 | int i; |
603 | |
|
604 | 0 | vlc_cancel (p_sys->thread); |
605 | 0 | vlc_join (p_sys->thread, NULL); |
606 | |
|
607 | 0 | for( i = p_sys->i_fd-1 ; i >= 0 ; i-- ) |
608 | 0 | { |
609 | 0 | net_Close( p_sys->pi_fd[i] ); |
610 | 0 | } |
611 | 0 | FREENULL( p_sys->pi_fd ); |
612 | |
|
613 | 0 | for( i = p_sys->i_announces - 1; i>= 0; i-- ) |
614 | 0 | { |
615 | 0 | RemoveAnnounce( p_sd, p_sys->pp_announces[i] ); |
616 | 0 | } |
617 | 0 | FREENULL( p_sys->pp_announces ); |
618 | |
|
619 | 0 | free( p_sys ); |
620 | 0 | } |
621 | | |
622 | | /***************************************************************************** |
623 | | * Module descriptor |
624 | | *****************************************************************************/ |
625 | | #define SAP_ADDR_TEXT N_( "SAP multicast address" ) |
626 | | #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \ |
627 | | "right addresses to listen to. However, you " \ |
628 | | "can specify a specific address." ) |
629 | | #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" ) |
630 | | #define SAP_TIMEOUT_LONGTEXT N_( \ |
631 | | "Delay after which SAP items get deleted if no new announcement " \ |
632 | | "is received." ) |
633 | | #define SAP_PARSE_TEXT N_( "Try to parse the announce" ) |
634 | | #define SAP_PARSE_LONGTEXT N_( \ |
635 | | "This enables actual parsing of the announces by the SAP module. " \ |
636 | | "Otherwise, all announcements are parsed by the \"live555\" " \ |
637 | | "(RTP/RTSP) module." ) |
638 | | |
639 | | VLC_SD_PROBE_HELPER("sap", N_("Network streams (SAP)"), SD_CAT_LAN) |
640 | | |
641 | 4 | vlc_module_begin() |
642 | 2 | set_shortname(N_("SAP")) |
643 | 2 | set_description(N_("Network streams (SAP)") ) |
644 | 2 | set_subcategory(SUBCAT_PLAYLIST_SD) |
645 | | |
646 | 2 | add_string("sap-addr", NULL, SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT) |
647 | 2 | add_integer("sap-timeout", 1800, |
648 | 2 | SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT) |
649 | | add_obsolete_bool("sap-parse") /* since 4.0.0 */ |
650 | | add_obsolete_bool("sap-strict") /* since 4.0.0 */ |
651 | | |
652 | 2 | set_capability("services_discovery", 0) |
653 | 4 | set_callbacks(Open, Close) |
654 | | |
655 | 2 | VLC_SD_PROBE_SUBMODULE |
656 | 2 | vlc_module_end() |