udp.c 23.9 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1 2
/*
 * UDP prototype streaming system
3
 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
4
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
11
 *
12
 * Libav is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
Fabrice Bellard's avatar
Fabrice Bellard committed
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard's avatar
Fabrice Bellard committed
20
 */
21 22

/**
23
 * @file
24 25 26
 * UDP protocol
 */

27
#define _BSD_SOURCE     /* Needed for using struct ip_mreq with recent glibc */
28

Fabrice Bellard's avatar
Fabrice Bellard committed
29
#include "avformat.h"
30
#include "avio_internal.h"
31
#include "libavutil/parseutils.h"
32
#include "libavutil/avstring.h"
Luca Barbato's avatar
Luca Barbato committed
33
#include "libavutil/opt.h"
34
#include "internal.h"
35
#include "network.h"
36
#include "os_support.h"
37
#include "url.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
38

39 40 41 42 43
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif

44
typedef struct UDPContext {
45
    const AVClass *class;
46 47
    int udp_fd;
    int ttl;
48
    int buffer_size;
Luca Barbato's avatar
Luca Barbato committed
49
    int pkt_size;
50 51
    int is_multicast;
    int local_port;
52
    int reuse_socket;
53
    struct sockaddr_storage dest_addr;
54
    int dest_addr_len;
55
    int is_connected;
Luca Barbato's avatar
Luca Barbato committed
56 57 58
    char *localaddr;
    char *sources;
    char *block;
Fabrice Bellard's avatar
Fabrice Bellard committed
59 60 61
} UDPContext;

#define UDP_TX_BUF_SIZE 32768
62
#define UDP_MAX_PKT_SIZE 65536
Fabrice Bellard's avatar
Fabrice Bellard committed
63

Luca Barbato's avatar
Luca Barbato committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
#define OFFSET(x) offsetof(UDPContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
    { "ttl",            "Time to live (in milliseconds, multicast only)",  OFFSET(ttl),            AV_OPT_TYPE_INT,    { .i64 = 16 },     0, INT_MAX, .flags = D|E },
    { "buffer_size",    "System data size (in bytes)",                     OFFSET(buffer_size),    AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
    { "local_port",     "Local port",                                      OFFSET(local_port),     AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
    { "reuse_socket",   "Reuse socket",                                    OFFSET(reuse_socket),   AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, 1,       .flags = D|E },
    { "connect",        "Connect socket",                                  OFFSET(is_connected),   AV_OPT_TYPE_INT,    { .i64 =  0 },     0, 1,       .flags = D|E },
    { "pkt_size",       "Maximum packet size",                             OFFSET(pkt_size),       AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
    { "localaddr",      "Local address",                                   OFFSET(localaddr),      AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
    { "sources",        "Source list",                                     OFFSET(sources),        AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
    { "block",          "Block list",                                      OFFSET(block),          AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
    { NULL }
};

static const AVClass udp_class = {
    .class_name = "udp",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

87 88 89 90 91 92 93
static void log_net_error(void *ctx, int level, const char* prefix)
{
    char errbuf[100];
    av_strerror(ff_neterrno(), errbuf, sizeof(errbuf));
    av_log(ctx, level, "%s: %s\n", prefix, errbuf);
}

94 95 96
static int udp_set_multicast_ttl(int sockfd, int mcastTTL,
                                 struct sockaddr *addr)
{
97
#ifdef IP_MULTICAST_TTL
98 99
    if (addr->sa_family == AF_INET) {
        if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &mcastTTL, sizeof(mcastTTL)) < 0) {
100
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_MULTICAST_TTL)");
101 102 103
            return -1;
        }
    }
104
#endif
105
#if defined(IPPROTO_IPV6) && defined(IPV6_MULTICAST_HOPS)
106 107
    if (addr->sa_family == AF_INET6) {
        if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastTTL, sizeof(mcastTTL)) < 0) {
108
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IPV6_MULTICAST_HOPS)");
109 110 111
            return -1;
        }
    }
112
#endif
113 114 115
    return 0;
}

116 117
static int udp_join_multicast_group(int sockfd, struct sockaddr *addr)
{
118
#ifdef IP_ADD_MEMBERSHIP
119
    if (addr->sa_family == AF_INET) {
120 121
        struct ip_mreq mreq;

122 123 124
        mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
        mreq.imr_interface.s_addr= INADDR_ANY;
        if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
125
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_MEMBERSHIP)");
126 127 128
            return -1;
        }
    }
129
#endif
130
#if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
131
    if (addr->sa_family == AF_INET6) {
132 133
        struct ipv6_mreq mreq6;

134 135 136
        memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
        mreq6.ipv6mr_interface= 0;
        if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
137
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP)");
138 139 140
            return -1;
        }
    }
141
#endif
142 143 144
    return 0;
}

145 146
static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr)
{
147
#ifdef IP_DROP_MEMBERSHIP
148
    if (addr->sa_family == AF_INET) {
149 150
        struct ip_mreq mreq;

151 152 153
        mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
        mreq.imr_interface.s_addr= INADDR_ANY;
        if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
154
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_DROP_MEMBERSHIP)");
155 156 157
            return -1;
        }
    }
158
#endif
159
#if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
160
    if (addr->sa_family == AF_INET6) {
161 162
        struct ipv6_mreq mreq6;

163 164 165
        memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
        mreq6.ipv6mr_interface= 0;
        if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
166
            log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IPV6_DROP_MEMBERSHIP)");
167 168 169
            return -1;
        }
    }
170
#endif
171 172 173
    return 0;
}

174 175
static struct addrinfo *udp_resolve_host(URLContext *h,
                                         const char *hostname, int port,
176 177
                                         int type, int family, int flags)
{
178
    struct addrinfo hints = { 0 }, *res = 0;
179 180
    int error;
    char sport[16];
181
    const char *node = 0, *service = "0";
182 183

    if (port > 0) {
Michael Niedermayer's avatar
Michael Niedermayer committed
184
        snprintf(sport, sizeof(sport), "%d", port);
185 186 187 188 189
        service = sport;
    }
    if ((hostname) && (hostname[0] != '\0') && (hostname[0] != '?')) {
        node = hostname;
    }
Luca Abeni's avatar
Luca Abeni committed
190 191 192 193
    hints.ai_socktype = type;
    hints.ai_family   = family;
    hints.ai_flags = flags;
    if ((error = getaddrinfo(node, service, &hints, &res))) {
194
        res = NULL;
195 196 197
        av_log(h, AV_LOG_ERROR, "getaddrinfo(%s, %s): %s\n",
               node ? node : "unknown",
               service ? service : "unknown",
198
               gai_strerror(error));
Luca Abeni's avatar
Luca Abeni committed
199 200
    }

201 202 203
    return res;
}

204 205
static int udp_set_multicast_sources(URLContext *h,
                                     int sockfd, struct sockaddr *addr,
206 207 208 209 210 211 212 213 214 215
                                     int addr_len, char **sources,
                                     int nb_sources, int include)
{
#if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE) && !defined(_WIN32)
    /* These ones are available in the microsoft SDK, but don't seem to work
     * as on linux, so just prefer the v4-only approach there for now. */
    int i;
    for (i = 0; i < nb_sources; i++) {
        struct group_source_req mreqs;
        int level = addr->sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
216
        struct addrinfo *sourceaddr = udp_resolve_host(h, sources[i], 0,
217
                                                       SOCK_DGRAM, AF_UNSPEC,
218
                                                       0);
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
        if (!sourceaddr)
            return AVERROR(ENOENT);

        mreqs.gsr_interface = 0;
        memcpy(&mreqs.gsr_group, addr, addr_len);
        memcpy(&mreqs.gsr_source, sourceaddr->ai_addr, sourceaddr->ai_addrlen);
        freeaddrinfo(sourceaddr);

        if (setsockopt(sockfd, level,
                       include ? MCAST_JOIN_SOURCE_GROUP : MCAST_BLOCK_SOURCE,
                       (const void *)&mreqs, sizeof(mreqs)) < 0) {
            if (include)
                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_JOIN_SOURCE_GROUP)");
            else
                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_BLOCK_SOURCE)");
            return ff_neterrno();
        }
    }
#elif HAVE_STRUCT_IP_MREQ_SOURCE && defined(IP_BLOCK_SOURCE)
    int i;
    if (addr->sa_family != AF_INET) {
        av_log(NULL, AV_LOG_ERROR,
               "Setting multicast sources only supported for IPv4\n");
        return AVERROR(EINVAL);
    }
    for (i = 0; i < nb_sources; i++) {
        struct ip_mreq_source mreqs;
246
        struct addrinfo *sourceaddr = udp_resolve_host(h, sources[i], 0,
247
                                                       SOCK_DGRAM, AF_UNSPEC,
248
                                                       0);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
        if (!sourceaddr)
            return AVERROR(ENOENT);
        if (sourceaddr->ai_addr->sa_family != AF_INET) {
            freeaddrinfo(sourceaddr);
            av_log(NULL, AV_LOG_ERROR, "%s is of incorrect protocol family\n",
                   sources[i]);
            return AVERROR(EINVAL);
        }

        mreqs.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
        mreqs.imr_interface.s_addr = INADDR_ANY;
        mreqs.imr_sourceaddr.s_addr = ((struct sockaddr_in *)sourceaddr->ai_addr)->sin_addr.s_addr;
        freeaddrinfo(sourceaddr);

        if (setsockopt(sockfd, IPPROTO_IP,
                       include ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE,
                       (const void *)&mreqs, sizeof(mreqs)) < 0) {
            if (include)
                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP)");
            else
                log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_BLOCK_SOURCE)");
            return ff_neterrno();
        }
    }
#else
    return AVERROR(ENOSYS);
#endif
    return 0;
}
278 279
static int udp_set_url(URLContext *h,
                       struct sockaddr_storage *addr,
280 281
                       const char *hostname, int port)
{
282
    struct addrinfo *res0;
283 284
    int addr_len;

285
    res0 = udp_resolve_host(h, hostname, port, SOCK_DGRAM, AF_UNSPEC, 0);
286
    if (res0 == 0) return AVERROR(EIO);
287 288
    memcpy(addr, res0->ai_addr, res0->ai_addrlen);
    addr_len = res0->ai_addrlen;
289
    freeaddrinfo(res0);
290 291

    return addr_len;
292 293
}

294
static int udp_socket_create(URLContext *h, struct sockaddr_storage *addr,
295
                             socklen_t *addr_len, const char *localaddr)
296
{
297
    UDPContext *s = h->priv_data;
298
    int udp_fd = -1;
299
    struct addrinfo *res0 = NULL, *res = NULL;
300
    int family = AF_UNSPEC;
301

302 303
    if (((struct sockaddr *) &s->dest_addr)->sa_family)
        family = ((struct sockaddr *) &s->dest_addr)->sa_family;
304 305
    res0 = udp_resolve_host(h, (localaddr && localaddr[0]) ? localaddr : NULL,
                            s->local_port,
306
                            SOCK_DGRAM, family, AI_PASSIVE);
Luca Abeni's avatar
Luca Abeni committed
307 308 309
    if (res0 == 0)
        goto fail;
    for (res = res0; res; res=res->ai_next) {
310
        udp_fd = ff_socket(res->ai_family, SOCK_DGRAM, 0);
311
        if (udp_fd != -1) break;
312
        log_net_error(NULL, AV_LOG_ERROR, "socket");
Luca Abeni's avatar
Luca Abeni committed
313
    }
314 315

    if (udp_fd < 0)
316
        goto fail;
317

318 319
    memcpy(addr, res->ai_addr, res->ai_addrlen);
    *addr_len = res->ai_addrlen;
320

321
    freeaddrinfo(res0);
322

323
    return udp_fd;
324

325 326 327
 fail:
    if (udp_fd >= 0)
        closesocket(udp_fd);
328 329
    if(res0)
        freeaddrinfo(res0);
330 331 332
    return -1;
}

333 334
static int udp_port(struct sockaddr_storage *addr, int addr_len)
{
335
    char sbuf[sizeof(int)*3+1];
336
    int error;
337

338 339
    if ((error = getnameinfo((struct sockaddr *)addr, addr_len, NULL, 0,  sbuf, sizeof(sbuf), NI_NUMERICSERV)) != 0) {
        av_log(NULL, AV_LOG_ERROR, "getnameinfo: %s\n", gai_strerror(error));
340 341 342 343 344 345
        return -1;
    }

    return strtol(sbuf, NULL, 10);
}

346

347 348 349 350 351 352
/**
 * If no filename is given to av_open_input_file because you want to
 * get the local port first, then you must call this function to set
 * the remote server address.
 *
 * url syntax: udp://host:port[?option=val...]
353
 * option: 'ttl=n'       : set the ttl value (for multicast only)
354
 *         'localport=n' : set the local port
355
 *         'pkt_size=n'  : set max packet size
356
 *         'reuse=1'     : enable reusing the socket
357
 *
358
 * @param h media file context
359 360 361
 * @param uri of the remote server
 * @return zero if no error.
 */
362
int ff_udp_set_remote_url(URLContext *h, const char *uri)
363 364
{
    UDPContext *s = h->priv_data;
365
    char hostname[256], buf[10];
366
    int port;
367
    const char *p;
368

369
    av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
Fabrice Bellard's avatar
Fabrice Bellard committed
370

371
    /* set the destination address */
372
    s->dest_addr_len = udp_set_url(h, &s->dest_addr, hostname, port);
373
    if (s->dest_addr_len < 0) {
374
        return AVERROR(EIO);
375
    }
376
    s->is_multicast = ff_is_multicast_address((struct sockaddr*) &s->dest_addr);
377 378
    p = strchr(uri, '?');
    if (p) {
379
        if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
380 381 382 383 384 385
            int was_connected = s->is_connected;
            s->is_connected = strtol(buf, NULL, 10);
            if (s->is_connected && !was_connected) {
                if (connect(s->udp_fd, (struct sockaddr *) &s->dest_addr,
                            s->dest_addr_len)) {
                    s->is_connected = 0;
386
                    log_net_error(h, AV_LOG_ERROR, "connect");
387 388 389 390 391
                    return AVERROR(EIO);
                }
            }
        }
    }
392

393 394 395 396
    return 0;
}

/**
397
 * Return the local port used by the UDP connection
398
 * @param h media file context
399 400
 * @return the local port number
 */
401
int ff_udp_get_local_port(URLContext *h)
402 403 404 405 406 407 408 409 410 411
{
    UDPContext *s = h->priv_data;
    return s->local_port;
}

/**
 * Return the udp file handle for select() usage to wait for several RTP
 * streams at the same time.
 * @param h media file context
 */
412
static int udp_get_file_handle(URLContext *h)
413 414 415 416 417
{
    UDPContext *s = h->priv_data;
    return s->udp_fd;
}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
static int parse_source_list(char *buf, char **sources, int *num_sources,
                             int max_sources)
{
    char *source_start;

    source_start = buf;
    while (1) {
        char *next = strchr(source_start, ',');
        if (next)
            *next = '\0';
        sources[*num_sources] = av_strdup(source_start);
        if (!sources[*num_sources])
            return AVERROR(ENOMEM);
        source_start = next + 1;
        (*num_sources)++;
        if (*num_sources >= max_sources || !next)
            break;
    }
    return 0;
}

439
/* put it in UDP context */
Fabrice Bellard's avatar
Fabrice Bellard committed
440 441 442
/* return non zero if error */
static int udp_open(URLContext *h, const char *uri, int flags)
{
443
    char hostname[1024], localaddr[1024] = "";
444
    int port, udp_fd = -1, tmp, bind_ret = -1;
445
    UDPContext *s = h->priv_data;
446
    int is_output;
447 448
    const char *p;
    char buf[256];
449
    struct sockaddr_storage my_addr;
450
    socklen_t len;
451 452
    int i, num_include_sources = 0, num_exclude_sources = 0;
    char *include_sources[32], *exclude_sources[32];
Fabrice Bellard's avatar
Fabrice Bellard committed
453 454

    h->is_streamed = 1;
455
    h->max_packet_size = 1472;
Fabrice Bellard's avatar
Fabrice Bellard committed
456

457
    is_output = !(flags & AVIO_FLAG_READ);
458

Luca Barbato's avatar
Luca Barbato committed
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
    if (s->buffer_size < 0)
        s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE;

    if (s->sources) {
        if (parse_source_list(s->sources, include_sources,
                              &num_include_sources,
                              FF_ARRAY_ELEMS(include_sources)))
            goto fail;
    }

    if (s->block) {
        if (parse_source_list(s->block, exclude_sources, &num_exclude_sources,
                              FF_ARRAY_ELEMS(exclude_sources)))
            goto fail;
    }

475
    if (s->pkt_size > 0)
Luca Barbato's avatar
Luca Barbato committed
476
        h->max_packet_size = s->pkt_size;
477

478 479
    p = strchr(uri, '?');
    if (p) {
480
        if (av_find_info_tag(buf, sizeof(buf), "reuse", p)) {
481
            char *endptr = NULL;
482 483 484 485 486
            s->reuse_socket = strtol(buf, &endptr, 10);
            /* assume if no digits were found it is a request to enable it */
            if (buf == endptr)
                s->reuse_socket = 1;
        }
487
        if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) {
488 489
            s->ttl = strtol(buf, NULL, 10);
        }
490
        if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
491 492
            s->local_port = strtol(buf, NULL, 10);
        }
493
        if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
494 495
            h->max_packet_size = strtol(buf, NULL, 10);
        }
496
        if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
497 498
            s->buffer_size = strtol(buf, NULL, 10);
        }
499
        if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
500 501
            s->is_connected = strtol(buf, NULL, 10);
        }
502 503 504
        if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
            av_strlcpy(localaddr, buf, sizeof(localaddr));
        }
505 506 507 508 509 510 511 512 513
        if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
            if (parse_source_list(buf, include_sources, &num_include_sources,
                                  FF_ARRAY_ELEMS(include_sources)))
                goto fail;
        }
        if (av_find_info_tag(buf, sizeof(buf), "block", p)) {
            if (parse_source_list(buf, exclude_sources, &num_exclude_sources,
                                  FF_ARRAY_ELEMS(exclude_sources)))
                goto fail;
514
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
515
    }
516 517

    /* fill the dest addr */
518
    av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
519

520
    /* XXX: fix av_url_split */
521 522
    if (hostname[0] == '\0' || hostname[0] == '?') {
        /* only accepts null hostname if input */
523
        if (!(flags & AVIO_FLAG_READ))
524 525
            goto fail;
    } else {
526
        if (ff_udp_set_remote_url(h, uri) < 0)
527
            goto fail;
528
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
529

530
    if ((s->is_multicast || s->local_port < 0) && (h->flags & AVIO_FLAG_READ))
531
        s->local_port = port;
Luca Barbato's avatar
Luca Barbato committed
532 533

    if (localaddr[0])
534
        udp_fd = udp_socket_create(h, &my_addr, &len, localaddr);
Luca Barbato's avatar
Luca Barbato committed
535
    else
536
        udp_fd = udp_socket_create(h, &my_addr, &len, s->localaddr);
537 538 539
    if (udp_fd < 0)
        goto fail;

540
    /* Follow the requested reuse option, unless it's multicast in which
541
     * case enable reuse unless explicitly disabled.
542
     */
Luca Barbato's avatar
Luca Barbato committed
543
    if (s->reuse_socket > 0 || (s->is_multicast && s->reuse_socket < 0)) {
544
        s->reuse_socket = 1;
545 546
        if (setsockopt (udp_fd, SOL_SOCKET, SO_REUSEADDR, &(s->reuse_socket), sizeof(s->reuse_socket)) != 0)
            goto fail;
547
    }
548

549 550
    /* If multicast, try binding the multicast address first, to avoid
     * receiving UDP packets from other sources aimed at the same UDP
551 552 553
     * port. This fails on windows. This makes sending to the same address
     * using sendto() fail, so only do it if we're opened in read-only mode. */
    if (s->is_multicast && !(h->flags & AVIO_FLAG_WRITE)) {
554 555 556 557
        bind_ret = bind(udp_fd,(struct sockaddr *)&s->dest_addr, len);
    }
    /* bind to the local address if not multicast or if the multicast
     * bind failed */
Martin Storsjö's avatar
Martin Storsjö committed
558
    /* the bind is needed to give a port to the socket now */
559
    if (bind_ret < 0 && bind(udp_fd,(struct sockaddr *)&my_addr, len) < 0) {
560
        log_net_error(h, AV_LOG_ERROR, "bind failed");
Fabrice Bellard's avatar
Fabrice Bellard committed
561
        goto fail;
562
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
563

564 565
    len = sizeof(my_addr);
    getsockname(udp_fd, (struct sockaddr *)&my_addr, &len);
566 567
    s->local_port = udp_port(&my_addr, len);

568
    if (s->is_multicast) {
569
        if (h->flags & AVIO_FLAG_WRITE) {
570
            /* output */
571
            if (udp_set_multicast_ttl(udp_fd, s->ttl, (struct sockaddr *)&s->dest_addr) < 0)
572
                goto fail;
573 574
        }
        if (h->flags & AVIO_FLAG_READ) {
575
            /* input */
576 577 578 579 580
            if (num_include_sources && num_exclude_sources) {
                av_log(h, AV_LOG_ERROR, "Simultaneously including and excluding multicast sources is not supported\n");
                goto fail;
            }
            if (num_include_sources) {
581 582 583 584 585
                if (udp_set_multicast_sources(h, udp_fd,
                                              (struct sockaddr *)&s->dest_addr,
                                              s->dest_addr_len,
                                              include_sources,
                                              num_include_sources, 1) < 0)
586 587
                    goto fail;
            } else {
588 589
                if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
                    goto fail;
590 591
            }
            if (num_exclude_sources) {
592 593 594 595 596
                if (udp_set_multicast_sources(h, udp_fd,
                                              (struct sockaddr *)&s->dest_addr,
                                              s->dest_addr_len,
                                              exclude_sources,
                                              num_exclude_sources, 0) < 0)
597 598
                    goto fail;
            }
599 600
        }
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
601

602 603
    if (is_output) {
        /* limit the tx buf size to limit latency */
604
        tmp = s->buffer_size;
605
        if (setsockopt(udp_fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) {
606
            log_net_error(h, AV_LOG_ERROR, "setsockopt(SO_SNDBUF)");
607 608
            goto fail;
        }
609 610 611
    } else {
        /* set udp recv buffer size to the largest possible udp packet size to
         * avoid losing data on OSes that set this too low by default. */
612 613
        tmp = s->buffer_size;
        if (setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0) {
614
            log_net_error(h, AV_LOG_WARNING, "setsockopt(SO_RECVBUF)");
615
        }
616 617
        /* make the socket non-blocking */
        ff_socket_nonblock(udp_fd, 1);
618
    }
619 620
    if (s->is_connected) {
        if (connect(udp_fd, (struct sockaddr *) &s->dest_addr, s->dest_addr_len)) {
621
            log_net_error(h, AV_LOG_ERROR, "connect");
622 623 624
            goto fail;
        }
    }
625

626 627 628 629
    for (i = 0; i < num_include_sources; i++)
        av_freep(&include_sources[i]);
    for (i = 0; i < num_exclude_sources; i++)
        av_freep(&exclude_sources[i]);
630

631
    s->udp_fd = udp_fd;
Fabrice Bellard's avatar
Fabrice Bellard committed
632 633
    return 0;
 fail:
634
    if (udp_fd >= 0)
635
        closesocket(udp_fd);
636 637 638 639
    for (i = 0; i < num_include_sources; i++)
        av_freep(&include_sources[i]);
    for (i = 0; i < num_exclude_sources; i++)
        av_freep(&exclude_sources[i]);
640
    return AVERROR(EIO);
Fabrice Bellard's avatar
Fabrice Bellard committed
641 642
}

643
static int udp_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
644 645
{
    UDPContext *s = h->priv_data;
646
    int ret;
647

648
    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
649 650 651
        ret = ff_network_wait_fd(s->udp_fd, 0);
        if (ret < 0)
            return ret;
652
    }
653 654
    ret = recv(s->udp_fd, buf, size, 0);
    return ret < 0 ? ff_neterrno() : ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
655 656
}

657
static int udp_write(URLContext *h, const uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
658 659
{
    UDPContext *s = h->priv_data;
660 661
    int ret;

662
    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
663 664 665
        ret = ff_network_wait_fd(s->udp_fd, 1);
        if (ret < 0)
            return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
666
    }
667 668 669 670 671 672 673 674 675

    if (!s->is_connected) {
        ret = sendto (s->udp_fd, buf, size, 0,
                      (struct sockaddr *) &s->dest_addr,
                      s->dest_addr_len);
    } else
        ret = send(s->udp_fd, buf, size, 0);

    return ret < 0 ? ff_neterrno() : ret;
676 677 678 679 680 681
}

static int udp_close(URLContext *h)
{
    UDPContext *s = h->priv_data;

682
    if (s->is_multicast && (h->flags & AVIO_FLAG_READ))
683
        udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr);
684
    closesocket(s->udp_fd);
685
    return 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
686 687
}

688
const URLProtocol ff_udp_protocol = {
689 690 691 692 693
    .name                = "udp",
    .url_open            = udp_open,
    .url_read            = udp_read,
    .url_write           = udp_write,
    .url_close           = udp_close,
694
    .url_get_file_handle = udp_get_file_handle,
695
    .priv_data_size      = sizeof(UDPContext),
696
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
Luca Barbato's avatar
Luca Barbato committed
697
    .priv_data_class     = &udp_class,
Fabrice Bellard's avatar
Fabrice Bellard committed
698
};