Scenes and layers
What a scene actually is, what a layer can be, and the exact rule that decides whether the GPU pod re-encodes your frame or copies it byte-for-byte.
A scene is a layout, not a platform
A StreamScene row (table stream_scenes) is keyed by frame — wide or tall — not
by platform. Its own model docstring is explicit about why: "two vertical platforms
reference one scene, not diverging copies." If you arm a vertical layout for two
platforms, editing it once updates the picture both of them get; there's no per-platform
copy to keep in sync.
A platform scene (StreamPlatformScene, table stream_platform_scenes) is the
exception — a second, platform-specific set of layers laid over the shared frame,
covered in Platforms, studio, and secrets
and Per-platform scene overrides.
A layer, and the one hard rule about its asset
layers is a JSON array stored on the scene row, parsed by
app/products/stream/core/scene_layers.py — "the shared contract of the panel, the
backend, and the video-motor" (the GPU pod; see
Going live and signal for why
that's not the StreamMotor row). Two things about layer geometry are load-bearing
enough that the module docstring states them directly:
- Geometry is in fractions of the frame, never pixels. A phone sending 720p instead of 1080p would otherwise scramble every layer's position.
- A layer's
assetfield can only be amedia:<id>reference — never a path or a URL. The GPU pod resolves that reference over an internal loop; accepting an arbitrary URL there would let a scene ask the box to fetch anything at all.
No-re-encode vs re-composing — the actual function, not a guess
Whether the GPU pod copies your feed byte-for-byte or decodes and re-encodes it is
decided by one function, needs_compose(layers, frame), and its logic is short enough to
state exactly:
- A vertical frame always needs compose. A widescreen source can't reposition itself into 9:16 on its own.
- An empty scene never needs compose. No visible layers means the prior byte-for-byte behavior, not a black frame.
- More than one visible layer always needs compose.
- Exactly one visible layer needs compose unless it is a plain, full-frame,
uncropped, default-fit live source —
kind == "source", nosrcoverride, positioned at(0,0)sized(1,1), no crop,fit == "cover". Anything else about that single layer — a crop, a non-default fit, an offset position — flips it to needing compose too.
"Visible" itself has its own filter: a layer only counts if visible is set, its kind
is one of the live kinds, its opacity is greater than zero, and it actually has
content (layer_has_content) — an added-but-empty layer doesn't force a re-encode.
This is why five platforms don't cost five times as much
The GPU pod runs needs_compose once per scene, not once per platform. A composed
frame is fanned out identically to every armed platform on that layout — the
recomposition cost is paid once regardless of how many platforms receive the result.
Only a platform scene — a layer set specific to one platform — pays its own,
separate re-encode.
What's next
Platforms, studio, and secrets — the three different per-platform configuration surfaces, and the three secrets this product keeps separate on purpose.
Going live and signal
The motor, the door, the GPU pod, and the cockpit — four pieces behind "signal" and "on-air", and why two of them share a name that means different things.
Platforms, studio, and secrets
What "platform" means in Stream specifically, the five different per-platform configuration surfaces, and the three secrets this product keeps deliberately separate.