[FFmpeg-devel] [PATCH 2/3] avformat/tcp: use generic socket API

Nablet Developer sdk at nablet.com
Thu Nov 9 11:31:31 EET 2017


this allows to implement other protocols which use
API similar to BSD sockets (e.g. Haivision SRT)

Signed-off-by: Nablet Developer <sdk at nablet.com>
---
 libavformat/tcp.c | 118 +++++++++++++++++++++++++++---------------------------
 libavformat/tcp.h |  59 +++++++++++++++++++++++++++
 2 files changed, 119 insertions(+), 58 deletions(-)
 create mode 100644 libavformat/tcp.h

diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index 07b4ed9..a775230 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -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,
diff --git a/libavformat/tcp.h b/libavformat/tcp.h
new file mode 100644
index 0000000..df6cc07
--- /dev/null
+++ b/libavformat/tcp.h
@@ -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);
-- 
2.7.4



More information about the ffmpeg-devel mailing list