diff --git a/Makefile b/Makefile index 132a9b4e55..593d91c254 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,11 @@ amd64_wheels: amd64_ffmpeg: docker build --tag blakeblackshear/frigate-ffmpeg:1.1.0-amd64 --file docker/Dockerfile.ffmpeg.amd64 . +nginx: + docker buildx build --platform linux/arm/v7,linux/arm64/v8,linux/amd64 --tag blakeblackshear/frigate-nginx:1.0.0 --file docker/Dockerfile.nginx . + amd64_frigate: version web - docker build --tag frigate-base --build-arg ARCH=amd64 --build-arg FFMPEG_VERSION=1.1.0 --build-arg WHEELS_VERSION=1.0.3 --file docker/Dockerfile.base . + docker build --tag frigate-base --build-arg ARCH=amd64 --build-arg FFMPEG_VERSION=1.1.0 --build-arg WHEELS_VERSION=1.0.3 --build-arg NGINX_VERSION=1.0.0 --file docker/Dockerfile.base . docker build --tag frigate --file docker/Dockerfile.amd64 . amd64_all: amd64_wheels amd64_ffmpeg amd64_frigate diff --git a/docker-compose.yml b/docker-compose.yml index ad88008ff5..7f1624f1a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,7 @@ services: - "5000:5000" - "5001:5001" - "8080:8080" - command: /bin/sh -c "sudo service nginx start; while sleep 1000; do :; done" + command: /bin/sh -c "sudo /usr/local/nginx/sbin/nginx; while sleep 1000; do :; done" mqtt: container_name: mqtt image: eclipse-mosquitto:1.6 diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index c3e9e4c258..0c8b5ebf4f 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,8 +1,10 @@ ARG ARCH=amd64 ARG WHEELS_VERSION ARG FFMPEG_VERSION +ARG NGINX_VERSION FROM blakeblackshear/frigate-wheels:${WHEELS_VERSION}-${ARCH} as wheels FROM blakeblackshear/frigate-ffmpeg:${FFMPEG_VERSION}-${ARCH} as ffmpeg +FROM blakeblackshear/frigate-nginx:${NGINX_VERSION} as nginx FROM frigate-web as web FROM ubuntu:20.04 @@ -18,16 +20,13 @@ ENV DEBIAN_FRONTEND=noninteractive # Install packages for apt repo RUN apt-get -qq update \ && apt-get upgrade -y \ - && apt-get -qq install --no-install-recommends -y \ - gnupg wget unzip tzdata nginx libnginx-mod-rtmp \ - && apt-get -qq install --no-install-recommends -y \ - python3-pip \ + && apt-get -qq install --no-install-recommends -y gnupg wget unzip tzdata libxml2 \ + && apt-get -qq install --no-install-recommends -y python3-pip \ && pip3 install -U /wheels/*.whl \ && APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn apt-key adv --fetch-keys https://packages.cloud.google.com/apt/doc/apt-key.gpg \ && echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" > /etc/apt/sources.list.d/coral-edgetpu.list \ && echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections \ - && apt-get -qq update && apt-get -qq install --no-install-recommends -y \ - libedgetpu1-max=15.0 \ + && apt-get -qq update && apt-get -qq install --no-install-recommends -y libedgetpu1-max=15.0 \ && rm -rf /var/lib/apt/lists/* /wheels \ && (apt-get autoremove -y; apt-get autoclean -y) @@ -39,7 +38,8 @@ RUN pip3 install \ gevent \ gevent-websocket -COPY nginx/nginx.conf /etc/nginx/nginx.conf +COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/ +COPY nginx/nginx.conf /usr/local/nginx/conf/nginx.conf # get model and labels COPY labelmap.txt /labelmap.txt diff --git a/docker/Dockerfile.nginx b/docker/Dockerfile.nginx new file mode 100644 index 0000000000..bfa7d277af --- /dev/null +++ b/docker/Dockerfile.nginx @@ -0,0 +1,46 @@ +FROM ubuntu:20.04 AS base + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get -yqq update && \ + apt-get install -yq --no-install-recommends ca-certificates expat libgomp1 && \ + apt-get autoremove -y && \ + apt-get clean -y + +FROM base as build + +ARG NGINX_VERSION=1.18.0 +ARG VOD_MODULE_VERSION=1.28 +ARG RTMP_MODULE_VERSION=1.2.1 + +RUN cp /etc/apt/sources.list /etc/apt/sources.list~ \ + && sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list \ + && apt-get update + +RUN apt-get -yqq build-dep nginx + +RUN apt-get -yqq install --no-install-recommends curl \ + && mkdir /tmp/nginx \ + && curl -sL https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar -C /tmp/nginx -zx --strip-components=1 \ + && mkdir /tmp/nginx-vod-module \ + && curl -sL https://github.com/kaltura/nginx-vod-module/archive/refs/tags/${VOD_MODULE_VERSION}.tar.gz | tar -C /tmp/nginx-vod-module -zx --strip-components=1 \ + && mkdir /tmp/nginx-rtmp-module \ + && curl -sL https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v${RTMP_MODULE_VERSION}.tar.gz | tar -C /tmp/nginx-rtmp-module -zx --strip-components=1 + +WORKDIR /tmp/nginx + +RUN ./configure --prefix=/usr/local/nginx \ + --with-file-aio \ + --with-http_sub_module \ + --with-http_ssl_module \ + --with-threads \ + --add-module=../nginx-vod-module \ + --add-module=../nginx-rtmp-module \ + --with-cc-opt="-O3 -Wno-error=implicit-fallthrough" + +RUN make && make install +RUN rm -rf /usr/local/nginx/html /usr/local/nginx/conf/*.default + +FROM base +COPY --from=build /usr/local/nginx /usr/local/nginx +ENTRYPOINT ["/usr/local/nginx/sbin/nginx"] +CMD ["-g", "daemon off;"] \ No newline at end of file diff --git a/frigate/app.py b/frigate/app.py index e599da715e..80992373c5 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -316,7 +316,11 @@ def receiveSignal(signalNumber, frame): server = pywsgi.WSGIServer( ("127.0.0.1", 5001), self.flask_app, handler_class=WebSocketHandler ) - server.serve_forever() + + try: + server.serve_forever() + except KeyboardInterrupt: + pass self.stop() diff --git a/frigate/http.py b/frigate/http.py index d94b1b597b..7630d2c056 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -1,6 +1,7 @@ import base64 import datetime import json +import glob import logging import os import time @@ -23,7 +24,7 @@ from peewee import SqliteDatabase, operator, fn, DoesNotExist from playhouse.shortcuts import model_to_dict -from frigate.const import CLIPS_DIR +from frigate.const import CLIPS_DIR, RECORD_DIR from frigate.models import Event from frigate.stats import stats_snapshot from frigate.util import calculate_region @@ -186,13 +187,15 @@ def event(id): except DoesNotExist: return "Event not found", 404 -@bp.route('/events/', methods=('DELETE',)) + +@bp.route("/events/", methods=("DELETE",)) def delete_event(id): try: event = Event.get(Event.id == id) except DoesNotExist: - return make_response(jsonify({"success": False, "message": "Event" + id + " not found"}),404) - + return make_response( + jsonify({"success": False, "message": "Event" + id + " not found"}), 404 + ) media_name = f"{event.camera}-{event.id}" if event.has_snapshot: @@ -203,12 +206,12 @@ def delete_event(id): media.unlink(missing_ok=True) event.delete_instance() - return make_response(jsonify({"success": True, "message": "Event" + id + " deleted"}),200) - - + return make_response( + jsonify({"success": True, "message": "Event" + id + " deleted"}), 200 + ) -@bp.route('/events//thumbnail.jpg') +@bp.route("/events//thumbnail.jpg") def event_thumbnail(id): format = request.args.get("format", "ios") thumbnail_bytes = None @@ -446,10 +449,37 @@ def latest_frame(camera_name): return "Camera named {} not found".format(camera_name), 404 +@bp.route("/vod/") +def vod(path): + if not os.path.isdir(f"{RECORD_DIR}/{path}"): + return "Recordings not found.", 404 + + files = glob.glob(f"{RECORD_DIR}/{path}/*.mp4") + files.sort() + + clips = [] + durations = [] + for filename in files: + clips.append({"type": "source", "path": filename}) + video = cv2.VideoCapture(filename) + duration = int( + video.get(cv2.CAP_PROP_FRAME_COUNT) / video.get(cv2.CAP_PROP_FPS) * 1000 + ) + durations.append(duration) + + return jsonify( + { + "discontinuity": False, + "durations": durations, + "sequences": [{"clips": clips}], + } + ) + + def imagestream(detected_frames_processor, camera_name, fps, height, draw_options): while True: # max out at specified FPS - gevent.sleep(1/fps) + gevent.sleep(1 / fps) frame = detected_frames_processor.get_current_frame(camera_name, draw_options) if frame is None: frame = np.zeros((height, int(height * 16 / 9), 3), np.uint8) diff --git a/frigate/log.py b/frigate/log.py index 69e653313b..efe7d554a4 100644 --- a/frigate/log.py +++ b/frigate/log.py @@ -35,7 +35,7 @@ def log_process(log_queue): while True: try: record = log_queue.get(timeout=5) - except queue.Empty: + except (queue.Empty, KeyboardInterrupt): continue logger = logging.getLogger(record.name) logger.handle(record) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 06760a08ec..293be3121d 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -1,23 +1,24 @@ worker_processes 1; -error_log /var/log/nginx/error.log warn; +error_log /usr/local/nginx/logs/error.log warn; pid /var/run/nginx.pid; -load_module "modules/ngx_rtmp_module.so"; +# load_module "modules/ngx_rtmp_module.so"; +# load_module "modules/ngx_http_vod_module.so"; events { worker_connections 1024; } http { - include /etc/nginx/mime.types; + include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; - access_log /var/log/nginx/access.log main; + access_log /usr/local/nginx/logs/access.log main; sendfile on; @@ -37,6 +38,39 @@ http { server { listen 5000; + # vod settings + vod_mode mapped; + vod_max_mapping_response_size 1m; + vod_upstream_location /api; + vod_last_modified 'Sun, 19 Nov 2000 08:52:00 GMT'; + vod_last_modified_types *; + + # vod caches + vod_metadata_cache metadata_cache 512m; + vod_response_cache response_cache 128m; + vod_mapping_cache mapping_cache 5m; + + # gzip manifests + gzip on; + gzip_types application/vnd.apple.mpegurl; + + # file handle caching / aio + open_file_cache max=1000 inactive=5m; + open_file_cache_valid 2m; + open_file_cache_min_uses 1; + open_file_cache_errors on; + aio on; + + location /vod/ { + vod hls; + + add_header Access-Control-Allow-Headers '*'; + add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range'; + add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS'; + add_header Access-Control-Allow-Origin '*'; + expires 100d; + } + location /stream/ { add_header 'Cache-Control' 'no-cache'; add_header 'Access-Control-Allow-Origin' "$http_origin" always; diff --git a/run.sh b/run.sh index 4ad3587e1a..6387d421cc 100644 --- a/run.sh +++ b/run.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash -service nginx start +/usr/local/nginx/sbin/nginx exec python3 -u -m frigate \ No newline at end of file