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 <unistd.h>
00023 #include "internal.h"
00024 #include "network.h"
00025 #include "os_support.h"
00026 #if HAVE_POLL_H
00027 #include <poll.h>
00028 #endif
00029 #include <sys/time.h>
00030
00031 typedef struct TCPContext {
00032 int fd;
00033 } TCPContext;
00034
00035
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 int ret;
00042 socklen_t optlen;
00043 char hostname[1024],proto[1024],path[1024];
00044 char portstr[10];
00045
00046 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
00047 &port, path, sizeof(path), uri);
00048 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
00049 return AVERROR(EINVAL);
00050
00051 memset(&hints, 0, sizeof(hints));
00052 hints.ai_family = AF_UNSPEC;
00053 hints.ai_socktype = SOCK_STREAM;
00054 snprintf(portstr, sizeof(portstr), "%d", port);
00055 ret = getaddrinfo(hostname, portstr, &hints, &ai);
00056 if (ret) {
00057 av_log(NULL, AV_LOG_ERROR,
00058 "Failed to resolve hostname %s: %s\n",
00059 hostname, gai_strerror(ret));
00060 return AVERROR(EIO);
00061 }
00062
00063 cur_ai = ai;
00064
00065 restart:
00066 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
00067 if (fd < 0)
00068 goto fail;
00069 ff_socket_nonblock(fd, 1);
00070
00071 redo:
00072 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
00073 if (ret < 0) {
00074 struct pollfd p = {fd, POLLOUT, 0};
00075 if (ff_neterrno() == AVERROR(EINTR)) {
00076 if (url_interrupt_cb()) {
00077 ret = AVERROR_EXIT;
00078 goto fail1;
00079 }
00080 goto redo;
00081 }
00082 if (ff_neterrno() != AVERROR(EINPROGRESS) &&
00083 ff_neterrno() != AVERROR(EAGAIN))
00084 goto fail;
00085
00086
00087 for(;;) {
00088 if (url_interrupt_cb()) {
00089 ret = AVERROR_EXIT;
00090 goto fail1;
00091 }
00092 ret = poll(&p, 1, 100);
00093 if (ret > 0)
00094 break;
00095 }
00096
00097
00098 optlen = sizeof(ret);
00099 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
00100 if (ret != 0) {
00101 av_log(NULL, AV_LOG_ERROR,
00102 "TCP connection to %s:%d failed: %s\n",
00103 hostname, port, strerror(ret));
00104 goto fail;
00105 }
00106 }
00107 s = av_malloc(sizeof(TCPContext));
00108 if (!s) {
00109 freeaddrinfo(ai);
00110 return AVERROR(ENOMEM);
00111 }
00112 h->priv_data = s;
00113 h->is_streamed = 1;
00114 s->fd = fd;
00115 freeaddrinfo(ai);
00116 return 0;
00117
00118 fail:
00119 if (cur_ai->ai_next) {
00120
00121 cur_ai = cur_ai->ai_next;
00122 if (fd >= 0)
00123 closesocket(fd);
00124 goto restart;
00125 }
00126 ret = AVERROR(EIO);
00127 fail1:
00128 if (fd >= 0)
00129 closesocket(fd);
00130 freeaddrinfo(ai);
00131 return ret;
00132 }
00133
00134 static int tcp_wait_fd(int fd, int write)
00135 {
00136 int ev = write ? POLLOUT : POLLIN;
00137 struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
00138 int ret;
00139
00140 ret = poll(&p, 1, 100);
00141 return ret < 0 ? ff_neterrno() : p.revents & ev ? 0 : AVERROR(EAGAIN);
00142 }
00143
00144 static int tcp_read(URLContext *h, uint8_t *buf, int size)
00145 {
00146 TCPContext *s = h->priv_data;
00147 int ret;
00148
00149 if (!(h->flags & URL_FLAG_NONBLOCK)) {
00150 ret = tcp_wait_fd(s->fd, 0);
00151 if (ret < 0)
00152 return ret;
00153 }
00154 ret = recv(s->fd, buf, size, 0);
00155 return ret < 0 ? ff_neterrno() : ret;
00156 }
00157
00158 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
00159 {
00160 TCPContext *s = h->priv_data;
00161 int ret;
00162
00163 if (!(h->flags & URL_FLAG_NONBLOCK)) {
00164 ret = tcp_wait_fd(s->fd, 1);
00165 if (ret < 0)
00166 return ret;
00167 }
00168 ret = send(s->fd, buf, size, 0);
00169 return ret < 0 ? ff_neterrno() : ret;
00170 }
00171
00172 static int tcp_close(URLContext *h)
00173 {
00174 TCPContext *s = h->priv_data;
00175 closesocket(s->fd);
00176 av_free(s);
00177 return 0;
00178 }
00179
00180 static int tcp_get_file_handle(URLContext *h)
00181 {
00182 TCPContext *s = h->priv_data;
00183 return s->fd;
00184 }
00185
00186 URLProtocol ff_tcp_protocol = {
00187 "tcp",
00188 tcp_open,
00189 tcp_read,
00190 tcp_write,
00191 NULL,
00192 tcp_close,
00193 .url_get_file_handle = tcp_get_file_handle,
00194 };