obs-streamplace #
An OBS Studio plugin (OBS 30.2 or newer) that streams to a Streamplace node as fragmented MP4 over HTTP. The node configures the stream through the same request OBS sends for Enhanced RTMP multitrack video, so it can choose how many video renditions and audio tracks the plugin encodes.
Using it #
Extract the archive for your platform into OBS's plugin directory:
| Platform | Directory |
|---|---|
| Linux | ~/.config/obs-studio/plugins/ |
| Windows | C:\ProgramData\obs-studio\plugins\ |
| macOS | ~/Library/Application Support/obs-studio/plugins/ |
Restart OBS, then pick Streamplace in Settings → Stream, the Local
Streamplace node server (http://127.0.0.1:38080), and your stream key. OBS
offers no free-form server field for listed services, so to use another
address, set server in the profile's service.json. The plugin only speaks
plain http://, and expects a node on the same machine.
OBS's Settings page only lists services from rtmp-services, so on startup the
plugin adds a Streamplace entry to the services.json copy in OBS's config
directory (plugin_config/rtmp-services/). The entry names the plugin's output
protocol, so OBS hides it whenever the plugin is not loaded. If OBS's service
updater replaces that file, the entry comes back on the next start.
Audio stems #
Viewers watching over WebRTC can turn parts of your audio up or down, such as the music. To offer that, name OBS mixer tracks in Settings → Output → Audio (Advanced output mode), and route your sources to them in Advanced Audio Properties:
- Track 1 stays the full mix. Everything else (HLS, VODs, multistream) uses it, and viewers hear it unless they change the mix.
- Named tracks 2–6 become stems, up to two of them, e.g. Track 2 "Voice & game" and Track 3 "Music". A viewer who adjusts a stem hears the stems instead of the full mix, so together they should add up to the full mix: with only a "Music" stem, turning it down leaves nothing else playing.
The plugin encodes each of these tracks in both AAC and Opus.
Protocol #
When you start streaming, the plugin does three things in order.
- It sends
POST {server}/api/ingest/client-configurationwith a JSON body in the shape of OBS's GoLiveApiPostData.authenticationcarries the stream key, andclient.supported_codecsis["h264"]. As a Streamplace extension,preferences.audio_trackslists OBS's six mixer tracks as{"track_id", "name"}, with the names set in Settings → Output → Audio (empty when unnamed), so the server can ask for named tracks as stems. - It reads the response as a GoLiveApi
Config:status.result: "error"stops the stream and showsstatus.html_en_us.ingest_endpoints: the plugin uses the first entry whoseprotocolisFMP4, replacing{stream_key}in itsurl_template.encoder_configurationsreplaces the encoders from OBS's output settings with one video encoder per entry, each becoming one video track. As a Streamplace extension an entry names acodec(h264) instead of an OBS encodertype: the plugin uses the encoder from OBS's output settings when it is that codec, with its settings under the entry'ssettings, and otherwise the first available encoder of the codec.width,heightandframeratescale it from the canvas.audio_configurations.livecreates one audio encoder per entry.codecisaacoropus, andtrack_idis the zero-based OBS audio mixer track to encode. Each such track is written withtkhd.alternate_groupset totrack_id + 1, so the AAC and Opus encodes of one mixer track share a group, and with the mixer track's name (or "Track N") as its title intrak/udta/name, the box ffmpeg writes a track title to.- If either list is empty, the plugin uses OBS's own encoders for that kind of track.
- It sends
POSTto the ingest URL withTransfer-Encoding: chunkedand avideo/mp4body:- First comes an init segment (
ftyp+moov) with every video track, then every audio track. - After that it sends one
moof+mdatfragment per frame of the first video track. Each fragment holds every sample completed since the last one. - Sample durations come from the real gaps between timestamps, so frames that OBS drops show up as longer samples.
- The plugin ends the body when streaming stops.
- First comes an init segment (
Building #
From the repository root, in the builder container (see the
streamplace-docker skill):
make obs-plugin-linux-amd64 # or linux-arm64, windows-amd64, darwin-amd64, darwin-arm64
The archive is written to bin/. The build only needs the libobs headers. A
Meson wrap downloads them, pinned to OBS 30.2.3, the oldest release the plugin
supports. On Windows, the build generates an import library for obs.dll from
obs.def. When the plugin starts calling a new libobs function, add it there.