Skip to content
This repository was archived by the owner on Jul 31, 2026. It is now read-only.

Repository files navigation

InnoLive Python Server (Archived)

더 이상 배포, 유지보수 하지 않는 기록용 레포지토리입니다.

init

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

run app

source .venv/bin/activate
AI_PRIVACY_DEVICE=cpu \
AI_PRIVACY_REQUIRE_GPU=false \
AI_PRIVACY_USE_HALF=false \
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload

Mac 로컬 실행은 위와 같이 CPU 모드를 사용한다. 운영 환경은 앱 시작 시 모델을 미리 로드하고 CUDA 또는 ONNX Runtime CUDA가 준비되지 않았으면 즉시 실패하도록 AI_PRIVACY_REQUIRE_GPU=true를 사용한다.

AI privacy runtime

privacy_blur는 모든 WebRTC 스트림이 하나의 GPU 런타임과 YOLO11s face pose, SFace 모델 세트를 공유한다. 각 스트림은 독립적인 얼굴 추적 상태와 client_id별 참조 얼굴 설정만 따로 가진다. 공유 프레임 워커가 짧은 시간 동안 들어온 프레임을 최대 AI_PRIVACY_MAX_BATCH_SIZE개까지 모아 한 번에 추론하며, 대기열은 AI_PRIVACY_MAX_PENDING_FRAMES로 제한된다.

처리 순서는 다음과 같다.

  1. YOLO FP16 배치로 얼굴과 5개 랜드마크를 탐지한다.
  2. 얼굴 추적 상태를 갱신하고, 식별 재확인이 필요한 트랙만 비동기 식별 큐에 넣는다. 랜드마크가 유효하지 않은 얼굴은 본인 제외 후보로 사용하지 않는다.
  3. 현재까지 확정된 본인 트랙을 제외한 얼굴에 확장 타원 마스크와 Gaussian blur를 GPU에서 적용한다. 프레임 처리는 SFace 식별 결과를 기다리지 않는다.
  4. 별도 식별 워커가 YOLO 랜드마크로 얼굴을 정렬하고 SFace 배치로 참조 얼굴과 비교한다. 결과는 이후 프레임의 해당 트랙 상태에 반영된다.
  5. 처리 결과를 원래 WebRTC 트랙으로 반환한다.

GET /health에서 장치, ONNX provider, 프레임 큐와 식별 큐 깊이, 배치 수, 평균 배치 크기와 평균 처리 지연을 확인할 수 있다. 얼굴 대조 수와 지연, YOLO 랜드마크 사용 수, 랜드마크 누락 수, 식별 큐 포화로 드롭된 요청 수도 별도 지표로 제공한다. identity_yunet_fallback_faces는 이전 클라이언트와의 호환성을 위해 남아 있지만, YuNet fallback은 더 이상 사용하지 않으므로 항상 0이다.

본인 트랙은 AI_PRIVACY_IDENTITY_RECHECK_INTERVAL, 비본인 또는 아직 불확실한 트랙은 AI_PRIVACY_IDENTITY_NEGATIVE_RECHECK_INTERVAL 주기로 재확인한다. 기본값은 둘 다 12프레임이라 화면 밖에서 들어온 본인이 첫 대조에서 실패해도 짧은 간격으로 다시 확인한다. 새 얼굴이 한 번에 많이 들어오면 현재 프레임은 기존 추적 상태로 바로 처리하고, 식별 워커가 처리한 결과가 다음 프레임들에 반영된다.

Reference face API

/reference-face API는 client_id별로 블러 제외 기준 얼굴을 분리한다. client_id는 쿼리, multipart/form-data 필드, 또는 X-Client-ID 헤더로 전달할 수 있다. 값이 없으면 기존 클라이언트 호환을 위해 default 클라이언트를 사용한다.

POST /reference-face
GET /reference-face
DELETE /reference-face
DELETE /reference-face/{face_id}

POST /reference-face는 JPEG, PNG, WebP 이미지를 최대 20장까지 받는다. 기존 단일 파일 필드 image만 보내면 기존 동작처럼 해당 클라이언트의 참조 얼굴을 교체한다. 새 다중 파일 필드 images를 보내면 해당 클라이언트에 얼굴을 추가한다. 응답에는 client_id, count, faces[].face_id, faces[].registered_at이 포함된다.

DELETE /reference-face는 해당 클라이언트의 참조 얼굴 전체를 삭제하고, DELETE /reference-face/{face_id}는 해당 클라이언트에서 지정한 한 장만 삭제한다. 참조 이미지는 data/reference_faces/{client_id}/{face_id}{ext} 형식으로 저장된다. 예전 단일 파일 data/reference_face.jpgdefault 클라이언트 조회 시에만 호환용으로 읽는다.

WebRTC 세션은 POST /sessions 요청의 metadata.client_id로 참조 얼굴을 선택한다. 브라우저 테스트 클라이언트는 이 값을 자동으로 넣는다. API 클라이언트가 세션을 직접 생성할 때 metadata.client_id를 생략하면 default 클라이언트의 참조 얼굴을 사용한다.

NVIDIA RTX 3090 배포

호스트에 NVIDIA 드라이버와 NVIDIA Container Toolkit을 설치한 뒤 .env.example을 기준으로 .env를 구성한다. 기본 운영값은 cuda:0, FP16, 최대 배치 8개다. 고정 참조 얼굴을 환경 변수로 넣고 싶으면 AI_PRIVACY_ME_IMAGE_PATH를 설정할 수 있지만, 일반적인 운영 흐름은 클라이언트가 /reference-face API로 자기 client_id의 참조 얼굴을 등록하는 방식이다.

docker build -t innolive-server .
docker run --rm -i --gpus all --env-file .env innolive-server python - <<'PY'
import onnxruntime as ort
import torch

print(torch.cuda.is_available(), torch.cuda.get_device_name(0))
print(ort.get_available_providers())
PY

첫 출력은 True와 GPU 이름, 두 번째 출력은 CUDAExecutionProvider를 포함해야 한다. 한 GPU에는 애플리케이션 프로세스를 하나만 실행한다. Uvicorn worker를 여러 개 띄우면 프로세스마다 모델과 GPU 메모리가 중복되고 스트림 간 동적 배치가 분리된다.

WebRTC test client

The server serves a browser client at:

http://127.0.0.1:8000/client/

Do not open app/static/client/index.html with a file:// URL. Browsers can deny camera access on file origins. Start the server and use the /client/ HTTP(S) URL instead.

On the deployed server, open:

https://innolive.duckdns.org/client/

Use Start WebRTC to create a session, open the camera, send a WebRTC offer over /signaling, trickle local ICE candidates, and receive the server's processed video track. The page also exposes health check, session create/list, session polling, deletion, signaling error probing, connection timing, media state diagnostics, and client-scoped reference face registration/deletion.

WebRTC signaling

Signaling uses a WebSocket endpoint:

ws://127.0.0.1:8000/signaling

Send an offer:

{
  "type": "offer",
  "session_id": "<session_id>",
  "sdp": "v=0\n..."
}

The server responds with an answer:

{
  "type": "answer",
  "session_id": "<session_id>",
  "sdp": "v=0\n..."
}

Send ICE candidates on the same socket:

{
  "type": "ice_candidate",
  "session_id": "<session_id>",
  "candidate": "candidate:...",
  "sdpMid": "0",
  "sdpMLineIndex": 0
}

Send the end-of-candidates signal with a null or empty candidate:

{
  "type": "ice_candidate",
  "session_id": "<session_id>",
  "candidate": null
}

The server acknowledges received candidates:

{
  "type": "ice_candidate_added",
  "session_id": "<session_id>",
  "end_of_candidates": false,
  "ice_connection_state": "checking",
  "connection_state": "connecting"
}

Peer connection timing is logged when the connection reaches connected and when ICE reaches completed. The same timing snapshot is included in session API responses under timing.

Errors are sent on the same socket:

{
  "type": "error",
  "error": {
    "code": "bad_request",
    "message": "Invalid signaling message."
  }
}

WebRTC ICE configuration

The browser client loads ICE servers from:

GET /webrtc/config

Configure the server with environment variables:

WEBRTC_STUN_URLS=stun:stun.l.google.com:19302
WEBRTC_TURN_URLS=turn:innolive.duckdns.org:3478?transport=udp,turn:innolive.duckdns.org:3478?transport=tcp,turns:innolive.duckdns.org:5349?transport=tcp
WEBRTC_TURN_USERNAME=
WEBRTC_TURN_CREDENTIAL=
WEBRTC_ANNOUNCED_IP=
WEBRTC_UDP_PORT_MIN=50000
WEBRTC_UDP_PORT_MAX=60000

WEBRTC_STUN_URLS and WEBRTC_TURN_URLS are comma-separated. For reliable connections across unrelated networks, run a TURN server and set the TURN URL, username, and credential. STUN alone can expose a server-reflexive candidate, but it is not sufficient for every NAT/firewall combination.

If this server is behind NAT and aiortc advertises only a private host candidate such as 192.168.x.x, set WEBRTC_ANNOUNCED_IP to the public IPv4 address that forwards UDP media traffic to this process. This rewrites host candidates in the answer SDP sent to browsers. The NAT/router must forward the WebRTC UDP range 1:1 to the same container/host ports; if the public port is remapped, use TURN.

The default server UDP media range is 50000-60000/udp. Open and forward:

sudo ufw allow 8000/tcp
sudo ufw allow 50000:60000/udp

The production Compose file uses host networking so aiortc binds the configured UDP range directly on the host. Keep router port forwarding, the host firewall, and the environment range aligned.

HTTPS/WSS signaling may pass through a reverse proxy such as Nginx Proxy Manager. WebRTC media UDP does not pass through a normal HTTP reverse proxy or Cloudflare orange-cloud proxy. Media must reach the server directly over UDP, or it must go through a TURN relay.

When the connection is healthy in chrome://webrtc-internals, the selected candidate pair for an external client should normally include one of:

  • local: host to remote: srflx or an announced public host candidate when direct UDP forwarding works.
  • local: relay or remote: relay when TURN is used.

If the selected pair only shows private addresses such as 192.168.x.x for a peer outside the LAN, the media path is still not externally reachable.

About

(Archived) Backend server providing REST APIs for the InnoLive solution.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages