Coverage for orchestrant/streaming/generator.py: 29%
38 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-18 00:48 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-18 00:48 +0000
1"""Helpers for streaming encoded frames."""
3from __future__ import annotations
5import threading
6import time
7from typing import TYPE_CHECKING
9import cv2
10from loguru import logger
13if TYPE_CHECKING:
14 from collections.abc import Iterator
16 from orchestrant.streaming.capture import FrameCapture
19# Re-armed, never rebound, so no function needs a `global` statement.
20_shutdown_event = threading.Event()
23def init_shutdown_event() -> None:
24 """Re-arm the shutdown event for a fresh streaming session."""
25 _shutdown_event.clear()
28def request_shutdown() -> None:
29 """Request shutdown of all active frame generators."""
30 _shutdown_event.set()
33def is_shutdown_requested() -> bool:
34 """Check if shutdown has been requested."""
35 return _shutdown_event.is_set()
38def gen_frames(
39 frame_capture: FrameCapture,
40 jpeg_quality: int = 30,
41 wait_for_frame: float = 0.1,
42 wait_on_empty: float = 0.5,
43) -> Iterator[bytes]:
44 """Yield MJPEG multipart chunks until shutdown is requested or capture stops."""
45 logger.info("Starting video stream...")
47 init_shutdown_event()
49 while frame_capture.frame_queue.empty():
50 if is_shutdown_requested():
51 logger.info("Shutdown requested during initial frame wait")
52 return
53 logger.debug("Waiting for the first frame...")
54 time.sleep(wait_for_frame)
56 while not is_shutdown_requested():
57 if is_shutdown_requested():
58 logger.info("Shutdown requested, stopping stream")
59 break
61 frame = frame_capture.get_frame()
62 if frame is None:
63 logger.warning("No frame available; skipping frame.")
64 time.sleep(wait_on_empty)
65 continue
67 ret, buffer = cv2.imencode(
68 ".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, jpeg_quality]
69 )
70 if not ret:
71 logger.warning("Frame encoding failed; skipping frame...")
72 continue
73 frame_bytes = buffer.tobytes()
75 yield (
76 b"--frame\r\nContent-Type: image/jpeg\r\n\r\n" + frame_bytes + b"\r\n\r\n"
77 )
79 logger.info("Video stream stopped")