sap.c 19.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * sap.c :  SAP interface module
 *****************************************************************************
4
 * Copyright (C) 2004-2005 the VideoLAN team
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
5
 * Copyright © 2007 Rémi Denis-Courmont
6
 *
7
 * Authors: Clément Stenac <zorglub@videolan.org>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
8
 *          Rémi Denis-Courmont
zorglub's avatar
zorglub committed
9
 *
10 11 12 13
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
15 16 17 18 19 20 21
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25
 *****************************************************************************/

/*****************************************************************************
zorglub's avatar
zorglub committed
26
 * Includes
27
 *****************************************************************************/
28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

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

zorglub's avatar
zorglub committed
37
#include <vlc_demux.h>
38
#include <vlc_services_discovery.h>
39

zorglub's avatar
zorglub committed
40 41
#include <vlc_network.h>
#include <vlc_charset.h>
zorglub's avatar
zorglub committed
42

43
#include <errno.h>
44
#include <unistd.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
45 46 47
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
48
#ifdef HAVE_POLL_H
49 50
# include <poll.h>
#endif
51

sigmunau's avatar
sigmunau committed
52 53 54
#ifdef HAVE_ZLIB_H
#   include <zlib.h>
#endif
55

56
#ifdef HAVE_NET_IF_H
57 58
#   include <net/if.h>
#endif
sigmunau's avatar
sigmunau committed
59

60 61
#include "access/rtp/sdp.h"

zorglub's avatar
zorglub committed
62 63 64 65
/************************************************************************
 * Macros and definitions
 ************************************************************************/

66 67
#define MAX_LINE_LENGTH 256

68
/* SAP is always on that port */
zorglub's avatar
zorglub committed
69
#define SAP_PORT 9875
70 71 72 73 74 75 76 77
/* Global-scope SAP address */
#define SAP_V4_GLOBAL_ADDRESS   "224.2.127.254"
/* Organization-local SAP address */
#define SAP_V4_ORG_ADDRESS      "239.195.255.255"
/* Local (smallest non-link-local scope) SAP address */
#define SAP_V4_LOCAL_ADDRESS    "239.255.255.255"
/* Link-local SAP address */
#define SAP_V4_LINK_ADDRESS     "224.0.0.255"
78
#define ADD_SESSION 1
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len )
{
#ifdef HAVE_ZLIB_H
    int i_result, i_dstsize, n = 0;
    unsigned char *psz_dst = NULL;
    z_stream d_stream;

    memset (&d_stream, 0, sizeof (d_stream));

    i_result = inflateInit(&d_stream);
    if( i_result != Z_OK )
        return( -1 );

    d_stream.next_in = (Bytef *)psz_src;
    d_stream.avail_in = i_len;

    do
97
    {
98 99 100 101 102 103 104 105 106 107 108 109
        n++;
        psz_dst = xrealloc( psz_dst, n * 1000 );
        d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
        d_stream.avail_out = 1000;

        i_result = inflate(&d_stream, Z_NO_FLUSH);
        if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
        {
            inflateEnd( &d_stream );
            free( psz_dst );
            return( -1 );
        }
110
    }
111 112
    while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
           ( i_result != Z_STREAM_END ) );
zorglub's avatar
zorglub committed
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    i_dstsize = d_stream.total_out;
    inflateEnd( &d_stream );

    *_dst = xrealloc( psz_dst, i_dstsize );

    return i_dstsize;
#else
    (void)psz_src;
    (void)_dst;
    (void)i_len;
    return -1;
#endif
}

typedef struct sap_announce_t
{
    vlc_tick_t i_last;
    vlc_tick_t i_period;
    uint8_t i_period_trust;

    uint16_t    i_hash;
    uint32_t    i_source[4];

    input_item_t * p_item;
} sap_announce_t;

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)
zorglub's avatar
zorglub committed
143
{
144
    /* Parse SDP info */
145
    struct vlc_sdp *p_sdp = vlc_sdp_parse(psz_sdp, strlen(psz_sdp));
146 147 148 149 150
    if (p_sdp == NULL)
        return NULL;

    char *uri = NULL;

151
    if (asprintf(&uri, "sdp://%s", psz_sdp) == -1)
152
    {
153
        vlc_sdp_free(p_sdp);
154 155 156
        return NULL;
    }

157
    input_item_t *p_input;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
158
    const char *psz_value;
159
    sap_announce_t *p_sap = malloc(sizeof (*p_sap));
160
    if( p_sap == NULL )
zorglub's avatar
zorglub committed
161
        return NULL;
162

163
    p_sap->i_last = vlc_tick_now();
164 165
    p_sap->i_period = 0;
    p_sap->i_period_trust = 0;
zorglub's avatar
zorglub committed
166
    p_sap->i_hash = i_hash;
167
    memcpy (p_sap->i_source, i_source, sizeof(p_sap->i_source));
168

169
    /* Released in RemoveAnnounce */
170
    p_input = input_item_NewStream(uri, p_sdp->name,
171
                                   INPUT_DURATION_INDEFINITE);
172
    if( unlikely(p_input == NULL) )
zorglub's avatar
zorglub committed
173
    {
174
        free( p_sap );
zorglub's avatar
zorglub committed
175
        return NULL;
176
    }
177 178 179 180 181
    p_sap->p_item = p_input;

    vlc_meta_t *p_meta = vlc_meta_New();
    if( likely(p_meta != NULL) )
    {
182
        vlc_meta_Set(p_meta, vlc_meta_Description, p_sdp->info);
183 184
        p_input->p_meta = p_meta;
    }
185

186
    psz_value = vlc_sdp_attr_value(p_sdp, "tool");
zorglub's avatar
zorglub committed
187 188
    if( psz_value != NULL )
    {
189
        input_item_AddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value );
zorglub's avatar
zorglub committed
190 191
    }

192
    /* Handle category */
193
    psz_value = vlc_sdp_attr_value(p_sdp, "cat");
194 195 196 197 198 199 200 201
    if (psz_value != NULL)
    {
        /* a=cat provides a dot-separated hierarchy.
         * For the time being only replace dots with pipe. TODO: FIXME */
        char *str = strdup(psz_value);
        if (likely(str != NULL))
            for (char *p = strchr(str, '.'); p != NULL; p = strchr(p, '.'))
                *(p++) = '|';
202
        services_discovery_AddItemCat(p_sd, p_input, str ? str : psz_value);
203 204
        free(str);
    }
205
    else
206 207
    {
        /* backward compatibility with VLC 0.7.3-2.0.0 senders */
208
        psz_value = vlc_sdp_attr_value(p_sdp, "x-plgroup");
209
        services_discovery_AddItemCat(p_sd, p_input, psz_value);
210
    }
zorglub's avatar
zorglub committed
211

212
    vlc_sdp_free(p_sdp);
zorglub's avatar
zorglub committed
213 214
    return p_sap;
}
Laurent Aimar's avatar
Laurent Aimar committed
215

216
typedef struct
zorglub's avatar
zorglub committed
217
{
218
    vlc_thread_t thread;
219

220 221 222
    /* Socket descriptors */
    int i_fd;
    int *pi_fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223

224 225 226
    /* Table of announces */
    int i_announces;
    struct sap_announce_t **pp_announces;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
227

228 229
    vlc_tick_t i_timeout;
} services_discovery_sys_t;
230

231 232 233 234
static int RemoveAnnounce( services_discovery_t *p_sd,
                           sap_announce_t *p_announce )
{
    if( p_announce->p_item )
235
    {
236 237 238
        services_discovery_RemoveItem( p_sd, p_announce->p_item );
        input_item_Release( p_announce->p_item );
        p_announce->p_item = NULL;
239
    }
zorglub's avatar
zorglub committed
240

241 242 243
    services_discovery_sys_t *p_sys = p_sd->p_sys;
    TAB_REMOVE(p_sys->i_announces, p_sys->pp_announces, p_announce);
    free( p_announce );
244

245 246
    return VLC_SUCCESS;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
247

248 249
static int InitSocket( services_discovery_t *p_sd, const char *psz_address,
                       int i_port )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
250
{
251 252 253
    int i_fd = net_ListenUDP1 ((vlc_object_t *)p_sd, psz_address, i_port);
    if (i_fd == -1)
        return VLC_EGENERIC;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254

255 256 257 258 259
    shutdown( i_fd, SHUT_WR );
    services_discovery_sys_t *p_sys = p_sd->p_sys;
    TAB_APPEND(p_sys->i_fd, p_sys->pi_fd, i_fd);
    return VLC_SUCCESS;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
260

261 262 263 264 265 266 267 268
/* i_read is at least > 6 */
static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
                     size_t len )
{
    services_discovery_sys_t *p_sys = p_sd->p_sys;
    const char          *psz_sdp;
    const uint8_t *end = buf + len;
    uint32_t            i_source[4];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
269

270
    assert (buf[len] == '\0');
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
271

272 273
    if (len < 4)
        return VLC_EGENERIC;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
274

275 276
    uint8_t flags = buf[0];
    uint8_t auth_len = buf[1];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
277

278 279 280
    /* First, check the sap announce is correct */
    if ((flags >> 5) != 1)
        return VLC_EGENERIC;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
281

282 283
    bool b_ipv6 = (flags & 0x10) != 0;
    bool b_need_delete = (flags & 0x04) != 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
284

285
    if (flags & 0x02)
286
    {
287 288
        msg_Dbg( p_sd, "encrypted packet, unsupported" );
        return VLC_EGENERIC;
289
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
290

291
    bool b_compressed = (flags & 0x01) != 0;
292

293
    uint16_t i_hash = U16_AT (buf + 2);
294

295 296
    if( i_hash == 0 )
        return VLC_EGENERIC;
297

298 299
    buf += 4;
    if( b_ipv6 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
300
    {
301 302 303 304 305 306 307 308 309 310 311 312 313
        for( int i = 0; i < 4; i++,buf+=4)
            i_source[i] = U32_AT(buf);
    }
    else
    {
        memset(i_source, 0, sizeof(i_source));
        i_source[3] = U32_AT(buf);
        buf+=4;
    }
    // Skips auth data
    buf += auth_len;
    if (buf > end)
        return VLC_EGENERIC;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
314

315 316 317 318 319
    uint8_t *decomp = NULL;
    if( b_compressed )
    {
        int newsize = Decompress (buf, &decomp, end - buf);
        if (newsize < 0)
320
        {
321 322
            msg_Dbg( p_sd, "decompression of SAP packet failed" );
            return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
323
        }
zorglub's avatar
zorglub committed
324

325 326 327 328 329 330 331 332 333 334
        decomp = xrealloc (decomp, newsize + 1);
        decomp[newsize] = '\0';
        psz_sdp = (const char *)decomp;
        len = newsize;
    }
    else
    {
        psz_sdp = (const char *)buf;
        len = end - buf;
    }
Laurent Aimar's avatar
Laurent Aimar committed
335

336 337
    /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
    assert( psz_sdp[len] == '\0');
Laurent Aimar's avatar
Laurent Aimar committed
338

339 340 341 342 343 344 345
    /* Skip payload type */
    /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
    if (strncmp (psz_sdp, "v=0", 3))
    {
        size_t clen = strlen (psz_sdp) + 1;

        if (strcmp (psz_sdp, "application/sdp"))
Laurent Aimar's avatar
Laurent Aimar committed
346
        {
347 348 349
            msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp);
            goto error;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
350

351 352 353
        // skips content type
        if (len <= clen)
            goto error;
354

355 356 357
        len -= clen;
        psz_sdp += clen;
    }
358

359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
    for( int i = 0 ; i < p_sys->i_announces ; i++ )
    {
        sap_announce_t * p_announce = p_sys->pp_announces[i];
        /* FIXME: slow */
        if (p_announce->i_hash == i_hash
         && memcmp(p_announce->i_source, i_source, sizeof (i_source)) == 0)
        {
            /* We don't support delete announcement as they can easily
             * Be used to highjack an announcement by a third party.
             * Instead we cleverly implement Implicit Announcement removal.
             *
             * if( b_need_delete )
             *    RemoveAnnounce( p_sd, p_sys->pp_announces[i]);
             * else
             */

            if( !b_need_delete )
zorglub's avatar
zorglub committed
376
            {
377 378 379 380
                /* No need to go after six, as we start to trust the
                 * average period at six */
                if( p_announce->i_period_trust <= 5 )
                    p_announce->i_period_trust++;
381

382 383 384 385 386 387 388 389 390
                /* Compute the average period */
                vlc_tick_t now = vlc_tick_now();
                p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust;
                p_announce->i_last = now;
            }
            free (decomp);
            return VLC_SUCCESS;
        }
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
391

392
    sap_announce_t *sap = CreateAnnounce(p_sd, i_source, i_hash, psz_sdp);
393 394
    if (sap != NULL)
        TAB_APPEND(p_sys->i_announces, p_sys->pp_announces, sap);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
395

396 397 398 399 400 401
    free (decomp);
    return VLC_SUCCESS;
error:
    free (decomp);
    return VLC_EGENERIC;
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
402

403 404 405 406 407 408
/*****************************************************************************
 * Run: main SAP thread
 *****************************************************************************
 * Listens to SAP packets, and sends them to packet_handle
 *****************************************************************************/
#define MAX_SAP_BUFFER 5000
409

410 411 412 413 414 415 416
static void *Run( void *data )
{
    services_discovery_t *p_sd = data;
    services_discovery_sys_t *p_sys = p_sd->p_sys;
    char *psz_addr;
    int timeout = -1;
    int canc = vlc_savecancel ();
417

418 419 420 421 422 423 424 425 426 427
    /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
     * DNS queries, even if the DNS server returns an error with milliseconds.
     * You don't want to know why the bug (as of XP SP2) wasn't fixed since
     * Winsock 1.1 from Windows 95, if not Windows 3.1.
     * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
     * we have to open sockets in Run() rather than Open(). */
    InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
    InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
    InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
    InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
428

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%";
#ifndef _WIN32
    struct if_nameindex *l = if_nameindex ();
    if (l != NULL)
    {
        char *ptr = strchr (psz_address, '%') + 1;
        for (unsigned i = 0; l[i].if_index; i++)
        {
            strcpy (ptr, l[i].if_name);
            InitSocket (p_sd, psz_address, SAP_PORT);
        }
        if_freenameindex (l);
    }
#else
        /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
           which if_nameindex uses internally anyway */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
445

446 447 448 449 450 451
        // first create a dummy socket to pin down the protocol family
        SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
        if( s != INVALID_SOCKET )
        {
            INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces
            DWORD len = sizeof(ifaces);
452

453 454 455 456 457 458 459 460 461 462 463
            if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) )
            {
                unsigned ifcount = len/sizeof(INTERFACE_INFO);
                char *ptr = strchr (psz_address, '%') + 1;
                for(unsigned i = 1; i<=ifcount; ++i )
                {
                    // append link-local zone identifier
                    sprintf(ptr, "%d", i);
                }
            }
            closesocket(s);
Laurent Aimar's avatar
Laurent Aimar committed
464
        }
465 466
#endif
    *strchr (psz_address, '%') = '\0';
467

468 469 470 471 472 473
    static const char ipv6_scopes[] = "1456789ABCDE";
    for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++)
    {
        psz_address[3] = *c_scope;
        InitSocket( p_sd, psz_address, SAP_PORT );
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
474

475 476 477 478
    psz_addr = var_CreateGetString( p_sd, "sap-addr" );
    if( psz_addr && *psz_addr )
        InitSocket( p_sd, psz_addr, SAP_PORT );
    free( psz_addr );
479

480 481 482 483 484
    if( p_sys->i_fd == 0 )
    {
        msg_Err( p_sd, "unable to listen on any address" );
        return NULL;
    }
zorglub's avatar
zorglub committed
485

486 487 488 489 490 491
    /* read SAP packets */
    for (;;)
    {
        vlc_restorecancel (canc);
        unsigned n = p_sys->i_fd;
        struct pollfd ufd[n];
492

493 494 495 496 497 498
        for (unsigned i = 0; i < n; i++)
        {
            ufd[i].fd = p_sys->pi_fd[i];
            ufd[i].events = POLLIN;
            ufd[i].revents = 0;
        }
499

500 501 502 503 504 505 506 507 508 509
        int val = poll (ufd, n, timeout);
        canc = vlc_savecancel ();
        if (val > 0)
        {
            for (unsigned i = 0; i < n; i++)
            {
                if (ufd[i].revents)
                {
                    uint8_t p_buffer[MAX_SAP_BUFFER+1];
                    ssize_t i_read;
510

511 512 513 514 515 516 517 518 519 520 521 522 523
                    i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0);
                    if (i_read < 0)
                        msg_Warn (p_sd, "receive error: %s",
                                  vlc_strerror_c(errno));
                    if (i_read > 6)
                    {
                        /* Parse the packet */
                        p_buffer[i_read] = '\0';
                        ParseSAP (p_sd, p_buffer, i_read);
                    }
                }
            }
        }
524

525
        vlc_tick_t now = vlc_tick_now();
526

527 528 529
        /* A 1 hour timeout correspond to the RFC Implicit timeout.
         * This timeout is tuned in the following loop. */
        timeout = 1000 * 60 * 60;
530

531 532
        /* Check for items that need deletion */
        for( int i = 0; i < p_sys->i_announces; i++ )
533
        {
534 535
            sap_announce_t * p_announce = p_sys->pp_announces[i];
            vlc_tick_t i_last_period = now - p_announce->i_last;
536

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
            /* Remove the announcement, if the last announcement was 1 hour ago
             * or if the last packet emitted was 10 times the average time
             * between two packets */
            if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) ||
                i_last_period > p_sys->i_timeout )
            {
                RemoveAnnounce( p_sd, p_announce );
            }
            else
            {
                /* Compute next timeout */
                if( p_announce->i_period_trust > 5 )
                    timeout = __MIN(MS_FROM_VLC_TICK(10 * p_announce->i_period - i_last_period), timeout);
                timeout = __MIN(MS_FROM_VLC_TICK(p_sys->i_timeout - i_last_period), timeout);
            }
        }
553

554 555 556 557 558 559
        if( !p_sys->i_announces )
            timeout = -1; /* We can safely poll indefinitely. */
        else if( timeout < 200 )
            timeout = 200; /* Don't wakeup too fast. */
    }
    vlc_assert_unreachable ();
560
}
561

562 563 564 565
/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
zorglub's avatar
zorglub committed
566
{
567 568 569 570 571
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
                                malloc( sizeof( services_discovery_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
zorglub's avatar
zorglub committed
572

573
    p_sys->i_timeout = vlc_tick_from_sec(var_CreateGetInteger( p_sd, "sap-timeout" ));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
574

575 576
    p_sd->p_sys  = p_sys;
    p_sd->description = _("Network streams (SAP)");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
577

578 579
    p_sys->pi_fd = NULL;
    p_sys->i_fd = 0;
zorglub's avatar
zorglub committed
580

581 582 583 584
    p_sys->i_announces = 0;
    p_sys->pp_announces = NULL;
    /* TODO: create sockets here, and fix racy sockets table */
    if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW))
585
    {
586 587
        free (p_sys);
        return VLC_EGENERIC;
588
    }
zorglub's avatar
zorglub committed
589

zorglub's avatar
zorglub committed
590 591
    return VLC_SUCCESS;
}
592

593 594 595 596
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
597
{
598 599 600
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
    int i;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
601

602 603 604 605
    vlc_cancel (p_sys->thread);
    vlc_join (p_sys->thread, NULL);

    for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
606
    {
607
        net_Close( p_sys->pi_fd[i] );
608
    }
609
    FREENULL( p_sys->pi_fd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
610

611 612 613 614 615
    for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
    {
        RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
    }
    FREENULL( p_sys->pp_announces );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
616

617
    free( p_sys );
618
}
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define SAP_ADDR_TEXT N_( "SAP multicast address" )
#define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \
                              "right addresses to listen to. However, you " \
                              "can specify a specific address." )
#define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
#define SAP_TIMEOUT_LONGTEXT N_( \
       "Delay after which SAP items get deleted if no new announcement " \
       "is received." )
#define SAP_PARSE_TEXT N_( "Try to parse the announce" )
#define SAP_PARSE_LONGTEXT N_( \
       "This enables actual parsing of the announces by the SAP module. " \
       "Otherwise, all announcements are parsed by the \"live555\" " \
       "(RTP/RTSP) module." )

VLC_SD_PROBE_HELPER("sap", N_("Network streams (SAP)"), SD_CAT_LAN)

vlc_module_begin()
    set_shortname(N_("SAP"))
    set_description(N_("Network streams (SAP)") )
    set_category(CAT_PLAYLIST)
    set_subcategory(SUBCAT_PLAYLIST_SD)

    add_string("sap-addr", NULL, SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, true)
    add_obsolete_bool("sap-ipv4") /* since 2.0.0 */
    add_obsolete_bool("sap-ipv6") /* since 2.0.0 */
    add_integer("sap-timeout", 1800,
                SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, true)
650
    add_obsolete_bool("sap-parse") /* since 4.0.0 */
651
    add_obsolete_bool("sap-strict") /* since 4.0.0 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
652
    add_obsolete_bool("sap-timeshift") /* Redundant since 1.0.0 */
653 654 655 656 657 658

    set_capability("services_discovery", 0)
    set_callbacks(Open, Close)

    VLC_SD_PROBE_SUBMODULE
vlc_module_end()