[FFmpeg-devel] [PATCH 6/8] ffserver.c: Add config file reading

Stephan Holljes klaxa1337 at googlemail.com
Sun May 20 21:54:02 EEST 2018


Signed-off-by: Stephan Holljes <klaxa1337 at googlemail.com>
---
 ffserver.c | 248 ++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 172 insertions(+), 76 deletions(-)

diff --git a/ffserver.c b/ffserver.c
index 44fc263..3d842d8 100644
--- a/ffserver.c
+++ b/ffserver.c
@@ -38,6 +38,7 @@
 #include "segment.h"
 #include "publisher.h"
 #include "httpd.h"
+#include "configreader.h"
 
 #define BUFFER_SECS 30
 #define LISTEN_TIMEOUT_MSEC 1000
@@ -54,9 +55,11 @@ struct WriteInfo {
 };
 
 struct AcceptInfo {
-    struct PublisherContext *pub;
+    struct PublisherContext **pubs;
     struct HTTPDInterface *httpd;
-    AVFormatContext *ifmt_ctx;
+    AVFormatContext **ifmt_ctxs;
+    struct HTTPDConfig *config;
+    int nb_pub; /* number of publishers (streams) equal to number of ifmt_ctx */
 };
 
 
@@ -287,52 +290,77 @@ void *accept_thread(void *arg)
 {
     struct AcceptInfo *info = (struct AcceptInfo*) arg;
     struct FFServerInfo *ffinfo = NULL;
+    struct PublisherContext *pub;
     char status[4096];
+    char *stream_name;
     struct HTTPClient *client = NULL;
     void *server = NULL;
     AVIOContext *client_ctx = NULL;
     AVFormatContext *ofmt_ctx = NULL;
+    AVFormatContext *ifmt_ctx;
     unsigned char *avio_buffer;
     AVOutputFormat *ofmt;
     AVDictionary *mkvopts = NULL;
     AVStream *in_stream, *out_stream;
     int ret, i, reply_code;
-    struct HTTPDConfig config = {
-        .bind_address = "0",
-        .port = 8080,
-        .accept_timeout = LISTEN_TIMEOUT_MSEC,
-    };
-    
-    info->httpd->init(&server, config);
-    
-    
+    int shutdown;
+    struct HTTPDConfig *config = info->config;
+
+    info->httpd->init(&server, *config);
+
     for (;;) {
-        if (info->pub->shutdown)
+        shutdown = 1;
+        for (i = 0; i < config->nb_streams; i++) {
+            if (info->pubs[i] && !info->pubs[i]->shutdown)
+                shutdown = 0;
+        }
+        if (shutdown)
             break;
-        publisher_gen_status_json(info->pub, status);
-        av_log(server, AV_LOG_INFO, status);
+        for (i = 0; i < config->nb_streams; i++) {
+            publisher_gen_status_json(info->pubs[i], status);
+            av_log(server, AV_LOG_INFO, status);
+        }
         client = NULL;
         av_log(server, AV_LOG_DEBUG, "Accepting new clients.\n");
         reply_code = 200;
-        if (publisher_reserve_client(info->pub)) {
-            av_log(client, AV_LOG_WARNING, "No more client slots free, Returning 503.\n");
-            reply_code = 503;
-        }
-        
+
         if ((ret = info->httpd->accept(server, &client, reply_code)) < 0) {
             if (ret == HTTPD_LISTEN_TIMEOUT) {
-                publisher_cancel_reserve(info->pub);
                 continue;
             } else if (ret == HTTPD_CLIENT_ERROR) {
                 info->httpd->close(server, client);
             }
             av_log(server, AV_LOG_WARNING, "Error during accept, retrying.\n");
-            publisher_cancel_reserve(info->pub);
             continue;
         }
-        
+
+        pub = NULL;
+        ifmt_ctx = NULL;
+        for (i = 0; i < config->nb_streams; i++) {
+            stream_name = info->pubs[i]->stream_name;
+            //       skip leading '/'  ---v
+            if(!strncmp(client->resource + 1, stream_name, strlen(stream_name))) {
+                pub = info->pubs[i];
+                ifmt_ctx = info->ifmt_ctxs[i];
+                break;
+            }
+        }
+
+        if (!pub || !ifmt_ctx) {
+            av_log(client_ctx, AV_LOG_WARNING, "No suitable publisher found for resource: %s.\n",
+                                                        client->resource ? client->resource : "(null)");
+            reply_code = 404;
+        }
+
+
+        if (pub && ifmt_ctx && publisher_reserve_client(pub)) {
+            av_log(client_ctx, AV_LOG_WARNING, "No more client slots free, Returning 503.\n");
+            reply_code = 503;
+        }
+
         if (reply_code != 200) {
-            publisher_cancel_reserve(info->pub);
+            if (pub && ifmt_ctx)
+                publisher_cancel_reserve(pub);
             info->httpd->close(server, client);
             continue;
         }
@@ -345,7 +373,7 @@ void *accept_thread(void *arg)
         client_ctx = avio_alloc_context(avio_buffer, AV_BUFSIZE, 1, ffinfo, NULL, &ffserver_write, NULL);
         if (!client_ctx) {
             av_log(client, AV_LOG_ERROR, "Could not allocate output format context.\n");
-            publisher_cancel_reserve(info->pub);
+            publisher_cancel_reserve(pub);
             info->httpd->close(server, client);
             av_free(client_ctx->buffer);
             avio_context_free(&client_ctx);
@@ -355,7 +383,7 @@ void *accept_thread(void *arg)
         avformat_alloc_output_context2(&ofmt_ctx, NULL, "matroska", NULL);
         if (!ofmt_ctx) {
             av_log(client, AV_LOG_ERROR, "Could not allocate output format context.\n");
-            publisher_cancel_reserve(info->pub);
+            publisher_cancel_reserve(pub);
             info->httpd->close(server, client);
             avformat_free_context(ofmt_ctx);
             av_free(client_ctx->buffer);
@@ -365,7 +393,7 @@ void *accept_thread(void *arg)
         }
         if ((ret = av_dict_set(&mkvopts, "live", "1", 0)) < 0) {
             av_log(client, AV_LOG_ERROR, "Failed to set live mode for matroska: %s\n", av_err2str(ret));
-            publisher_cancel_reserve(info->pub);
+            publisher_cancel_reserve(pub);
             info->httpd->close(server, client);
             avformat_free_context(ofmt_ctx);
             av_free(client_ctx->buffer);
@@ -377,13 +405,13 @@ void *accept_thread(void *arg)
         ofmt = ofmt_ctx->oformat;
         ofmt->flags |= AVFMT_NOFILE | AVFMT_FLAG_AUTO_BSF;
         
-        for (i = 0; i < info->ifmt_ctx->nb_streams; i++) {
-            in_stream = info->ifmt_ctx->streams[i];
+        for (i = 0; i < ifmt_ctx->nb_streams; i++) {
+            in_stream = ifmt_ctx->streams[i];
             out_stream = avformat_new_stream(ofmt_ctx, NULL);
             
             if (!out_stream) {
                 av_log(client, AV_LOG_ERROR, "Could not allocate output stream.\n");
-                publisher_cancel_reserve(info->pub);
+                publisher_cancel_reserve(pub);
                 info->httpd->close(server, client);
                 avformat_free_context(ofmt_ctx);
                 av_free(client_ctx->buffer);
@@ -395,7 +423,7 @@ void *accept_thread(void *arg)
             ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
             if (ret < 0) {
                 av_log(client, AV_LOG_ERROR, "Failed to copy context from input to output stream codec context: %s.\n", av_err2str(ret));
-                publisher_cancel_reserve(info->pub);
+                publisher_cancel_reserve(pub);
                 info->httpd->close(server, client);
                 avformat_free_context(ofmt_ctx);
                 av_free(client_ctx->buffer);
@@ -412,12 +440,12 @@ void *accept_thread(void *arg)
             }
             av_dict_copy(&out_stream->metadata, in_stream->metadata, 0);
         }
-        av_dict_copy(&info->ifmt_ctx->metadata, ofmt_ctx->metadata, 0);
+        av_dict_copy(&ifmt_ctx->metadata, ofmt_ctx->metadata, 0);
         ofmt_ctx->pb = client_ctx;
         ret = avformat_write_header(ofmt_ctx, &mkvopts);
         if (ret < 0) {
             av_log(client, AV_LOG_ERROR, "Could not write header to client: %s.\n", av_err2str(ret));
-            publisher_cancel_reserve(info->pub);
+            publisher_cancel_reserve(pub);
             info->httpd->close(server, client);
             avformat_free_context(ofmt_ctx);
             av_free(client_ctx->buffer);
@@ -425,7 +453,7 @@ void *accept_thread(void *arg)
             av_free(ffinfo);
             continue;
         }
-        publisher_add_client(info->pub, ofmt_ctx, ffinfo);
+        publisher_add_client(pub, ofmt_ctx, ffinfo);
         ofmt_ctx = NULL;
         
     }
@@ -467,59 +495,127 @@ void *write_thread(void *arg)
     return NULL;
 }
 
-
-int main(int argc, char *argv[])
-{
-    struct ReadInfo rinfo;
+void *run_server(void *arg) {
     struct AcceptInfo ainfo;
-    struct WriteInfo *winfos;
-    struct PublisherContext *pub;
-    int ret, i;
-    pthread_t r_thread, a_thread;
-    pthread_t *w_threads;
+    struct ReadInfo *rinfos;
+    struct WriteInfo **winfos_p;
+    struct HTTPDConfig *config = (struct HTTPDConfig*) arg;
+    struct PublisherContext **pubs;
+    AVFormatContext **ifmt_ctxs;
+    int ret, i, stream_index;
+    pthread_t *r_threads;
+    pthread_t **w_threads_p;
     
-    AVFormatContext *ifmt_ctx = NULL;
-    
-    rinfo.in_filename = "pipe:0";
-    if (argc > 1)
-        rinfo.in_filename = argv[1];
+    pubs = av_mallocz(config->nb_streams * sizeof(struct PublisherContext*));
+    ifmt_ctxs = av_mallocz(config->nb_streams * sizeof(AVFormatContext*));
     
     av_log_set_level(AV_LOG_INFO);
     
-    if ((ret = avformat_open_input(&ifmt_ctx, rinfo.in_filename, NULL, NULL))) {
-        av_log(NULL, AV_LOG_ERROR, "main: Could not open input\n");
-        return 1;
-    }
-    
-    publisher_init(&pub);
-    
-    rinfo.ifmt_ctx = ifmt_ctx;
-    rinfo.pub = pub;
-    ainfo.ifmt_ctx = ifmt_ctx;
-    ainfo.pub = pub;
+    ainfo.pubs = pubs;
+    ainfo.ifmt_ctxs = ifmt_ctxs;
+    ainfo.nb_pub = config->nb_streams;
     ainfo.httpd = &lavfhttpd;
+    ainfo.config = config;
     
-    w_threads = (pthread_t*) av_malloc(sizeof(pthread_t) * pub->nb_threads);
-    winfos = (struct WriteInfo*) av_malloc(sizeof(struct WriteInfo) * pub->nb_threads);
+    rinfos = av_mallocz(config->nb_streams * sizeof(struct ReadInfo));
+    winfos_p = av_mallocz(config->nb_streams * sizeof(struct WriteInfo*));
+    r_threads = av_mallocz(config->nb_streams * sizeof(pthread_t));
+    w_threads_p = av_mallocz(config->nb_streams * sizeof(pthread_t*));
     
-    for (i = 0; i < pub->nb_threads; i++) {
-        winfos[i].pub = pub;
-        winfos[i].thread_id = i;
-        pthread_create(&w_threads[i], NULL, write_thread, &winfos[i]);
+    for (stream_index = 0; stream_index < config->nb_streams; stream_index++) {
+        struct PublisherContext *pub = NULL;
+        struct AVFormatContext *ifmt_ctx = NULL;
+        struct ReadInfo rinfo;
+        struct WriteInfo *winfos = NULL;
+        pthread_t *w_threads = NULL;
+        pthread_t r_thread;
+        rinfo.input_uri = config->streams[stream_index].input_uri;
+
+        if ((ret = avformat_open_input(&ifmt_ctx, rinfo.input_uri, NULL, NULL))) {
+            av_log(NULL, AV_LOG_ERROR, "run_server: Could not open input\n");
+            continue;
+        }
+
+        ifmt_ctxs[stream_index] = ifmt_ctx;
+
+        publisher_init(&pub, config->streams[stream_index].stream_name);
+        pubs[stream_index] = pub;
+
+        rinfo.ifmt_ctx = ifmt_ctx;
+        rinfo.pub = pub;
+
+        rinfos[stream_index] = rinfo;
+
+        w_threads = av_malloc(sizeof(pthread_t) * pub->nb_threads);
+        winfos = av_malloc(sizeof(struct WriteInfo) * pub->nb_threads);
+
+        w_threads_p[stream_index] = w_threads;
+        winfos_p[stream_index] = winfos;
+
+        for (i = 0; i < pub->nb_threads; i++) {
+            winfos[i].pub = pub;
+            winfos[i].thread_id = i;
+            pthread_create(&w_threads[i], NULL, write_thread, &winfos_p[stream_index][i]);
+        }
+        w_threads_p[stream_index] = w_threads;
+        pthread_create(&r_thread, NULL, read_thread, &rinfos[stream_index]);
+        r_threads[stream_index] = r_thread;
     }
-    
-    pthread_create(&r_thread, NULL, read_thread, &rinfo);
-    
+
+
+    //pthread_create(&a_thread, NULL, accept_thread, &ainfo);
     accept_thread(&ainfo);
-    
-    pthread_join(r_thread, NULL);
-    
-    for (i = 0; i < pub->nb_threads; i++) {
-        pthread_join(w_threads[i], NULL);
+    for (stream_index = 0; stream_index < config->nb_streams; stream_index++) {
+        pthread_join(r_threads[stream_index], NULL);
+        if (pubs[stream_index]) {
+            for (i = 0; i < pubs[stream_index]->nb_threads; i++) {
+                pthread_join(w_threads_p[stream_index][i], NULL);
+            }
+        }
+        av_free(winfos_p[stream_index]);
+        av_free(w_threads_p[stream_index]);
+        // pubs[stream_index] could be null if the file could not be opened
+        if (pubs[stream_index])
+            publisher_free(pubs[stream_index]);
     }
-    av_free(w_threads);
-    av_free(winfos);
-    
-    publisher_freep(&pub);
+    av_free(rinfos);
+    av_free(winfos_p);
+    av_free(r_threads);
+    av_free(w_threads_p);
+    av_free(pubs);
+    av_free(ifmt_ctxs);
+
+    return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+    struct HTTPDConfig *configs;
+    int nb_configs;
+    pthread_t *server_threads;
+    int i;
+
+    if (argc < 2) {
+        printf("Usage: %s config.lua\n", argv[0]);
+        return 1;
+    }
+
+    nb_configs = configs_read(&configs, argv[1]);
+    if (nb_configs <= 0) {
+        printf("No valid configurations parsed.\n");
+        return 1;
+    }
+    server_threads = av_malloc(nb_configs * sizeof(pthread_t));
+    for (i = 0; i < nb_configs; i++) {
+        config_dump(configs + i);
+        pthread_create(&server_threads[i], NULL, run_server, configs + i);
+    }
+
+    for (i = 0; i < nb_configs; i++) {
+        pthread_join(server_threads[i], NULL);
+        config_free(configs + i);
+    }
+    av_free(configs);
+    av_free(server_threads);
     return 0;
 }
-- 
2.16.2



More information about the ffmpeg-devel mailing list