Test sources
A source is a throwaway container that pumps media at one of your instances so you have something to look at. norsk-ctl starts it, labels it as belonging to the instance, and tears it down with instance delete — you never write a compose file for it.
Find them under Developer → Sources in the UI, or norsk-ctl source on the CLI.
The quickest path
Section titled “The quickest path”Pick an instance, press Start on camera1. That’s it — the built-in presets stream a downloaded sample clip to the instance’s SRT listener on port 5001, which is where a stock product listens.
Everything below is a detour from that default, reached by the slider icon next to a preset or by Custom source. Nothing in the panel is required: every field either has a working default or is filled in from the instance itself.
What you can send
Section titled “What you can send”There are three kinds of asset, and you name exactly one. Asking for none — or more than one — is refused rather than resolved in your favour, so a request never streams something you didn’t choose.
Built-in presets
Section titled “Built-in presets”camera1 (MP4) and camera2 (MPEG-TS). The clips are downloaded once from S3 into ~/.norsk-ctl/sample-media/ and re-used forever after. They are remuxed, never re-encoded, so a running preset source costs almost no CPU.
Testcards (generated patterns)
Section titled “Testcards (generated patterns)”Two patterns, both with a 1 kHz tone:
| Pattern | What it looks like |
|---|---|
bars | SMPTE color bars — a static reference |
testsrc | A moving pattern with the elapsed time burnt into the picture |
You choose the resolution, frame rate and audio sample rate:
- Resolution —
WIDTHxHEIGHT, 16 to 7680 on each side. Both dimensions must be even: h.264’syuv420psubsamples chroma by two and cannot encode an odd one. - Frame rate — whole (
50), decimal (29.97), or an exact broadcast rational (30000/1001). Prefer the rational when you care about drift. - Sample rate — any rate AAC encodes; the UI offers 44100 and 48000.
Each is checked before anything starts, so a typo is an error you can read rather than a container that fails deep inside a render.
How a testcard is produced
Section titled “How a testcard is produced”This is worth understanding, because the obvious implementation is expensive.
Generating a pattern live means encoding h.264 for as long as the source runs — measured on the shipped image, about a third of a CPU core, continuously, per source. Against a host that is also running the media pipeline you actually care about, that is real money.
So norsk-ctl doesn’t do that. The first time you ask for a given combination, the source container renders a 20-second loop into the sample-media cache and then streams that file on repeat, copying rather than re-encoding — exactly like a preset. The measured difference, on the same 4-second stream:
| CPU | |
|---|---|
| Encoding live | ~1.33 s |
| First start (renders the cache, then streams) | ~4.2 s, once |
| Every start afterwards | ~0.12 s |
Consequences worth knowing:
- The first start of a new combination takes a few seconds longer. The render is its own step — the UI and CLI report Rendering pattern while it runs, and the source only starts once it has something to send. Subsequent starts skip it entirely.
- Every combination gets its own cache file, named for what it contains —
generated-bars-1920x1080-25fps-48000hz.ts. Asking for 1080p50 never hands you the cached 720p25 render. - The render is published by rename, so two sources starting together cannot tear each other’s file, and a killed container leaves no half-written clip behind.
testsrc’s burnt-in timestamp restarts every 20 seconds, because the clip loops. It still shows motion and position-in-loop; it is not a continuously rising clock.
To reclaim the space, or to force a re-render, delete the files:
rm ~/.norsk-ctl/sample-media/generated-*.tsYour own media file
Section titled “Your own media file”Point a source at any file on the daemon’s host — not the browser’s machine, which is a different computer whenever the daemon is remote. Browse to it in the UI, or:
norsk-ctl source start my-instance --media-file /srv/media/wedding.mp4The file’s directory is bind-mounted read-only into the source container; the media is never copied, uploaded, or transcoded. That makes a file source the cheapest kind, and it means a 40 GB mezzanine costs nothing to “add”.
The file must contain codecs the destination accepts, since it is remuxed as-is. The source loops it forever.
Keeping one for reuse
Section titled “Keeping one for reuse”A source you configured once can become a row of its own, started with a single click like camera1. Give it a Name in the Custom source panel and press Save.
A saved source stores only the asset and the protocol — the pattern and its format, or the file path, and whether it publishes over SRT or RTMP. It deliberately does not store where it sends:
- A port and a service belong to one instance. A saved source that remembered “gateway:7000” would point at nothing the moment you used it against an instance without that service.
- So the endpoint is re-picked from the target instance’s own list each time, preferring one whose transport can carry the saved protocol. The list is cheap to fetch, and always current.
The name is the identity: it must be unique, it is what the started source is called, and it is what source stop takes. Saving validates the asset, so a saved source is startable by construction rather than failing the first time someone presses Start.
Where it sends
Section titled “Where it sends”The Send to list is built from the instance itself, best answer first:
- Ports the product’s compose publishes, with this instance’s launch parameters filled in, and named for the parameter that sets them —
PROGRAM_INGEST_PORT 5001/udp. This is how a product with a listener takes media, so it is the default target. Studio’s own port, its web UI, is left out. - Endpoints the product declared. A product template that declares an
allocatedPortsentry gets its label shown verbatim — “MoQ preview (WebTransport)”. A MoQ preview port is listed for completeness; a sample source cannot feed it. - Host-port bindings you set at launch, listed by number and protocol.
An instance that publishes, declares and binds nothing (a 2110 capture, say) lists no endpoint.
You can always override the port by hand.
Two things about this list are easy to get wrong by hand, which is why the daemon builds it:
- It reports the container-side port. A binding written
6000:1935publishes 6000 on the host but the listener is on 1935, and a source dials from inside the instance’s network. An instance launched--internal-onlypublishes nothing at all and is still perfectly reachable. - It knows which service hosts the listener. A sidecar service is dialled directly, and in hybrid mode — where only
mediasits on the host network — the source joins whichever network that particular endpoint needs.
How it sends
Section titled “How it sends”SRT is the default and carries the stream’s identity as a streamid. You can set:
- Stream ID — defaults to the source’s name; override when the listener is fussy about what it accepts.
- Passphrase — 10 to 79 characters, matching the listener’s.
- Latency — receiver latency in milliseconds; libsrt’s default applies when unset.
RTMP publishes over FLV, and uses the same Stream ID value as its <app>/<key> path — live/cam becomes rtmp://…:1935/live/cam.
An SRT-only option sent with RTMP is refused, not ignored: a passphrase you typed should never be silently dropped.
From the CLI
Section titled “From the CLI”# The one-click equivalentnorsk-ctl source start my-instance camera1
# A 1080p59.94 testcard with a burnt-in clocknorsk-ctl source start my-instance \ --generate testsrc --resolution 1920x1080 --frame-rate 60000/1001
# Your own file, encrypted, to a named endpointnorsk-ctl source start my-instance \ --media-file /srv/media/wedding.mp4 \ --passphrase correcthorsebattery --latency 250
# RTMP to a sidecar servicenorsk-ctl source start my-instance \ --generate bars --protocol rtmp --port 1935 \ --service my-gateway --stream-id live/cam
norsk-ctl source listnorsk-ctl source stop my-instance camera1Run several sources against one instance by giving each a --name; it must be unique within the instance, and it is what source stop takes.
When nothing arrives
Section titled “When nothing arrives”- Check the source’s own logs first —
norsk-ctl source logs <instance> <name>. A source that cannot reach its destination retries rather than exiting, so “running” does not mean “connected”. - Wrong transport. SRT needs a UDP endpoint and RTMP a TCP one. The Send to list marks an endpoint that cannot carry the protocol you chose; a bare port number typed at launch defaults to TCP.
Incorrect passphrasein the logs means exactly that — SRT rejects the handshake outright rather than degrading.- The product isn’t listening where you’re sending. Nothing forces a product’s workflow to listen on the port it declared, and a product that declares nothing gets the 5001 default whether or not it uses it. Check the workflow.