2 * UDP prototype streaming system
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard.
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "os_support.h"
32 #ifndef IPV6_ADD_MEMBERSHIP
33 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
34 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
37 #define IN_MULTICAST(a) ((((uint32_t)(a)) & 0xf0000000) == 0xe0000000)
39 #ifndef IN6_IS_ADDR_MULTICAST
40 #define IN6_IS_ADDR_MULTICAST(a) (((uint8_t *) (a))[0] == 0xff)
50 struct sockaddr_in dest_addr
;
52 struct sockaddr_storage dest_addr
;
57 #define UDP_TX_BUF_SIZE 32768
58 #define UDP_MAX_PKT_SIZE 65536
60 static int udp_set_multicast_ttl(int sockfd
, int mcastTTL
, struct sockaddr
*addr
) {
61 #ifdef IP_MULTICAST_TTL
62 if (addr
->sa_family
== AF_INET
) {
63 if (setsockopt(sockfd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &mcastTTL
, sizeof(mcastTTL
)) < 0) {
64 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IP_MULTICAST_TTL): %s\n", strerror(errno
));
70 if (addr
->sa_family
== AF_INET6
) {
71 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &mcastTTL
, sizeof(mcastTTL
)) < 0) {
72 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IPV6_MULTICAST_HOPS): %s\n", strerror(errno
));
80 static int udp_join_multicast_group(int sockfd
, struct sockaddr
*addr
) {
81 #ifdef IP_ADD_MEMBERSHIP
82 if (addr
->sa_family
== AF_INET
) {
85 mreq
.imr_multiaddr
.s_addr
= ((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
;
86 mreq
.imr_interface
.s_addr
= INADDR_ANY
;
87 if (setsockopt(sockfd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (const void *)&mreq
, sizeof(mreq
)) < 0) {
88 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
94 if (addr
->sa_family
== AF_INET6
) {
95 struct ipv6_mreq mreq6
;
97 memcpy(&mreq6
.ipv6mr_multiaddr
, &(((struct sockaddr_in6
*)addr
)->sin6_addr
), sizeof(struct in6_addr
));
98 mreq6
.ipv6mr_interface
= 0;
99 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
)) < 0) {
100 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IPV6_ADD_MEMBERSHIP): %s\n", strerror(errno
));
108 static int udp_leave_multicast_group(int sockfd
, struct sockaddr
*addr
) {
109 #ifdef IP_DROP_MEMBERSHIP
110 if (addr
->sa_family
== AF_INET
) {
113 mreq
.imr_multiaddr
.s_addr
= ((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
;
114 mreq
.imr_interface
.s_addr
= INADDR_ANY
;
115 if (setsockopt(sockfd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (const void *)&mreq
, sizeof(mreq
)) < 0) {
116 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IP_DROP_MEMBERSHIP): %s\n", strerror(errno
));
122 if (addr
->sa_family
== AF_INET6
) {
123 struct ipv6_mreq mreq6
;
125 memcpy(&mreq6
.ipv6mr_multiaddr
, &(((struct sockaddr_in6
*)addr
)->sin6_addr
), sizeof(struct in6_addr
));
126 mreq6
.ipv6mr_interface
= 0;
127 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
)) < 0) {
128 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(IPV6_DROP_MEMBERSHIP): %s\n", strerror(errno
));
137 static struct addrinfo
* udp_ipv6_resolve_host(const char *hostname
, int port
, int type
, int family
, int flags
) {
138 struct addrinfo hints
, *res
= 0;
141 const char *node
= 0, *service
= "0";
144 snprintf(sport
, sizeof(sport
), "%d", port
);
147 if ((hostname
) && (hostname
[0] != '\0') && (hostname
[0] != '?')) {
150 memset(&hints
, 0, sizeof(hints
));
151 hints
.ai_socktype
= type
;
152 hints
.ai_family
= family
;
153 hints
.ai_flags
= flags
;
154 if ((error
= getaddrinfo(node
, service
, &hints
, &res
))) {
155 av_log(NULL
, AV_LOG_ERROR
, "udp_ipv6_resolve_host: %s\n", gai_strerror(error
));
161 static int udp_set_url(struct sockaddr_storage
*addr
, const char *hostname
, int port
) {
162 struct addrinfo
*res0
;
165 res0
= udp_ipv6_resolve_host(hostname
, port
, SOCK_DGRAM
, AF_UNSPEC
, 0);
166 if (res0
== 0) return AVERROR(EIO
);
167 memcpy(addr
, res0
->ai_addr
, res0
->ai_addrlen
);
168 addr_len
= res0
->ai_addrlen
;
174 static int is_multicast_address(struct sockaddr_storage
*addr
)
176 if (addr
->ss_family
== AF_INET
) {
177 return IN_MULTICAST(ntohl(((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
));
179 if (addr
->ss_family
== AF_INET6
) {
180 return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6
*)addr
)->sin6_addr
);
186 static int udp_socket_create(UDPContext
*s
, struct sockaddr_storage
*addr
, int *addr_len
)
189 struct addrinfo
*res0
= NULL
, *res
= NULL
;
190 int family
= AF_UNSPEC
;
192 if (((struct sockaddr
*) &s
->dest_addr
)->sa_family
)
193 family
= ((struct sockaddr
*) &s
->dest_addr
)->sa_family
;
194 res0
= udp_ipv6_resolve_host(0, s
->local_port
, SOCK_DGRAM
, family
, AI_PASSIVE
);
197 for (res
= res0
; res
; res
=res
->ai_next
) {
198 udp_fd
= socket(res
->ai_family
, SOCK_DGRAM
, 0);
199 if (udp_fd
> 0) break;
200 av_log(NULL
, AV_LOG_ERROR
, "socket: %s\n", strerror(errno
));
206 memcpy(addr
, res
->ai_addr
, res
->ai_addrlen
);
207 *addr_len
= res
->ai_addrlen
;
221 static int udp_port(struct sockaddr_storage
*addr
, int addr_len
)
223 char sbuf
[sizeof(int)*3+1];
225 if (getnameinfo((struct sockaddr
*)addr
, addr_len
, NULL
, 0, sbuf
, sizeof(sbuf
), NI_NUMERICSERV
) != 0) {
226 av_log(NULL
, AV_LOG_ERROR
, "getnameinfo: %s\n", strerror(errno
));
230 return strtol(sbuf
, NULL
, 10);
235 static int udp_set_url(struct sockaddr_in
*addr
, const char *hostname
, int port
)
237 /* set the destination address */
238 if (resolve_host(&addr
->sin_addr
, hostname
) < 0)
240 addr
->sin_family
= AF_INET
;
241 addr
->sin_port
= htons(port
);
243 return sizeof(struct sockaddr_in
);
246 static int is_multicast_address(struct sockaddr_in
*addr
)
248 return IN_MULTICAST(ntohl(addr
->sin_addr
.s_addr
));
251 static int udp_socket_create(UDPContext
*s
, struct sockaddr_in
*addr
, int *addr_len
)
255 fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
259 addr
->sin_family
= AF_INET
;
260 addr
->sin_addr
.s_addr
= htonl (INADDR_ANY
);
261 addr
->sin_port
= htons(s
->local_port
);
262 *addr_len
= sizeof(struct sockaddr_in
);
267 static int udp_port(struct sockaddr_in
*addr
, int len
)
269 return ntohs(addr
->sin_port
);
271 #endif /* CONFIG_IPV6 */
275 * If no filename is given to av_open_input_file because you want to
276 * get the local port first, then you must call this function to set
277 * the remote server address.
279 * url syntax: udp://host:port[?option=val...]
280 * option: 'ttl=n' : set the ttl value (for multicast only)
281 * 'localport=n' : set the local port
282 * 'pkt_size=n' : set max packet size
283 * 'reuse=1' : enable reusing the socket
285 * @param s1 media file context
286 * @param uri of the remote server
287 * @return zero if no error.
289 int udp_set_remote_url(URLContext
*h
, const char *uri
)
291 UDPContext
*s
= h
->priv_data
;
295 url_split(NULL
, 0, NULL
, 0, hostname
, sizeof(hostname
), &port
, NULL
, 0, uri
);
297 /* set the destination address */
298 s
->dest_addr_len
= udp_set_url(&s
->dest_addr
, hostname
, port
);
299 if (s
->dest_addr_len
< 0) {
302 s
->is_multicast
= is_multicast_address(&s
->dest_addr
);
308 * Return the local port used by the UDP connexion
309 * @param s1 media file context
310 * @return the local port number
312 int udp_get_local_port(URLContext
*h
)
314 UDPContext
*s
= h
->priv_data
;
315 return s
->local_port
;
319 * Return the udp file handle for select() usage to wait for several RTP
320 * streams at the same time.
321 * @param h media file context
323 int udp_get_file_handle(URLContext
*h
)
325 UDPContext
*s
= h
->priv_data
;
329 /* put it in UDP context */
330 /* return non zero if error */
331 static int udp_open(URLContext
*h
, const char *uri
, int flags
)
334 int port
, udp_fd
= -1, tmp
;
335 UDPContext
*s
= NULL
;
340 struct sockaddr_in my_addr
;
342 struct sockaddr_storage my_addr
;
347 h
->max_packet_size
= 1472;
349 is_output
= (flags
& URL_WRONLY
);
351 s
= av_mallocz(sizeof(UDPContext
));
353 return AVERROR(ENOMEM
);
357 p
= strchr(uri
, '?');
359 s
->reuse_socket
= find_info_tag(buf
, sizeof(buf
), "reuse", p
);
360 if (find_info_tag(buf
, sizeof(buf
), "ttl", p
)) {
361 s
->ttl
= strtol(buf
, NULL
, 10);
363 if (find_info_tag(buf
, sizeof(buf
), "localport", p
)) {
364 s
->local_port
= strtol(buf
, NULL
, 10);
366 if (find_info_tag(buf
, sizeof(buf
), "pkt_size", p
)) {
367 h
->max_packet_size
= strtol(buf
, NULL
, 10);
371 /* fill the dest addr */
372 url_split(NULL
, 0, NULL
, 0, hostname
, sizeof(hostname
), &port
, NULL
, 0, uri
);
374 /* XXX: fix url_split */
375 if (hostname
[0] == '\0' || hostname
[0] == '?') {
376 /* only accepts null hostname if input */
377 if (flags
& URL_WRONLY
)
380 udp_set_remote_url(h
, uri
);
383 if(!ff_network_init())
386 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
))
387 s
->local_port
= port
;
388 udp_fd
= udp_socket_create(s
, &my_addr
, &len
);
393 if (setsockopt (udp_fd
, SOL_SOCKET
, SO_REUSEADDR
, &(s
->reuse_socket
), sizeof(s
->reuse_socket
)) != 0)
396 /* the bind is needed to give a port to the socket now */
397 if (bind(udp_fd
,(struct sockaddr
*)&my_addr
, len
) < 0)
400 len
= sizeof(my_addr
);
401 getsockname(udp_fd
, (struct sockaddr
*)&my_addr
, &len
);
402 s
->local_port
= udp_port(&my_addr
, len
);
404 if (s
->is_multicast
) {
405 if (h
->flags
& URL_WRONLY
) {
407 if (udp_set_multicast_ttl(udp_fd
, s
->ttl
, (struct sockaddr
*)&s
->dest_addr
) < 0)
411 if (udp_join_multicast_group(udp_fd
, (struct sockaddr
*)&s
->dest_addr
) < 0)
417 /* limit the tx buf size to limit latency */
418 tmp
= UDP_TX_BUF_SIZE
;
419 if (setsockopt(udp_fd
, SOL_SOCKET
, SO_SNDBUF
, &tmp
, sizeof(tmp
)) < 0) {
420 av_log(NULL
, AV_LOG_ERROR
, "setsockopt(SO_SNDBUF): %s\n", strerror(errno
));
424 /* set udp recv buffer size to the largest possible udp packet size to
425 * avoid losing data on OSes that set this too low by default. */
426 tmp
= UDP_MAX_PKT_SIZE
;
427 setsockopt(udp_fd
, SOL_SOCKET
, SO_RCVBUF
, &tmp
, sizeof(tmp
));
439 static int udp_read(URLContext
*h
, uint8_t *buf
, int size
)
441 UDPContext
*s
= h
->priv_data
;
445 len
= recv(s
->udp_fd
, buf
, size
, 0);
447 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
448 ff_neterrno() != FF_NETERROR(EINTR
))
457 static int udp_write(URLContext
*h
, uint8_t *buf
, int size
)
459 UDPContext
*s
= h
->priv_data
;
463 ret
= sendto (s
->udp_fd
, buf
, size
, 0,
464 (struct sockaddr
*) &s
->dest_addr
,
467 if (ff_neterrno() != FF_NETERROR(EINTR
) &&
468 ff_neterrno() != FF_NETERROR(EAGAIN
))
477 static int udp_close(URLContext
*h
)
479 UDPContext
*s
= h
->priv_data
;
481 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
))
482 udp_leave_multicast_group(s
->udp_fd
, (struct sockaddr
*)&s
->dest_addr
);
483 closesocket(s
->udp_fd
);
489 URLProtocol udp_protocol
= {