3 * Copyright (c) 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
22 #include "libavutil/parseutils.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/time.h"
28 #include "os_support.h"
34 typedef struct TCPContext
{
43 #define OFFSET(x) offsetof(TCPContext, x)
44 #define D AV_OPT_FLAG_DECODING_PARAM
45 #define E AV_OPT_FLAG_ENCODING_PARAM
46 static const AVOption options
[] = {
47 { "listen", "Listen for incoming connections", OFFSET(listen
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, .flags
= D
|E
},
48 { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
49 { "listen_timeout", "Connection awaiting timeout", OFFSET(listen_timeout
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
53 static const AVClass tcp_class
= {
55 .item_name
= av_default_item_name
,
57 .version
= LIBAVUTIL_VERSION_INT
,
60 /* return non zero if error */
61 static int tcp_open(URLContext
*h
, const char *uri
, int flags
)
63 struct addrinfo hints
= { 0 }, *ai
, *cur_ai
;
65 TCPContext
*s
= h
->priv_data
;
69 char hostname
[1024],proto
[1024],path
[1024];
71 s
->open_timeout
= 5000000;
73 av_url_split(proto
, sizeof(proto
), NULL
, 0, hostname
, sizeof(hostname
),
74 &port
, path
, sizeof(path
), uri
);
75 if (strcmp(proto
, "tcp"))
76 return AVERROR(EINVAL
);
77 if (port
<= 0 || port
>= 65536) {
78 av_log(h
, AV_LOG_ERROR
, "Port missing in uri\n");
79 return AVERROR(EINVAL
);
83 if (av_find_info_tag(buf
, sizeof(buf
), "listen", p
)) {
85 s
->listen
= strtol(buf
, &endptr
, 10);
86 /* assume if no digits were found it is a request to enable it */
90 if (av_find_info_tag(buf
, sizeof(buf
), "timeout", p
)) {
91 s
->rw_timeout
= strtol(buf
, NULL
, 10);
93 if (av_find_info_tag(buf
, sizeof(buf
), "listen_timeout", p
)) {
94 s
->listen_timeout
= strtol(buf
, NULL
, 10);
97 if (s
->rw_timeout
>= 0) {
99 h
->rw_timeout
= s
->rw_timeout
;
101 hints
.ai_family
= AF_UNSPEC
;
102 hints
.ai_socktype
= SOCK_STREAM
;
103 snprintf(portstr
, sizeof(portstr
), "%d", port
);
105 hints
.ai_flags
|= AI_PASSIVE
;
107 ret
= getaddrinfo(NULL
, portstr
, &hints
, &ai
);
109 ret
= getaddrinfo(hostname
, portstr
, &hints
, &ai
);
111 av_log(h
, AV_LOG_ERROR
,
112 "Failed to resolve hostname %s: %s\n",
113 hostname
, gai_strerror(ret
));
120 fd
= ff_socket(cur_ai
->ai_family
,
122 cur_ai
->ai_protocol
);
129 if ((fd
= ff_listen_bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
130 s
->listen_timeout
, h
)) < 0) {
135 if ((ret
= ff_listen_connect(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
136 s
->open_timeout
/ 1000, h
, !!cur_ai
->ai_next
)) < 0) {
138 if (ret
== AVERROR_EXIT
)
151 if (cur_ai
->ai_next
) {
152 /* Retry with the next sockaddr */
153 cur_ai
= cur_ai
->ai_next
;
166 static int tcp_read(URLContext
*h
, uint8_t *buf
, int size
)
168 TCPContext
*s
= h
->priv_data
;
171 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
172 ret
= ff_network_wait_fd_timeout(s
->fd
, 0, h
->rw_timeout
, &h
->interrupt_callback
);
176 ret
= recv(s
->fd
, buf
, size
, 0);
177 return ret
< 0 ? ff_neterrno() : ret
;
180 static int tcp_write(URLContext
*h
, const uint8_t *buf
, int size
)
182 TCPContext
*s
= h
->priv_data
;
185 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
186 ret
= ff_network_wait_fd_timeout(s
->fd
, 1, h
->rw_timeout
, &h
->interrupt_callback
);
190 ret
= send(s
->fd
, buf
, size
, MSG_NOSIGNAL
);
191 return ret
< 0 ? ff_neterrno() : ret
;
194 static int tcp_shutdown(URLContext
*h
, int flags
)
196 TCPContext
*s
= h
->priv_data
;
199 if (flags
& AVIO_FLAG_WRITE
&& flags
& AVIO_FLAG_READ
) {
201 } else if (flags
& AVIO_FLAG_WRITE
) {
207 return shutdown(s
->fd
, how
);
210 static int tcp_close(URLContext
*h
)
212 TCPContext
*s
= h
->priv_data
;
217 static int tcp_get_file_handle(URLContext
*h
)
219 TCPContext
*s
= h
->priv_data
;
223 URLProtocol ff_tcp_protocol
= {
225 .url_open
= tcp_open
,
226 .url_read
= tcp_read
,
227 .url_write
= tcp_write
,
228 .url_close
= tcp_close
,
229 .url_get_file_handle
= tcp_get_file_handle
,
230 .url_shutdown
= tcp_shutdown
,
231 .priv_data_size
= sizeof(TCPContext
),
232 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
233 .priv_data_class
= &tcp_class
,