annotate ffmpeg/libavformat/tcp.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * TCP protocol
yading@11 3 * Copyright (c) 2002 Fabrice Bellard
yading@11 4 *
yading@11 5 * This file is part of FFmpeg.
yading@11 6 *
yading@11 7 * FFmpeg is free software; you can redistribute it and/or
yading@11 8 * modify it under the terms of the GNU Lesser General Public
yading@11 9 * License as published by the Free Software Foundation; either
yading@11 10 * version 2.1 of the License, or (at your option) any later version.
yading@11 11 *
yading@11 12 * FFmpeg is distributed in the hope that it will be useful,
yading@11 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@11 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@11 15 * Lesser General Public License for more details.
yading@11 16 *
yading@11 17 * You should have received a copy of the GNU Lesser General Public
yading@11 18 * License along with FFmpeg; if not, write to the Free Software
yading@11 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@11 20 */
yading@11 21 #include "avformat.h"
yading@11 22 #include "libavutil/parseutils.h"
yading@11 23 #include "libavutil/opt.h"
yading@11 24 #include "libavutil/time.h"
yading@11 25 #include "internal.h"
yading@11 26 #include "network.h"
yading@11 27 #include "os_support.h"
yading@11 28 #include "url.h"
yading@11 29 #if HAVE_POLL_H
yading@11 30 #include <poll.h>
yading@11 31 #endif
yading@11 32
yading@11 33 typedef struct TCPContext {
yading@11 34 const AVClass *class;
yading@11 35 int fd;
yading@11 36 int listen;
yading@11 37 int rw_timeout;
yading@11 38 int listen_timeout;
yading@11 39 } TCPContext;
yading@11 40
yading@11 41 #define OFFSET(x) offsetof(TCPContext, x)
yading@11 42 #define D AV_OPT_FLAG_DECODING_PARAM
yading@11 43 #define E AV_OPT_FLAG_ENCODING_PARAM
yading@11 44 static const AVOption options[] = {
yading@11 45 {"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
yading@11 46 {"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
yading@11 47 {"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
yading@11 48 {NULL}
yading@11 49 };
yading@11 50
yading@11 51 static const AVClass tcp_context_class = {
yading@11 52 .class_name = "tcp",
yading@11 53 .item_name = av_default_item_name,
yading@11 54 .option = options,
yading@11 55 .version = LIBAVUTIL_VERSION_INT,
yading@11 56 };
yading@11 57
yading@11 58 /* return non zero if error */
yading@11 59 static int tcp_open(URLContext *h, const char *uri, int flags)
yading@11 60 {
yading@11 61 struct addrinfo hints = { 0 }, *ai, *cur_ai;
yading@11 62 int port, fd = -1;
yading@11 63 TCPContext *s = h->priv_data;
yading@11 64 const char *p;
yading@11 65 char buf[256];
yading@11 66 int ret;
yading@11 67 socklen_t optlen;
yading@11 68 char hostname[1024],proto[1024],path[1024];
yading@11 69 char portstr[10];
yading@11 70 h->rw_timeout = 5000000;
yading@11 71
yading@11 72 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
yading@11 73 &port, path, sizeof(path), uri);
yading@11 74 if (strcmp(proto, "tcp"))
yading@11 75 return AVERROR(EINVAL);
yading@11 76 if (port <= 0 || port >= 65536) {
yading@11 77 av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
yading@11 78 return AVERROR(EINVAL);
yading@11 79 }
yading@11 80 p = strchr(uri, '?');
yading@11 81 if (p) {
yading@11 82 if (av_find_info_tag(buf, sizeof(buf), "listen", p))
yading@11 83 s->listen = 1;
yading@11 84 if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
yading@11 85 s->rw_timeout = strtol(buf, NULL, 10);
yading@11 86 }
yading@11 87 if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
yading@11 88 s->listen_timeout = strtol(buf, NULL, 10);
yading@11 89 }
yading@11 90 }
yading@11 91 h->rw_timeout = s->rw_timeout;
yading@11 92 hints.ai_family = AF_UNSPEC;
yading@11 93 hints.ai_socktype = SOCK_STREAM;
yading@11 94 snprintf(portstr, sizeof(portstr), "%d", port);
yading@11 95 if (s->listen)
yading@11 96 hints.ai_flags |= AI_PASSIVE;
yading@11 97 if (!hostname[0])
yading@11 98 ret = getaddrinfo(NULL, portstr, &hints, &ai);
yading@11 99 else
yading@11 100 ret = getaddrinfo(hostname, portstr, &hints, &ai);
yading@11 101 if (ret) {
yading@11 102 av_log(h, AV_LOG_ERROR,
yading@11 103 "Failed to resolve hostname %s: %s\n",
yading@11 104 hostname, gai_strerror(ret));
yading@11 105 return AVERROR(EIO);
yading@11 106 }
yading@11 107
yading@11 108 cur_ai = ai;
yading@11 109
yading@11 110 restart:
yading@11 111 ret = AVERROR(EIO);
yading@11 112 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
yading@11 113 if (fd < 0)
yading@11 114 goto fail;
yading@11 115
yading@11 116 if (s->listen) {
yading@11 117 int fd1;
yading@11 118 int reuse = 1;
yading@11 119 struct pollfd lp = { fd, POLLIN, 0 };
yading@11 120 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
yading@11 121 ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
yading@11 122 if (ret) {
yading@11 123 ret = ff_neterrno();
yading@11 124 goto fail1;
yading@11 125 }
yading@11 126 ret = listen(fd, 1);
yading@11 127 if (ret) {
yading@11 128 ret = ff_neterrno();
yading@11 129 goto fail1;
yading@11 130 }
yading@11 131 ret = poll(&lp, 1, s->listen_timeout >= 0 ? s->listen_timeout : -1);
yading@11 132 if (ret <= 0) {
yading@11 133 ret = AVERROR(ETIMEDOUT);
yading@11 134 goto fail1;
yading@11 135 }
yading@11 136 fd1 = accept(fd, NULL, NULL);
yading@11 137 if (fd1 < 0) {
yading@11 138 ret = ff_neterrno();
yading@11 139 goto fail1;
yading@11 140 }
yading@11 141 closesocket(fd);
yading@11 142 fd = fd1;
yading@11 143 ff_socket_nonblock(fd, 1);
yading@11 144 } else {
yading@11 145 redo:
yading@11 146 ff_socket_nonblock(fd, 1);
yading@11 147 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
yading@11 148 }
yading@11 149
yading@11 150 if (ret < 0) {
yading@11 151 struct pollfd p = {fd, POLLOUT, 0};
yading@11 152 int64_t wait_started;
yading@11 153 ret = ff_neterrno();
yading@11 154 if (ret == AVERROR(EINTR)) {
yading@11 155 if (ff_check_interrupt(&h->interrupt_callback)) {
yading@11 156 ret = AVERROR_EXIT;
yading@11 157 goto fail1;
yading@11 158 }
yading@11 159 goto redo;
yading@11 160 }
yading@11 161 if (ret != AVERROR(EINPROGRESS) &&
yading@11 162 ret != AVERROR(EAGAIN))
yading@11 163 goto fail;
yading@11 164
yading@11 165 /* wait until we are connected or until abort */
yading@11 166 wait_started = av_gettime();
yading@11 167 do {
yading@11 168 if (ff_check_interrupt(&h->interrupt_callback)) {
yading@11 169 ret = AVERROR_EXIT;
yading@11 170 goto fail1;
yading@11 171 }
yading@11 172 ret = poll(&p, 1, 100);
yading@11 173 if (ret > 0)
yading@11 174 break;
yading@11 175 } while (!h->rw_timeout || (av_gettime() - wait_started < h->rw_timeout));
yading@11 176 if (ret <= 0) {
yading@11 177 ret = AVERROR(ETIMEDOUT);
yading@11 178 goto fail;
yading@11 179 }
yading@11 180 /* test error */
yading@11 181 optlen = sizeof(ret);
yading@11 182 if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen))
yading@11 183 ret = AVUNERROR(ff_neterrno());
yading@11 184 if (ret != 0) {
yading@11 185 char errbuf[100];
yading@11 186 ret = AVERROR(ret);
yading@11 187 av_strerror(ret, errbuf, sizeof(errbuf));
yading@11 188 av_log(h, AV_LOG_ERROR,
yading@11 189 "TCP connection to %s:%d failed: %s\n",
yading@11 190 hostname, port, errbuf);
yading@11 191 goto fail;
yading@11 192 }
yading@11 193 }
yading@11 194 h->is_streamed = 1;
yading@11 195 s->fd = fd;
yading@11 196 freeaddrinfo(ai);
yading@11 197 return 0;
yading@11 198
yading@11 199 fail:
yading@11 200 if (cur_ai->ai_next) {
yading@11 201 /* Retry with the next sockaddr */
yading@11 202 cur_ai = cur_ai->ai_next;
yading@11 203 if (fd >= 0)
yading@11 204 closesocket(fd);
yading@11 205 goto restart;
yading@11 206 }
yading@11 207 fail1:
yading@11 208 if (fd >= 0)
yading@11 209 closesocket(fd);
yading@11 210 freeaddrinfo(ai);
yading@11 211 return ret;
yading@11 212 }
yading@11 213
yading@11 214 static int tcp_read(URLContext *h, uint8_t *buf, int size)
yading@11 215 {
yading@11 216 TCPContext *s = h->priv_data;
yading@11 217 int ret;
yading@11 218
yading@11 219 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
yading@11 220 ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
yading@11 221 if (ret)
yading@11 222 return ret;
yading@11 223 }
yading@11 224 ret = recv(s->fd, buf, size, 0);
yading@11 225 return ret < 0 ? ff_neterrno() : ret;
yading@11 226 }
yading@11 227
yading@11 228 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
yading@11 229 {
yading@11 230 TCPContext *s = h->priv_data;
yading@11 231 int ret;
yading@11 232
yading@11 233 if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
yading@11 234 ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
yading@11 235 if (ret)
yading@11 236 return ret;
yading@11 237 }
yading@11 238 ret = send(s->fd, buf, size, 0);
yading@11 239 return ret < 0 ? ff_neterrno() : ret;
yading@11 240 }
yading@11 241
yading@11 242 static int tcp_shutdown(URLContext *h, int flags)
yading@11 243 {
yading@11 244 TCPContext *s = h->priv_data;
yading@11 245 int how;
yading@11 246
yading@11 247 if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
yading@11 248 how = SHUT_RDWR;
yading@11 249 } else if (flags & AVIO_FLAG_WRITE) {
yading@11 250 how = SHUT_WR;
yading@11 251 } else {
yading@11 252 how = SHUT_RD;
yading@11 253 }
yading@11 254
yading@11 255 return shutdown(s->fd, how);
yading@11 256 }
yading@11 257
yading@11 258 static int tcp_close(URLContext *h)
yading@11 259 {
yading@11 260 TCPContext *s = h->priv_data;
yading@11 261 closesocket(s->fd);
yading@11 262 return 0;
yading@11 263 }
yading@11 264
yading@11 265 static int tcp_get_file_handle(URLContext *h)
yading@11 266 {
yading@11 267 TCPContext *s = h->priv_data;
yading@11 268 return s->fd;
yading@11 269 }
yading@11 270
yading@11 271 URLProtocol ff_tcp_protocol = {
yading@11 272 .name = "tcp",
yading@11 273 .url_open = tcp_open,
yading@11 274 .url_read = tcp_read,
yading@11 275 .url_write = tcp_write,
yading@11 276 .url_close = tcp_close,
yading@11 277 .url_get_file_handle = tcp_get_file_handle,
yading@11 278 .url_shutdown = tcp_shutdown,
yading@11 279 .priv_data_size = sizeof(TCPContext),
yading@11 280 .priv_data_class = &tcp_context_class,
yading@11 281 .flags = URL_PROTOCOL_FLAG_NETWORK,
yading@11 282 };