Libav
|
00001 /* 00002 * TCP protocol 00003 * Copyright (c) 2002 Fabrice Bellard 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg 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 * FFmpeg 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 FFmpeg; 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 <unistd.h> 00023 #include "internal.h" 00024 #include "network.h" 00025 #include "os_support.h" 00026 #if HAVE_SYS_SELECT_H 00027 #include <sys/select.h> 00028 #endif 00029 #include <sys/time.h> 00030 00031 typedef struct TCPContext { 00032 int fd; 00033 } TCPContext; 00034 00035 /* return non zero if error */ 00036 static int tcp_open(URLContext *h, const char *uri, int flags) 00037 { 00038 struct addrinfo hints, *ai, *cur_ai; 00039 int port, fd = -1; 00040 TCPContext *s = NULL; 00041 fd_set wfds; 00042 int fd_max, ret; 00043 struct timeval tv; 00044 socklen_t optlen; 00045 char hostname[1024],proto[1024],path[1024]; 00046 char portstr[10]; 00047 00048 ff_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), 00049 &port, path, sizeof(path), uri); 00050 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) 00051 return AVERROR(EINVAL); 00052 00053 memset(&hints, 0, sizeof(hints)); 00054 hints.ai_family = AF_UNSPEC; 00055 hints.ai_socktype = SOCK_STREAM; 00056 snprintf(portstr, sizeof(portstr), "%d", port); 00057 if (getaddrinfo(hostname, portstr, &hints, &ai)) 00058 return AVERROR(EIO); 00059 00060 cur_ai = ai; 00061 00062 restart: 00063 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); 00064 if (fd < 0) 00065 goto fail; 00066 ff_socket_nonblock(fd, 1); 00067 00068 redo: 00069 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 00070 if (ret < 0) { 00071 if (ff_neterrno() == FF_NETERROR(EINTR)) 00072 goto redo; 00073 if (ff_neterrno() != FF_NETERROR(EINPROGRESS) && 00074 ff_neterrno() != FF_NETERROR(EAGAIN)) 00075 goto fail; 00076 00077 /* wait until we are connected or until abort */ 00078 for(;;) { 00079 if (url_interrupt_cb()) { 00080 ret = AVERROR(EINTR); 00081 goto fail1; 00082 } 00083 fd_max = fd; 00084 FD_ZERO(&wfds); 00085 FD_SET(fd, &wfds); 00086 tv.tv_sec = 0; 00087 tv.tv_usec = 100 * 1000; 00088 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); 00089 if (ret > 0 && FD_ISSET(fd, &wfds)) 00090 break; 00091 } 00092 00093 /* test error */ 00094 optlen = sizeof(ret); 00095 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); 00096 if (ret != 0) 00097 goto fail; 00098 } 00099 s = av_malloc(sizeof(TCPContext)); 00100 if (!s) { 00101 freeaddrinfo(ai); 00102 return AVERROR(ENOMEM); 00103 } 00104 h->priv_data = s; 00105 h->is_streamed = 1; 00106 s->fd = fd; 00107 freeaddrinfo(ai); 00108 return 0; 00109 00110 fail: 00111 if (cur_ai->ai_next) { 00112 /* Retry with the next sockaddr */ 00113 cur_ai = cur_ai->ai_next; 00114 if (fd >= 0) 00115 closesocket(fd); 00116 goto restart; 00117 } 00118 ret = AVERROR(EIO); 00119 fail1: 00120 if (fd >= 0) 00121 closesocket(fd); 00122 freeaddrinfo(ai); 00123 return ret; 00124 } 00125 00126 static int tcp_read(URLContext *h, uint8_t *buf, int size) 00127 { 00128 TCPContext *s = h->priv_data; 00129 int len, fd_max, ret; 00130 fd_set rfds; 00131 struct timeval tv; 00132 00133 for (;;) { 00134 if (url_interrupt_cb()) 00135 return AVERROR(EINTR); 00136 fd_max = s->fd; 00137 FD_ZERO(&rfds); 00138 FD_SET(s->fd, &rfds); 00139 tv.tv_sec = 0; 00140 tv.tv_usec = 100 * 1000; 00141 ret = select(fd_max + 1, &rfds, NULL, NULL, &tv); 00142 if (ret > 0 && FD_ISSET(s->fd, &rfds)) { 00143 len = recv(s->fd, buf, size, 0); 00144 if (len < 0) { 00145 if (ff_neterrno() != FF_NETERROR(EINTR) && 00146 ff_neterrno() != FF_NETERROR(EAGAIN)) 00147 return AVERROR(ff_neterrno()); 00148 } else return len; 00149 } else if (ret < 0) { 00150 if (ff_neterrno() == FF_NETERROR(EINTR)) 00151 continue; 00152 return -1; 00153 } 00154 } 00155 } 00156 00157 static int tcp_write(URLContext *h, uint8_t *buf, int size) 00158 { 00159 TCPContext *s = h->priv_data; 00160 int ret, size1, fd_max, len; 00161 fd_set wfds; 00162 struct timeval tv; 00163 00164 size1 = size; 00165 while (size > 0) { 00166 if (url_interrupt_cb()) 00167 return AVERROR(EINTR); 00168 fd_max = s->fd; 00169 FD_ZERO(&wfds); 00170 FD_SET(s->fd, &wfds); 00171 tv.tv_sec = 0; 00172 tv.tv_usec = 100 * 1000; 00173 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); 00174 if (ret > 0 && FD_ISSET(s->fd, &wfds)) { 00175 len = send(s->fd, buf, size, 0); 00176 if (len < 0) { 00177 if (ff_neterrno() != FF_NETERROR(EINTR) && 00178 ff_neterrno() != FF_NETERROR(EAGAIN)) 00179 return AVERROR(ff_neterrno()); 00180 continue; 00181 } 00182 size -= len; 00183 buf += len; 00184 } else if (ret < 0) { 00185 if (ff_neterrno() == FF_NETERROR(EINTR)) 00186 continue; 00187 return -1; 00188 } 00189 } 00190 return size1 - size; 00191 } 00192 00193 static int tcp_close(URLContext *h) 00194 { 00195 TCPContext *s = h->priv_data; 00196 closesocket(s->fd); 00197 av_free(s); 00198 return 0; 00199 } 00200 00201 static int tcp_get_file_handle(URLContext *h) 00202 { 00203 TCPContext *s = h->priv_data; 00204 return s->fd; 00205 } 00206 00207 URLProtocol tcp_protocol = { 00208 "tcp", 00209 tcp_open, 00210 tcp_read, 00211 tcp_write, 00212 NULL, /* seek */ 00213 tcp_close, 00214 .url_get_file_handle = tcp_get_file_handle, 00215 };