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
25 #ifndef IPV6_ADD_MEMBERSHIP
26 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
27 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
38 struct sockaddr_in dest_addr
;
40 struct sockaddr_storage dest_addr
;
45 #define UDP_TX_BUF_SIZE 32768
49 static int udp_ipv6_set_multicast_ttl(int sockfd
, int mcastTTL
, struct sockaddr
*addr
) {
50 if (addr
->sa_family
== AF_INET
) {
51 if (setsockopt(sockfd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &mcastTTL
, sizeof(mcastTTL
)) < 0) {
52 perror("setsockopt(IP_MULTICAST_TTL)");
56 if (addr
->sa_family
== AF_INET6
) {
57 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &mcastTTL
, sizeof(mcastTTL
)) < 0) {
58 perror("setsockopt(IPV6_MULTICAST_HOPS)");
65 static int udp_ipv6_join_multicast_group(int sockfd
, struct sockaddr
*addr
) {
67 struct ipv6_mreq mreq6
;
68 if (addr
->sa_family
== AF_INET
) {
69 mreq
.imr_multiaddr
.s_addr
= ((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
;
70 mreq
.imr_interface
.s_addr
= INADDR_ANY
;
71 if (setsockopt(sockfd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (const void *)&mreq
, sizeof(mreq
)) < 0) {
72 perror("setsockopt(IP_ADD_MEMBERSHIP)");
76 if (addr
->sa_family
== AF_INET6
) {
77 memcpy(&mreq6
.ipv6mr_multiaddr
, &(((struct sockaddr_in6
*)addr
)->sin6_addr
), sizeof(struct in6_addr
));
78 mreq6
.ipv6mr_interface
= 0;
79 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mreq6
, sizeof(mreq6
)) < 0) {
80 perror("setsockopt(IPV6_ADD_MEMBERSHIP)");
87 static int udp_ipv6_leave_multicast_group(int sockfd
, struct sockaddr
*addr
) {
89 struct ipv6_mreq mreq6
;
90 if (addr
->sa_family
== AF_INET
) {
91 mreq
.imr_multiaddr
.s_addr
= ((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
;
92 mreq
.imr_interface
.s_addr
= INADDR_ANY
;
93 if (setsockopt(sockfd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (const void *)&mreq
, sizeof(mreq
)) < 0) {
94 perror("setsockopt(IP_DROP_MEMBERSHIP)");
98 if (addr
->sa_family
== AF_INET6
) {
99 memcpy(&mreq6
.ipv6mr_multiaddr
, &(((struct sockaddr_in6
*)addr
)->sin6_addr
), sizeof(struct in6_addr
));
100 mreq6
.ipv6mr_interface
= 0;
101 if (setsockopt(sockfd
, IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, &mreq6
, sizeof(mreq6
)) < 0) {
102 perror("setsockopt(IPV6_DROP_MEMBERSHIP)");
109 static struct addrinfo
* udp_ipv6_resolve_host(const char *hostname
, int port
, int type
, int family
, int flags
) {
110 struct addrinfo hints
, *res
= 0;
113 const char *node
= 0, *service
= 0;
116 snprintf(sport
, sizeof(sport
), "%d", port
);
119 if ((hostname
) && (hostname
[0] != '\0') && (hostname
[0] != '?')) {
122 if ((node
) || (service
)) {
123 memset(&hints
, 0, sizeof(hints
));
124 hints
.ai_socktype
= type
;
125 hints
.ai_family
= family
;
126 hints
.ai_flags
= flags
;
127 if ((error
= getaddrinfo(node
, service
, &hints
, &res
))) {
128 av_log(NULL
, AV_LOG_ERROR
, "udp_ipv6_resolve_host: %s\n", gai_strerror(error
));
134 static int udp_ipv6_set_remote_url(URLContext
*h
, const char *uri
) {
135 UDPContext
*s
= h
->priv_data
;
138 struct addrinfo
*res0
;
139 url_split(NULL
, 0, NULL
, 0, hostname
, sizeof(hostname
), &port
, NULL
, 0, uri
);
140 res0
= udp_ipv6_resolve_host(hostname
, port
, SOCK_DGRAM
, AF_UNSPEC
, 0);
141 if (res0
== 0) return AVERROR(EIO
);
142 memcpy(&s
->dest_addr
, res0
->ai_addr
, res0
->ai_addrlen
);
143 s
->dest_addr_len
= res0
->ai_addrlen
;
148 static int udp_ipv6_set_local(URLContext
*h
) {
149 UDPContext
*s
= h
->priv_data
;
151 struct sockaddr_storage clientaddr
;
153 char sbuf
[NI_MAXSERV
];
154 char hbuf
[NI_MAXHOST
];
155 struct addrinfo
*res0
= NULL
, *res
= NULL
;
157 if (s
->local_port
!= 0) {
158 res0
= udp_ipv6_resolve_host(0, s
->local_port
, SOCK_DGRAM
, AF_UNSPEC
, AI_PASSIVE
);
161 for (res
= res0
; res
; res
=res
->ai_next
) {
162 udp_fd
= socket(res
->ai_family
, SOCK_DGRAM
, 0);
163 if (udp_fd
> 0) break;
167 udp_fd
= socket(s
->dest_addr
.ss_family
, SOCK_DGRAM
, 0);
175 if (s
->local_port
!= 0) {
176 if (bind(udp_fd
, res0
->ai_addr
, res0
->ai_addrlen
) < 0) {
184 addrlen
= sizeof(clientaddr
);
185 if (getsockname(udp_fd
, (struct sockaddr
*)&clientaddr
, &addrlen
) < 0) {
186 perror("getsockname");
190 if (getnameinfo((struct sockaddr
*)&clientaddr
, addrlen
, hbuf
, sizeof(hbuf
), sbuf
, sizeof(sbuf
), NI_NUMERICHOST
| NI_NUMERICSERV
) != 0) {
191 perror("getnameinfo");
195 s
->local_port
= strtol(sbuf
, NULL
, 10);
207 #endif /* CONFIG_IPV6 */
211 * If no filename is given to av_open_input_file because you want to
212 * get the local port first, then you must call this function to set
213 * the remote server address.
215 * url syntax: udp://host:port[?option=val...]
216 * option: 'multicast=1' : enable multicast
217 * 'ttl=n' : set the ttl value (for multicast only)
218 * 'localport=n' : set the local port
219 * 'pkt_size=n' : set max packet size
220 * 'reuse=1' : enable reusing the socket
222 * @param s1 media file context
223 * @param uri of the remote server
224 * @return zero if no error.
226 int udp_set_remote_url(URLContext
*h
, const char *uri
)
229 return udp_ipv6_set_remote_url(h
, uri
);
231 UDPContext
*s
= h
->priv_data
;
235 url_split(NULL
, 0, NULL
, 0, hostname
, sizeof(hostname
), &port
, NULL
, 0, uri
);
237 /* set the destination address */
238 if (resolve_host(&s
->dest_addr
.sin_addr
, hostname
) < 0)
240 s
->dest_addr
.sin_family
= AF_INET
;
241 s
->dest_addr
.sin_port
= htons(port
);
247 * Return the local port used by the UDP connexion
248 * @param s1 media file context
249 * @return the local port number
251 int udp_get_local_port(URLContext
*h
)
253 UDPContext
*s
= h
->priv_data
;
254 return s
->local_port
;
258 * Return the udp file handle for select() usage to wait for several RTP
259 * streams at the same time.
260 * @param h media file context
262 int udp_get_file_handle(URLContext
*h
)
264 UDPContext
*s
= h
->priv_data
;
268 /* put it in UDP context */
269 /* return non zero if error */
270 static int udp_open(URLContext
*h
, const char *uri
, int flags
)
273 int port
, udp_fd
= -1, tmp
;
274 UDPContext
*s
= NULL
;
279 struct sockaddr_in my_addr
, my_addr1
;
284 h
->max_packet_size
= 1472;
286 is_output
= (flags
& URL_WRONLY
);
288 s
= av_malloc(sizeof(UDPContext
));
290 return AVERROR(ENOMEM
);
297 p
= strchr(uri
, '?');
299 s
->is_multicast
= find_info_tag(buf
, sizeof(buf
), "multicast", p
);
300 s
->reuse_socket
= find_info_tag(buf
, sizeof(buf
), "reuse", p
);
301 if (find_info_tag(buf
, sizeof(buf
), "ttl", p
)) {
302 s
->ttl
= strtol(buf
, NULL
, 10);
304 if (find_info_tag(buf
, sizeof(buf
), "localport", p
)) {
305 s
->local_port
= strtol(buf
, NULL
, 10);
307 if (find_info_tag(buf
, sizeof(buf
), "pkt_size", p
)) {
308 h
->max_packet_size
= strtol(buf
, NULL
, 10);
312 /* fill the dest addr */
313 url_split(NULL
, 0, NULL
, 0, hostname
, sizeof(hostname
), &port
, NULL
, 0, uri
);
315 /* XXX: fix url_split */
316 if (hostname
[0] == '\0' || hostname
[0] == '?') {
317 /* only accepts null hostname if input */
318 if (s
->is_multicast
|| (flags
& URL_WRONLY
))
321 udp_set_remote_url(h
, uri
);
325 udp_fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
329 my_addr
.sin_family
= AF_INET
;
330 my_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
331 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
)) {
332 /* special case: the bind must be done on the multicast address port */
333 my_addr
.sin_port
= s
->dest_addr
.sin_port
;
335 my_addr
.sin_port
= htons(s
->local_port
);
339 if (setsockopt (udp_fd
, SOL_SOCKET
, SO_REUSEADDR
, &(s
->reuse_socket
), sizeof(s
->reuse_socket
)) != 0)
342 /* the bind is needed to give a port to the socket now */
343 if (bind(udp_fd
,(struct sockaddr
*)&my_addr
, sizeof(my_addr
)) < 0)
346 len
= sizeof(my_addr1
);
347 getsockname(udp_fd
, (struct sockaddr
*)&my_addr1
, &len
);
348 s
->local_port
= ntohs(my_addr1
.sin_port
);
350 #ifdef IP_MULTICAST_TTL
351 if (s
->is_multicast
) {
352 if (h
->flags
& URL_WRONLY
) {
354 if (setsockopt(udp_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
355 &s
->ttl
, sizeof(s
->ttl
)) < 0) {
356 perror("IP_MULTICAST_TTL");
361 memset(&s
->mreq
, 0, sizeof(s
->mreq
));
362 s
->mreq
.imr_multiaddr
= s
->dest_addr
.sin_addr
;
363 s
->mreq
.imr_interface
.s_addr
= htonl (INADDR_ANY
);
364 if (setsockopt(udp_fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
365 &s
->mreq
, sizeof(s
->mreq
)) < 0) {
366 perror("rtp: IP_ADD_MEMBERSHIP");
373 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
))
374 s
->local_port
= port
;
375 udp_fd
= udp_ipv6_set_local(h
);
378 if (s
->is_multicast
) {
379 if (h
->flags
& URL_WRONLY
) {
380 if (udp_ipv6_set_multicast_ttl(udp_fd
, s
->ttl
, (struct sockaddr
*)&s
->dest_addr
) < 0)
383 if (udp_ipv6_join_multicast_group(udp_fd
, (struct sockaddr
*)&s
->dest_addr
) < 0)
387 #endif /* CONFIG_IPV6 */
390 /* limit the tx buf size to limit latency */
391 tmp
= UDP_TX_BUF_SIZE
;
392 if (setsockopt(udp_fd
, SOL_SOCKET
, SO_SNDBUF
, &tmp
, sizeof(tmp
)) < 0) {
393 perror("setsockopt sndbuf");
407 static int udp_read(URLContext
*h
, uint8_t *buf
, int size
)
409 UDPContext
*s
= h
->priv_data
;
411 struct sockaddr_in from
;
413 struct sockaddr_storage from
;
419 from_len
= sizeof(from
);
420 len
= recvfrom (s
->udp_fd
, buf
, size
, 0,
421 (struct sockaddr
*)&from
, &from_len
);
423 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
424 ff_neterrno() != FF_NETERROR(EINTR
))
433 static int udp_write(URLContext
*h
, uint8_t *buf
, int size
)
435 UDPContext
*s
= h
->priv_data
;
439 ret
= sendto (s
->udp_fd
, buf
, size
, 0,
440 (struct sockaddr
*) &s
->dest_addr
,
442 sizeof (s
->dest_addr
));
447 if (ff_neterrno() != FF_NETERROR(EINTR
) &&
448 ff_neterrno() != FF_NETERROR(EAGAIN
))
457 static int udp_close(URLContext
*h
)
459 UDPContext
*s
= h
->priv_data
;
462 #ifdef IP_DROP_MEMBERSHIP
463 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
)) {
464 if (setsockopt(s
->udp_fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
465 &s
->mreq
, sizeof(s
->mreq
)) < 0) {
466 perror("IP_DROP_MEMBERSHIP");
471 if (s
->is_multicast
&& !(h
->flags
& URL_WRONLY
))
472 udp_ipv6_leave_multicast_group(s
->udp_fd
, (struct sockaddr
*)&s
->dest_addr
);
474 closesocket(s
->udp_fd
);
479 URLProtocol udp_protocol
= {