StreamCrafter — Advanced
WebCodecs Encoder
Section titled “WebCodecs Encoder”WebCodecs encoding is auto-enabled on supported browsers (Chromium-family with RTCRtpScriptTransform). It provides background-safe encoding — the stream continues even when the browser tab loses focus.
How It Works
Section titled “How It Works”The encoder runs in a Web Worker and uses RTCRtpScriptTransform to inject encoded frames into the WebRTC connection. This bypasses the browser’s default encoder, which pauses when the tab is backgrounded.
Disabling WebCodecs
Section titled “Disabling WebCodecs”If you need to force standard WebRTC encoding:
// Vanilla / headlessconst studio = createStreamCrafter({ whipUrl: "...", useWebCodecs: false,});Encoder Overrides
Section titled “Encoder Overrides”Override default encoding parameters for fine-grained control:
studio.setEncoderOverrides({ video: { width: 1920, height: 1080, bitrate: 4_000_000, framerate: 30 }, audio: { bitrate: 128_000, sampleRate: 48_000, numberOfChannels: 2 },});| Override | Type | Description |
|---|---|---|
video.width | number | Output width in pixels |
video.height | number | Output height in pixels |
video.bitrate | number | Target video bitrate in bps |
video.framerate | number | Target frame rate |
audio.bitrate | number | Target audio bitrate in bps |
audio.sampleRate | number | Audio sample rate in Hz |
audio.numberOfChannels | number | Audio channel count |
Every field is optional; unset fields keep the quality profile’s value. There is no keyframe-interval override.
Audio Mixing
Section titled “Audio Mixing”With audio mixing enabled, StreamCrafter mixes the audio of all active sources through the Web
Audio API. The React, Svelte, and Web Component drop-in components enable it. createStreamCrafter()
and the useStreamCrafterV2 / createStreamCrafterContextV2 controllers leave it off unless you
pass audioMixing: true:
const studio = createStreamCrafter({ whipUrl: "...", audioMixing: true,});Per-Source Volume
Section titled “Per-Source Volume”// Volume ranges from 0 to 2 (1 = unchanged, 2 = 200% boost)studio.setSourceVolume("camera-1", 0.8);studio.setSourceVolume("screen-1", 0.3);
// Mute/unmutestudio.setSourceMuted("camera-1", true);Master Volume
Section titled “Master Volume”studio.masterVolume = 0.5; // 0 to 2; applies to the mixed outputMaster volume requires audioMixing: true. Without it, setting masterVolume does nothing and
reading it returns 1.
Audio Processing
Section titled “Audio Processing”The mixed output passes through a fixed compressor (threshold -24 dB, ratio 4:1) followed by a limiter at -1 dB to prevent clipping. Levels are not normalized by the number of sources.
Reconnection
Section titled “Reconnection”StreamCrafter automatically reconnects when the WHIP connection drops. Enabled by default.
Configuration
Section titled “Configuration”const studio = createStreamCrafter({ whipUrl: "...", reconnection: { enabled: true, maxAttempts: 5, // Maximum reconnection attempts baseDelay: 1000, // Initial delay (ms) maxDelay: 30000, // Maximum delay (ms) backoffMultiplier: 2, // Exponential backoff factor },});Reconnection Events
Section titled “Reconnection Events”studio.on("reconnectionAttempt", ({ attempt, maxAttempts }) => { console.log(`Reconnecting: attempt ${attempt} of ${maxAttempts}`);});
studio.on("reconnectionSuccess", () => { console.log("Reconnected!");});
studio.on("reconnectionFailed", ({ error }) => { console.log("Reconnection failed:", error);});Reactive State
Section titled “Reactive State”studio.reactiveState.on("reconnecting", (isReconnecting) => { reconnectBanner.hidden = !isReconnecting;});Gateway Resolution
Section titled “Gateway Resolution”The SDK can call the public resolveIngestEndpoint GraphQL field to resolve a WHIP endpoint from a
stream key. The React, Svelte, and Web Component drop-in components do this when given
gatewayUrl and streamKey without a whipUrl. createStreamCrafter() requires a whipUrl, so
resolve it first with IngestClient (see Manual Gateway Resolution).
The resolver chooses an ingest node based on:
- Geographic proximity
- Node load and capacity
- Stream configuration
Run Gateway resolution from the publisher’s browser or device. If your application backend resolves
the ingest endpoint, Foghorn scores the backend or proxy IP instead of the publisher location. For
OBS, hardware encoders, or server-side publishing, use the dashboard DNS ingest URL
(edge-ingest.{tenant}.cdn.{base} when present, then global or cluster-concrete fallback) or
Foghorn’s /ingest/{streamKey} front door, which applies the same scoring without a GraphQL client.
Manual Gateway Resolution
Section titled “Manual Gateway Resolution”For custom UIs that need to resolve a WHIP URL before initializing:
import { IngestClient } from "@livepeer-frameworks/streamcrafter-core";
const client = new IngestClient({ gatewayUrl: "https://bridge.frameworks.network/graphql", streamKey: "my-stream-key",});const endpoints = await client.resolve();const whipUrl = endpoints.primary.whipUrl;if (!whipUrl) throw new Error("No WHIP endpoint resolved");
// Now use the resolved URLconst studio = createStreamCrafter({ whipUrl });Multi-Source Streaming
Section titled “Multi-Source Streaming”Adding Sources
Section titled “Adding Sources”// Camera with a specific device and quality profileawait studio.startCamera({ videoDeviceId: "<VIDEO_DEVICE_ID>", audioDeviceId: "<AUDIO_DEVICE_ID>", profile: "professional",});
// Or pass raw MediaStreamConstraintsawait studio.startCamera({ customConstraints: { video: { width: 1920, height: 1080, frameRate: 30 }, audio: true, },});
// Screen share with system audioawait studio.startScreenShare({ audio: true });
// Custom MediaStreamstudio.addCustomSource(customStream, "External Source");Source Management
Section titled “Source Management”// List active sourcesconst sources = studio.sources;// [// { id: "camera-1", type: "camera", label: "FaceTime HD", muted: false, volume: 1 },// { id: "screen-1", type: "screen", label: "Screen", muted: false, volume: 1 }// ]
// Set primary video (determines which source is shown in non-compositor mode)studio.setPrimaryVideo("camera-1");
// Remove a sourcestudio.removeSource("screen-1");Source Events
Section titled “Source Events”studio.on("sourceAdded", ({ source }) => { console.log("New source:", source.label);});
studio.on("sourceRemoved", ({ sourceId }) => { console.log("Removed:", sourceId);});
studio.on("sourceUpdated", ({ source, changes }) => { console.log("Updated:", source.id, changes);});startCamera() accepts videoDeviceId, audioDeviceId, facingMode ("user" or
"environment"), profile, and customConstraints. Device IDs come from await studio.devices.
Runtime Theming
Section titled “Runtime Theming”StreamCrafter ships with 17 theme presets matching the player’s theme system.
// Switch theme at runtimestudio.theme = "tokyo-night";CSS Custom Properties
Section titled “CSS Custom Properties”All tokens use the --fw-sc-* prefix:
| Token | Purpose |
|---|---|
--fw-sc-bg | Background color |
--fw-sc-surface | Panel surfaces |
--fw-sc-border | Border color |
--fw-sc-text | Primary text |
--fw-sc-text-muted | Secondary text |
--fw-sc-accent | Interactive elements |
--fw-sc-success | Success/live indicators |
--fw-sc-danger | Error/destructive actions |
fw-streamcrafter,.fw-sc-root { --fw-sc-accent: 262 80% 60%; --fw-sc-bg: 230 15% 12%;}Internationalization
Section titled “Internationalization”Five built-in locales: en, es, fr, de, nl.
const studio = createStreamCrafter({ whipUrl: "...", locale: "de",});
// Or with custom translationsconst studio = createStreamCrafter({ whipUrl: "...", locale: "de", translations: { goLive: "Live gehen", stopStreaming: "Stoppen", addCamera: "Kamera hinzufugen", },});Configurable Hotkeys
Section titled “Configurable Hotkeys”Override default keyboard shortcuts:
const studio = createStreamCrafter({ whipUrl: "...", keyMap: { toggleStream: ["g"], toggleMute: ["m"], addCamera: ["c"], shareScreen: ["s"], },});Default hotkeys:
| Action | Default Key |
|---|---|
| Toggle streaming | Shift+Enter |
| Toggle mute | m |
| Add camera | c |
| Add screen share | s |
| Toggle settings | , |