Problem
After the watermark→local-temp fix (#26), in-cluster measurement showed FFmpeg (0.27s/seg) and CPU (2.8/8) underused, per-device concurrency <1, throughput ~14/s. The remaining bottleneck: the per-frame watermark (GCS download + Pillow) runs serially inside accumulation, plus a per-frame Redis session call — each device's worker spends most of its time serially watermarking before it can encode.
Fix (PR #29)
- Cheap accumulation: store the raw frame + its request timestamp only; no per-frame watermark or Redis call.
- Parallel watermark at generation: watermark a segment's frames concurrently (
asyncio.gather, one thread each); FFmpeg reads the local results directly.
- Session update moved to once per segment.
Timestamp semantics unchanged (request_timestamp — the moment we received the frame). Content-time ordering (#23 part 1) and temp cleanup preserved.
Acceptance
Note re #23
The HLS-muxer batch encoder (#25, merged) stays unwired: FFmpeg is not the bottleneck, so batching processes adds little here. It remains available for a future single-device deep-backlog case.
Problem
After the watermark→local-temp fix (#26), in-cluster measurement showed FFmpeg (0.27s/seg) and CPU (2.8/8) underused, per-device concurrency <1, throughput ~14/s. The remaining bottleneck: the per-frame watermark (GCS download + Pillow) runs serially inside accumulation, plus a per-frame Redis session call — each device's worker spends most of its time serially watermarking before it can encode.
Fix (PR #29)
asyncio.gather, one thread each); FFmpeg reads the local results directly.Timestamp semantics unchanged (
request_timestamp— the moment we received the frame). Content-time ordering (#23 part 1) and temp cleanup preserved.Acceptance
Note re #23
The HLS-muxer batch encoder (#25, merged) stays unwired: FFmpeg is not the bottleneck, so batching processes adds little here. It remains available for a future single-device deep-backlog case.