mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	remove gevent fixes #920
This commit is contained in:
		
							parent
							
								
									bbe3f07ec6
								
							
						
					
					
						commit
						cbdf2c2c71
					
				@ -38,9 +38,6 @@ RUN pip3 install \
 | 
			
		||||
    peewee_migrate \
 | 
			
		||||
    zeroconf \
 | 
			
		||||
    voluptuous\
 | 
			
		||||
    Flask-Sockets \
 | 
			
		||||
    gevent \
 | 
			
		||||
    gevent-websocket \
 | 
			
		||||
    ws4py
 | 
			
		||||
 | 
			
		||||
COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/
 | 
			
		||||
 | 
			
		||||
@ -34,8 +34,7 @@ RUN pip3 wheel --wheel-dir=/wheels \
 | 
			
		||||
    matplotlib \
 | 
			
		||||
    click \
 | 
			
		||||
    setproctitle \
 | 
			
		||||
    peewee \
 | 
			
		||||
    gevent 
 | 
			
		||||
    peewee
 | 
			
		||||
 | 
			
		||||
FROM scratch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,11 @@ http {
 | 
			
		||||
      keepalive 1024;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    upstream mqtt_ws {
 | 
			
		||||
      server localhost:5002;
 | 
			
		||||
      keepalive 1024;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    upstream jsmpeg {
 | 
			
		||||
      server localhost:8082;
 | 
			
		||||
      keepalive 1024;
 | 
			
		||||
@ -139,7 +144,7 @@ http {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        location /ws {
 | 
			
		||||
            proxy_pass http://frigate_api/ws;
 | 
			
		||||
            proxy_pass http://mqtt_ws/;
 | 
			
		||||
            proxy_http_version 1.1;
 | 
			
		||||
            proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
            proxy_set_header Connection "Upgrade";
 | 
			
		||||
 | 
			
		||||
@ -2,26 +2,25 @@ import json
 | 
			
		||||
import logging
 | 
			
		||||
import multiprocessing as mp
 | 
			
		||||
import os
 | 
			
		||||
import signal
 | 
			
		||||
import sys
 | 
			
		||||
import threading
 | 
			
		||||
from logging.handlers import QueueHandler
 | 
			
		||||
from typing import Dict, List
 | 
			
		||||
import sys
 | 
			
		||||
import signal
 | 
			
		||||
 | 
			
		||||
import yaml
 | 
			
		||||
from gevent import pywsgi
 | 
			
		||||
from geventwebsocket.handler import WebSocketHandler
 | 
			
		||||
from peewee_migrate import Router
 | 
			
		||||
from playhouse.sqlite_ext import SqliteExtDatabase
 | 
			
		||||
from playhouse.sqliteq import SqliteQueueDatabase
 | 
			
		||||
 | 
			
		||||
from frigate.config import FrigateConfig
 | 
			
		||||
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
 | 
			
		||||
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
 | 
			
		||||
from frigate.edgetpu import EdgeTPUProcess
 | 
			
		||||
from frigate.events import EventProcessor, EventCleanup
 | 
			
		||||
from frigate.events import EventCleanup, EventProcessor
 | 
			
		||||
from frigate.http import create_app
 | 
			
		||||
from frigate.log import log_process, root_configurer
 | 
			
		||||
from frigate.models import Event, Recordings
 | 
			
		||||
from frigate.mqtt import create_mqtt_client
 | 
			
		||||
from frigate.mqtt import create_mqtt_client, MqttSocketRelay
 | 
			
		||||
from frigate.object_processing import TrackedObjectProcessor
 | 
			
		||||
from frigate.output import output_frames
 | 
			
		||||
from frigate.record import RecordingMaintainer
 | 
			
		||||
@ -121,8 +120,8 @@ class FrigateApp:
 | 
			
		||||
        for log, level in self.config.logger.logs.items():
 | 
			
		||||
            logging.getLogger(log).setLevel(level)
 | 
			
		||||
 | 
			
		||||
        if not "geventwebsocket.handler" in self.config.logger.logs:
 | 
			
		||||
            logging.getLogger("geventwebsocket.handler").setLevel("ERROR")
 | 
			
		||||
        if not "werkzeug" in self.config.logger.logs:
 | 
			
		||||
            logging.getLogger("werkzeug").setLevel("ERROR")
 | 
			
		||||
 | 
			
		||||
    def init_queues(self):
 | 
			
		||||
        # Queues for clip processing
 | 
			
		||||
@ -166,12 +165,18 @@ class FrigateApp:
 | 
			
		||||
            self.db,
 | 
			
		||||
            self.stats_tracking,
 | 
			
		||||
            self.detected_frames_processor,
 | 
			
		||||
            self.mqtt_client,
 | 
			
		||||
            # self.mqtt_client,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def init_mqtt(self):
 | 
			
		||||
        self.mqtt_client = create_mqtt_client(self.config, self.camera_metrics)
 | 
			
		||||
 | 
			
		||||
    def start_mqtt_relay(self):
 | 
			
		||||
        self.mqtt_relay = MqttSocketRelay(
 | 
			
		||||
            self.mqtt_client, self.config.mqtt.topic_prefix
 | 
			
		||||
        )
 | 
			
		||||
        self.mqtt_relay.start()
 | 
			
		||||
 | 
			
		||||
    def start_detectors(self):
 | 
			
		||||
        model_shape = (self.config.model.height, self.config.model.width)
 | 
			
		||||
        for name in self.config.cameras.keys():
 | 
			
		||||
@ -267,10 +272,6 @@ class FrigateApp:
 | 
			
		||||
            capture_process.start()
 | 
			
		||||
            logger.info(f"Capture process started for {name}: {capture_process.pid}")
 | 
			
		||||
 | 
			
		||||
    def start_birdseye_outputter(self):
 | 
			
		||||
        self.birdseye_outputter = BirdsEyeFrameOutputter(self.stop_event)
 | 
			
		||||
        self.birdseye_outputter.start()
 | 
			
		||||
 | 
			
		||||
    def start_event_processor(self):
 | 
			
		||||
        self.event_processor = EventProcessor(
 | 
			
		||||
            self.config,
 | 
			
		||||
@ -330,6 +331,7 @@ class FrigateApp:
 | 
			
		||||
        self.start_camera_capture_processes()
 | 
			
		||||
        self.init_stats()
 | 
			
		||||
        self.init_web_server()
 | 
			
		||||
        self.start_mqtt_relay()
 | 
			
		||||
        self.start_event_processor()
 | 
			
		||||
        self.start_event_cleanup()
 | 
			
		||||
        self.start_recording_maintainer()
 | 
			
		||||
@ -343,12 +345,8 @@ class FrigateApp:
 | 
			
		||||
 | 
			
		||||
        signal.signal(signal.SIGTERM, receiveSignal)
 | 
			
		||||
 | 
			
		||||
        server = pywsgi.WSGIServer(
 | 
			
		||||
            ("127.0.0.1", 5001), self.flask_app, handler_class=WebSocketHandler
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            server.serve_forever()
 | 
			
		||||
            self.flask_app.run(host="127.0.0.1", port=5001, debug=False)
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
@ -358,6 +356,7 @@ class FrigateApp:
 | 
			
		||||
        logger.info(f"Stopping...")
 | 
			
		||||
        self.stop_event.set()
 | 
			
		||||
 | 
			
		||||
        self.mqtt_relay.stop()
 | 
			
		||||
        self.detected_frames_processor.join()
 | 
			
		||||
        self.event_processor.join()
 | 
			
		||||
        self.event_cleanup.join()
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ from functools import reduce
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
import cv2
 | 
			
		||||
import gevent
 | 
			
		||||
 | 
			
		||||
import numpy as np
 | 
			
		||||
from flask import (
 | 
			
		||||
    Blueprint,
 | 
			
		||||
@ -22,7 +22,7 @@ from flask import (
 | 
			
		||||
    make_response,
 | 
			
		||||
    request,
 | 
			
		||||
)
 | 
			
		||||
from flask_sockets import Sockets
 | 
			
		||||
 | 
			
		||||
from peewee import SqliteDatabase, operator, fn, DoesNotExist, Value
 | 
			
		||||
from playhouse.shortcuts import model_to_dict
 | 
			
		||||
 | 
			
		||||
@ -35,74 +35,6 @@ from frigate.version import VERSION
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
bp = Blueprint("frigate", __name__)
 | 
			
		||||
ws = Blueprint("ws", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MqttBackend:
 | 
			
		||||
    """Interface for registering and updating WebSocket clients."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, mqtt_client, topic_prefix):
 | 
			
		||||
        self.clients = list()
 | 
			
		||||
        self.mqtt_client = mqtt_client
 | 
			
		||||
        self.topic_prefix = topic_prefix
 | 
			
		||||
 | 
			
		||||
    def register(self, client):
 | 
			
		||||
        """Register a WebSocket connection for Mqtt updates."""
 | 
			
		||||
        self.clients.append(client)
 | 
			
		||||
 | 
			
		||||
    def publish(self, message):
 | 
			
		||||
        try:
 | 
			
		||||
            json_message = json.loads(message)
 | 
			
		||||
            json_message = {
 | 
			
		||||
                "topic": f"{self.topic_prefix}/{json_message['topic']}",
 | 
			
		||||
                "payload": json_message["payload"],
 | 
			
		||||
                "retain": json_message.get("retain", False),
 | 
			
		||||
            }
 | 
			
		||||
        except:
 | 
			
		||||
            logger.warning("Unable to parse websocket message as valid json.")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        logger.debug(
 | 
			
		||||
            f"Publishing mqtt message from websockets at {json_message['topic']}."
 | 
			
		||||
        )
 | 
			
		||||
        self.mqtt_client.publish(
 | 
			
		||||
            json_message["topic"],
 | 
			
		||||
            json_message["payload"],
 | 
			
		||||
            retain=json_message["retain"],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        def send(client, userdata, message):
 | 
			
		||||
            """Sends mqtt messages to clients."""
 | 
			
		||||
            try:
 | 
			
		||||
                logger.debug(f"Received mqtt message on {message.topic}.")
 | 
			
		||||
                ws_message = json.dumps(
 | 
			
		||||
                    {
 | 
			
		||||
                        "topic": message.topic.replace(f"{self.topic_prefix}/", ""),
 | 
			
		||||
                        "payload": message.payload.decode(),
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            except:
 | 
			
		||||
                # if the payload can't be decoded don't relay to clients
 | 
			
		||||
                logger.debug(
 | 
			
		||||
                    f"MQTT payload for {message.topic} wasn't text. Skipping..."
 | 
			
		||||
                )
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            for client in self.clients:
 | 
			
		||||
                try:
 | 
			
		||||
                    client.send(ws_message)
 | 
			
		||||
                except:
 | 
			
		||||
                    logger.debug(
 | 
			
		||||
                        "Removing websocket client due to a closed connection."
 | 
			
		||||
                    )
 | 
			
		||||
                    self.clients.remove(client)
 | 
			
		||||
 | 
			
		||||
        self.mqtt_client.message_callback_add(f"{self.topic_prefix}/#", send)
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """Maintains mqtt subscription in the background."""
 | 
			
		||||
        gevent.spawn(self.run)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_app(
 | 
			
		||||
@ -110,10 +42,8 @@ def create_app(
 | 
			
		||||
    database: SqliteDatabase,
 | 
			
		||||
    stats_tracking,
 | 
			
		||||
    detected_frames_processor,
 | 
			
		||||
    mqtt_client,
 | 
			
		||||
):
 | 
			
		||||
    app = Flask(__name__)
 | 
			
		||||
    sockets = Sockets(app)
 | 
			
		||||
 | 
			
		||||
    @app.before_request
 | 
			
		||||
    def _db_connect():
 | 
			
		||||
@ -129,10 +59,6 @@ def create_app(
 | 
			
		||||
    app.detected_frames_processor = detected_frames_processor
 | 
			
		||||
 | 
			
		||||
    app.register_blueprint(bp)
 | 
			
		||||
    sockets.register_blueprint(ws)
 | 
			
		||||
 | 
			
		||||
    app.mqtt_backend = MqttBackend(mqtt_client, frigate_config.mqtt.topic_prefix)
 | 
			
		||||
    app.mqtt_backend.start()
 | 
			
		||||
 | 
			
		||||
    return app
 | 
			
		||||
 | 
			
		||||
@ -613,7 +539,7 @@ def vod(year_month, day, hour, camera):
 | 
			
		||||
def imagestream(detected_frames_processor, camera_name, fps, height, draw_options):
 | 
			
		||||
    while True:
 | 
			
		||||
        # max out at specified FPS
 | 
			
		||||
        gevent.sleep(1 / fps)
 | 
			
		||||
        time.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)
 | 
			
		||||
@ -626,16 +552,3 @@ def imagestream(detected_frames_processor, camera_name, fps, height, draw_option
 | 
			
		||||
            b"--frame\r\n"
 | 
			
		||||
            b"Content-Type: image/jpeg\r\n\r\n" + jpg.tobytes() + b"\r\n\r\n"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ws.route("/ws")
 | 
			
		||||
def echo_socket(socket):
 | 
			
		||||
    current_app.mqtt_backend.register(socket)
 | 
			
		||||
 | 
			
		||||
    while not socket.closed:
 | 
			
		||||
        # Sleep to prevent *constant* context-switches.
 | 
			
		||||
        gevent.sleep(0.1)
 | 
			
		||||
 | 
			
		||||
        message = socket.receive()
 | 
			
		||||
        if message:
 | 
			
		||||
            current_app.mqtt_backend.publish(message)
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,16 @@
 | 
			
		||||
import json
 | 
			
		||||
import logging
 | 
			
		||||
import threading
 | 
			
		||||
from wsgiref.simple_server import make_server
 | 
			
		||||
 | 
			
		||||
import paho.mqtt.client as mqtt
 | 
			
		||||
from ws4py.server.wsgirefserver import (
 | 
			
		||||
    WebSocketWSGIHandler,
 | 
			
		||||
    WebSocketWSGIRequestHandler,
 | 
			
		||||
    WSGIServer,
 | 
			
		||||
)
 | 
			
		||||
from ws4py.server.wsgiutils import WebSocketWSGIApplication
 | 
			
		||||
from ws4py.websocket import WebSocket
 | 
			
		||||
 | 
			
		||||
from frigate.config import FrigateConfig
 | 
			
		||||
 | 
			
		||||
@ -117,8 +126,15 @@ def create_mqtt_client(config: FrigateConfig, camera_metrics):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if not mqtt_config.tls_ca_certs is None:
 | 
			
		||||
        if not mqtt_config.tls_client_cert is None and not mqtt_config.tls_client_key is None:
 | 
			
		||||
            client.tls_set(mqtt_config.tls_ca_certs, mqtt_config.tls_client_cert, mqtt_config.tls_client_key)
 | 
			
		||||
        if (
 | 
			
		||||
            not mqtt_config.tls_client_cert is None
 | 
			
		||||
            and not mqtt_config.tls_client_key is None
 | 
			
		||||
        ):
 | 
			
		||||
            client.tls_set(
 | 
			
		||||
                mqtt_config.tls_ca_certs,
 | 
			
		||||
                mqtt_config.tls_client_cert,
 | 
			
		||||
                mqtt_config.tls_client_key,
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            client.tls_set(mqtt_config.tls_ca_certs)
 | 
			
		||||
    if not mqtt_config.tls_insecure is None:
 | 
			
		||||
@ -151,3 +167,79 @@ def create_mqtt_client(config: FrigateConfig, camera_metrics):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MqttSocketRelay:
 | 
			
		||||
    def __init__(self, mqtt_client, topic_prefix):
 | 
			
		||||
        self.mqtt_client = mqtt_client
 | 
			
		||||
        self.topic_prefix = topic_prefix
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        class MqttWebSocket(WebSocket):
 | 
			
		||||
            topic_prefix = self.topic_prefix
 | 
			
		||||
            mqtt_client = self.mqtt_client
 | 
			
		||||
 | 
			
		||||
            def received_message(self, message):
 | 
			
		||||
                try:
 | 
			
		||||
                    json_message = json.loads(message.data.decode("utf-8"))
 | 
			
		||||
                    json_message = {
 | 
			
		||||
                        "topic": f"{self.topic_prefix}/{json_message['topic']}",
 | 
			
		||||
                        "payload": json_message["payload"],
 | 
			
		||||
                        "retain": json_message.get("retain", False),
 | 
			
		||||
                    }
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    logger.warning("Unable to parse websocket message as valid json.")
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
                logger.debug(
 | 
			
		||||
                    f"Publishing mqtt message from websockets at {json_message['topic']}."
 | 
			
		||||
                )
 | 
			
		||||
                self.mqtt_client.publish(
 | 
			
		||||
                    json_message["topic"],
 | 
			
		||||
                    json_message["payload"],
 | 
			
		||||
                    retain=json_message["retain"],
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        # start a websocket server on 5002
 | 
			
		||||
        WebSocketWSGIHandler.http_version = "1.1"
 | 
			
		||||
        self.websocket_server = make_server(
 | 
			
		||||
            "127.0.0.1",
 | 
			
		||||
            5002,
 | 
			
		||||
            server_class=WSGIServer,
 | 
			
		||||
            handler_class=WebSocketWSGIRequestHandler,
 | 
			
		||||
            app=WebSocketWSGIApplication(handler_cls=MqttWebSocket),
 | 
			
		||||
        )
 | 
			
		||||
        self.websocket_server.initialize_websockets_manager()
 | 
			
		||||
        self.websocket_thread = threading.Thread(
 | 
			
		||||
            target=self.websocket_server.serve_forever
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        def send(client, userdata, message):
 | 
			
		||||
            """Sends mqtt messages to clients."""
 | 
			
		||||
            try:
 | 
			
		||||
                logger.debug(f"Received mqtt message on {message.topic}.")
 | 
			
		||||
                ws_message = json.dumps(
 | 
			
		||||
                    {
 | 
			
		||||
                        "topic": message.topic.replace(f"{self.topic_prefix}/", ""),
 | 
			
		||||
                        "payload": message.payload.decode(),
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                # if the payload can't be decoded don't relay to clients
 | 
			
		||||
                logger.debug(
 | 
			
		||||
                    f"MQTT payload for {message.topic} wasn't text. Skipping..."
 | 
			
		||||
                )
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            self.websocket_server.manager.broadcast(ws_message)
 | 
			
		||||
 | 
			
		||||
        self.mqtt_client.message_callback_add(f"{self.topic_prefix}/#", send)
 | 
			
		||||
 | 
			
		||||
        self.websocket_thread.start()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        self.websocket_server.manager.close_all()
 | 
			
		||||
        self.websocket_server.manager.stop()
 | 
			
		||||
        self.websocket_server.manager.join()
 | 
			
		||||
        self.websocket_server.shutdown()
 | 
			
		||||
        self.websocket_thread.join()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user