X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Ftcp.c;h=1d953e3a113099779cb9ee1a4ba3b30efbbb89c3;hb=08cd95e8a37674401ed24e5e6f4f7402edb7fdeb;hp=37e6d6f10e3671b70ee9acbc5fcfc503fb193359;hpb=2912e87a6c9264d556734e2bf94a99c64cf9b102;p=ffmpeg diff --git a/libavformat/tcp.c b/libavformat/tcp.c index 37e6d6f10e3..1d953e3a113 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -19,14 +19,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" -#include +#include "libavutil/parseutils.h" #include "internal.h" #include "network.h" #include "os_support.h" +#include "url.h" #if HAVE_POLL_H #include #endif -#include typedef struct TCPContext { int fd; @@ -35,11 +35,15 @@ typedef struct TCPContext { /* return non zero if error */ static int tcp_open(URLContext *h, const char *uri, int flags) { - struct addrinfo hints, *ai, *cur_ai; + struct addrinfo hints = { 0 }, *ai, *cur_ai; int port, fd = -1; - TCPContext *s = NULL; + TCPContext *s = h->priv_data; + int listen_socket = 0; + const char *p; + char buf[256]; int ret; socklen_t optlen; + int timeout = 100, listen_timeout = -1; char hostname[1024],proto[1024],path[1024]; char portstr[10]; @@ -48,13 +52,28 @@ static int tcp_open(URLContext *h, const char *uri, int flags) if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) return AVERROR(EINVAL); - memset(&hints, 0, sizeof(hints)); + p = strchr(uri, '?'); + if (p) { + if (av_find_info_tag(buf, sizeof(buf), "listen", p)) + listen_socket = 1; + if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { + timeout = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { + listen_timeout = strtol(buf, NULL, 10); + } + } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, sizeof(portstr), "%d", port); - ret = getaddrinfo(hostname, portstr, &hints, &ai); + if (listen_socket) + hints.ai_flags |= AI_PASSIVE; + if (!hostname[0]) + ret = getaddrinfo(NULL, portstr, &hints, &ai); + else + ret = getaddrinfo(hostname, portstr, &hints, &ai); if (ret) { - av_log(NULL, AV_LOG_ERROR, + av_log(h, AV_LOG_ERROR, "Failed to resolve hostname %s: %s\n", hostname, gai_strerror(ret)); return AVERROR(EIO); @@ -63,29 +82,62 @@ static int tcp_open(URLContext *h, const char *uri, int flags) cur_ai = ai; restart: + ret = AVERROR(EIO); fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); if (fd < 0) goto fail; - ff_socket_nonblock(fd, 1); + if (listen_socket) { + int fd1; + int reuse = 1; + struct pollfd lp = { fd, POLLIN, 0 }; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); + if (ret) { + ret = ff_neterrno(); + goto fail1; + } + ret = listen(fd, 1); + if (ret) { + ret = ff_neterrno(); + goto fail1; + } + ret = poll(&lp, 1, listen_timeout >= 0 ? listen_timeout : -1); + if (ret <= 0) { + ret = AVERROR(ETIMEDOUT); + goto fail1; + } + fd1 = accept(fd, NULL, NULL); + if (fd1 < 0) { + ret = ff_neterrno(); + goto fail1; + } + closesocket(fd); + fd = fd1; + ff_socket_nonblock(fd, 1); + } else { redo: - ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); + ff_socket_nonblock(fd, 1); + ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); + } + if (ret < 0) { struct pollfd p = {fd, POLLOUT, 0}; - if (ff_neterrno() == AVERROR(EINTR)) { - if (url_interrupt_cb()) { + ret = ff_neterrno(); + if (ret == AVERROR(EINTR)) { + if (ff_check_interrupt(&h->interrupt_callback)) { ret = AVERROR_EXIT; goto fail1; } goto redo; } - if (ff_neterrno() != AVERROR(EINPROGRESS) && - ff_neterrno() != AVERROR(EAGAIN)) + if (ret != AVERROR(EINPROGRESS) && + ret != AVERROR(EAGAIN)) goto fail; /* wait until we are connected or until abort */ - for(;;) { - if (url_interrupt_cb()) { + while(timeout--) { + if (ff_check_interrupt(&h->interrupt_callback)) { ret = AVERROR_EXIT; goto fail1; } @@ -93,23 +145,24 @@ static int tcp_open(URLContext *h, const char *uri, int flags) if (ret > 0) break; } - + if (ret <= 0) { + ret = AVERROR(ETIMEDOUT); + goto fail; + } /* test error */ optlen = sizeof(ret); - getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); + if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) + ret = AVUNERROR(ff_neterrno()); if (ret != 0) { - av_log(NULL, AV_LOG_ERROR, + char errbuf[100]; + ret = AVERROR(ret); + av_strerror(ret, errbuf, sizeof(errbuf)); + av_log(h, AV_LOG_ERROR, "TCP connection to %s:%d failed: %s\n", - hostname, port, strerror(ret)); + hostname, port, errbuf); goto fail; } } - s = av_malloc(sizeof(TCPContext)); - if (!s) { - freeaddrinfo(ai); - return AVERROR(ENOMEM); - } - h->priv_data = s; h->is_streamed = 1; s->fd = fd; freeaddrinfo(ai); @@ -123,7 +176,6 @@ static int tcp_open(URLContext *h, const char *uri, int flags) closesocket(fd); goto restart; } - ret = AVERROR(EIO); fail1: if (fd >= 0) closesocket(fd); @@ -131,23 +183,13 @@ static int tcp_open(URLContext *h, const char *uri, int flags) return ret; } -static int tcp_wait_fd(int fd, int write) -{ - int ev = write ? POLLOUT : POLLIN; - struct pollfd p = { .fd = fd, .events = ev, .revents = 0 }; - int ret; - - ret = poll(&p, 1, 100); - return ret < 0 ? ff_neterrno() : p.revents & ev ? 0 : AVERROR(EAGAIN); -} - static int tcp_read(URLContext *h, uint8_t *buf, int size) { TCPContext *s = h->priv_data; int ret; - if (!(h->flags & URL_FLAG_NONBLOCK)) { - ret = tcp_wait_fd(s->fd, 0); + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd(s->fd, 0); if (ret < 0) return ret; } @@ -160,8 +202,8 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) TCPContext *s = h->priv_data; int ret; - if (!(h->flags & URL_FLAG_NONBLOCK)) { - ret = tcp_wait_fd(s->fd, 1); + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd(s->fd, 1); if (ret < 0) return ret; } @@ -169,11 +211,26 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) return ret < 0 ? ff_neterrno() : ret; } +static int tcp_shutdown(URLContext *h, int flags) +{ + TCPContext *s = h->priv_data; + int how; + + if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { + how = SHUT_RDWR; + } else if (flags & AVIO_FLAG_WRITE) { + how = SHUT_WR; + } else { + how = SHUT_RD; + } + + return shutdown(s->fd, how); +} + static int tcp_close(URLContext *h) { TCPContext *s = h->priv_data; closesocket(s->fd); - av_free(s); return 0; } @@ -184,11 +241,13 @@ static int tcp_get_file_handle(URLContext *h) } URLProtocol ff_tcp_protocol = { - "tcp", - tcp_open, - tcp_read, - tcp_write, - NULL, /* seek */ - tcp_close, + .name = "tcp", + .url_open = tcp_open, + .url_read = tcp_read, + .url_write = tcp_write, + .url_close = tcp_close, .url_get_file_handle = tcp_get_file_handle, + .url_shutdown = tcp_shutdown, + .priv_data_size = sizeof(TCPContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, };