Libav 0.7.1
|
00001 /* 00002 * TCP protocol 00003 * Copyright (c) 2002 Fabrice Bellard 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 #include "avformat.h" 00022 #include "libavutil/parseutils.h" 00023 #include <unistd.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 #include <sys/time.h> 00032 00033 typedef struct TCPContext { 00034 int fd; 00035 } TCPContext; 00036 00037 /* return non zero if error */ 00038 static int tcp_open(URLContext *h, const char *uri, int flags) 00039 { 00040 struct addrinfo hints, *ai, *cur_ai; 00041 int port, fd = -1; 00042 TCPContext *s = NULL; 00043 int listen_socket = 0; 00044 const char *p; 00045 char buf[256]; 00046 int ret; 00047 socklen_t optlen; 00048 int timeout = 100; 00049 char hostname[1024],proto[1024],path[1024]; 00050 char portstr[10]; 00051 00052 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), 00053 &port, path, sizeof(path), uri); 00054 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) 00055 return AVERROR(EINVAL); 00056 00057 p = strchr(uri, '?'); 00058 if (p) { 00059 if (av_find_info_tag(buf, sizeof(buf), "listen", p)) 00060 listen_socket = 1; 00061 if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { 00062 timeout = strtol(buf, NULL, 10); 00063 } 00064 } 00065 memset(&hints, 0, sizeof(hints)); 00066 hints.ai_family = AF_UNSPEC; 00067 hints.ai_socktype = SOCK_STREAM; 00068 snprintf(portstr, sizeof(portstr), "%d", port); 00069 ret = getaddrinfo(hostname, portstr, &hints, &ai); 00070 if (ret) { 00071 av_log(h, AV_LOG_ERROR, 00072 "Failed to resolve hostname %s: %s\n", 00073 hostname, gai_strerror(ret)); 00074 return AVERROR(EIO); 00075 } 00076 00077 cur_ai = ai; 00078 00079 restart: 00080 ret = AVERROR(EIO); 00081 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); 00082 if (fd < 0) 00083 goto fail; 00084 00085 if (listen_socket) { 00086 int fd1; 00087 ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 00088 listen(fd, 1); 00089 fd1 = accept(fd, NULL, NULL); 00090 closesocket(fd); 00091 fd = fd1; 00092 ff_socket_nonblock(fd, 1); 00093 } else { 00094 redo: 00095 ff_socket_nonblock(fd, 1); 00096 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 00097 } 00098 00099 if (ret < 0) { 00100 struct pollfd p = {fd, POLLOUT, 0}; 00101 ret = ff_neterrno(); 00102 if (ret == AVERROR(EINTR)) { 00103 if (url_interrupt_cb()) { 00104 ret = AVERROR_EXIT; 00105 goto fail1; 00106 } 00107 goto redo; 00108 } 00109 if (ret != AVERROR(EINPROGRESS) && 00110 ret != AVERROR(EAGAIN)) 00111 goto fail; 00112 00113 /* wait until we are connected or until abort */ 00114 while(timeout--) { 00115 if (url_interrupt_cb()) { 00116 ret = AVERROR_EXIT; 00117 goto fail1; 00118 } 00119 ret = poll(&p, 1, 100); 00120 if (ret > 0) 00121 break; 00122 } 00123 if (ret <= 0) { 00124 ret = AVERROR(ETIMEDOUT); 00125 goto fail; 00126 } 00127 /* test error */ 00128 optlen = sizeof(ret); 00129 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); 00130 if (ret != 0) { 00131 av_log(h, AV_LOG_ERROR, 00132 "TCP connection to %s:%d failed: %s\n", 00133 hostname, port, strerror(ret)); 00134 ret = AVERROR(ret); 00135 goto fail; 00136 } 00137 } 00138 s = av_malloc(sizeof(TCPContext)); 00139 if (!s) { 00140 freeaddrinfo(ai); 00141 return AVERROR(ENOMEM); 00142 } 00143 h->priv_data = s; 00144 h->is_streamed = 1; 00145 s->fd = fd; 00146 freeaddrinfo(ai); 00147 return 0; 00148 00149 fail: 00150 if (cur_ai->ai_next) { 00151 /* Retry with the next sockaddr */ 00152 cur_ai = cur_ai->ai_next; 00153 if (fd >= 0) 00154 closesocket(fd); 00155 goto restart; 00156 } 00157 fail1: 00158 if (fd >= 0) 00159 closesocket(fd); 00160 freeaddrinfo(ai); 00161 return ret; 00162 } 00163 00164 static int tcp_read(URLContext *h, uint8_t *buf, int size) 00165 { 00166 TCPContext *s = h->priv_data; 00167 int ret; 00168 00169 if (!(h->flags & AVIO_FLAG_NONBLOCK)) { 00170 ret = ff_network_wait_fd(s->fd, 0); 00171 if (ret < 0) 00172 return ret; 00173 } 00174 ret = recv(s->fd, buf, size, 0); 00175 return ret < 0 ? ff_neterrno() : ret; 00176 } 00177 00178 static int tcp_write(URLContext *h, const uint8_t *buf, int size) 00179 { 00180 TCPContext *s = h->priv_data; 00181 int ret; 00182 00183 if (!(h->flags & AVIO_FLAG_NONBLOCK)) { 00184 ret = ff_network_wait_fd(s->fd, 1); 00185 if (ret < 0) 00186 return ret; 00187 } 00188 ret = send(s->fd, buf, size, 0); 00189 return ret < 0 ? ff_neterrno() : ret; 00190 } 00191 00192 static int tcp_close(URLContext *h) 00193 { 00194 TCPContext *s = h->priv_data; 00195 closesocket(s->fd); 00196 av_free(s); 00197 return 0; 00198 } 00199 00200 static int tcp_get_file_handle(URLContext *h) 00201 { 00202 TCPContext *s = h->priv_data; 00203 return s->fd; 00204 } 00205 00206 URLProtocol ff_tcp_protocol = { 00207 .name = "tcp", 00208 .url_open = tcp_open, 00209 .url_read = tcp_read, 00210 .url_write = tcp_write, 00211 .url_close = tcp_close, 00212 .url_get_file_handle = tcp_get_file_handle, 00213 };