diff --git a/Makefile b/Makefile index e6de824..ad116d9 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CC ?= gcc # ===== LIBS = -lm -ljpeg -pthread -levent -levent_pthreads -luuid override CFLAGS += -c -std=c11 -Wall -Wextra -D_GNU_SOURCE -SOURCES = $(shell ls src/*.c src/encoders/cpu/*.c src/encoders/hw/*.c) +SOURCES = $(shell ls src/*.c src/http/*.c src/encoders/cpu/*.c src/encoders/hw/*.c) PROG = ustreamer ifeq ($(shell ls -d /opt/vc/include 2>/dev/null), /opt/vc/include) @@ -29,8 +29,8 @@ install: $(PROG) regen: - tools/make-jpeg-h.py src/data/blank.jpeg src/data/blank_jpeg.h BLANK 640 480 - tools/make-html-h.py src/data/index.html src/data/index_html.h HTML_INDEX_PAGE + tools/make-jpeg-h.py src/http/data/blank.jpeg src/http/data/blank_jpeg.h BLANK + tools/make-html-h.py src/http/data/index.html src/http/data/index_html.h INDEX $(PROG): $(OBJECTS) @@ -59,5 +59,6 @@ push: clean-all: clean clean: - rm -f src/*.o src/encoders/*/*.o vgcore.* *.sock $(PROG) + find src -name '*.o' -exec rm '{}' \; + rm -f vgcore.* *.sock $(PROG) rm -rf pkg src/$(PROG)-* src/v*.tar.gz v*.tar.gz $(PROG)-*.pkg.tar.xz diff --git a/src/base64.c b/src/http/base64.c similarity index 99% rename from src/base64.c rename to src/http/base64.c index f9a299c..92e8cec 100644 --- a/src/base64.c +++ b/src/http/base64.c @@ -24,7 +24,7 @@ #include #include -#include "tools.h" +#include "../tools.h" #include "base64.h" diff --git a/src/base64.h b/src/http/base64.h similarity index 99% rename from src/base64.h rename to src/http/base64.h index 531e5df..ba55911 100644 --- a/src/base64.h +++ b/src/http/base64.h @@ -20,6 +20,8 @@ *****************************************************************************/ +#pragma once + #include diff --git a/src/data/blank.jpeg b/src/http/data/blank.jpeg similarity index 100% rename from src/data/blank.jpeg rename to src/http/data/blank.jpeg diff --git a/src/data/blank_jpeg.h b/src/http/data/blank_jpeg.h similarity index 99% rename from src/data/blank_jpeg.h rename to src/http/data/blank_jpeg.h index f83fb52..5a9f545 100644 --- a/src/data/blank_jpeg.h +++ b/src/http/data/blank_jpeg.h @@ -20,6 +20,9 @@ *****************************************************************************/ +#pragma once + + const unsigned BLANK_JPEG_WIDTH = 640; const unsigned BLANK_JPEG_HEIGHT = 480; diff --git a/src/data/index.html b/src/http/data/index.html similarity index 100% rename from src/data/index.html rename to src/http/data/index.html diff --git a/src/data/index_html.h b/src/http/data/index_html.h similarity index 99% rename from src/data/index_html.h rename to src/http/data/index_html.h index bab2b91..409e4f0 100644 --- a/src/data/index_html.h +++ b/src/http/data/index_html.h @@ -22,7 +22,7 @@ #pragma once -#include "../config.h" +#include "../../config.h" const char HTML_INDEX_PAGE[] = " \ diff --git a/src/http.c b/src/http/server.c similarity index 98% rename from src/http.c rename to src/http/server.c index 6b34704..1d12e72 100644 --- a/src/http.c +++ b/src/http/server.c @@ -46,12 +46,13 @@ # error Required libevent-pthreads support #endif -#include "tools.h" -#include "logging.h" -#include "encoder.h" -#include "stream.h" +#include "../tools.h" +#include "../logging.h" +#include "../encoder.h" +#include "../stream.h" + #include "base64.h" -#include "http.h" +#include "server.h" #include "data/index_html.h" #include "data/blank_jpeg.h" @@ -92,6 +93,7 @@ struct http_server_t *http_server_init(struct stream_t *stream) { A_CALLOC(server, 1); server->host = "127.0.0.1"; server->port = 8080; + server->unix_path = ""; server->user = ""; server->passwd = ""; server->static_path = ""; @@ -188,7 +190,7 @@ int http_server_listen(struct http_server_t *server) { LOG_INFO("Using HTTP basic auth"); } - if (server->unix_path) { + if (server->unix_path[0] != '\0') { struct sockaddr_un unix_addr; int unix_fd_flags; @@ -255,7 +257,12 @@ static bool _http_get_param_true(struct evkeyvalq *params, const char *key) { const char *value_str; if ((value_str = evhttp_find_header(params, key)) != NULL) { - if (!strcasecmp(value_str, "true") || !strcasecmp(value_str, "yes") || value_str[0] == '1') { + if ( + value_str[0] == '1' + || value_str[0] == 'y' + || !evutil_ascii_strcasecmp(value_str, "true") + || !evutil_ascii_strcasecmp(value_str, "yes") + ) { return true; } } diff --git a/src/http.h b/src/http/server.h similarity index 98% rename from src/http.h rename to src/http/server.h index 74b1a42..9ca4622 100644 --- a/src/http.h +++ b/src/http/server.h @@ -20,6 +20,8 @@ *****************************************************************************/ +#pragma once + #include #include @@ -28,8 +30,8 @@ #include #include -#include "tools.h" -#include "stream.h" +#include "../tools.h" +#include "../stream.h" struct stream_client_t { diff --git a/src/main.c b/src/main.c index 130db1e..97823b0 100644 --- a/src/main.c +++ b/src/main.c @@ -38,7 +38,7 @@ #include "device.h" #include "encoder.h" #include "stream.h" -#include "http.h" +#include "http/server.h" static const char _SHORT_OPTS[] = "d:i:x:y:m:a:f:z:ntb:w:q:c:s:p:u:ro:e:lhv"; diff --git a/tools/make-html-h.py b/tools/make-html-h.py index 72ef4d4..8263565 100755 --- a/tools/make-html-h.py +++ b/tools/make-html-h.py @@ -22,56 +22,36 @@ import sys +import os import textwrap # ===== def main(): - assert len(sys.argv) == 4, "%s " % (sys.argv[0]) - - src = sys.argv[1] - dest = sys.argv[2] + assert len(sys.argv) == 4, "%s " % (sys.argv[0]) + html_path = sys.argv[1] + header_path = sys.argv[2] name = sys.argv[3] - with open(src, "r") as html_file: + with open(html_path, "r") as html_file: text = html_file.read() + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "prepend.h")) as prepend_file: + prepend = prepend_file.read() + text = text.strip() text = text.replace("\"", "\\\"") text = text.replace("%VERSION%", "\" VERSION \"") text = textwrap.indent(text, "\t", (lambda line: True)) - text = "\n".join(("%s \\" if line.strip() else "%s\\") % (line) for line in text.split("\n")) - text = "const char %s[] = \" \\\n%s\n\";\n" % (name, text) - text = textwrap.dedent(""" - /***************************************************************************** - # # - # uStreamer - Lightweight and fast MJPG-HTTP streamer. # - # # - # Copyright (C) 2018 Maxim Devaev # - # # - # This program is free software: you can redistribute it and/or modify # - # it under the terms of the GNU General Public License as published by # - # the Free Software Foundation, either version 3 of the License, or # - # (at your option) any later version. # - # # - # This program 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 General Public License for more details. # - # # - # You should have received a copy of the GNU General Public License # - # along with this program. If not, see . # - # # - *****************************************************************************/ + text = "\n".join( + ("%s \\" if line.strip() else "%s\\") % (line) + for line in text.split("\n") + ) + text = "const char HTML_%s_PAGE[] = \" \\\n%s\n\";\n" % (name, text) + text = prepend + "\n#include \"../../config.h\"\n\n\n" + text - - #pragma once - - #include "../config.h" - """).strip() + "\n\n\n" + text - - with open(dest, "w") as h_file: - h_file.write(text) + with open(header_path, "w") as header_file: + header_file.write(text) # ===== diff --git a/tools/make-jpeg-h.py b/tools/make-jpeg-h.py index cc6d0fc..e6faf25 100755 --- a/tools/make-jpeg-h.py +++ b/tools/make-jpeg-h.py @@ -22,22 +22,53 @@ import sys -import textwrap +import os +import io +import struct + + +# ===== +def _get_jpeg_size(data): + # https://sheep.horse/2013/9/finding_the_dimensions_of_a_jpeg_file_in_python.html + + stream = io.BytesIO(data) + while True: + marker = struct.unpack(">H", stream.read(2))[0] + if ( + marker == 0xFFD8 # Start of image + or marker == 0xFF01 # Private marker + or (marker >= 0xFFD0 and marker <= 0xFFD7) # Restart markers + ): + continue + elif marker == 0xFFD9: + raise RuntimeError("Can't find jpeg size") + + # All other markers specify chunks with lengths + length = struct.unpack(">H", stream.read(2))[0] + + if marker == 0xFFC0: # Baseline DCT chunk, has the info we want + (_, height, width) = struct.unpack(">BHH", stream.read(5)) + return (width, height) + + # Not the chunk we want, skip it + stream.seek(length - 2, 1) # ===== def main(): - assert len(sys.argv) == 6, "%s " % (sys.argv[0]) + assert len(sys.argv) == 4, "%s " % (sys.argv[0]) + jpeg_path = sys.argv[1] + header_path = sys.argv[2] + name = sys.argv[3] - src = sys.argv[1] - dest = sys.argv[2] - prefix = sys.argv[3] - width = int(sys.argv[4]) - height = int(sys.argv[5]) - - with open(src, "rb") as jpeg_file: + with open(jpeg_path, "rb") as jpeg_file: jpeg_data = jpeg_file.read() + (width, height) = _get_jpeg_size(jpeg_data) + + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "prepend.h")) as prepend_file: + prepend = prepend_file.read() + rows = [[]] for ch in jpeg_data: if len(rows[-1]) > 20: @@ -45,34 +76,13 @@ def main(): rows[-1].append("0x%.2X" % (ch)) text = ",\n\t".join(", ".join(row) for row in rows) - text = "const unsigned char %s_JPEG_DATA[] = {\n\t%s\n};\n" % (prefix, text) - text = "const unsigned %s_JPEG_HEIGHT = %d;\n\n" % (prefix, height) + text - text = "const unsigned %s_JPEG_WIDTH = %d;\n" % (prefix, width) + text - text = textwrap.dedent(""" - /***************************************************************************** - # # - # uStreamer - Lightweight and fast MJPG-HTTP streamer. # - # # - # Copyright (C) 2018 Maxim Devaev # - # # - # This program is free software: you can redistribute it and/or modify # - # it under the terms of the GNU General Public License as published by # - # the Free Software Foundation, either version 3 of the License, or # - # (at your option) any later version. # - # # - # This program 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 General Public License for more details. # - # # - # You should have received a copy of the GNU General Public License # - # along with this program. If not, see . # - # # - *****************************************************************************/ - """).strip() + "\n\n\n" + text + text = "const unsigned char %s_JPEG_DATA[] = {\n\t%s\n};\n" % (name, text) + text = "const unsigned %s_JPEG_HEIGHT = %d;\n\n" % (name, height) + text + text = "const unsigned %s_JPEG_WIDTH = %d;\n" % (name, width) + text + text = prepend + "\n\n" + text - with open(dest, "w") as h_file: - h_file.write(text) + with open(header_path, "w") as header_file: + header_file.write(text) # ===== diff --git a/tools/prepend.h b/tools/prepend.h new file mode 100644 index 0000000..4788ffb --- /dev/null +++ b/tools/prepend.h @@ -0,0 +1,23 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program 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 General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#pragma once