Problem
With WATERMARK_ENABLED=true + GCS, each frame did ~3 GCS ops: the watermark service downloaded the frame, drew the timestamp, and re-uploaded the bytes (overwriting the original); then the encoder downloaded the frame again. Live diagnosis showed this per-frame GCS cost is the dominant per-device bottleneck (FFmpeg concurrency stuck ~1.0 even in-cluster), and it also mutated the original frames in the bucket.
Fix (PR #27)
_add_watermark_gcs now writes the watermarked image to a local temp and returns that path — no upload. FFmpeg reads it directly → 1 GCS download/frame. Original GCS objects untouched. The consumer deletes the temps after segment generation.
- Timestamp source unchanged:
request_timestamp (the moment we received the frame); no fallback to the unreliable capture time.
- Rendering unchanged (same Pillow draw) — no visual change.
Acceptance
Note
Full move into the FFmpeg filter (drawtext/ASS, removing Pillio entirely) was evaluated but rejected: a time-expression can't render accurate per-frame request timestamps (display interval != capture interval), and burned ASS has font-sizing risk that can't be verified without visual review. This change captures the GCS win safely with identical rendering.
Problem
With
WATERMARK_ENABLED=true+ GCS, each frame did ~3 GCS ops: the watermark service downloaded the frame, drew the timestamp, and re-uploaded the bytes (overwriting the original); then the encoder downloaded the frame again. Live diagnosis showed this per-frame GCS cost is the dominant per-device bottleneck (FFmpeg concurrency stuck ~1.0 even in-cluster), and it also mutated the original frames in the bucket.Fix (PR #27)
_add_watermark_gcsnow writes the watermarked image to a local temp and returns that path — no upload. FFmpeg reads it directly → 1 GCS download/frame. Original GCS objects untouched. The consumer deletes the temps after segment generation.request_timestamp(the moment we received the frame); no fallback to the unreliable capture time.Acceptance
Note
Full move into the FFmpeg filter (drawtext/ASS, removing Pillio entirely) was evaluated but rejected: a time-expression can't render accurate per-frame request timestamps (display interval != capture interval), and burned ASS has font-sizing risk that can't be verified without visual review. This change captures the GCS win safely with identical rendering.