-
Notifications
You must be signed in to change notification settings - Fork 0
COMPARISON
Generated from
docs/COMPARISON.md. Edit that file and re-runnode scripts/publish-wiki.mjs --push. An edit made here is a fork of the documentation that nothing reconciles, and the next run of this script will overwrite it without asking.
An honest comparison, including the parts where polyemesis loses.
Sourced from a survey of Restreamer's issue trackers (263 open, 548 closed) and restream.io's published plan comparison, done 2026-07-27 — with the polyemesis column verified against the code rather than assumed.
The polyemesis column was re-verified 2026-07-30, and three rows had gone stale in the product's favour: overlays and MQTT had shipped while still listed as missing, and LL-HLS had been deliberately declined rather than merely absent. A comparison page drifts in whichever direction the project moves, so the column is worth re-checking whenever this page is cited.
Re-verified again 2026-08-07 while surveying Castr, and the same drift had happened twice more: both playlist rows still said "Partial — no sequencing" long after
internal/playlistmedia,-stream_loop -1and the scheduler'splaylist.start/playlist.stopactions shipped. Note the direction — every stale row so far has understated the product, because the page is written when a gap is found and nobody returns to it when the gap closes.2026-08-09: three tools were missing from the page entirely. obs-multi-rtmp, Aitum Multistream and MistServer are what a prospect is usually already running when they find this project, and a comparison that omits the incumbent is not credible. They are added below. Each claim about them was read in their own source rather than taken from their marketing, and every external fact on this page now carries the date it was checked — including the ones that were already here. The drift warning above applies to those rows too: they are one person's reading of somebody else's repository on one day.
If you do not need different audio per destination, you already have a tool. Multistreaming from the stream PC: obs-multi-rtmp is free, mature and enough. Want a server instead: Restreamer is more mature and far more widely deployed than this. Want no server at all: restream.io. Want an industrial-strength media server with real track selection: MistServer.
polyemesis exists for one case none of them serve: this platform gets the clean mix, that one gets the full mix, from one upload and one video encode. If that is not your problem, the rest of this page is academic.
This is the whole difference, and it is narrower and more checkable than "better audio support", so it is worth stating as something you can go and read rather than as a claim you have to take on trust.
Every tool here can decide which audio track leaves for a destination. None of them can decide what is in it.
-
obs-multi-rtmp —
src/output-config.h, checked 2026-08-09:struct AudioTrackConfig { int mixer_track; int output_track; };
A pair of integers: this OBS mixer track goes in that output track slot. No gain, no sum, no matrix. It is an assignment.
-
Aitum Multistream —
multistream.cpp, checked 2026-08-09, builds each destination's encoder withobs_audio_encoder_create(..., obs_data_get_int(settings, "audio_track"), ...). One integer per destination: the mixer index that destination takes. -
MistServer —
Util::wouldSelect()inlib/stream.cpp, checked 2026-08-09, readsaudio=andvideo=off a push target's query string and resolves them throughUtil::findTracks, which also acceptsalland*. So a MistServer push target genuinely can carry different tracks from the one before it, and anyone who tells you a media server cannot select tracks has not read this file. It selects well. Its routing server does not mix: there is noamix, no downmix, noloudnormand noebur128anywhere undersrc/orlib/. The audio path insrc/process/process_av.cppisswr_alloc_set_opts, a resampler — one track in, one track out.Scoped deliberately, because the unscoped version is wrong. An external Stream Process (
MistProcAV, an FFmpeg pipeline) can decode, filter and inject a track back into the buffer, soamixandloudnormare reachable — just not in the server that does the routing (competitor-facts §6, checked 2026-08-15). Saying "MistServer does not mix" is the same error as saying the OBS plugins cannot send different audio per destination: true of one layer, false of the product. -
OBS itself — the Twitch VOD track is
obs_audio_encoder_create(..., vodTrack, ...), andVodTrackMixerIdx()returns nothing unlessServiceSupportsVodTrack(service)(obsproject/obs-studiofrontend/utility/AdvancedOutput.cpp, checked 2026-08-09). One extra selected track, for a service that has opted into it.
polyemesis sums. A destination's mix is the set of ingest tracks you chose, summed, with a gain per input-channel-to-output-channel cell, and a loudness target measured after the routing — see AUDIO-ROUTING.md. That is a different operation from selection, which is why "add per-destination audio to obs-multi-rtmp" is not a small patch to obs-multi-rtmp.
And here is what mixing costs. Selecting a track is a copy; mixing means decoding the audio and encoding it again, once per destination. polyemesis does that on every destination. The video is stream-copied so the bill stays small, but it is not zero, and a selector's is.
| Capability | Restreamer | restream.io | obs-multi-rtmp | Aitum | MistServer |
|---|---|---|---|---|---|
| Per-destination audio mix from one multitrack ingest | No | No | No | No | No |
| Channel-level mix matrix with per-cell gain | No | No | No | No | No |
| Per-destination loudness TARGET you set — I, LRA and TP, measured after routing | Filter only4 | No | No | No | No |
| Multitrack archive — every ingest track preserved, stream-copied | No | No | Via OBS | Via OBS | Yes |
| Per-track stems as 24-bit WAV or FLAC, segment-aligned to the master | No | No | No | No | No |
| Track annotations — what each incoming track actually is | No | No | n/a | n/a | Unverified |
| Typed SRT rejections — the publisher is told why it was refused | No | n/a | n/a | n/a | Unverified |
| A second audio mix to the same destination, from one ingest | No | n/a | n/a | n/a | Twitch Enhanced Broadcasting, and it needs a supported GPU; competitors unverified |
EXPERIMENTAL, on the last row only — no broadcast has been published through a key Twitch minted. The negotiation is not the gap: polyemesis's own tests reach
ingest.twitch.tvon every run, and Twitch accepts a supported-GPU inventory, grants the VOD audio track and mints a key. What has never been observed is everything after that. On a non-Twitch destination the two-mix egress is a different mechanism and is not covered by this caveat. Nothing is gated: a negotiation that does not succeed falls back to the ordinary Twitch ingest. See AUDIO-ROUTING.md § Two mixes to one destination.
Two cells say Unverified rather than "No" on purpose. MistServer's own
recording row is a loss and is marked as one: a recording target with
?audio=all keeps every track (Util::findTracks, lib/stream.cpp, checked
2026-08-09), which is the same outcome as our multitrack archive by a different
route. The two "Unverified" cells are things nobody has gone and read, and this
page does not guess.
The multitrack ask is not hypothetical. "Multiple audio tracks for Twitch" is an open request on Restreamer's tracker with 8 reactions (surveyed 2026-07-27) — users asking Restreamer for the thing polyemesis was built to be.
obs-multi-rtmp (GPL-2.0, last pushed 2026-08-01) and Aitum Multistream (GPL-2.0, last pushed 2026-05-19), both checked 2026-08-09. Star counts used to appear here and are gone: a number nobody re-checks is the kind of claim this document cannot afford, and it is not in the facts file. They are the honest incumbent: free, installed in two minutes, and already on the machine.
| What they have | polyemesis |
|---|---|
| No server at all. A plugin in OBS, no host to provision, no port to open | You run a server, or there is nothing to run |
| Nothing new to learn. The destinations sit in OBS beside the ones already there | A second UI, a second set of concepts |
| No extra hop. OBS talks to the platform directly, so nothing in the middle can fail | The server is one more thing between you and air |
| Per-target video encoder settings, decided in OBS | Shared renditions, decided on the server |
What they cost you is upload. Each target in MultiOutputConfig is another
OBS output with its own service parameters and its own optional encoder config
(src/output-config.h, checked 2026-08-09), so N destinations is N uploads off
one connection, and N video encodes on the machine that is also running the
show. polyemesis takes one upload and stream-copies the video to all of them.
That trade is the entire reason to put a server in the path, and if your upload
is comfortable it is not a reason at all.
Ranked by how often it was asked for, which is the honest ordering. Reaction counts surveyed 2026-07-27.
| Gap | Evidence | Status here |
|---|---|---|
| Overlays — text, logo, watermark, channel name | Asked 5 separate times (6+5+4+2+1 reactions), every one closed unimplemented | ✅ Have. Image watermarks and text overlays on renditions, where re-encoding is already the contract. Nine anchors, sizes as a percentage of the frame so one logo is correct on landscape and vertical tiers alike; two weights of Inter ship embedded because drawtext needs a font path |
| WebRTC / WHIP output | 6 reactions, closed unimplemented | Missing. Sizeable subsystem; the real use case is sub-second self-monitoring |
| Decklink / SDI capture, in and out | 4 + 2 reactions |
Missing. Needs an FFmpeg built with decklink, so a third image variant |
| Deinterlacing | 1 reaction | ✅ Have. bwdif with off / only-interlaced / every-frame, placed first in the filter chain because scaling interlaced content bakes the combing in |
| Playlist / scheduled file broadcast | Five issues circling one capability | ✅ Have, with one caveat. An ordered list of uploads played through FFmpeg's concat demuxer under -stream_loop -1, and playlist.start / playlist.stop are scheduler actions. Every upload is normalised once on import to a single fixed profile (playlistmedia), because concat refuses a set whose codecs, timebase, resolution or channel layout disagree — and plays a drifting, tearing one if it does not refuse. The caveat: the playlist lives under failover.playlist and goes on air when no encoder is delivering. It is the fill tier, not a channel you can programme a day of content into |
| Multi-input compositing / video grid | 1 reaction | Missing. Natural once multi-source is settled |
| MQTT | Core tracker | ✅ Have. Retained telemetry with Home Assistant discovery, so the stream appears as entities in a dashboard the operator already runs. Alert webhooks exist alongside it — see MQTT.md |
| HDR 10-bit HEVC | 3 reactions, closed |
Partial. libx265 and hevc_nvenc exist; no HDR tone-map path |
| LL-HLS | 2 reactions, closed | Addressed by tuning, not by building LL-HLS. FFmpeg cannot emit LL-HLS partial segments at all — verified against the pinned binary — so the protocol would need a Go-side packager, which roadmap/LL-HLS.md declines. Tuning instead took preview latency from 4.2–6.2 s to 2.2–3.2 s, measured. Two of the wins were bug fixes rather than knobs: a wrong GOP calculation, and a player flag that was inert because of an unrelated default |
| Maturity | — | Restreamer is established; polyemesis is pre-release with one maintainer |
One caveat on Restreamer's side. Two of its four most-reacted open issues are not feature requests — "State of Restreamer" (31 reactions) and "Future release plans? (last release September 2024)" (11). Roughly a fifth of the open tracker's reaction weight is users asking whether the project is alive. That is context for the maturity row, not a criticism of the software.
MistServer (Unlicense, ~508 stars, last pushed 2026-08-06, checked 2026-08-09) is the one tool here that is a peer rather than a different shape: a media server that ingests and pushes out, and does it at a scale and protocol breadth this project does not approach.
| What it has | polyemesis |
|---|---|
Protocol breadth — an src/output/ directory of muxers, including WebRTC and its own SRT output |
SRT and RTMP in, RTMP/SRT out, HLS for preview |
Per-push track selection, on any target, by index or codec or all
|
Selection is per destination too, but the point here is the mix |
| Years of production deployment, and a commercial vendor behind it | Pre-release, one maintainer |
| Selection without decoding. A push target that only selects tracks is a pure copy end to end; transcoding is a separate opt-in process | Every destination decodes and re-encodes its audio, always, because that is what mixing is |
A caveat on reading their source. The track-selection block in
Util::wouldSelect sits between /*LTS-START*/ and /*LTS-END*/ comment
markers. Those are plain comments, not preprocessor guards, and nothing in
meson.build or meson_options.txt strips them, so the code compiles in the
Unlicense repository as published (checked 2026-08-09). Whether every binary
DDVTECH distributes includes it is unverified — check before you depend on
it.
restream.io is SaaS, so self-hosting beats it on cost, privacy and limits by construction — their plans cap simultaneous channels at 2 free, 3 standard and 5 professional (competitor-facts §2.D, checked 2026-08-15), which is a billing artefact rather than a technical one. This said "2/3/5/8" until 2026-08-15; the fourth number was wrong and contradicted the site's own table.
| restream.io feature | polyemesis |
|---|---|
| Studio — browser production, remote guests | Missing. Multi-source plus compositing is the closest path |
| Pre-recorded upload, go live later | ✅ Have. Upload the file, it is normalised on import, and a scheduled playlist.start puts it on air at a chosen time. Needs the failover tier on, since that is where the playlist lives |
| Teams, roles, multiple workspaces | Missing. Exactly one admin identity |
| Webinars — private, attendee caps, Slack delivery (+$99–299/mo) | Missing. No webinar mode. A private destination plus the built-in chat is the closest path |
| Website player — embeddable, 1000 viewers (Business) | Missing. The HLS preview is operator-facing and auth-gated; there is no public player route |
| Guest/paired channels — guests attach their own destinations to your event | Missing. Follows from the single admin identity |
| Automatic clip selection — "TikTok-ready clips" (+$19–59/mo) |
Partial. internal/clipper cuts keyframe-accurate clips from the archive and internal/clips holds a live 30-second ring buffer — but an operator picks the in and out points. The missing half is automatic highlight selection, not clipping |
| Dual format — vertical and horizontal at once (Professional, $49/mo) | ✅ Have. A rendition takes an explicit width×height with AspectCrop (centre-crop) or AspectBlurredPad; a 1080x1920 preset ships. internal/ffmpeg/rendition.go:61
|
| SRT ingest (enterprise only — "book a demo") | ✅ Have, and it is the primary operated ingest path |
| Stream backup (enterprise only — "book a demo") | ✅ Have, and the implementation is the stronger one — see below |
| Hosted chat across platforms | Have, self-hosted |
| Live health monitor | Have |
| No server to run | By design, no |
The seven rows above were added 2026-08-25 from restream.io's live pricing page. Four are gaps this table had missed entirely; three are capabilities polyemesis already had and this document was not crediting — dual format and SRT ingest are priced features there, and stream backup is behind "book a demo".
On stream backup specifically. Restream's is a hosted safety net. polyemesis's
source-selector tier (internal/engine/selector.go) is a different and stronger
shape: destinations subscribe to a permanent hub whose identity never changes, and
only the feed into it is switched — primary, standby ingest, a looping playlist
file, then the slate. The platform connection is therefore never dropped when the
encoder disappears, which is the failure the whole tier exists to prevent. It is
OFF by default (failover.enabled), which is its real weakness — see issue #512.
Teams and roles is the one to take seriously. polyemesis has a single admin identity, and access to the UI is full control of the server's streaming. Any multi-operator deployment — a church A/V team, an agency running several clients — hits that immediately. See ../SECURITY.md.
All of them do the basic job: ingest once, fan out to several platforms, reconnect when a platform drops, show you whether it is working.
| polyemesis | Restreamer | restream.io | MistServer | |
|---|---|---|---|---|
| Self-hosted | Yes | Yes | No | Yes |
| Simultaneous destinations | No configured cap1 | not assessed | 2–5 by plan tier (checked 15 Aug 2026) | Unverified |
| Video re-encoded per destination |
No (-c:v copy) |
Optional | Yes, server-side | Optional, as a process |
| Recording | Multitrack, stream-copied | No built-in2 | Yes | Yes, tracks selectable |
| Unified chat | Yes | not assessed | not assessed | not assessed |
| Metrics / API | Prometheus + REST | See4 | REST | REST |
| Hardware encoding | NVENC, QSV, VA-API, VideoToolbox, AMF5 | Yes | n/a | NVENC |
| Public release in the last 12 months | Yes3 | No3 | n/a, hosted | Yes3 |
| Cost | Your hardware | Your hardware | Subscription | Your hardware |
1 No limit in the software. Upload bandwidth and CPU are the real ones: measured at roughly 4% of one core per destination on a 6-core VPS, so a 4-core box runs out somewhere near 96. "Unlimited" was the previous wording and it was not defensible.
2 This row said "Yes" until 2026-08-09 and was wrong. Recording is an open feature request on Restreamer's own tracker — datarhei/restreamer#692, opened 2024-02-14, still open when checked 2026-08-09. The underlying datarhei Core can write files; the Restreamer product does not put recording in front of you, which is why the request exists.
3 Release recency, not quality, and it cuts against the maturity row above rather than replacing it. Restreamer's latest release is v2.12.0, 2024-09-13. MistServer's repository was last pushed 2026-08-06. polyemesis tagged v0.1.0 on 2026-07-31 and v0.6.0 on 2026-08-09 — which is the velocity of something pre-release with one maintainer, and is worth exactly that much. All three checked 2026-08-09.
4 This row said "No" against Restreamer and that was wrong.
Restreamer mounts a filter select per publication service, and loudnorm is one
of the filters it offers, so it does apply a per-destination loudness filter.
What it does not offer is a target you set: its control is an on/off checkbox
that emits the bare string loudnorm with no I, LRA or TP, so it takes FFmpeg's
defaults and an operator cannot ask for −16 LUFS rather than −24. That is the
real difference and it is narrower than the row used to claim. Corrected
2026-08-14 after a sweep read their source rather than their feature list — the
same correction the recording row needed in the other direction, and the second
time a cell here has asserted a competitor lacks something they ship.
The metrics row above was wrong the same way and is now blank on their side: Restreamer's own README advertises resource monitoring "optionally by Prom-Metrics", datarhei Core documents Prometheus support, and it serves GraphQL as well as REST — so "REST" understated them twice over and implied Prometheus was ours alone.
5 All five are offered and all five are probed with a real one-frame
encode at startup, so an encoder that is listed but unusable is caught before a
broadcast is. What is EXPERIMENTAL is narrower: the per-encoder command-line
flags handed to NVENC, QSV, VA-API and AMF — rate control, preset, profile —
were read out of FFmpeg's own option tables rather than measured on silicon, and
no such encode has been observed. VideoToolbox is not in that set:
TestEveryConfiguredEncoderOpensWithItsOwnFlags runs a real encode per
registered encoder with that encoder's own flags and both VideoToolbox rows
pass. See ENCODING.md § Per-encoder flags.
- AUDIO-ROUTING.md — the capability the comparison turns on
- RENDITIONS.md — how per-destination video specs work here
Getting it running
- Quickstart: from nothing to a live restream
- Install polyemesis — an SRT server on your own box
- OBS SRT setup: multitrack audio to one ingest
- TLS certificates for a self-hosted SRT server
The routing
- Audio routing: a different mix per destination
- Renditions: one shared video encode
- Encoding: what is copied and what is encoded
- Hardware encoding: NVENC, QSV, VA-API, AMF
Operating it
- Configuration: config.yaml and the web UI
- Streaming platforms: what can be automated
- Broadcasting from a file, on a schedule
- What a settings change restarts, and what it does not
- Upgrading polyemesis and its database
- Troubleshooting: SRT, RTMP and audio problems
Automating it
- Monitoring: Prometheus metrics and alerts
- Lifecycle webhooks: one signed POST per event
- MQTT telemetry and Home Assistant
- HTTP API reference — polyemesis /api/v1
Understanding it