[FFmpeg-devel,3/5] ffserver: Implement http interface and implementation

Submitted by Stephan Holljes on May 13, 2018, 12:07 a.m.

Details

Message ID 20180513000740.12548-4-klaxa1337@googlemail.com
State New
Headers show

Commit Message

Stephan Holljes May 13, 2018, 12:07 a.m.
Signed-off-by: Stephan Holljes <klaxa1337@googlemail.com>
---
 httpd.h     |  58 +++++++++++++++++++++++
 lavfhttpd.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+)
 create mode 100644 httpd.h
 create mode 100644 lavfhttpd.c

Patch hide | download patch | download mbox

diff --git a/httpd.h b/httpd.h
new file mode 100644
index 0000000..6fb91bd
--- /dev/null
+++ b/httpd.h
@@ -0,0 +1,58 @@ 
+/*
+ * 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
+ */
+
+#ifndef HTTPDINTERFACE_H
+#define HTTPDINTERFACE_H
+
+#define HTTPD_OK 0
+#define HTTPD_LISTEN_TIMEOUT -1
+#define HTTPD_CLIENT_ERROR -2
+#define HTTPD_OTHER_ERROR -3
+
+#include "publisher.h"
+
+/* HTTPD Config struct */
+struct HTTPDConfig {
+    char *bind_address;
+    int port;
+    int accept_timeout;
+};
+
+/* HTTPClient struct, this information is shared between ffserver and the httpd implementation */
+struct HTTPClient {
+    /* the method requested by the client, this field has to be set and freed by the httpd implementation */
+    char *method;
+    /* the resource requested by the client, this field has to be set and freed by the httpd implementation */
+    char *resource;
+    void *httpd_data; // httpd implementation specific data
+};
+
+/* HTTPDInterface that an httpd implementation must provide */
+struct HTTPDInterface {
+    int (*init)  (void **server, struct HTTPDConfig config);
+    int (*free)  (void *server);
+    int (*accept)(void *server, struct HTTPClient **client, int reply_code);
+    int (*write) (void *server, struct HTTPClient *client, const unsigned char *buf, int size);
+    int (*read)  (void *server, struct HTTPClient *client, unsigned char *buf, int size);
+    void (*close)(void *server, struct HTTPClient *client);
+    void (*shutdown)(void *server);
+};
+
+/* Current HTTPDInterface implementation using lavformat */
+extern struct HTTPDInterface lavfhttpd;
+#endif
diff --git a/lavfhttpd.c b/lavfhttpd.c
new file mode 100644
index 0000000..3cf9958
--- /dev/null
+++ b/lavfhttpd.c
@@ -0,0 +1,153 @@ 
+/*
+ * 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
+ */
+ 
+#ifndef LAVFHTTPD_H
+#define LAVFHTTPD_H
+
+#include "httpd.h"
+#include <libavutil/opt.h>
+
+
+int lavfhttpd_init(void **server, struct HTTPDConfig config)
+{
+    char out_uri[1024];
+    int ret;
+    AVDictionary *opts = NULL;
+    AVIOContext *server_ctx = NULL;
+    
+    snprintf(out_uri, 1024, "http://%s:%d", config.bind_address, config.port);
+    
+    avformat_network_init();
+    
+    if ((ret = av_dict_set(&opts, "listen", "2", 0)) < 0) {
+        av_log(opts, AV_LOG_ERROR, "Failed to set listen mode for server: %s\n", av_err2str(ret));
+        av_free(opts);
+        return -1;
+    }
+    
+    if ((ret = av_dict_set_int(&opts, "listen_timeout", config.accept_timeout, 0)) < 0) {
+        av_log(opts, AV_LOG_ERROR, "Failed to set listen_timeout for server: %s\n", av_err2str(ret));
+        av_free(opts);
+        return -1;
+    }
+    
+    if ((ret = avio_open2(&server_ctx, out_uri, AVIO_FLAG_WRITE, NULL, &opts)) < 0) {
+        av_log(server, AV_LOG_ERROR, "Failed to open server: %s\n", av_err2str(ret));
+        av_free(opts);
+        return -1;
+    }
+    av_free(opts);
+    
+    *server = server_ctx;
+    return 0;
+}
+
+int lavfhttpd_accept(void *server, struct HTTPClient **client, int reply_code)
+{
+    AVIOContext *server_ctx = (AVIOContext*) server;
+    AVIOContext *client_ctx = NULL;
+    struct HTTPClient *client_http = NULL;
+    int ret, ret2, handshake;
+    int reply_code2 = reply_code;
+    char *method, *resource;
+    if ((ret = avio_accept(server_ctx, &client_ctx)) < 0) {
+        if (ret == AVERROR(ETIMEDOUT))
+            return HTTPD_LISTEN_TIMEOUT;
+        else
+            return HTTPD_OTHER_ERROR;
+    }
+    client_ctx->seekable = 0;
+    ret2 = HTTPD_OK;
+    client_http = av_malloc(sizeof(struct HTTPClient));
+    client_http->method = NULL;
+    client_http->resource = NULL;
+    client_http->httpd_data = client_ctx;
+    while ((handshake = avio_handshake(client_ctx)) > 0) {
+        av_opt_get(client_ctx, "method", AV_OPT_SEARCH_CHILDREN, (uint8_t**) &method);
+        av_opt_get(client_ctx, "resource", AV_OPT_SEARCH_CHILDREN, (uint8_t**) &resource);
+        av_log(client_ctx, AV_LOG_DEBUG, "method: %s resource: %s\n", method, resource);
+        if (method && strlen(method) && strncmp("GET", method, 3)) {
+            ret2 = HTTPD_CLIENT_ERROR;
+            reply_code2 = 400;
+            if ((ret = av_opt_set_int(client_ctx, "reply_code", reply_code2, AV_OPT_SEARCH_CHILDREN)) < 0) {
+                av_log(client_ctx, AV_LOG_WARNING, "Failed to set reply_code: %s.\n", av_err2str(ret));
+            }
+        }
+        if (client_http->method)
+            av_free(client_http->method);
+        if (client_http->resource)
+            av_free(client_http->resource);
+        client_http->method = av_strdup(method);
+        client_http->resource = av_strdup(resource);
+        av_free(method);
+        av_free(resource);
+    }
+    if (handshake < 0) {
+        ret2 = HTTPD_CLIENT_ERROR;
+        reply_code2 = 400;
+    }
+    
+    if ((ret = av_opt_set_int(client_ctx, "reply_code", reply_code2, AV_OPT_SEARCH_CHILDREN)) < 0) {
+        av_log(client_ctx, AV_LOG_WARNING, "Failed to set reply_code: %s.\n", av_err2str(ret));
+    }
+    
+    *client = client_http;
+    return ret2;
+}
+
+int lavfhttpd_write(void *server, struct HTTPClient *client, const unsigned char *buf, int size)
+{
+    AVIOContext *client_ctx = (AVIOContext*) client->httpd_data;
+    avio_write(client_ctx, buf, size);
+    avio_flush(client_ctx);
+    return size;
+}
+
+int lavfhttpd_read(void *server, struct HTTPClient *client, unsigned char *buf, int size)
+{
+    AVIOContext *client_ctx = (AVIOContext*) client->httpd_data;
+    return avio_read(client_ctx, buf, size);
+}
+
+void lavfhttpd_close(void *server, struct HTTPClient *client)
+{
+    AVIOContext *client_ctx = (AVIOContext*) client->httpd_data;
+    avio_close(client_ctx);
+    if (client->method)
+        av_free(client->method);
+    if (client->resource)
+        av_free(client->resource);
+    av_free(client);
+}
+
+void lavfhttpd_shutdown(void *server)
+{
+    AVIOContext *server_ctx = (AVIOContext*) server;
+    avio_close(server_ctx);
+    avformat_network_deinit();
+}
+
+struct HTTPDInterface lavfhttpd = {
+    .init = lavfhttpd_init,
+    .accept = lavfhttpd_accept,
+    .write = lavfhttpd_write,
+    .close = lavfhttpd_close,
+    .shutdown = lavfhttpd_shutdown,
+};
+
+#endif