Files
jc-video-recognize/apps/server/main.py
2026-06-05 09:27:01 +08:00

135 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import FastAPI, UploadFile, File, WebSocket, WebSocketDisconnect
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import uvicorn
import os
import signal
import sys
import logging
from api import detection, models
from services.model_service import ModelService
from services.camera_service import CameraService
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
model_service = ModelService()
camera_service = None
def setup_signal_handlers():
"""设置信号处理器,确保进程异常退出时能清理资源"""
def signal_handler(signum, frame):
sig_name = signal.Signals(signum).name
logger.info(f"收到信号 {sig_name},正在清理资源...")
# 强制释放摄像头资源
import subprocess
try:
# 查找并终止占用摄像头的Python进程除了当前进程
current_pid = os.getpid()
result = subprocess.run(
['lsof', '+D', '/dev', '-a', '-c', 'python'],
capture_output=True,
text=True
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if '/dev/video' in line:
parts = line.split()
if len(parts) >= 2:
try:
pid = int(parts[1])
if pid != current_pid:
logger.info(f"终止占用摄像头的进程: {pid}")
os.kill(pid, signal.SIGTERM)
except (ValueError, ProcessLookupError):
pass
except Exception as e:
logger.error(f"清理摄像头资源失败: {e}")
sys.exit(0)
# 注册信号处理器
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
if hasattr(signal, 'SIGQUIT'):
signal.signal(signal.SIGQUIT, signal_handler)
@asynccontextmanager
async def lifespan(app: FastAPI):
global camera_service
camera_service = CameraService(model_service)
yield
# 关闭时清理资源
logger.info("正在关闭服务,清理资源...")
if camera_service:
await camera_service.stop()
app = FastAPI(
title="视频模型检测平台",
description="基于YOLO的实时视频检测平台",
version="1.0.0",
lifespan=lifespan
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.mount("/static", StaticFiles(directory="static"), name="static")
docker_output_dir = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
"external", "video-recognition-system", "PaddlePaddle", "PaddleDetection", "output"
)
os.makedirs(docker_output_dir, exist_ok=True)
app.mount("/docker-output", StaticFiles(directory=docker_output_dir), name="docker-output")
app.include_router(detection.router, prefix="/api")
app.include_router(models.router, prefix="/api")
@app.get("/")
async def root():
return {"message": "视频模型检测平台 API", "version": "1.0.0"}
@app.get("/api/health")
async def health():
return {"status": "healthy"}
@app.websocket("/ws/camera")
async def camera_websocket_endpoint(websocket: WebSocket):
await camera_service.handle_connection(websocket)
if __name__ == "__main__":
os.makedirs("static/uploads", exist_ok=True)
os.makedirs("static/results", exist_ok=True)
os.makedirs("static/temp", exist_ok=True)
# 设置信号处理器
setup_signal_handlers()
# 检测是否处于uvicorn重载模式的子进程中
is_reload_worker = os.environ.get('UVICORN_RELOAD') == 'true'
if is_reload_worker:
logger.info("检测到uvicorn重载子进程跳过摄像头预清理")
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
reload_dirs=["./"],
reload_includes=["*.py"]
)