00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "avformat.h"
00022 #include "libavutil/parseutils.h"
00023 #include "libavutil/opt.h"
00024 #include "internal.h"
00025 #include "network.h"
00026 #include "os_support.h"
00027 #include "url.h"
00028 #if HAVE_POLL_H
00029 #include <poll.h>
00030 #endif
00031
00032 typedef struct TCPContext {
00033 const AVClass *class;
00034 int fd;
00035 int listen;
00036 int rw_timeout;
00037 int listen_timeout;
00038 } TCPContext;
00039
00040 #define OFFSET(x) offsetof(TCPContext, x)
00041 #define D AV_OPT_FLAG_DECODING_PARAM
00042 #define E AV_OPT_FLAG_ENCODING_PARAM
00043 static const AVOption options[] = {
00044 {"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
00045 {"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
00046 {"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
00047 {NULL}
00048 };
00049
00050 static const AVClass tcp_context_class = {
00051 .class_name = "tcp",
00052 .item_name = av_default_item_name,
00053 .option = options,
00054 .version = LIBAVUTIL_VERSION_INT,
00055 };
00056
00057
00058 static int tcp_open(URLContext *h, const char *uri, int flags)
00059 {
00060 struct addrinfo hints = { 0 }, *ai, *cur_ai;
00061 int port, fd = -1;
00062 TCPContext *s = h->priv_data;
00063 const char *p;
00064 char buf[256];
00065 int ret;
00066 socklen_t optlen;
00067 char hostname[1024],proto[1024],path[1024];
00068 char portstr[10];
00069 h->rw_timeout = 5000000;
00070
00071 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
00072 &port, path, sizeof(path), uri);
00073 if (strcmp(proto, "tcp"))
00074 return AVERROR(EINVAL);
00075 if (port <= 0 || port >= 65536) {
00076 av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
00077 return AVERROR(EINVAL);
00078 }
00079 p = strchr(uri, '?');
00080 if (p) {
00081 if (av_find_info_tag(buf, sizeof(buf), "listen", p))
00082 s->listen = 1;
00083 if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
00084 s->rw_timeout = strtol(buf, NULL, 10);
00085 }
00086 if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
00087 s->listen_timeout = strtol(buf, NULL, 10);
00088 }
00089 }
00090 h->rw_timeout = s->rw_timeout;
00091 hints.ai_family = AF_UNSPEC;
00092 hints.ai_socktype = SOCK_STREAM;
00093 snprintf(portstr, sizeof(portstr), "%d", port);
00094 if (s->listen)
00095 hints.ai_flags |= AI_PASSIVE;
00096 if (!hostname[0])
00097 ret = getaddrinfo(NULL, portstr, &hints, &ai);
00098 else
00099 ret = getaddrinfo(hostname, portstr, &hints, &ai);
00100 if (ret) {
00101 av_log(h, AV_LOG_ERROR,
00102 "Failed to resolve hostname %s: %s\n",
00103 hostname, gai_strerror(ret));
00104 return AVERROR(EIO);
00105 }
00106
00107 cur_ai = ai;
00108
00109 restart:
00110 ret = AVERROR(EIO);
00111 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
00112 if (fd < 0)
00113 goto fail;
00114
00115 if (s->listen) {
00116 int fd1;
00117 int reuse = 1;
00118 struct pollfd lp = { fd, POLLIN, 0 };
00119 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
00120 ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
00121 if (ret) {
00122 ret = ff_neterrno();
00123 goto fail1;
00124 }
00125 ret = listen(fd, 1);
00126 if (ret) {
00127 ret = ff_neterrno();
00128 goto fail1;
00129 }
00130 ret = poll(&lp, 1, s->listen_timeout >= 0 ? s->listen_timeout : -1);
00131 if (ret <= 0) {
00132 ret = AVERROR(ETIMEDOUT);
00133 goto fail1;
00134 }
00135 fd1 = accept(fd, NULL, NULL);
00136 if (fd1 < 0) {
00137 ret = ff_neterrno();
00138 goto fail1;
00139 }
00140 closesocket(fd);
00141 fd = fd1;
00142 ff_socket_nonblock(fd, 1);
00143 } else {
00144 redo:
00145 ff_socket_nonblock(fd, 1);
00146 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
00147 }
00148
00149 if (ret < 0) {
00150 struct pollfd p = {fd, POLLOUT, 0};
00151 int64_t wait_started;
00152 ret = ff_neterrno();
00153 if (ret == AVERROR(EINTR)) {
00154 if (ff_check_interrupt(&h->interrupt_callback)) {
00155 ret = AVERROR_EXIT;
00156 goto fail1;
00157 }
00158 goto redo;
00159 }
00160 if (ret != AVERROR(EINPROGRESS) &&
00161 ret != AVERROR(EAGAIN))
00162 goto fail;
00163
00164
00165 wait_started = av_gettime();
00166 do {
00167 if (ff_check_interrupt(&h->interrupt_callback)) {
00168 ret = AVERROR_EXIT;
00169 goto fail1;
00170 }
00171 ret = poll(&p, 1, 100);
00172 if (ret > 0)
00173 break;
00174 } while (!h->rw_timeout || (av_gettime() - wait_started < h->rw_timeout));
00175 if (ret <= 0) {
00176 ret = AVERROR(ETIMEDOUT);
00177 goto fail;
00178 }
00179
00180 optlen = sizeof(ret);
00181 if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen))
00182 ret = AVUNERROR(ff_neterrno());
00183 if (ret != 0) {
00184 char errbuf[100];
00185 ret = AVERROR(ret);
00186 av_strerror(ret, errbuf, sizeof(errbuf));
00187 av_log(h, AV_LOG_ERROR,
00188 "TCP connection to %s:%d failed: %s\n",
00189 hostname, port, errbuf);
00190 goto fail;
00191 }
00192 }
00193 h->is_streamed = 1;
00194 s->fd = fd;
00195 freeaddrinfo(ai);
00196 return 0;
00197
00198 fail:
00199 if (cur_ai->ai_next) {
00200
00201 cur_ai = cur_ai->ai_next;
00202 if (fd >= 0)
00203 closesocket(fd);
00204 goto restart;
00205 }
00206 fail1:
00207 if (fd >= 0)
00208 closesocket(fd);
00209 freeaddrinfo(ai);
00210 return ret;
00211 }
00212
00213 static int tcp_read(URLContext *h, uint8_t *buf, int size)
00214 {
00215 TCPContext *s = h->priv_data;
00216 int ret;
00217
00218 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
00219 ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
00220 if (ret)
00221 return ret;
00222 }
00223 ret = recv(s->fd, buf, size, 0);
00224 return ret < 0 ? ff_neterrno() : ret;
00225 }
00226
00227 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
00228 {
00229 TCPContext *s = h->priv_data;
00230 int ret;
00231
00232 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
00233 ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
00234 if (ret)
00235 return ret;
00236 }
00237 ret = send(s->fd, buf, size, 0);
00238 return ret < 0 ? ff_neterrno() : ret;
00239 }
00240
00241 static int tcp_shutdown(URLContext *h, int flags)
00242 {
00243 TCPContext *s = h->priv_data;
00244 int how;
00245
00246 if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
00247 how = SHUT_RDWR;
00248 } else if (flags & AVIO_FLAG_WRITE) {
00249 how = SHUT_WR;
00250 } else {
00251 how = SHUT_RD;
00252 }
00253
00254 return shutdown(s->fd, how);
00255 }
00256
00257 static int tcp_close(URLContext *h)
00258 {
00259 TCPContext *s = h->priv_data;
00260 closesocket(s->fd);
00261 return 0;
00262 }
00263
00264 static int tcp_get_file_handle(URLContext *h)
00265 {
00266 TCPContext *s = h->priv_data;
00267 return s->fd;
00268 }
00269
00270 URLProtocol ff_tcp_protocol = {
00271 .name = "tcp",
00272 .url_open = tcp_open,
00273 .url_read = tcp_read,
00274 .url_write = tcp_write,
00275 .url_close = tcp_close,
00276 .url_get_file_handle = tcp_get_file_handle,
00277 .url_shutdown = tcp_shutdown,
00278 .priv_data_size = sizeof(TCPContext),
00279 .priv_data_class = &tcp_context_class,
00280 .flags = URL_PROTOCOL_FLAG_NETWORK,
00281 };