From patchwork Wed Aug 1 23:00:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephan Holljes X-Patchwork-Id: 9864 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:104:0:0:0:0:0 with SMTP id c4-v6csp1386207jad; Wed, 1 Aug 2018 16:08:28 -0700 (PDT) X-Google-Smtp-Source: AAOMgpeZ+GFofOd5gYn5OqJKs6Vi4Bp1wYt7Tuvg++tUsgWV3c1Xyzp22Px1eiK3EeRdtSVEkwjR X-Received: by 2002:a1c:8291:: with SMTP id e139-v6mr238779wmd.39.1533164908161; Wed, 01 Aug 2018 16:08:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533164908; cv=none; d=google.com; s=arc-20160816; b=tq1jCoe2IO6bfo1DNb3cmtQJWMleRCn9j8D6W/ReyMqL1+WZXe9QQXFqYA6k2epV2M sNH/vUHJVsqR0ijvqrrsZOXJd2HBGCYcAoHH6B7m/0zQMQlwSiBv+rq6/JtYBfQgmdYz WdmgD72/GJk6SUSu/l9+8QnV3DxZeR4aaghMefKsPi4f9knzxhAuig5CmBEhHkDiGbHd uc6VNZ+p3kiyfbVgm3MVi78431dvVuW3AuKUvUOBrqytCmRc5tR76JN21uC8vgLGFr0M XRppulE6SQw7vLobF8z1HtCfUeU4XN8rrlxPLN00GwMkDhNlcW6ooTz8ezTVnqeUvS0W pNeg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=lk6P+BHDnPJx2xySTz+/tMXLPFnOzn8G5SnP4/hWUaY=; b=Cou/sFyUvQnYRTebqFswkrrJjcjt+7XjoVU+T15Ahkc3/EmB3QBw5DmR9LRI78zXfP ZDiOX+t6sXFwg1t5KDcU+NwMNzXA2n4i1kSjZrXKQABddBt6KXPvgK+XSXQrcDFT3npe cLzxrYVEl2e5QS6c1cpYuvN9iH2QHD+ajnnNos2C4zbp5DywqQM6eGzjOJ4SQquNBV0V rq/H4Zsvd220xcjGtedSQmwjIDwRYQd345RjeTs8bwc2aV2iNyt/c0eVz1oFccqqndfb wM32x/vo2Xq96/yNv1ijNrIj0jRS1kKlmo/KZlilE/UBAK8pYG+o+QViyEfZS+kG+pSt c+tQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=sVOUK1la; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id l18-v6si56146wme.197.2018.08.01.16.08.27; Wed, 01 Aug 2018 16:08:28 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=sVOUK1la; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id DF6A568A558; Thu, 2 Aug 2018 02:08:08 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 26C3E680C3E for ; Thu, 2 Aug 2018 02:08:02 +0300 (EEST) Received: by mail-wm0-f68.google.com with SMTP id r24-v6so756002wmh.0 for ; Wed, 01 Aug 2018 16:08:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Al+enLmKRSnLJSuvCU4nTkrF4P+Yjz1mIAb6AVb/BxA=; b=sVOUK1lagbqSznCbvlyEu0RvroTJCwJijW+vwa0EoEgVguuiF/GXsuEd8DjQedfBm/ 7aVBe63y/8X8oehcxjF9E9FlC01Is8BpYw7y+HFWuakRW0L64v1sQxcSpY+lO/oZ2P8k 4LRDGmMOe13rOsNLO3W1V6o8xNf0NwvozuCDndLvjcehJHHzbeDieX8dFNKb71p+iv6D NxCWXDqVe8zsGk2+T9JlvngJHVI1IjC4tj7MUNp3Is+sZF1OGDYskyEUQsxJpI5taoZd pNxjepHcrRz77zvrwJ/YWlahttzk47AEpk0QlRfHS7PPTK+1SNpppfP9Tz0WovobpNm9 7Tzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Al+enLmKRSnLJSuvCU4nTkrF4P+Yjz1mIAb6AVb/BxA=; b=MLoMKLC4QeRoJJ31vgBO1QiLnWNmTAIVAjATcCI+bl0RSFa0YUl8PJ7As7VOT8QNxs FBwzOjmtxZFp92EMmQTJvyDBE1yLRI+CfaakJ4j89q6lu+3wiWXi5IGY/Fx020hf5Qmq lc41LKEVKygKCN+7DlVB31Iv9wq+5htAAaEvNNY2GwX7OXt1Cse/jj1LgsniplFMlbz3 xSGYJekrZHSfFyk4o0XzOjRVLVrKomvA2Xf6P/E3tgnGcZYQh1y60ydP5JiUvk9gAC+0 b9dWY7visPzJ2UsIVxT9F8lRRIELqFmqV35m0+Yi22hh7q3xjZ38fuhzdmoFLMnKmQ69 daoA== X-Gm-Message-State: AOUpUlFcDhclxb+OxKjorXluuKX2X255ODfAs7GdJPJhxy4DrdDZh5PU B9e9A5h6ehZyMiVjr/6NZJIRT+0K X-Received: by 2002:a1c:1203:: with SMTP id 3-v6mr302237wms.46.1533164434387; Wed, 01 Aug 2018 16:00:34 -0700 (PDT) Received: from localhost.localdomain ([46.5.2.0]) by smtp.gmail.com with ESMTPSA id e133-v6sm101201wma.33.2018.08.01.16.00.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 01 Aug 2018 16:00:33 -0700 (PDT) From: Stephan Holljes To: ffmpeg-devel@ffmpeg.org Date: Thu, 2 Aug 2018 01:00:22 +0200 Message-Id: <1533164424-12395-6-git-send-email-klaxa1337@googlemail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1533164424-12395-1-git-send-email-klaxa1337@googlemail.com> References: <1533164424-12395-1-git-send-email-klaxa1337@googlemail.com> Subject: [FFmpeg-devel] [PATCH 5/7] lmhttpd.c/ffserver.c/httpd.h/Makefile: Add libmicrohttpd httpd interface implementation. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Stephan Holljes MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Stephan Holljes --- Makefile | 8 +- ffserver.c | 2 +- httpd.h | 1 + lmhttpd.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 lmhttpd.c diff --git a/Makefile b/Makefile index 18f3ac3..4714734 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,12 @@ all: ffserver LAV_FLAGS = $(shell pkg-config --libs --cflags libavformat libavcodec libavutil) LUA_FLAGS = $(shell pkg-config --libs --cflags lua5.3) +MHD_FLAGS = $(shell pkg-config --libs --cflags libmicrohttpd) CFLAGS=-fsanitize=address -fsanitize=undefined # LAV_FLAGS = -L/usr/local/lib -lavcodec -lavformat -lavutil -ffserver: segment.o publisher.o fileserver.o lavfhttpd.o configreader.o ffserver.c - cc -g -Wall $(CFLAGS) $(LAV_FLAGS) $(LUA_FLAGS) -lpthread -o ffserver segment.o publisher.o fileserver.o lavfhttpd.o configreader.o ffserver.c +ffserver: segment.o publisher.o fileserver.o lavfhttpd.o lmhttpd.o configreader.o ffserver.c + cc -g -Wall $(CFLAGS) $(LAV_FLAGS) $(LUA_FLAGS) $(MHD_FLAGS) -lpthread -o ffserver segment.o publisher.o fileserver.o lavfhttpd.o lmhttpd.o configreader.o ffserver.c segment.o: segment.c segment.h cc -g -Wall $(CFLAGS) $(LAV_FLAGS) -lpthread -c segment.c @@ -19,6 +20,9 @@ fileserver.o: fileserver.c fileserver.h lavfhttpd.o: lavfhttpd.c httpd.h cc -g -Wall $(CFLAGS) $(LAV_FLAGS) -lpthread -c lavfhttpd.c +lmhttpd.o: lmhttpd.c httpd.h + cc -g -Wall $(CFLAGS) $(LAV_FLAGS) $(MHD_FLAGS) -lpthread -c lmhttpd.c + configreader.o: configreader.c configreader.h httpd.h cc -g -Wall $(CFLAGS) $(LAV_FLAGS) $(LUA_FLAGS) -c configreader.c clean: diff --git a/ffserver.c b/ffserver.c index 59c1b4d..de96c7b 100644 --- a/ffserver.c +++ b/ffserver.c @@ -797,7 +797,7 @@ void *run_server(void *arg) { ainfo.fs = fs; ainfo.ifmt_ctxs = ifmt_ctxs; ainfo.nb_pub = config->nb_streams; - ainfo.httpd = &lavfhttpd; + ainfo.httpd = &lmhttpd; ainfo.config = config; rinfos = av_mallocz_array(config->nb_streams, sizeof(struct ReadInfo)); diff --git a/httpd.h b/httpd.h index 1b2566e..e61211a 100644 --- a/httpd.h +++ b/httpd.h @@ -83,4 +83,5 @@ struct FFServerInfo { /** Current HTTPDInterface implementation using lavformat */ extern struct HTTPDInterface lavfhttpd; +extern struct HTTPDInterface lmhttpd; #endif diff --git a/lmhttpd.c b/lmhttpd.c new file mode 100644 index 0000000..b4fcdc6 --- /dev/null +++ b/lmhttpd.c @@ -0,0 +1,310 @@ +/* + * 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 LMHTTPD_H +#define LMHTTPD_H + +#define MAX_CLIENTS 16 +#define INITIAL_BUFSIZE 16 * 1024 * 1024 + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "httpd.h" + +struct MHDServer { + struct MHD_Daemon *daemon; + AVFifoBuffer *clients; +}; + +struct ConnectionInfo { + AVFifoBuffer *buffer; + pthread_mutex_t buffer_lock; + struct MHD_Connection *connection; + int close_connection; +}; + + +/** + * Helper callback that fills the libmicrohttpd-client buffer with data. + */ + +ssize_t helper_callback(void *cls, uint64_t pos, char *buf, size_t max) +{ + struct ConnectionInfo *cinfo = (struct ConnectionInfo*) cls; + pthread_mutex_lock(&cinfo->buffer_lock); + int buf_size = av_fifo_size(cinfo->buffer); + if (buf_size > 0) { + max = max > buf_size ? buf_size : max; + av_fifo_generic_read(cinfo->buffer, buf, max, NULL); + } else { + max = 0; + } + if (max == 0 && cinfo->close_connection) { + av_fifo_free(cinfo->buffer); + pthread_mutex_unlock(&cinfo->buffer_lock); + av_free(cinfo); + return MHD_CONTENT_READER_END_OF_STREAM; + } + pthread_mutex_unlock(&cinfo->buffer_lock); + return max; +} + +/** + * Free allocated callback params. Usually passed to MHD_create_response_from_callback, however it frees data too soon. + */ + +static void free_callback_param (void *cls) +{ + av_free(cls); +} + +/** + * Callback that handles incoming connections. + * + * Incoming connections are initialized and added to a queue of new clients. + */ + +static int answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) +{ + static int aptr; + struct MHD_Response *response; + struct HTTPClient *client; + int ret; + struct MHDServer *server = (struct MHDServer*) cls; + if (&aptr != *con_cls) + { + /* do never respond on first call (why? this is in every example... something about keeping track of con_cls?) */ + *con_cls = &aptr; + return MHD_YES; + } + *con_cls = NULL; + struct ConnectionInfo *cinfo = av_malloc(sizeof(struct ConnectionInfo)); + if (!cinfo) + return MHD_NO; + + pthread_mutex_init(&cinfo->buffer_lock, NULL); + if (MHD_set_connection_option(connection, MHD_CONNECTION_OPTION_TIMEOUT, (unsigned int) 10) != MHD_YES) + return MHD_NO; + + av_log(NULL, AV_LOG_ERROR, "Accepted new client %s %s %p\n", method, url, connection); + client = av_malloc(sizeof(struct HTTPClient)); + if (!client) + return MHD_NO; + // no locking needed, running on the same thread + + if (!strcmp("GET", method)) { + if (av_fifo_space(server->clients) > sizeof(struct HTTPClient*)) { + cinfo->buffer = av_fifo_alloc(INITIAL_BUFSIZE); + cinfo->connection = connection; + cinfo->close_connection = 0; + + if (!cinfo->buffer) + return MHD_NO; + *con_cls = cinfo; + client->resource = av_strdup(url); + client->method = av_strdup(method); + client->httpd_data = cinfo; + av_fifo_generic_write(server->clients, &client, sizeof(struct HTTPClient*), NULL); + } + response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, + 1024, + &helper_callback, + cinfo, + NULL); + + } else { + response = MHD_create_response_from_buffer (0, + (void *) "", + MHD_RESPMEM_MUST_COPY); + } + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + + return ret; +} + +/** + * Initialize the libmicrohttpd server with a config. + * + * Allocates and starts daemon. + */ + +int lmhttpd_init(void **server, struct HTTPDConfig config) { + struct MHDServer *server_p = av_malloc(sizeof(struct MHDServer)); + *server = NULL; + if (!server_p) { + av_log(NULL, AV_LOG_ERROR, "Could not allocate MHDServer struct\n"); + return -1; + } + server_p->clients = av_fifo_alloc_array(sizeof(struct HTTPClient), MAX_CLIENTS); + server_p->daemon = MHD_start_daemon (0, config.port, NULL, NULL, + &answer_to_connection, server_p, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 10, MHD_OPTION_END); + if (!server_p->daemon || !server_p->clients) { + av_log(NULL, AV_LOG_ERROR, "Could not allocate MHD_Daemon\n"); + return -1; + } + *server = server_p; + return 0; +} + +/** + * Return a single new client that connected and run webserver write and read operations. + * + * This basically synchronizes the libmicrohttpd client API through a queue. + */ + +int lmhttpd_accept(void *server, struct HTTPClient **client, const char **valid_files) +{ + fd_set rs; + fd_set ws; + fd_set es; + struct timeval tv; + MHD_socket max; + //MHD_UNSIGNED_LONG_LONG mhd_timeout; + struct MHDServer *s = (struct MHDServer*) server; + + max = 0; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + + if (MHD_get_fdset (s->daemon, &rs, &ws, &es, &max) != MHD_YES) + return HTTPD_OTHER_ERROR; + tv.tv_sec = 0; + tv.tv_usec = 500000; // 0.5 seconds + if (select (max + 1, &rs, &ws, &es, &tv) == -1) + return HTTPD_OTHER_ERROR; + + // run read and write operations + if (MHD_run_from_select(s->daemon, &rs, &ws, &es) != MHD_YES) + return HTTPD_OTHER_ERROR; + if(av_fifo_size(s->clients)) { + av_fifo_generic_read(s->clients, client, sizeof(struct HTTPClient*), NULL); + + return 0; + } + return HTTPD_LISTEN_TIMEOUT; +} + +/** + * Write data into a client buffer managed by libmicrohttpd. + */ + +int lmhttpd_write(void *server, struct HTTPClient *client, const unsigned char *buf, int size) +{ + struct ConnectionInfo* cinfo = (struct ConnectionInfo*) client->httpd_data; + int ret; + pthread_mutex_lock(&cinfo->buffer_lock); + if (!cinfo->close_connection && av_fifo_space(cinfo->buffer) >= size) { + ret = av_fifo_generic_write(cinfo->buffer, (void*) buf, size, NULL); + + } else { + ret = -1; + } + pthread_mutex_unlock(&cinfo->buffer_lock); + if (cinfo->close_connection) { + free_callback_param(cinfo); + ret = -1; + } + return ret; +} + +/** + * Unimplemented + */ + +int lmhttpd_read(void *server, struct HTTPClient *client, unsigned char *buf, int size) +{ + return 0; +} + +/** + * Close a connection by signaling to close it through the ConnectionInfo struct. + */ +void lmhttpd_close(void *server, struct HTTPClient *client) +{ + struct ConnectionInfo* cinfo = (struct ConnectionInfo*) client->httpd_data; + cinfo->close_connection = 1; + av_free(client->method); + av_free(client->resource); + av_free(client); +} + +/** + * Shutdown the libmicrohttpd daemon. + */ +void lmhttpd_shutdown(void *server) +{ + struct MHDServer *mhd_server = (struct MHDServer*) server; + fd_set rs; + fd_set ws; + fd_set es; + struct timeval tv; + MHD_socket max; + tv.tv_sec = 0; + tv.tv_usec = 500000; // 0.5 seconds + + MHD_quiesce_daemon(mhd_server->daemon); + + while (1) { + max = 0; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + + if (MHD_get_fdset (mhd_server->daemon, &rs, &ws, &es, &max) != MHD_YES) + break; + + if (max == 0) + break; + + if (select (max + 1, &rs, &ws, &es, &tv) == -1) + break; + + if (MHD_run_from_select(mhd_server->daemon, &rs, &ws, &es) != MHD_YES) + break; + } + + MHD_stop_daemon(mhd_server->daemon); + av_fifo_free(mhd_server->clients); + av_free(mhd_server); +} + +struct HTTPDInterface lmhttpd = { + .init = lmhttpd_init, + .accept = lmhttpd_accept, + .write = lmhttpd_write, + .close = lmhttpd_close, + .shutdown = lmhttpd_shutdown, +}; + +#endif