Video export service
Implementation: src/app/application/services/export_service.py
ExportService renders the blurred video: it reads every frame of a
session, blurs the boxes of the chosen layer, and writes a new MP4. It is
separate from layer JSON/CSV exchange (see Layer services). Note
that rendering lives in this Application service, not in infrastructure; the
export worker only runs it on a background
thread.
Preview versus render
Preview controls change what the editor shows; Render controls (blur
enabled, blur strength) affect exported video. The export uses the same
apply_masked_blur helper as the preview, so the footprint matches.
Which boxes are blurred
Export prefers layer D (reviewed tracks) and falls back to layer C
(raw tracking output) when D has no boxes. If neither has boxes,
session_is_ready_for_export is false and export_session raises
RuntimeError. Layers A and B are never exported.
Rendering one video
Arrows are the order of operations; the dashed arrow is the frame loop.
- Create the output folder if needed.
- Open an OpenCV
VideoWriterwith codecmp4v, the source FPS, and the source width/height. If it cannot open,RuntimeErroris raised. - For each frame index
0 .. frame_count - 1: read the RGB frame, convert to BGR (OpenCV's order), fetch that frame's boxes from the chosen layer, blur them whenblur_enabled, write the frame, then call the progress callback with(frame_index + 1, frame_count). - Release the writer in a
finallyblock.
How the blur works: all boxes on a frame are merged into one region, clipped
to the frame, blurred with a Gaussian whose kernel is blur_strength
(forced odd, at least 3), and blended back with a 10-pixel outward feather
so edges are soft. Box interiors are fully blurred.
Limitations
- A frame that cannot be read is skipped with a warning: it is not written, so the output has fewer frames than the source and no progress is reported for it.
- The code writes video frames only; no audio track is copied.
- Docstring claims of H.264, a JSON export, or tab-separated CSV do not
match this implementation: the codec is
mp4vand the CSV method below is comma-separated. - If OpenCV lacks
VideoWriter_fourccthe export fails withRuntimeError.
Running it in the background
Arrows are calls or signals in time order. The worker's callback emits
progress_updated and, once stop() has been requested, raises
ExportCancelled from inside the render loop. The writer is released, and the
worker emits cancelled.
| Outcome | Signals from ExportWorker |
Handler action |
|---|---|---|
| Success | succeeded, then finished_processing |
Report success |
| Cancelled | cancelled, then finished_processing |
Report cancel |
| Error | error_occurred(message), then finished_processing |
Show error dialog |
The service logs failures at the worker boundary with exception details; handlers own the dialogs. Cancellation is cooperative: it takes effect at the next frame, not instantly.
Partial files remain
Neither cancellation nor failure deletes the partially written MP4.
Exporting every open session (ExportAllWorker) calls this same service once
per session and adds per-session signals; see
export infrastructure.
CSV helper
export_annotations_csv(s_id, output_path) writes a comma-separated file with
columns frame_index, item_id, source, label, x1, y1, x2, y2, confidence,
color_hex. Nothing in the application calls it, and it iterates
session.state.items(), which should be verified before relying on it.
Use the layer export services for supported annotation interchange.
API contracts: generated reference.