@@ -32,26 +32,12 @@
#include <poll.h>
#endif
-typedef struct TCPContext {
- const AVClass *class;
- int fd;
- int listen;
- int open_timeout;
- int rw_timeout;
- int listen_timeout;
- int recv_buffer_size;
- int send_buffer_size;
-} TCPContext;
-
-#define OFFSET(x) offsetof(TCPContext, x)
+#include "tcp.h"
+
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .flags = D|E },
- { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
- { "listen_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
- { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
- { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ TCP_COMMON_OPTS
{ NULL }
};
@@ -63,7 +49,7 @@ static const AVClass tcp_class = {
};
/* return non zero if error */
-static int tcp_open(URLContext *h, const char *uri, int flags)
+int ff_tcp_open(URLContext *h, const char *uri, int flags)
{
struct addrinfo hints = { 0 }, *ai, *cur_ai;
int port, fd = -1;
@@ -75,10 +61,15 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
char portstr[10];
s->open_timeout = 5000000;
+ s->api = s->api ? s->api : &bsd_socket_api;
+ s->proto = s->proto ? s->proto : "tcp";
+
av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
&port, path, sizeof(path), uri);
- if (strcmp(proto, "tcp"))
+ if (strcmp(proto, s->proto)) {
+ av_log(h, AV_LOG_ERROR, "incorrect protocol %s %s\n", proto, s->proto);
return AVERROR(EINVAL);
+ }
if (port <= 0 || port >= 65536) {
av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
return AVERROR(EINVAL);
@@ -132,37 +123,42 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
}
#endif
- fd = ff_socket(cur_ai->ai_family,
- cur_ai->ai_socktype,
- cur_ai->ai_protocol);
+ fd = s->api->socket(cur_ai->ai_family,
+ cur_ai->ai_socktype,
+ cur_ai->ai_protocol);
if (fd < 0) {
- ret = ff_neterrno();
+ ret = s->api->neterrno();
goto fail;
}
/* Set the socket's send or receive buffer sizes, if specified.
If unspecified or setting fails, system default is used. */
if (s->recv_buffer_size > 0) {
- setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
+ s->api->setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
}
if (s->send_buffer_size > 0) {
- setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
+ s->api->setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
+ }
+
+ if (s->set_options_pre) {
+ if ((ret = s->set_options_pre(h, fd)) < 0)
+ goto fail1;
}
if (s->listen == 2) {
// multi-client
- if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
+ if ((ret = ff_listen_ex(s->api, fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
goto fail1;
} else if (s->listen == 1) {
// single client
- if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
- s->listen_timeout, h)) < 0)
+ if ((ret = ff_listen_bind_ex(s->api, fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+ s->listen_timeout, h)) < 0)
goto fail1;
// Socket descriptor already closed here. Safe to overwrite to client one.
fd = ret;
} else {
- if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
- s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
+ if ((ret = ff_listen_connect_ex(s->api, fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+ s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
if (ret == AVERROR_EXIT)
goto fail1;
@@ -171,6 +167,11 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
}
}
+ if (s->set_options_post) {
+ if ((ret = s->set_options_post(h, fd)) < 0)
+ goto fail1;
+ }
+
h->is_streamed = 1;
s->fd = fd;
@@ -182,18 +183,18 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
/* Retry with the next sockaddr */
cur_ai = cur_ai->ai_next;
if (fd >= 0)
- closesocket(fd);
+ s->api->close(fd);
ret = 0;
goto restart;
}
fail1:
if (fd >= 0)
- closesocket(fd);
+ s->api->close(fd);
freeaddrinfo(ai);
return ret;
}
-static int tcp_accept(URLContext *s, URLContext **c)
+int ff_tcp_accept(URLContext *s, URLContext **c)
{
TCPContext *sc = s->priv_data;
TCPContext *cc;
@@ -202,42 +203,43 @@ static int tcp_accept(URLContext *s, URLContext **c)
if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
return ret;
cc = (*c)->priv_data;
- ret = ff_accept(sc->fd, sc->listen_timeout, s);
+ ret = ff_accept_ex(sc->api, sc->fd, sc->listen_timeout, s);
if (ret < 0)
return ret;
cc->fd = ret;
+ cc->api = sc->api;
return 0;
}
-static int tcp_read(URLContext *h, uint8_t *buf, int size)
+int ff_tcp_read(URLContext *h, uint8_t *buf, int size)
{
TCPContext *s = h->priv_data;
int ret;
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
- ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
+ ret = ff_network_wait_fd_timeout_ex(s->api, s->fd, 0, h->rw_timeout, &h->interrupt_callback);
if (ret)
return ret;
}
- ret = recv(s->fd, buf, size, 0);
- return ret < 0 ? ff_neterrno() : ret;
+ ret = s->api->recv(s->fd, buf, size, 0);
+ return ret < 0 ? s->api->neterrno() : ret;
}
-static int tcp_write(URLContext *h, const uint8_t *buf, int size)
+int ff_tcp_write(URLContext *h, const uint8_t *buf, int size)
{
TCPContext *s = h->priv_data;
int ret;
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
- ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+ ret = ff_network_wait_fd_timeout_ex(s->api, s->fd, 1, h->rw_timeout, &h->interrupt_callback);
if (ret)
return ret;
}
- ret = send(s->fd, buf, size, MSG_NOSIGNAL);
- return ret < 0 ? ff_neterrno() : ret;
+ ret = s->api->send(s->fd, buf, size, MSG_NOSIGNAL);
+ return ret < 0 ? s->api->neterrno() : ret;
}
-static int tcp_shutdown(URLContext *h, int flags)
+int ff_tcp_shutdown(URLContext *h, int flags)
{
TCPContext *s = h->priv_data;
int how;
@@ -250,23 +252,23 @@ static int tcp_shutdown(URLContext *h, int flags)
how = SHUT_RD;
}
- return shutdown(s->fd, how);
+ return s->api->shutdown(s->fd, how);
}
-static int tcp_close(URLContext *h)
+int ff_tcp_close(URLContext *h)
{
TCPContext *s = h->priv_data;
- closesocket(s->fd);
+ s->api->close(s->fd);
return 0;
}
-static int tcp_get_file_handle(URLContext *h)
+int ff_tcp_get_file_handle(URLContext *h)
{
TCPContext *s = h->priv_data;
return s->fd;
}
-static int tcp_get_window_size(URLContext *h)
+int ff_tcp_get_window_size(URLContext *h)
{
TCPContext *s = h->priv_data;
int avail;
@@ -280,22 +282,22 @@ static int tcp_get_window_size(URLContext *h)
}
#endif
- if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &avail, &avail_len)) {
- return ff_neterrno();
+ if (s->api->getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &avail, &avail_len)) {
+ return s->api->neterrno();
}
return avail;
}
const URLProtocol ff_tcp_protocol = {
.name = "tcp",
- .url_open = tcp_open,
- .url_accept = tcp_accept,
- .url_read = tcp_read,
- .url_write = tcp_write,
- .url_close = tcp_close,
- .url_get_file_handle = tcp_get_file_handle,
- .url_get_short_seek = tcp_get_window_size,
- .url_shutdown = tcp_shutdown,
+ .url_open = ff_tcp_open,
+ .url_accept = ff_tcp_accept,
+ .url_read = ff_tcp_read,
+ .url_write = ff_tcp_write,
+ .url_close = ff_tcp_close,
+ .url_get_file_handle = ff_tcp_get_file_handle,
+ .url_get_short_seek = ff_tcp_get_window_size,
+ .url_shutdown = ff_tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tcp_class,
new file mode 100644
@@ -0,0 +1,59 @@
+/*
+ * TCP protocol
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+typedef struct TCPContext {
+ const AVClass *class;
+ int fd;
+ int listen;
+ int open_timeout;
+ int rw_timeout;
+ int listen_timeout;
+ int recv_buffer_size;
+ int send_buffer_size;
+ const char * proto;
+ const struct socket_api * api;
+ int (*set_options_pre)(URLContext *h, int fd);
+ int (*set_options_post)(URLContext *h, int fd);
+} TCPContext;
+
+#define OFFSET(x) offsetof(TCPContext, x)
+#define TCP_COMMON_OPTS \
+ { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .flags = D|E }, \
+ { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, \
+ { "listen_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, \
+ { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, \
+ { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+
+int ff_tcp_open(URLContext *h, const char *uri, int flags);
+
+int ff_tcp_accept(URLContext *s, URLContext **c);
+
+int ff_tcp_read(URLContext *h, uint8_t *buf, int size);
+
+int ff_tcp_write(URLContext *h, const uint8_t *buf, int size);
+
+int ff_tcp_shutdown(URLContext *h, int flags);
+
+int ff_tcp_close(URLContext *h);
+
+int ff_tcp_get_file_handle(URLContext *h);
+
+int ff_tcp_get_window_size(URLContext *h);
this allows to implement other protocols which use API similar to BSD sockets (e.g. Haivision SRT) Signed-off-by: Nablet Developer <sdk@nablet.com> --- libavformat/tcp.c | 118 +++++++++++++++++++++++++++--------------------------- libavformat/tcp.h | 59 +++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 libavformat/tcp.h