
1.Overview: What YouTube Functionality Is Used
StreamStudio integrates with the YouTube Live Streaming API (part of the YouTube Data API v3) to let you schedule, control, and monitor live broadcasts directly from this dashboard — without needing to open YouTube Studio.
OAuth 2.0 Authentication
| Step | What happens |
|---|---|
| Connect YouTube | The app redirects you to Google's OAuth consent screen. You authorize the app to manage your YouTube channel. |
| Token storage | After you consent, Google sends back a refresh token (long-lived) stored securely against your user account. Your Google password is never stored. |
| Token refresh | Every API call exchanges the refresh token for a short-lived access token behind the scenes. You stay connected until you click Disconnect. |
OAuth scopes requested
youtube— manage your YouTube account (broadcasts, streams, videos, thumbnails)youtube.force-ssl— see, edit, and permanently delete your YouTube videos
YouTube API Endpoints Used
| Feature | YouTube API Endpoint | Method |
|---|---|---|
| Connect channel | channels?part=snippet&mine=true | GET |
| List stream keys | liveStreams?part=snippet,cdn,contentDetails&mine=true | GET |
| Create broadcast | liveBroadcasts | POST |
| Create stream key | liveStreams | POST |
| Bind broadcast ↔ stream | liveBroadcasts/bind | POST |
| Re-bind to different key | liveBroadcasts/bind | POST |
| Go live (transition) | liveBroadcasts/transition?broadcastStatus=live | POST |
| Transition to testing | liveBroadcasts/transition?broadcastStatus=testing | POST |
| End stream (transition) | liveBroadcasts/transition?broadcastStatus=complete | POST |
| Check stream signal | liveStreams?part=status | GET |
| Check broadcast status | liveBroadcasts?part=status | GET |
| List all broadcasts | liveBroadcasts?broadcastStatus=… | GET |
| Fetch video stats | videos?part=statistics,liveStreamingDetails,snippet | GET |
| Set category / embeddable | videos?part=snippet,status | PUT |
| Upload custom thumbnail | upload/youtube/v3/thumbnails/set | POST |
| Delete broadcast | liveBroadcasts | DELETE |
Automated Background Jobs
Two scheduled workflows run automatically (unless an admin disables them in Settings):
| Workflow | What it does |
|---|---|
| Auto Sync YouTube Streams | Periodically imports new broadcasts from your YouTube channel into the dashboard so they appear even if created directly in YouTube Studio. |
| Auto Refresh Stream Statuses | Checks the live lifecycle status of all your non-ended streams and updates their dashboard status (scheduled → live → ended) automatically. |
2.Getting Started
Prerequisites
- You must be logged in to StreamStudio and have been invited by an admin.
- You must have a YouTube channel (not just a Google account). Create one at youtube.com first if needed.
- You must be the channel owner or a manager of the brand account. Viewers and editors cannot authorize the Live Streaming API.
Connecting Your YouTube Channel
- Open the Stream Dashboard (the "YouTube" page in the sidebar).
- Click the Connect YouTube button in the top-right area.
- A new browser tab opens with Google's consent screen. Choose the Google account that owns your YouTube channel.
- Review the permissions and click Allow.
- Return to the dashboard — your channel name and profile picture now appear in the button area.
Disconnecting
Click the logout icon next to your channel name. This deletes the stored refresh token. Your streams remain on YouTube but will no longer be controllable from this dashboard until you reconnect.
3.The Stream Dashboard
The dashboard is your command center, organized into a toolbar, summary stats, and stream sections.
Header Toolbar
| Button | Action |
|---|---|
| Connect YouTube / Channel badge | Connect or disconnect your YouTube channel. |
| Refresh Statuses | Manually triggers a status check on all your non-ended streams. Updates each stream's status from YouTube's current lifecycle state. |
| Sync from YouTube | Imports broadcasts that exist on your YouTube channel but aren't yet in the dashboard. A dialog asks whether to clear existing records first. |
| Test Stream | Creates a private, unlisted test broadcast for verifying your encoder setup before going live. |
| New Stream | Opens the Create Stream form. |
Summary Stats
- Live Now — number of your streams currently broadcasting.
- Scheduled — number of upcoming streams not yet started.
Stream Sections
| Section | What it shows |
|---|---|
| Live Now (red) | Streams with status "live". |
| Scheduled (yellow) | Streams with status "scheduled" that haven't started yet. |
| Recently Ended (grey) | Streams that ended within the last 7 days. |
Test streams appear in a separate Test Streams section above the regular sections.
4.Creating a New Stream
- Click New Stream on the dashboard.
- Fill in the form (see field table below).
- Click Create Stream.
| Field | Required | Description |
|---|---|---|
| Stream Title | Yes | The title shown on YouTube. |
| Description | No | Appears below the video on YouTube. |
| Visibility | Yes | Public (anyone can watch), Unlisted (link only), or Private (only you). Default: Public. |
| Scheduled Date and Time | No | When the stream is planned to start. Leave blank for "start now." |
| Stream Key | No | Choose an existing reusable stream key from the dropdown, or leave it on "Create a new stream key" to generate a fresh one. |
| Thumbnail | No | Paste a URL or click Upload to choose an image. Pushed to YouTube after creation. |
| Internal Notes | No | Private notes visible only in the dashboard (not on YouTube). |
What happens behind the scenes
- A live broadcast is created on YouTube with your title, description, visibility, and scheduled time.
- A live stream (stream key) is either fetched (if you selected an existing one) or created new.
- The broadcast is bound to the stream key.
- The video category is set to Sports (17).
- If you uploaded a thumbnail, it is pushed to YouTube.
- A stream record is saved in the dashboard with the YouTube broadcast ID, stream key, and watch URL.
5.Managing a Stream (Stream Card Actions)
Each stream appears as a card with action buttons in the footer.
Go Live
Available on scheduled streams only.
- Checks whether YouTube is receiving an active signal on your stream key (retries for ~10 seconds).
- If no signal: shows an error — "No incoming stream detected yet. Start streaming from your encoder (e.g. OBS) before going live."
- If signal detected: transitions the broadcast to live (via testing if needed).
- Marks the video as embeddable so the in-app preview can play it.
- Updates the dashboard status to live and records the start time.
End Stream
Available on live streams. Transitions the YouTube broadcast to complete, updates the dashboard status to ended, records the end time, and calculates the duration.
Preview
- Scheduled streams: Transitions the broadcast to testing state so the incoming RTMP feed becomes previewable. Shows a signal indicator (green = signal detected, amber = no signal yet).
- Live streams: Shows the live embedded YouTube player directly in the card.
- Testing state: A Watch Page link opens the YouTube watch page in a new tab (you must be logged into the channel's Google account).
Refresh Stats
Fetches the latest statistics from YouTube: view count, concurrent viewers (peak tracked), thumbnail, title, and description.
Share
Opens a share menu with options to share the stream URL via WhatsApp or copy it to the clipboard.
Edit
Opens the Create Stream form in edit mode (see Section 7).
Delete
Removes the stream record from the dashboard. This does not delete the broadcast on YouTube — it only removes it from this dashboard's list.
6.Test Streams
A test stream lets you verify your encoder setup (OBS, camera, audio) without going public.
Starting a Test Stream
- Click Test Stream on the dashboard toolbar.
- Confirm in the dialog that appears.
- The app creates a private, unlisted broadcast on YouTube, binds it to a stream key, and transitions it to testing state.
- A test stream card appears in the Test Streams section with the stream key and watch URL.
Using a Test Stream
- Copy the stream key from the test stream card.
- Paste it into your encoder (OBS) as the stream key.
- Start streaming from your encoder.
- Open the watch URL to preview the feed.
- When done, use the Stop button on the test stream card to end and clean up.
7.Editing a Stream
- Click the Edit icon on any stream card.
- The Create Stream form opens in edit mode, pre-filled with the stream's current details.
What you can edit
- Title, description, visibility, scheduled time
- Thumbnail (upload a new one — pushed to YouTube on save)
- YouTube Stream URL
- Stream Key — click Change Key to select a different existing key. The app re-binds the broadcast to the new key on YouTube.
- Internal notes
What happens on save
- If you changed the stream key, the broadcast is re-bound to the new key via the YouTube bind endpoint.
- If you changed the thumbnail, the new image is uploaded to YouTube.
- The dashboard record is updated.
8.Syncing from YouTube
If you create broadcasts directly in YouTube Studio and want them to appear in this dashboard:
- Click Sync from YouTube on the dashboard toolbar.
- A dialog asks: "Clear existing streams before importing?"
- Yes — Deletes all current dashboard stream records, then imports fresh from YouTube. Use if your dashboard is stale or has duplicates.
- No — Merges: only imports broadcasts that don't already exist. Existing records are preserved.
- The app fetches all your broadcasts (upcoming, active, and completed) from YouTube and creates dashboard records.
9.Stream History
The History page shows all your streams in a searchable, filterable table.
Analytics Summary
- Total Streams — all streams ever created.
- Total Views — sum of view counts across all streams.
- Total Air Time — cumulative duration (hours and minutes).
- Currently Live — number of streams live right now.
Filtering
- Search box — filter by stream title.
- Status filter buttons — All / Live / Scheduled / Ended.
Table Columns
| Column | Content |
|---|---|
| Title | Stream title + internal notes |
| Status | Scheduled / Live / Ended badge |
| Scheduled | Scheduled start date and time |
| Views | Total view count |
| Duration | Duration in minutes (if ended) |
| Visibility | Public / Unlisted / Private |
10.Troubleshooting
| Message | Cause & Fix |
|---|---|
| "Connect your YouTube channel first." | You haven't connected a channel, or the connection was lost. Click Connect YouTube on the dashboard. |
| "No incoming stream detected yet. Start streaming from your encoder." | You clicked Go Live but YouTube isn't receiving video. Start your encoder (OBS) with the correct stream key first, wait a few seconds, then try again. |
| "Could not go live yet — YouTube isn't receiving a stream signal." | Same as above — the broadcast can't transition to live without an active RTMP feed. |
| "Failed to refresh token" | Your YouTube connection may have been revoked. Disconnect and reconnect your channel. |
| Test stream won't start | YouTube may reject the testing transition if the broadcast was just created. Wait a few seconds and try again, or check that your channel has live streaming enabled (some channels need 24 hours after first enabling). |
| Preview shows "Video unavailable — Playback on other websites has been disabled" | The video's embeddable flag isn't set. The app attempts to set this automatically when going live, but if it fails, open the stream directly on YouTube via the Watch Page link. |
| Stream key not appearing in the dropdown | YouTube's liveStreams endpoint sometimes returns transient errors. The app retries up to 3 times. If it still fails, create a new stream key instead of selecting an existing one. |
11.Data Storage
The following data is stored in the StreamStudio database (not on YouTube):
| Entity | What it holds |
|---|---|
| Stream | Your dashboard stream records (title, status, YouTube IDs, stream key, stats, timestamps, notes, test flag). |
| YoutubeAccount | Your YouTube channel ID, title, thumbnail, and OAuth refresh token. |
| ObsSettings | Automation toggles (auto-sync, auto-refresh) and timeout values. |
1.Overview: OBS Controller
The OBS Controller lets you remotely control OBS Studio running on a streaming device at a remote site — start/stop streaming and recording, switch scenes, toggle scene items, manage stream keys, refresh browser overlays, open projectors on specific monitors, and mix audio — all from your browser, without touching the remote machine.
Two Communication Paths
The controller uses two separate communication paths to OBS, depending on the feature:
| Path | Used by | How it works |
|---|---|---|
| WebConnect (HTTP polling) | Streaming, Recording, Replay Buffer, Scenes, Scene Items, Stream Key, Browser Overlays, Monitors, Projectors | The cloud sends a command to the WebSports server → WebConnect on the device picks it up → OBS executes it → the response is polled back. |
| OBS WebSocket v5 (direct WSS) | Audio Mixer | The browser opens a persistent WebSocket connection directly to the device's OBS WebSocket endpoint through the Tailscale proxy for real-time volume and mute control. |
2.Prerequisites
On the Streaming Device
- OBS Studio must be installed and running.
- WebConnect must be running on the device — this is the bridge that receives commands from the cloud and executes them in OBS. Without it, nothing on this page will work.
- OBS WebSocket Server must be enabled in OBS (Tools → WebSocket Server Settings) for the Audio Mixer to connect. If you set a WebSocket password, it must be saved on the device record in StreamStudio.
- Caddy (or equivalent reverse proxy) must be running with routes for both the WebConnect HTTP path and the
/obs-wsWebSocket path, exposed via Tailscale Funnel.
In StreamStudio
- You must be logged in and have been granted access to at least one device by an admin.
- The device must have a Scoreboard ID (used by WebConnect) and a Proxy URL (Tailscale Funnel URL used for the WebSocket connection).
3.How It Communicates With OBS
WebConnect Command Flow (HTTP path)
Every command button (except Audio Mixer) follows this three-step flow through the obsCommand backend function:
| Step | What happens |
|---|---|
| 1. Ping | The cloud checks the WebSports server for the device's last active timestamp. If the device hasn't pinged recently (within the configured timeout, default 20 seconds), the command is rejected as "offline." |
| 2. Post | The cloud posts the command (e.g., StartStream, SetCurrentProgramScene) to the WebSports server, where WebConnect on the device will pick it up. |
| 3. Poll | The cloud polls the server for OBS's response. If OBS responds within the configured poll duration (default 5 seconds), the result is returned. Otherwise, the command times out. |
OBS WebSocket v5 (Audio Mixer path)
The Audio Mixer opens a direct, persistent WebSocket connection to the device's OBS WebSocket server (routed through the Tailscale proxy at <proxy_url>/obs-ws). This provides real-time volume and mute changes with no polling delay, OBS WebSocket v5 authentication (SHA-256 challenge-response if a password is set), and automatic reconnection.
The connection status is shown as a colored dot:
| Status | Meaning |
|---|---|
| 🟡 Connecting… | Establishing the WebSocket connection |
| 🟢 Connected | Authenticated and ready — you can control audio |
| ⚫ Disconnected | No active connection |
| 🟡 Auth required | OBS WebSocket has a password, but none is set on the device. An admin needs to add the OBS WebSocket password on the Devices page. |
| 🔴 Error | Cannot reach OBS WebSocket — check the Caddy /obs-ws route and that OBS WebSocket is enabled |
OBS Commands Used
| Feature | OBS WebSocket Command | Via |
|---|---|---|
| Start / Stop Stream | StartStream / StopStream | WebConnect |
| Get Stream Status | GetStreamStatus | WebConnect |
| Start / Stop Recording | StartRecording / StopRecording | WebConnect |
| Get Recording Status | GetRecordingStatus | WebConnect |
| Start / Stop Replay Buffer | StartReplayBuffer / StopReplayBuffer | WebConnect |
| Get Replay Buffer Status | GetReplayBufferStatus | WebConnect |
| Get all scenes | GetSceneList | WebConnect |
| Switch scene | SetCurrentProgramScene | WebConnect |
| Get scene items | GetSceneItemList | WebConnect |
| Show / Hide scene item | UnHideInputSource / HideInputSource | WebConnect |
| Get all inputs (overlays) | GetInputList | WebConnect |
| Refresh browser source | refreshnocache | WebConnect |
| Get monitors | GetMonitorList | WebConnect |
| Open projector | OpenVideoMixProjector | WebConnect |
| Get / Set stream key | GetStreamServiceSettings / SetStreamKey | WebConnect |
| Get / Set volume | GetInputVolume / SetInputVolume | OBS WebSocket |
| Get / Set mute | GetInputMute / SetInputMute | OBS WebSocket |
4.The Controller Interface
Device Selector
At the top-right of the page, a dropdown lets you select which streaming device to control. Only devices you've been granted access to appear. Your selection is remembered between sessions.
Next to the dropdown, a status indicator shows whether the device is online:
| Indicator | Meaning |
|---|---|
| 🟢 Online | WebConnect is active and responding |
| 🔴 Offline | WebConnect hasn't pinged recently, or OBS isn't running |
| ⚫ Unknown | Hasn't been checked yet |
Click the check button to manually ping the device and refresh its status.
Command Panels
| Panel | Icon | What it controls |
|---|---|---|
| Streaming | MonitorPlay | Start/stop the live stream, check stream status, manage the stream key |
| Recording | Circle | Start/stop local recording, check recording status |
| Replay Buffer | Layers | Start/stop the replay buffer, check its status |
| Scenes | Layers | List all scenes, switch the active scene, show/hide individual scene items |
| Browser Overlays | ListVideo | List all browser sources and refresh them without cache |
| Monitors | Monitor | List all displays, open a projector on a specific monitor, save the default monitor |
| Audio Mixer | Volume2 | Real-time volume and mute control for all audio sources |
Command Output
At the bottom of the page, a collapsible Command Output panel shows the label and result of the last command sent. Click the panel header to expand it and see the full output.
When a command succeeds (and isn't a silent query like listing scenes), a response dialog pops up showing:
| Status | Meaning |
|---|---|
| ✅ Success | OBS confirmed the command |
| ❌ Device Unreachable | WebConnect is offline |
| ⏱️ No Response | OBS didn't respond within the poll duration |
| ⚠️ Error | The command failed |
The dialog also shows the raw OBS response message (expandable under "OBS Message (debug)"), the response status (true/false), the action taken, and the timestamp when OBS processed the command.
5.Streaming Control
Start Stream
Click the green Start Stream button to begin live streaming in OBS. This sends the StartStream command via WebConnect.
Stop Stream
Click the red Stop Stream button to stop streaming in OBS.
Get Stream Status
Click the grey Get Stream Status button to query OBS for the current streaming state (active/inactive, bitrate, uptime, etc.). The result appears in the response dialog.
Manage Stream Key
Click the Manage Stream Key button to open the Stream Key dialog. Here you can:
| Field | Description |
|---|---|
| Stream Key | The stream key OBS uses to send video. Paste from clipboard or type manually. |
| Type | The stream service type (e.g., rtmp_common). |
| Server | The RTMP server URL (default: rtmps://a.rtmps.youtube.com:443/live2 for YouTube). |
- Get Current Key — fetches the stream key currently configured in OBS so you can verify it.
- Set Stream Key — pushes the entered key, type, and server to OBS.
6.Recording & Replay Buffer
OBS can record locally to disk while also streaming. This is useful for keeping a high-quality archive.
| Button | Action |
|---|---|
| Start Recording (green) | Begins local recording in OBS. |
| Stop Recording (red) | Stops the local recording. |
| Get Recording Status (grey) | Queries OBS for the current recording state, file path, and duration. |
Replay Buffer
The Replay Buffer continuously captures the last few seconds of video, letting you save a replay on demand (commonly used in sports broadcasting for instant replays).
| Button | Action |
|---|---|
| Start Replay Buffer (green) | Begins continuous capture. |
| Stop Replay Buffer (red) | Stops the capture. |
| Get Replay Buffer Status (grey) | Queries OBS for the current replay buffer state. |
7.Scenes
Scenes are the different layouts in your OBS production (e.g., "Pre-match", "Live Game", "Scorebug", "Interview").
Getting the Scene List
Click Get All Scenes & Items to fetch all scenes from OBS. The scene list appears below the button, showing each scene name, the active scene (highlighted green with a "Live" badge), and a Set Active label on non-active scenes.
Switching Scenes
Click any scene name to make it the active program scene. The SetCurrentProgramScene command is sent via WebConnect. While switching, the scene shows a "Switching…" indicator.
Viewing and Toggling Scene Items
Click the chevron (▶) next to a scene name to expand it and load its scene items (individual sources within the scene, like cameras, overlays, images).
Each scene item shows:
- The source name
- A visibility toggle (eye icon) — click to show or hide that item within the scene
When you toggle an item, the HideInputSource or UnHideInputSource command is sent. This is useful for quickly hiding/showing a scoreboard overlay, logo, or camera angle without switching scenes.
8.Browser Overlays
Browser sources in OBS are web pages loaded as overlays (e.g., a live scoreboard widget, a lower-third graphic, a chat overlay).
Getting the Overlay List
Click Get All Overlays to fetch all browser sources from OBS. Each browser source appears in a list below the button.
Refreshing a Browser Source
Click the Refresh button next to any browser source to force OBS to reload it without cache (refreshnocache command). This is useful when:
- The overlay's content has changed but OBS is showing a stale version
- The overlay page has errored and needs to be reloaded
- You've updated a remote scoreboard or graphic and want it to appear immediately
9.Monitors & Projectors
Projectors let you output a specific scene or source to a physical monitor or display connected to the streaming device (e.g., a projector at the venue showing the live mix).
Getting the Monitor List
Click Get All Monitors to fetch all connected displays from OBS. Each monitor appears in a list showing its name and index.
Opening a Projector
Click the Projector button next to any monitor to open a full-screen projector output on that display. The OpenVideoMixProjector command is sent with the monitor's name and index.
Saving the Default Monitor
Click a monitor name to select it (highlighted blue), then click Save Monitor to Device to persist it as the default projector target for this device. The saved monitor is shown with a green "Saved:" indicator.
10.Audio Mixer
The Audio Mixer provides real-time volume and mute control for all audio sources in OBS. Unlike the other panels, it connects directly to OBS via WebSocket for instant feedback.
Connection Status
A colored dot and label at the top of the panel shows the WebSocket connection status:
| Status | Meaning |
|---|---|
| 🟡 Connecting… | Establishing the WebSocket connection to OBS |
| 🟢 Connected | Authenticated and ready — you can control audio |
| ⚫ Disconnected | No active connection |
| 🟡 Auth required | OBS WebSocket has a password, but none is set on the device. An admin needs to add the OBS WebSocket password on the Devices page. |
| 🔴 Error | Cannot reach OBS WebSocket — check the Caddy /obs-ws route and that OBS WebSocket is enabled |
Getting Audio Sources
Click Get Audio Sources (enabled only when connected) to fetch all audio inputs from OBS. The panel automatically detects native audio captures (WASAPI, PulseAudio, CoreAudio, ALSA) and potential audio sources (browser sources, VLC, media files, FFmpeg — tested for audio capability).
Each audio source appears as a row with:
- Source name (e.g., "Mic/Aux", "Desktop Audio", "Browser Overlay")
- Mute toggle — click the speaker icon to mute (red) or unmute (blue). Changes apply instantly.
- Volume slider — drag to adjust the volume (0–100%). Changes are debounced and sent in real-time.
- Volume readout — shows the percentage and decibel (dB) value (e.g.,
75% · -2.5 dB)
How Volume Changes Work
When you drag the slider:
- The new volume is sent to OBS immediately via WebSocket (
SetInputVolume) - The displayed value updates optimistically (no waiting for a response)
- The dB value is calculated from the multiplier:
dB = 20 × log₁₀(volume) - If the command fails, the slider reverts to the previous value and an error is shown
11.Command Settings
Admins can configure the WebConnect communication timeouts on the Settings page:
| Setting | Default | Description |
|---|---|---|
| WebConnect Ping Timeout | 20 seconds | If the device hasn't pinged the server within this duration, it's considered offline and commands are rejected. |
| Response Poll Duration | 5 seconds | How long the cloud waits for OBS to respond to a command before timing out. |
When to adjust these:
- Increase ping timeout if your device has an unstable internet connection and pings are occasionally delayed.
- Increase poll duration if commands frequently time out (OBS takes more than 5 seconds to respond — common on slow hardware like Raspberry Pi).
- Decrease poll duration if you want faster timeout feedback (at the risk of more false timeouts).
The same page also has toggles for the two background automations (Auto Refresh Stream Statuses, Auto Sync YouTube Streams) — these affect the YouTube dashboard, not the OBS Controller directly.
12.Troubleshooting
| Message / Symptom | Cause & Fix |
|---|---|
| "OBS is not active on the streaming device" / "WebConnect not active" | WebConnect isn't running or hasn't pinged recently. Check that WebConnect is running on the device and that the device has internet connectivity. If the issue persists, an admin may need to restart WebConnect. |
| "OBS did not respond within X seconds" | The command was sent but OBS didn't respond within the poll duration. This can happen if OBS is busy (e.g., switching scenes with heavy transitions) or on slow hardware. Try the command again, or ask an admin to increase the Response Poll Duration in Settings. |
| "Unable to reach the WebSports server" | The cloud can't reach the intermediate WebSports server. Check your internet connection. If the problem persists, the WebSports server may be temporarily unavailable. |
| Audio Mixer shows "Auth required — set OBS WS password on Devices page" | OBS WebSocket has a password enabled, but the device record in StreamStudio doesn't have it. An admin needs to go to the Devices page and enter the OBS WebSocket password for this device. |
| Audio Mixer shows "Error — Cannot reach OBS WebSocket" | The WebSocket connection can't be established. Check: (1) OBS WebSocket Server is enabled in OBS, (2) the Caddy reverse proxy has an /obs-ws route, (3) the Tailscale Funnel is running, (4) the device's Proxy URL is correct. |
| Commands work but Audio Mixer doesn't (or vice versa) | The two use different communication paths. WebConnect (HTTP polling) and OBS WebSocket (direct WSS) are independent. One can work while the other is broken — check the specific infrastructure for the failing path. |
| Scene items don't appear when expanding a scene | Click the Get All Scenes & Items button first to load the scene list, then expand a scene. If items still don't appear, the scene may be empty in OBS. |
| "Please make sure this Stream Key MATCHES the Stream Key of your YouTube Stream" | The stream key in OBS must match the one YouTube generated for your broadcast. Copy the stream key from the YouTube Stream Dashboard and paste it into the Manage Stream Key dialog. |
13.Data Storage
| Entity | What it holds | Used by |
|---|---|---|
| Device | Scoreboard ID, proxy URL, OBS WebSocket password, saved monitor name and index. | Device selector, WebConnect commands, Audio Mixer WebSocket, projector defaults. |
| ObsSettings | Ping timeout, poll duration, automation toggles. | Command timeout behavior, background automations. |
| UserDeviceAccess | Which users can access which devices. | Device selector visibility. |
1.Overview: PTZ Control
StreamStudio provides two PTZ (Pan-Tilt-Zoom) control surfaces for ONVIF-compatible cameras at your remote sites. Both let you aim and zoom cameras from your browser, but they serve different use cases:
| Page | Route | What it provides |
|---|---|---|
| PTZ Camera Control | /ptz | Remote pan, tilt, zoom, and park commands sent through the ONVIF proxy. No live video — pure camera movement control. |
| Live Camera Feed | /local-ptz | A live MJPEG video feed from the camera (Linux devices only) plus the same PTZ controls, image adjustment (brightness/contrast/saturation/sharpness), and a built-in diagnostics panel. |
Both pages communicate with cameras using the ONVIF protocol (an open standard for IP camera control). Commands are sent from the cloud through an ONVIF proxy running at the remote site, which forwards them to the camera over the local network.
2.Prerequisites
On the Camera
- The camera must be ONVIF-compatible (Profile S for streaming, Profile PTZ for movement). Most Hikvision, Dahua, and Axis PTZ cameras support ONVIF out of the box.
- ONVIF must be enabled in the camera's web interface (usually under Network → Advanced → ONVIF or Integration → ONVIF).
- An ONVIF user account must be configured on the camera with PTZ control permissions. The credentials are stored in the ONVIF proxy's environment, not in StreamStudio.
- The camera must have a fixed IP address on the local network at the remote site.
On the Remote Site (Raspberry Pi / Windows PC)
- An ONVIF proxy must be running (typically on port 3000) to translate cloud commands into ONVIF SOAP requests to the camera.
- For the Live Camera Feed (Linux only): FFmpeg + mjpg-streamer must be running to convert the camera's RTSP stream into an MJPEG feed served over HTTP.
- Caddy (or equivalent reverse proxy) must expose both the ONVIF proxy and the mjpg-streamer via Tailscale Funnel.
In StreamStudio
- You must be logged in and have been granted access to at least one device by an admin.
- The device must have a Camera IP (the camera's local IP at the site) and a Proxy URL (the Tailscale Funnel URL of the ONVIF proxy).
- For the Live Camera Feed, the device must also have a Stream URL (the Tailscale Funnel URL of the mjpg-streamer) and the OS Type must be set to
linux.
3.How PTZ Commands Work
All PTZ movement commands go through the ptzCommand backend function, which forwards them to the ONVIF proxy at the remote site. The proxy then sends an ONVIF SOAP request to the camera.
| Action | What it sends | ONVIF result |
|---|---|---|
| Move (pan/tilt) | action: move, x: -1/0/1, y: -1/0/1, speed: 0.1–1.0 | ContinuousMove — camera pans/tilts in the given direction at the given speed |
| Zoom in | action: move, zoom: 1, speed | ContinuousMove with zoom velocity — lens zooms in |
| Zoom out | action: move, zoom: -1, speed | ContinuousMove with negative zoom velocity — lens zooms out |
| Stop | action: stop | Stop — all movement halts immediately |
| Park (Home) | action: home | GotoHomePosition — camera returns to its saved preset position |
Press-and-Hold Behaviour
Pan, tilt, and zoom buttons use press-and-hold semantics. When you press a direction button, a continuous-move command is sent — the camera starts moving. When you release the button (pointer up or pointer leave), a stop command is sent immediately. This mimics a physical joystick.
Speed
The speed slider (0.1–1.0) controls how fast the camera moves. Lower values are for fine adjustments; higher values are for quick repositioning. The speed is included in every move command sent to the camera.
4.Remote PTZ Control
The PTZ Camera Control page (/ptz) is a focused control surface for camera movement — no video feed, just the controls. It's ideal when you already have a separate video monitor and just need to aim the camera.
Device Selector
At the top-right, a dropdown lets you pick which device's camera to control. Only devices you've been granted access to appear. A status indicator shows whether the camera is online (green) or offline (red). Click the check button to ping the camera via the ONVIF proxy.
If the selected device has no camera IP configured, a warning message appears: "No camera configured — Ask an admin to set a camera IP for this device."
Layout
The page is a single centered card containing three sections:
| Section | Controls |
|---|---|
| Pan & Tilt | A 3×3 directional pad (8 directions + center stop button) |
| Zoom | Zoom In and Zoom Out buttons (press-and-hold) |
| Park | A single button that returns the camera to its home position |
A speed slider sits at the bottom of the card, and the camera's IP address is shown below it.
5.Pan & Tilt
The pan-and-tilt pad is a 3×3 grid of buttons. The eight outer buttons move the camera in eight directions; the center button stops all movement.
| Button | Direction | Command |
|---|---|---|
| Top-left | Up-Left | move (x: -1, y: 1) |
| Top-center | Up | move (x: 0, y: 1) |
| Top-right | Up-Right | move (x: 1, y: 1) |
| Middle-left | Left | move (x: -1, y: 0) |
| Center (red) | Stop | stop |
| Middle-right | Right | move (x: 1, y: 0) |
| Bottom-left | Down-Left | move (x: -1, y: -1) |
| Bottom-center | Down | move (x: 0, y: -1) |
| Bottom-right | Down-Right | move (x: 1, y: -1) |
How to use it
- Press and hold a direction button — the camera starts moving in that direction at the current speed.
- Release the button — a stop command is sent and the camera halts.
- If you drag your finger/cursor off the button, the stop command fires automatically (pointer-leave).
- The center red button sends an immediate stop — useful if the camera is drifting after a release.
6.Zoom & Park
Zoom In / Zoom Out
Two buttons below the pan-and-tilt pad control the camera's optical zoom. Like the directional buttons, they use press-and-hold semantics:
- Press and hold Zoom In — the lens zooms in continuously at the current speed.
- Press and hold Zoom Out — the lens zooms out continuously.
- Release — a stop command halts the zoom immediately.
Park (Home)
The Park button sends a home command that returns the camera to its saved home preset position. This is useful for:
- Resetting the camera to a known position before or after a broadcast
- Quickly re-centering on a default framing
- Recovering if the camera has been moved to an unknown position
7.Speed Control
A speed slider at the bottom of the control card sets how fast the camera moves for all pan, tilt, and zoom commands.
| Page | Default speed | Range | Notes |
|---|---|---|---|
| Remote PTZ Control (/ptz) | 0.5 | 0.1 – 1.0 | Balanced default for general use |
| Live Camera Feed (/local-ptz) | 0.2 | 0.1 – 1.0 | Lower default for fine adjustments while watching the feed |
When to adjust:
- 0.1 – 0.3 — fine adjustments, framing a shot precisely while watching the live feed.
- 0.4 – 0.7 — general repositioning, moving between subjects.
- 0.8 – 1.0 — quick pans across a wide area, repositioning for a completely different angle.
8.Live Camera Feed
The Live Camera Feed page (/local-ptz) combines a live video preview with PTZ controls, image adjustment, and diagnostics. It's the full-featured camera control surface.
Two-Column Layout
The page is split into two columns:
| Column | Contents |
|---|---|
| Left | Live MJPEG video feed from the camera with connection status indicator and a Refresh button |
| Right | PTZ controls (pan/tilt pad, zoom, park, speed slider) — identical to the Remote PTZ Control page |
Below the two columns, the Image Adjustment panel and the Diagnostics panel appear full-width.
Windows Devices
9.Video Feed (MJPEG)
The live video feed is an MJPEG (Motion JPEG) stream served by mjpg-streamer on the remote device, which captures frames from the camera's RTSP stream via FFmpeg and serves them over HTTP through Caddy and the Tailscale Funnel.
Connection States
A status indicator in the video panel header shows the current feed state:
| Indicator | State | Meaning |
|---|---|---|
| 🟡 pulsing dot | Connecting… | Establishing the stream connection |
| 🟢 dot | Connected | Receiving live video frames |
| 🔴 dot | Offline | Stream failed or is unreachable |
How the stream works
Unlike a plain <img> tag, the feed uses the Fetch API with a ReadableStream to decode each JPEG frame individually. This provides true per-frame timing so a silent mid-stream stall is detected and the connection self-heals:
- If no bytes arrive for 10 seconds, the watchdog aborts the connection and reconnects automatically.
- If the Fetch API fails (e.g., CORS blocked), it falls back to a plain
<img>tag that displays the stream without frame-level monitoring. - If the fallback
<img>also fails, the feed shows an error state.
Refresh Button
Click Refresh in the video panel footer to force a reconnection. This aborts the current stream connection and starts a fresh one — useful if the feed has frozen or the camera was restarted.
10.Image Adjustment
The Image Adjustment panel (Linux devices only) lets you remotely adjust the camera's image settings — brightness, contrast, saturation, and sharpness — via the ONVIF imaging service. Changes apply directly to the camera's image sensor.
Settings
| Setting | Range | Default | What it controls |
|---|---|---|---|
| Brightness | 0 – 100 | 50 | Overall image brightness |
| Contrast | 0 – 100 | 50 | Difference between light and dark areas |
| Saturation | 0 – 100 | 50 | Color intensity |
| Sharpness | 0 – 100 | 50 | Edge clarity and detail |
How to use it
- The panel auto-loads the camera's current settings when you select a device.
- Drag any slider or type a value in the number field to adjust.
- Click Apply to push the new values to the camera. A green check appears briefly on success.
- Click Reset to restore the camera's factory defaults (all values return to 50).
cameraImageCommand backend function, which forwards them to the ONVIF proxy. The proxy sends ONVIF GetImagingSettings / SetImagingSettings requests to the camera.11.Diagnostics
The Diagnostics panel (on the Live Camera Feed page) runs a 3-layer connectivity test to help pinpoint where a connection problem lies — browser, proxy, or camera.
Running Diagnostics
Click Run Diagnostics to start the test. The panel auto-expands and shows a timestamped log of each step with a colored severity level (info, success, warn, error).
The 3 Layers Tested
| Layer | What it tests | How |
|---|---|---|
| 1/3 — Stream URL | Browser → Tailscale Funnel → Caddy → mjpg-streamer | Fetches the stream URL and checks the HTTP response. Distinguishes between CORS errors (server reachable but header missing), 502 errors (mjpg-streamer down), and network errors (server unreachable). |
| 2/3 — PTZ Proxy | Cloud → Tailscale Funnel → Caddy → ONVIF proxy → Camera | Sends a stop command via the ptzCommand backend function. If it returns an error, the ONVIF proxy or camera is unreachable. |
| 3/3 — Summary | Overall verdict | Summarizes which layers passed and which failed, with specific fix suggestions for each failure type. |
Common diagnostic outcomes
- Layer 1 returns 502: mjpg-streamer is down — restart it on the device.
- Layer 1 CORS error: Caddy is reachable but missing the
Access-Control-Allow-Originheader — fix the Caddyfile. - Layer 1 network error: Caddy or Tailscale Funnel is down — check both services.
- Layer 2 error: ONVIF proxy is down or the camera credentials in the proxy's
.envare wrong.
12.Troubleshooting
| Message / Symptom | Cause & Fix |
|---|---|
| "No camera IP configured for this device." | An admin needs to set the camera IP on the Devices page. The PTZ controls cannot function without it. |
| Camera shows "offline" when checked | The ONVIF proxy is down, the camera is unreachable on the local network, or ONVIF isn't enabled on the camera. Run diagnostics on the Live Camera Feed page to pinpoint the layer. |
| PTZ commands don't move the camera but no error appears | The camera may not support ONVIF PTZ control, or the ONVIF user account lacks PTZ permissions. Check the camera's ONVIF settings and user roles. |
| Live feed shows "No stream URL configured" | An admin needs to set the Stream URL on the device record (the Tailscale Funnel URL of mjpg-streamer). This is only needed for the Live Camera Feed page. |
| Live feed shows "Local feed not available" / "WINDOWS DEVICE" | The live MJPEG feed is only supported on Linux devices. Windows devices can still use PTZ controls but not the video feed or image adjustment. |
| Live feed freezes and shows "Reconnecting…" | The stream stalled (no frames for 10 seconds). The watchdog auto-reconnects. If it keeps happening, check mjpg-streamer and FFmpeg on the device, or click Refresh. |
| Live feed shows "Stream failed to load" | Both the Fetch-based stream and the fallback <img> failed. Check that mjpg-streamer is running and the stream URL is correct. |
| Image Adjustment panel not visible | The panel only appears for Linux devices with a camera IP configured. Windows devices don't support ONVIF image adjustment in this app. |
| Image Adjustment "Apply" shows an error | The ONVIF proxy couldn't reach the camera or the camera doesn't support imaging settings. Run diagnostics to check connectivity. |
| Camera moves too fast / too slow | Adjust the speed slider. Lower values (0.1–0.3) for fine control, higher values (0.7–1.0) for quick repositioning. |
13.Data Storage
| Entity | What it holds | Used by |
|---|---|---|
| Device | Camera IP, proxy URL, stream URL, OS type, OBS WebSocket password, monitor settings. | Device selector, PTZ commands, live feed URL, image adjustment. |
| UserDeviceAccess | Which users can access which devices. | Device selector visibility. |