Releases: stageaudioworks/obs-multisite
Release list
v0.1.18-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.18-alpha
A campus on the same network as the main site, or reachable over a VPN the
church already runs, can now receive over that path directly instead of
through the bucket — on both the OBS decoder plugin and the Raspberry Pi
appliance. Cloud storage can also be turned off entirely, for a single
building with no interest in an off-site copy at all. Two real bugs were
found live-testing this, both fixed, both written up below rather than
quietly folded in.
LAN / direct delivery
An encoder can now serve satellites directly over plain HTTP on the same
network or an existing VPN, instead of every satellite going through the
bucket regardless of where it actually sits. A satellite prefers that path
automatically whenever it answers, and falls back to cloud per request —
not per session — the instant it doesn't, so one segment that happens to
have aged out of the LAN side's retention window falls back for that
segment alone rather than dropping the whole connection to cloud.
Cloud upload stays on by default throughout: every other satellite, and the
archival recording, still depend on it. It can now be switched off entirely
for an event — a new checkbox in the encoder's Storage settings, shown only
once LAN delivery is turned on, with a confirmation prompt before it takes
effect (turning it back on needs no confirmation, since that's always the
safe direction). With it off, nothing for that event ever reaches the
bucket at all; every satellite still gets the full event, provided it can
reach the encoder directly.
The Raspberry Pi appliance gained the identical capability in the same
pass — LAN host/port/token fields in its web settings page, the same
automatic preference and fallback, and the same ability to run cloud-free.
A shutdown hang that could look like a crash
Ending a broadcast could freeze OBS's main thread for as long as a stuck
upload had left to retry — with retries set to continue forever in
production, that had no real upper bound. A hang long enough looks, from
the outside, exactly like a crash: the window stops responding, and
force-quitting it is indistinguishable from OBS actually having crashed on
the next launch. Found after a report of exactly that on Windows, and
reproduced on macOS too once we knew what to look for.
The drain now genuinely respects its own deadline — bounded to 8 seconds by
default, down from a technical (but non-functional) 30-second cap — so a
segment that's still stuck when time runs out is left in the local queue
for the next resume, exactly as a real crash would have left it, instead of
holding the whole application hostage to it.
The decoder's "receiving via LAN" indicator could say the wrong thing
Built alongside LAN delivery, and caught before it reached anyone: the
indicator tracked which path answered the single most recent request,
which is the wrong question. A request for something LAN legitimately
doesn't have yet — a marker before the first one was dropped, a segment
that aged out of the retention window — isn't evidence that LAN itself is
down, but it was being read as exactly that, and the display would flip to
"via cloud" on requests that had nothing to do with the actual link. It now
distinguishes a real connection failure from an ordinary miss, and reports
accordingly. The same fix applies to the Pi appliance's equivalent readout.
The Raspberry Pi appliance catches up
Beyond LAN delivery: the storage provider dropdown (Cloudflare R2 / AWS S3 /
Backblaze B2 / Wasabi / Custom) that the two OBS docks have had since
v0.1.13-alpha now appears on the appliance's own web settings page too — it
had never reached there until now. And that settings page, grown long
across several releases of new fields, now collapses into named sections
you open one at a time instead of scrolling past everything to find the
one you came for.
What's new in v0.1.17-alpha
A satellite on the network audio card could put digital noise on air
A box with Sound on the network switched on could, within a minute of
starting to play an event, put loud digital noise onto the AES67 stream during
any quiet stretch — the moment nothing else was queued to send. It looked like
a hardware fault; it was a software one.
While nothing is playing, the satellite keeps the sound card open by feeding it
silence, so the stream stays live and a receiver never has to resubscribe. The
buffer holding that silence was sized from the wrong number — the card's
smallest internal chunk, rather than the larger amount actually written to keep
it fed — so every top-up read past the end of a small buffer of zeros into
whatever else was in memory, and played that as audio. This is now fixed at the
source: the buffer is sized for the largest write that can ever be made from
it, and a write that would exceed it is refused outright rather than allowed to
run past the end.
If your network audio output has ever gone briefly to static or a loud hiss
right after starting an event, this was it.
Pressing Stop on a satellite now actually stops the sound
Stop cleared what was queued and marked the box stopped, but nothing in the
audio path actually checked that flag — so the next moment of programme
refilled the queue anyway and kept playing. The box would report stopped
while sound kept coming out of it. Delivery now genuinely gates on play state:
Stop discards anything queued and stays silent; Hold still keeps its
queue, so Continue picks up exactly where it left off. Those were already
meant to behave differently from each other — now they do.
The encoder's local disk can no longer fill up unnoticed
The encoder writes every segment to a local durable queue before it is ever
uploaded, precisely so nothing is lost if the link drops. That queue was also
allowed to grow without limit: a link that stayed down, or stayed too slow to
keep up, for long enough would eventually fill the encoder machine's disk.
It now has a cap (4 GB by default). Past it, the OLDEST still-unsent segment is
the one let go — never the one that just arrived, so a recovering link is never
blocked behind a segment it will never get to — and every satellite still
downstream is told plainly that segment is gone rather than being left to wait
on it forever. A satellite that was sitting on exactly that segment jumps
forward instead of freezing.
Both the encoder dock and a Pi satellite's own operator page now show a plain
reading of the local disk — healthy, getting low, or almost full — checked
whether or not anything is currently broadcasting, so a drive running low is
something you notice before it starts costing you segments rather than after.
Also
A related but separate gap was found and written up rather than fixed here: a
satellite that crashes mid-event (rather than switching away from it cleanly)
can leave behind a cache folder nothing ever cleans up afterwards. It costs
disk slowly rather than breaking anything, and is tracked in BUGS.md.
What's new in v0.1.16-alpha
Three things that stopped work, all of which looked like something else.
Hardware encoding works on a Mac
Choosing any of the Apple VideoToolbox encoders failed to go live at all,
with "NO VIDEO EXTRADATA available from the encoder". On a Mac that is every
hardware option there is, so the only way to broadcast was x264 on the CPU.
Nothing was wrong with the encoder or the setting. Some encoders can describe
what they are about to produce before they produce anything; VideoToolbox
cannot, and only says once it has encoded its first frame. We asked too early
and took the silence for a failure.
Going live now waits for that answer when it has to, and holds the first moment
of the broadcast until it arrives rather than discarding it. Encoders that can
answer immediately are unaffected — including still refusing to start at all
when the storage settings are wrong, which is worth keeping: you find out
before OBS tells you you are on air.
Tested on an M5 with the HEVC hardware encoder, start to finish.
OBS could refuse to quit after a broadcast
Closing OBS after broadcasting could hang with no window, no error and no way
forward except forcing it to quit — which OBS then reports as a crash the next
time it opens.
It was never a crash. Two parts of the plugin each held something the other was
waiting for, so shutdown stopped rather than finished. Reproduced deliberately,
found, fixed, and reproduced again to confirm: OBS now closes in abou...
v0.1.17-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.17-alpha
A satellite on the network audio card could put digital noise on air
A box with Sound on the network switched on could, within a minute of
starting to play an event, put loud digital noise onto the AES67 stream during
any quiet stretch — the moment nothing else was queued to send. It looked like
a hardware fault; it was a software one.
While nothing is playing, the satellite keeps the sound card open by feeding it
silence, so the stream stays live and a receiver never has to resubscribe. The
buffer holding that silence was sized from the wrong number — the card's
smallest internal chunk, rather than the larger amount actually written to keep
it fed — so every top-up read past the end of a small buffer of zeros into
whatever else was in memory, and played that as audio. This is now fixed at the
source: the buffer is sized for the largest write that can ever be made from
it, and a write that would exceed it is refused outright rather than allowed to
run past the end.
If your network audio output has ever gone briefly to static or a loud hiss
right after starting an event, this was it.
Pressing Stop on a satellite now actually stops the sound
Stop cleared what was queued and marked the box stopped, but nothing in the
audio path actually checked that flag — so the next moment of programme
refilled the queue anyway and kept playing. The box would report stopped
while sound kept coming out of it. Delivery now genuinely gates on play state:
Stop discards anything queued and stays silent; Hold still keeps its
queue, so Continue picks up exactly where it left off. Those were already
meant to behave differently from each other — now they do.
The encoder's local disk can no longer fill up unnoticed
The encoder writes every segment to a local durable queue before it is ever
uploaded, precisely so nothing is lost if the link drops. That queue was also
allowed to grow without limit: a link that stayed down, or stayed too slow to
keep up, for long enough would eventually fill the encoder machine's disk.
It now has a cap (4 GB by default). Past it, the OLDEST still-unsent segment is
the one let go — never the one that just arrived, so a recovering link is never
blocked behind a segment it will never get to — and every satellite still
downstream is told plainly that segment is gone rather than being left to wait
on it forever. A satellite that was sitting on exactly that segment jumps
forward instead of freezing.
Both the encoder dock and a Pi satellite's own operator page now show a plain
reading of the local disk — healthy, getting low, or almost full — checked
whether or not anything is currently broadcasting, so a drive running low is
something you notice before it starts costing you segments rather than after.
Also
A related but separate gap was found and written up rather than fixed here: a
satellite that crashes mid-event (rather than switching away from it cleanly)
can leave behind a cache folder nothing ever cleans up afterwards. It costs
disk slowly rather than breaking anything, and is tracked in BUGS.md.
What's new in v0.1.16-alpha
Three things that stopped work, all of which looked like something else.
Hardware encoding works on a Mac
Choosing any of the Apple VideoToolbox encoders failed to go live at all,
with "NO VIDEO EXTRADATA available from the encoder". On a Mac that is every
hardware option there is, so the only way to broadcast was x264 on the CPU.
Nothing was wrong with the encoder or the setting. Some encoders can describe
what they are about to produce before they produce anything; VideoToolbox
cannot, and only says once it has encoded its first frame. We asked too early
and took the silence for a failure.
Going live now waits for that answer when it has to, and holds the first moment
of the broadcast until it arrives rather than discarding it. Encoders that can
answer immediately are unaffected — including still refusing to start at all
when the storage settings are wrong, which is worth keeping: you find out
before OBS tells you you are on air.
Tested on an M5 with the HEVC hardware encoder, start to finish.
OBS could refuse to quit after a broadcast
Closing OBS after broadcasting could hang with no window, no error and no way
forward except forcing it to quit — which OBS then reports as a crash the next
time it opens.
It was never a crash. Two parts of the plugin each held something the other was
waiting for, so shutdown stopped rather than finished. Reproduced deliberately,
found, fixed, and reproduced again to confirm: OBS now closes in about two
seconds, and no longer reports anything amiss on the next launch.
If you saw a crash report after quitting, this was almost certainly it, and
nothing was lost — the broadcast had already finished and uploaded.
Play after Stop works again
At a satellite, pressing Stop and then Play left the picture blank.
Recovering it took a seek, which is not something anyone should have to
discover.
Stop releases the decoder — that is what makes it a real stop rather than a
pause — but it did not tell the feed that the next one would need its setup
data again. It does now. This was introduced by the Stop changes in
v0.1.12-alpha.
A satellite's network audio goes to the right channels
Three faults that all presented as a silent room, and none of which said so.
The one worth knowing about: a box with network audio switched on opened its
card before any feed had arrived, guessed two channels, and then held that
guess against an eight-channel feed for the life of the process. Every listener
heard channels 1 and 2 of eight. Nothing was logged, because nothing had
failed — the card opened and the stream was published; the sound was simply in
the wrong places.
The channel count is now decided in one place, and with nothing to go on the
answer is "not yet" rather than a guess. The card is also held open and metered,
so a silent room can be told apart from a stopped one.
Also
The satellite's preview can now show either what is going to the screen or the
whole feed behind it — useful when a tile is being used, where those are no
longer the same picture.
Contributors are asked for a sign-off rather than a copyright assignment, and
the relay ships an nginx configuration for putting TLS in front of it.
What's new in v0.1.15-alpha
One new thing a room can do, a crash on quit, and several fixes — including
one correction to what the previous notes told you.
Several cameras in one feed, pulled apart at the far end
A room that needs two or four separate pictures at its satellites has always
been able to composite them into a single feed and send that. Taking them apart
again was the satellite's problem, solved by hand with crop filters, at every
satellite, every time.
The main site can now say how it composited. Settings → Media → Pictures in
this feed offers one picture, two side by side, two stacked, or four in a
square. At the satellite, each region becomes a source of its own — Multisite
Picture (Decoder) — already cropped, ready to drop into a scene or send
fullscreen to a screen of your choosing. A campus player can be pointed at one
of them too.
It costs nothing to receive. The feed is downloaded once and decoded once
however many pictures you pull out of it; each one is a view of that, not
another stream.
It has to be set before you go live. The layout is written down when the
event starts, so changing it part way through does nothing until the next one.
It cannot be worked out from the picture, which is why you have to say. A
very wide frame is a perfectly good single picture as well as a plausible pair,
and a wrong guess would cut a programme in half. Left alone it stays on one
picture, which is what every room sending one camera wants and what every
recording made before this existed already is.
OBS could crash when you quit after a broadcast
On Windows, closing OBS after broadcasting could end in a crash dialog. The
broadcast itself was fine and nothing was lost — the fault was on the way out,
as OBS closed its plugins down.
This plugin was talking to obs-websocket while OBS was already unloading. Which
plugin goes first is not decided, so if obs-websocket went first, we were
speaking to something that no longer existed. There was nothing to be gained by
that conversation at that point, so it no longer happens.
The settings dialogs fit the screen again
Both settin...
v0.1.16-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.16-alpha
Three things that stopped work, all of which looked like something else.
Hardware encoding works on a Mac
Choosing any of the Apple VideoToolbox encoders failed to go live at all,
with "NO VIDEO EXTRADATA available from the encoder". On a Mac that is every
hardware option there is, so the only way to broadcast was x264 on the CPU.
Nothing was wrong with the encoder or the setting. Some encoders can describe
what they are about to produce before they produce anything; VideoToolbox
cannot, and only says once it has encoded its first frame. We asked too early
and took the silence for a failure.
Going live now waits for that answer when it has to, and holds the first moment
of the broadcast until it arrives rather than discarding it. Encoders that can
answer immediately are unaffected — including still refusing to start at all
when the storage settings are wrong, which is worth keeping: you find out
before OBS tells you you are on air.
Tested on an M5 with the HEVC hardware encoder, start to finish.
OBS could refuse to quit after a broadcast
Closing OBS after broadcasting could hang with no window, no error and no way
forward except forcing it to quit — which OBS then reports as a crash the next
time it opens.
It was never a crash. Two parts of the plugin each held something the other was
waiting for, so shutdown stopped rather than finished. Reproduced deliberately,
found, fixed, and reproduced again to confirm: OBS now closes in about two
seconds, and no longer reports anything amiss on the next launch.
If you saw a crash report after quitting, this was almost certainly it, and
nothing was lost — the broadcast had already finished and uploaded.
Play after Stop works again
At a satellite, pressing Stop and then Play left the picture blank.
Recovering it took a seek, which is not something anyone should have to
discover.
Stop releases the decoder — that is what makes it a real stop rather than a
pause — but it did not tell the feed that the next one would need its setup
data again. It does now. This was introduced by the Stop changes in
v0.1.12-alpha.
A satellite's network audio goes to the right channels
Three faults that all presented as a silent room, and none of which said so.
The one worth knowing about: a box with network audio switched on opened its
card before any feed had arrived, guessed two channels, and then held that
guess against an eight-channel feed for the life of the process. Every listener
heard channels 1 and 2 of eight. Nothing was logged, because nothing had
failed — the card opened and the stream was published; the sound was simply in
the wrong places.
The channel count is now decided in one place, and with nothing to go on the
answer is "not yet" rather than a guess. The card is also held open and metered,
so a silent room can be told apart from a stopped one.
Also
The satellite's preview can now show either what is going to the screen or the
whole feed behind it — useful when a tile is being used, where those are no
longer the same picture.
Contributors are asked for a sign-off rather than a copyright assignment, and
the relay ships an nginx configuration for putting TLS in front of it.
What's new in v0.1.15-alpha
One new thing a room can do, a crash on quit, and several fixes — including
one correction to what the previous notes told you.
Several cameras in one feed, pulled apart at the far end
A room that needs two or four separate pictures at its satellites has always
been able to composite them into a single feed and send that. Taking them apart
again was the satellite's problem, solved by hand with crop filters, at every
satellite, every time.
The main site can now say how it composited. Settings → Media → Pictures in
this feed offers one picture, two side by side, two stacked, or four in a
square. At the satellite, each region becomes a source of its own — Multisite
Picture (Decoder) — already cropped, ready to drop into a scene or send
fullscreen to a screen of your choosing. A campus player can be pointed at one
of them too.
It costs nothing to receive. The feed is downloaded once and decoded once
however many pictures you pull out of it; each one is a view of that, not
another stream.
It has to be set before you go live. The layout is written down when the
event starts, so changing it part way through does nothing until the next one.
It cannot be worked out from the picture, which is why you have to say. A
very wide frame is a perfectly good single picture as well as a plausible pair,
and a wrong guess would cut a programme in half. Left alone it stays on one
picture, which is what every room sending one camera wants and what every
recording made before this existed already is.
OBS could crash when you quit after a broadcast
On Windows, closing OBS after broadcasting could end in a crash dialog. The
broadcast itself was fine and nothing was lost — the fault was on the way out,
as OBS closed its plugins down.
This plugin was talking to obs-websocket while OBS was already unloading. Which
plugin goes first is not decided, so if obs-websocket went first, we were
speaking to something that no longer existed. There was nothing to be gained by
that conversation at that point, so it no longer happens.
The settings dialogs fit the screen again
Both settings dialogs had grown taller than a laptop screen — and the part that
fell off the bottom was the buttons, so there was no way to close the window.
They are in tabs now: where the video goes, how it is encoded, and what this
machine is. Each page fits without scrolling, and the buttons are always there.
The Tag uploads for expiry line carried its warnings in the label, which is
one long unwrapping line that was setting the width of the whole dialog; those
have moved to a tooltip.
A companion source stops asking which room
The audio track and picture sources each had a room box that was almost always
left empty, because empty already means "the room the Multisite Source is
following". It now shows which room it is following, and only asks when there is
genuinely more than one to choose between.
The stream keeps running when nothing is playing
On a receiver, the AES67 source used to disappear the moment the picture
stopped. The daemon publishes what is written to the AES67 card, and the player
only writes when there is a frame to deliver — so an idle box left a card that
was open and never written to, and a card in that state stops producing samples.
Nothing was wrong at the receiver; there was simply nothing being sent.
The card is now kept fed with silence while the box is idle. That is audible as
nothing at all, and visible as a receiver that stays online.
The first attempt at it changed nothing, and the reason is worth recording: it
kept one period of audio queued, which is only a cushion on a card with a large
period. The AES67 card's is one millisecond, so it under-ran on every cycle. It
keeps twenty milliseconds now, checked four times over.
The trade, said plainly: while the box is idle, up to 20 ms of silence sits
in front of the sound when playback starts again. That is inside what the card
buffers anyway.
A second thing came out of the same work. The card is opened as soon as the box
is idle rather than at the first audio frame, so a satellite that has not played
anything since it was switched on still has a stream on the network — which is
the state a satellite is in for most of the week.
Putting the sound on the network moves the sound
The previous notes were wrong about this, and the error sent somebody looking
in the wrong place. They said the HDMI output and the network stream both
carried the audio. There is one output device on this box, not two, and the
daemon publishes what is written to the AES67 card — so a player writing to HDMI
leaves the stream silent however healthy it looks.
Choosing yes now moves the sound onto the AES67 card, and choosing no
puts it back on the device it was on before. The output device picker is greyed
out while it is on, because the two disagreeing is the fault worth preventing
rather than reporting.
Renamed, and the audio is the campus's business
Settings → Sound on the network is now Settings → Network audio output,
in the player and everywhere it is written about.
The wording around it no longer assumes what the audio contains. It used to say
the stream carries "the productio...
v0.1.15-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.15-alpha
One new thing a room can do, a crash on quit, and several fixes — including
one correction to what the previous notes told you.
Several cameras in one feed, pulled apart at the far end
A room that needs two or four separate pictures at its satellites has always
been able to composite them into a single feed and send that. Taking them apart
again was the satellite's problem, solved by hand with crop filters, at every
satellite, every time.
The main site can now say how it composited. Settings → Media → Pictures in
this feed offers one picture, two side by side, two stacked, or four in a
square. At the satellite, each region becomes a source of its own — Multisite
Picture (Decoder) — already cropped, ready to drop into a scene or send
fullscreen to a screen of your choosing. A campus player can be pointed at one
of them too.
It costs nothing to receive. The feed is downloaded once and decoded once
however many pictures you pull out of it; each one is a view of that, not
another stream.
It has to be set before you go live. The layout is written down when the
event starts, so changing it part way through does nothing until the next one.
It cannot be worked out from the picture, which is why you have to say. A
very wide frame is a perfectly good single picture as well as a plausible pair,
and a wrong guess would cut a programme in half. Left alone it stays on one
picture, which is what every room sending one camera wants and what every
recording made before this existed already is.
OBS could crash when you quit after a broadcast
On Windows, closing OBS after broadcasting could end in a crash dialog. The
broadcast itself was fine and nothing was lost — the fault was on the way out,
as OBS closed its plugins down.
This plugin was talking to obs-websocket while OBS was already unloading. Which
plugin goes first is not decided, so if obs-websocket went first, we were
speaking to something that no longer existed. There was nothing to be gained by
that conversation at that point, so it no longer happens.
The settings dialogs fit the screen again
Both settings dialogs had grown taller than a laptop screen — and the part that
fell off the bottom was the buttons, so there was no way to close the window.
They are in tabs now: where the video goes, how it is encoded, and what this
machine is. Each page fits without scrolling, and the buttons are always there.
The Tag uploads for expiry line carried its warnings in the label, which is
one long unwrapping line that was setting the width of the whole dialog; those
have moved to a tooltip.
A companion source stops asking which room
The audio track and picture sources each had a room box that was almost always
left empty, because empty already means "the room the Multisite Source is
following". It now shows which room it is following, and only asks when there is
genuinely more than one to choose between.
The stream keeps running when nothing is playing
On a receiver, the AES67 source used to disappear the moment the picture
stopped. The daemon publishes what is written to the AES67 card, and the player
only writes when there is a frame to deliver — so an idle box left a card that
was open and never written to, and a card in that state stops producing samples.
Nothing was wrong at the receiver; there was simply nothing being sent.
The card is now kept fed with silence while the box is idle. That is audible as
nothing at all, and visible as a receiver that stays online.
The first attempt at it changed nothing, and the reason is worth recording: it
kept one period of audio queued, which is only a cushion on a card with a large
period. The AES67 card's is one millisecond, so it under-ran on every cycle. It
keeps twenty milliseconds now, checked four times over.
The trade, said plainly: while the box is idle, up to 20 ms of silence sits
in front of the sound when playback starts again. That is inside what the card
buffers anyway.
A second thing came out of the same work. The card is opened as soon as the box
is idle rather than at the first audio frame, so a satellite that has not played
anything since it was switched on still has a stream on the network — which is
the state a satellite is in for most of the week.
Putting the sound on the network moves the sound
The previous notes were wrong about this, and the error sent somebody looking
in the wrong place. They said the HDMI output and the network stream both
carried the audio. There is one output device on this box, not two, and the
daemon publishes what is written to the AES67 card — so a player writing to HDMI
leaves the stream silent however healthy it looks.
Choosing yes now moves the sound onto the AES67 card, and choosing no
puts it back on the device it was on before. The output device picker is greyed
out while it is on, because the two disagreeing is the fault worth preventing
rather than reporting.
Renamed, and the audio is the campus's business
Settings → Sound on the network is now Settings → Network audio output,
in the player and everywhere it is written about.
The wording around it no longer assumes what the audio contains. It used to say
the stream carries "the production bus: main mix, mic ISOs and click", which is
one kind of event's answer to a question that belongs to whoever is running it.
The box carries up to eight channels and up to six tracks; what is on them is
their decision, and where an example helps it is now offered as one.
Also fixed
Saving the settings page quietly discarded the network audio output's switch: the
control sat inside the settings form but was applied by its own button, so
pressing Save put the old value back and the choice looked like it had not
stuck. Saving now applies it along with everything else.
What's new in v0.1.14-alpha
The appliance can now put its sound on the network without anybody logging into
the daemon to arrange it, and it says why it is quiet when it is.
One switch, in the player's own page
A campus that wants its audio on a console rather than only on the HDMI socket
runs one installer, and from then on the player's own page carries the control.
Settings → Network audio output is on or off, the multicast address to
publish to, and the channel count — one button for all three, because they are
one decision rather than three. Switching it on starts the daemon, sets it to
come back after a power cut, and creates the stream; switching it off stops the
stream rather than deleting it, so the address survives and switching it back on
is one click and not a re-entry of everything.
This box → Network audio output is the other half, and it is the half worth
having: what is actually being sent, read back from the daemon rather than
assumed from the settings beside it. Whether it is running, whether its clock is
locked and to which grandmaster, the address and port on the wire, and the
session description it publishes — which is the thing a console's engineer will
ask you for.
The stream is created as part of the install, so a box that has just been
prepared is already sending — eight channels, 48 kHz, one-millisecond packets —
rather than waiting for somebody to add a source by hand. The daemon's own
interface is still there on port 8081 for everything else the daemon can do.
Silence now has a reason attached
Four faults look identical from a settings page, and the page names them: the
clock is not locked, the stream is switched off, the sound card is not registered
with ALSA, or the player is still writing the sound to HDMI. The last is the
one worth having in writing — the stream configured, enabled, announced, and
carrying nothing.
The clock is the one this cannot fix, and it does not pretend to. The daemon is a
PTP slave: with nothing on the network handing out the clock it sends no audio at
all, and that is a network question rather than a fault in the box. The page says
so in as many words, instead of leaving it to be discovered at the receiver.
The route is Merging's open stack, and the old one has gone
Audio leaves on the network through Merging's open RAVENNA kernel module and the
GPL aes67-daemon. That route can be aimed — multicast address, port and channel
map are all settable — which the licensed virtual sound card it replaced could
not do, and its buffer is a normal one, so the under-run that gapped the sound
once per frame on the old card does ...
v0.1.14-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.14-alpha
The appliance can now put its sound on the network without anybody logging into
the daemon to arrange it, and it says why it is quiet when it is.
One switch, in the player's own page
A campus that wants its audio on a console rather than only on the HDMI socket
runs one installer, and from then on the player's own page carries the control.
Settings → Sound on the network is on or off, the multicast address to
publish to, and the channel count — one button for all three, because they are
one decision rather than three. Switching it on starts the daemon, sets it to
come back after a power cut, and creates the stream; switching it off stops the
stream rather than deleting it, so the address survives and switching it back on
is one click and not a re-entry of everything.
This box → Sound on the network is the other half, and it is the half worth
having: what is actually being sent, read back from the daemon rather than
assumed from the settings beside it. Whether it is running, whether its clock is
locked and to which grandmaster, the address and port on the wire, and the
session description it publishes — which is the thing a console's engineer will
ask you for.
The stream is created as part of the install, so a box that has just been
prepared is already sending — eight channels, 48 kHz, one-millisecond packets —
rather than waiting for somebody to add a source by hand. The daemon's own
interface is still there on port 8081 for everything else the daemon can do.
Silence now has a reason attached
Four faults look identical from a settings page, and the page names them: the
clock is not locked, the stream is switched off, the sound card is not registered
with ALSA, or the player is still writing the sound to HDMI. The last is the
one worth having in writing — the stream configured, enabled, announced, and
carrying nothing.
The clock is the one this cannot fix, and it does not pretend to. The daemon is a
PTP slave: with nothing on the network handing out the clock it sends no audio at
all, and that is a network question rather than a fault in the box. The page says
so in as many words, instead of leaving it to be discovered at the receiver.
The route is Merging's open stack, and the old one has gone
Audio leaves on the network through Merging's open RAVENNA kernel module and the
GPL aes67-daemon. That route can be aimed — multicast address, port and channel
map are all settable — which the licensed virtual sound card it replaced could
not do, and its buffer is a normal one, so the under-run that gapped the sound
once per frame on the old card does not happen.
The player no longer carries anything that knows about that old card. If this box
was set up with it, read the note under Installing / upgrading before you
update — removing it changes what happens on a box that still has it.
Still not proven
Eight channels have arrived cleanly at a bench Pi. What is not measured is
whether the picture and the sound stay together across a two-hour service, and
how accurate PTP becomes when a Pi's network interface does no hardware
timestamping. Both have to be measured at the receiver, on a real event; ten
seconds of test tone settles neither. The detail is in BUGS.md entry 3.
What's new in v0.1.13-alpha
Two things an operator does constantly — scrubbing to a moment, and reading how
far behind the main site they are — now do what they say. Both were wrong in
ways that are easy to work around once you know, and hard to trust around if
you don't.
Scrubbing to a time goes there, and the clock agrees
Choosing a time on the timeline used to start playing immediately from the
position you had just left, run on for several seconds, jump somewhere else,
and settle showing a time that was not the one you picked — on a long recording,
minutes out. The picture and the readout disagreed, so neither could be
trusted for lining up a cue.
Underneath, the seek itself had been picking the right segment all along. What
went wrong was everything built on top of it. A decoder holds several seconds
of already-decoded pictures, and clearing the queue on a seek did not stop it
handing those over — so the position you had left kept playing, and worse, the
first of those frames defined the clock that every displayed time was then
measured from. Because that mapping is learned once and kept, a single frame
from the wrong place put the whole readout out for as long as playback
continued.
A frame now carries which timeline it belongs to, and anything from a position
already left is discarded rather than believed. The clock is fixed to the
fragment the seek landed on, which is what makes the reported time the
requested one: measured against a live event, asking for 14:40:43.768 now
reports 14:40:43.769.
Seeking within a segment is also honest now. Segments are six seconds long and
landing part-way into one means skipping the frames before your moment; the
readout used to describe the start of the segment rather than where it had
actually landed, up to six seconds early.
"Behind live" holds still
The delay readout swung by six seconds while nothing about the delay had
changed. It was counting whole segments on both sides, and each side steps
independently as segments publish and playback advances.
It is now the gap between two real times, so setting a two-minute delay reads
as two minutes and stays there instead of flicking between 1:54 and 2:00. The
main site's content advances continuously; we simply learn about it in six
second pieces, so the live edge is carried forward between updates rather than
waiting for the next one.
Smaller things in the decode dock
The playhead is redrawn between state updates instead of stepping twice a
second, so the scrub bar moves the way one should.
The header is now two readings rather than one: what this box is doing —
PLAYING, HELD, STOPPED, READY — and, separately, what the main
site is doing. A single chip could only ever show one of them, so playing a
finished recording read BROADCAST ENDED with nothing to say it was playing,
and Hold on a finished recording showed no indication at all.
For anyone working on the decoder
The rules governing what a decoded frame may do, and in what order, are now one
tested component (src/core/playout_timeline.h) that the delivery loop calls,
rather than several blocks whose order mattered and was not written down.
Seeking broke five times in one afternoon getting here, every one of them found
by an operator rather than the suite, because nothing tested that path;
tests/test_playout_timeline.cpp fails on all five.
One known limit, unchanged by this release: on an event whose encoder has been
restarted, a segment's recorded wall-clock time and its position in the media
can disagree by up to a minute. Seeking is unaffected, because the same measure
is used at both ends, but the two are not interchangeable and a few places still
estimate one from the other for segments outside the current manifest window.
What's new in v0.1.12-alpha
Holding the picture no longer hands the screen to the box's own address
On a campus player, pressing Hold picture could be followed a second or two
later by the identity screen — hostname, IP, room — appearing in place of the
picture the operator had just frozen. On the screen in the room it reads as the
player having died at the exact moment somebody asked it to hold.
The cause was a collision between two things that meant different things by the
same word. Holding the picture was recognised only when the box's own idle mode
was set to Hold the last picture, so on a box left on the default idle screen
the poll loop found neither frames arriving nor a reason to leave the picture
alone, and fell through to drawing the identity screen. Everything else was
working: the hold itself, the audio, the cache, and — reassuringly — the
preview, which is rendered from the last decoded frame and so kept showing the
frozen picture the screen no longer had.
A held picture now outranks the idle screen whatever the idle mode is set to.
An idle screen is for having nothing to show: waiting for the main site, or
stopped, and both still put one up as before. Stop and waiting are
unchanged in this respect; on hold the last picture with nothing ever
decoded the identity screen still comes up, because there is no frame to hold
and a blank screen says nothing about which box has...
v0.1.13-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.13-alpha
Two things an operator does constantly — scrubbing to a moment, and reading how
far behind the main site they are — now do what they say. Both were wrong in
ways that are easy to work around once you know, and hard to trust around if
you don't.
Scrubbing to a time goes there, and the clock agrees
Choosing a time on the timeline used to start playing immediately from the
position you had just left, run on for several seconds, jump somewhere else,
and settle showing a time that was not the one you picked — on a long recording,
minutes out. The picture and the readout disagreed, so neither could be
trusted for lining up a cue.
Underneath, the seek itself had been picking the right segment all along. What
went wrong was everything built on top of it. A decoder holds several seconds
of already-decoded pictures, and clearing the queue on a seek did not stop it
handing those over — so the position you had left kept playing, and worse, the
first of those frames defined the clock that every displayed time was then
measured from. Because that mapping is learned once and kept, a single frame
from the wrong place put the whole readout out for as long as playback
continued.
A frame now carries which timeline it belongs to, and anything from a position
already left is discarded rather than believed. The clock is fixed to the
fragment the seek landed on, which is what makes the reported time the
requested one: measured against a live event, asking for 14:40:43.768 now
reports 14:40:43.769.
Seeking within a segment is also honest now. Segments are six seconds long and
landing part-way into one means skipping the frames before your moment; the
readout used to describe the start of the segment rather than where it had
actually landed, up to six seconds early.
"Behind live" holds still
The delay readout swung by six seconds while nothing about the delay had
changed. It was counting whole segments on both sides, and each side steps
independently as segments publish and playback advances.
It is now the gap between two real times, so setting a two-minute delay reads
as two minutes and stays there instead of flicking between 1:54 and 2:00. The
main site's content advances continuously; we simply learn about it in six
second pieces, so the live edge is carried forward between updates rather than
waiting for the next one.
Smaller things in the decode dock
The playhead is redrawn between state updates instead of stepping twice a
second, so the scrub bar moves the way one should.
The header is now two readings rather than one: what this box is doing —
PLAYING, HELD, STOPPED, READY — and, separately, what the main
site is doing. A single chip could only ever show one of them, so playing a
finished recording read BROADCAST ENDED with nothing to say it was playing,
and Hold on a finished recording showed no indication at all.
For anyone working on the decoder
The rules governing what a decoded frame may do, and in what order, are now one
tested component (src/core/playout_timeline.h) that the delivery loop calls,
rather than several blocks whose order mattered and was not written down.
Seeking broke five times in one afternoon getting here, every one of them found
by an operator rather than the suite, because nothing tested that path;
tests/test_playout_timeline.cpp fails on all five.
One known limit, unchanged by this release: on an event whose encoder has been
restarted, a segment's recorded wall-clock time and its position in the media
can disagree by up to a minute. Seeking is unaffected, because the same measure
is used at both ends, but the two are not interchangeable and a few places still
estimate one from the other for segments outside the current manifest window.
What's new in v0.1.12-alpha
Holding the picture no longer hands the screen to the box's own address
On a campus player, pressing Hold picture could be followed a second or two
later by the identity screen — hostname, IP, room — appearing in place of the
picture the operator had just frozen. On the screen in the room it reads as the
player having died at the exact moment somebody asked it to hold.
The cause was a collision between two things that meant different things by the
same word. Holding the picture was recognised only when the box's own idle mode
was set to Hold the last picture, so on a box left on the default idle screen
the poll loop found neither frames arriving nor a reason to leave the picture
alone, and fell through to drawing the identity screen. Everything else was
working: the hold itself, the audio, the cache, and — reassuringly — the
preview, which is rendered from the last decoded frame and so kept showing the
frozen picture the screen no longer had.
A held picture now outranks the idle screen whatever the idle mode is set to.
An idle screen is for having nothing to show: waiting for the main site, or
stopped, and both still put one up as before. Stop and waiting are
unchanged in this respect; on hold the last picture with nothing ever
decoded the identity screen still comes up, because there is no frame to hold
and a blank screen says nothing about which box has come up empty.
The rule is now a function of four booleans rather than a chain of conditions
inside the poll loop, and a new test checks all of its combinations — including
the default one on the identity screen — with no display, no decoder and no
network. That combination could only be reproduced by hand with a live event and
an HDMI socket attached, which is how it reached a congregation in the first
place.
Stopping, and the header above it, say what they mean
Two changes to the decode dock, both about a control or a readout reporting
something other than what it was doing.
Stop now releases the picture. It was already ending playback, but the
source kept polling and kept pulling in flight, so a stopped decoder went on
downloading. Now Stop cancels the requests already in flight, stops the poll
loop issuing new ones, and lets go of the decoder. It deliberately keeps the
cache: Play resumes from disk rather than sitting through the full start-up
buffer again, which is what you want from a button you press and press back.
The header reads Stopped — nothing downloading, and Hold is labelled Hold
picture (keeps recording) so that its contrast with Stop is on the button.
The header no longer hides one answer behind another. It had been a single
label carrying three unrelated facts — what the main site is doing, what this
decoder is doing, and how the link is — behind a precedence order, so only one
could ever be visible. Playing a finished recording therefore read BROADCAST
ENDED, which was true of the venue and silent about the playback, and holding
a finished recording showed nothing about the hold at all. There are now two
labels, each answering one question:
- Playback —
STOPPED,LOADING…,BUFFERING…,HELD,PLAYING,
READY - Source —
LIVE,BROADCAST ENDED,INTERRUPTED,RECORDING (not live),
OFFLINE,CONNECTING,CONNECTION LOST
Playback sits first because it is what you are acting on. Neither can now
misstate the other, because neither can express the other's facts. READY is
new and names what Load leaves behind — buffered, not on air, waiting for
Play on cue, which previously had no name and read as whatever the room
happened to be doing.
Neither is a setting and nothing moves; the strings are in both locale files,
and the web remote — which already separated the two — needed only the stopped
state to be added to its status.
What's new in v0.1.11-alpha
Markers reach a control surface as they happen
The vendor events the plugin pushes carried the transport state but not the
markers, so a cue dropped at the main site reached an obs-websocket client only
on the next poll — up to five seconds later. markers and marker_labels are
now part of what the state event watches, so a cue reaches a Stream Deck's marker
button as it is dropped rather than when something else happens to change.
Nothing to configure, and nothing changes for a client that does not use it: the
same events, carrying the same document.
This is what the Companion module's generated marker buttons rely on. Every cue
the main site is configured with becomes a button of its own, and a new cue is
offered within about a second.
What's new in v0.1.10-alpha
Control it from a Stream Dec...
v0.1.12-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.12-alpha
Holding the picture no longer hands the screen to the box's own address
On a campus player, pressing Hold picture could be followed a second or two
later by the identity screen — hostname, IP, room — appearing in place of the
picture the operator had just frozen. On the screen in the room it reads as the
player having died at the exact moment somebody asked it to hold.
The cause was a collision between two things that meant different things by the
same word. Holding the picture was recognised only when the box's own idle mode
was set to Hold the last picture, so on a box left on the default idle screen
the poll loop found neither frames arriving nor a reason to leave the picture
alone, and fell through to drawing the identity screen. Everything else was
working: the hold itself, the audio, the cache, and — reassuringly — the
preview, which is rendered from the last decoded frame and so kept showing the
frozen picture the screen no longer had.
A held picture now outranks the idle screen whatever the idle mode is set to.
An idle screen is for having nothing to show: waiting for the main site, or
stopped, and both still put one up as before. Stop and waiting are
unchanged in this respect; on hold the last picture with nothing ever
decoded the identity screen still comes up, because there is no frame to hold
and a blank screen says nothing about which box has come up empty.
The rule is now a function of four booleans rather than a chain of conditions
inside the poll loop, and a new test checks all of its combinations — including
the default one on the identity screen — with no display, no decoder and no
network. That combination could only be reproduced by hand with a live event and
an HDMI socket attached, which is how it reached a congregation in the first
place.
Stopping, and the header above it, say what they mean
Two changes to the decode dock, both about a control or a readout reporting
something other than what it was doing.
Stop now releases the picture. It was already ending playback, but the
source kept polling and kept pulling in flight, so a stopped decoder went on
downloading. Now Stop cancels the requests already in flight, stops the poll
loop issuing new ones, and lets go of the decoder. It deliberately keeps the
cache: Play resumes from disk rather than sitting through the full start-up
buffer again, which is what you want from a button you press and press back.
The header reads Stopped — nothing downloading, and Hold is labelled Hold
picture (keeps recording) so that its contrast with Stop is on the button.
The header no longer hides one answer behind another. It had been a single
label carrying three unrelated facts — what the main site is doing, what this
decoder is doing, and how the link is — behind a precedence order, so only one
could ever be visible. Playing a finished recording therefore read BROADCAST
ENDED, which was true of the venue and silent about the playback, and holding
a finished recording showed nothing about the hold at all. There are now two
labels, each answering one question:
- Playback —
STOPPED,LOADING…,BUFFERING…,HELD,PLAYING,
READY - Source —
LIVE,BROADCAST ENDED,INTERRUPTED,RECORDING (not live),
OFFLINE,CONNECTING,CONNECTION LOST
Playback sits first because it is what you are acting on. Neither can now
misstate the other, because neither can express the other's facts. READY is
new and names what Load leaves behind — buffered, not on air, waiting for
Play on cue, which previously had no name and read as whatever the room
happened to be doing.
Neither is a setting and nothing moves; the strings are in both locale files,
and the web remote — which already separated the two — needed only the stopped
state to be added to its status.
What's new in v0.1.11-alpha
Markers reach a control surface as they happen
The vendor events the plugin pushes carried the transport state but not the
markers, so a cue dropped at the main site reached an obs-websocket client only
on the next poll — up to five seconds later. markers and marker_labels are
now part of what the state event watches, so a cue reaches a Stream Deck's marker
button as it is dropped rather than when something else happens to change.
Nothing to configure, and nothing changes for a client that does not use it: the
same events, carrying the same document.
This is what the Companion module's generated marker buttons rely on. Every cue
the main site is configured with becomes a button of its own, and a new cue is
offered within about a second.
What's new in v0.1.10-alpha
Control it from a Stream Deck
Every command the plugin offers is now also an obs-websocket vendor request
under the vendor obs-multisite, and the plugin emits vendor events when the
state changes. Any obs-websocket client can therefore drive it — a script, an
automation system, or a Stream Deck through Bitfocus Companion.
There is nothing to switch on. obs-websocket ships with OBS 28 and later and is
enabled in Tools → WebSocket Server Settings; with it off, or absent, the plugin
logs one line and everything else carries on exactly as before.
The commands are the ones the plugin's own remote-control pages already use,
under the same names, so the two cannot drift — the list lives in one place and a
test pins it. Go live, End and the marker buttons for a main site; play, stop,
hold, resume, catch up, jog, seek, delay, markers, recordings and return-to-live
for a campus; a status request and events for everything watching.
A Companion module
There is now a purpose-built Bitfocus Companion module —
companion-module-obs-multisite
— so those commands arrive as buttons that light up: on air, held, buffering,
behind live, link offline, with variables for the same figures and two preset
banks to drag straight onto a page.
Alpha, and not in the Companion store yet. Until it is listed, Companion
loads it as a developer module — the module's README has the steps. It needs
Companion 4.0 or later, and this release of the plugin.
Tested against a real OBS, but not yet through a whole event.
What's new in v0.1.9-alpha
For v0.1.8's users: the Manage storage tool arrived in the previous
release; this one is what makes it usable on a full bucket.
Storage you can actually see and clear
Manage storage… in the encoder dock lists a room's events and lets them be
deleted — one, or everything older than a chosen number of days — with a
confirmation and a check afterwards. On a full bucket it used to sit on
"Looking…" for minutes while it walked every object of every event to add up
bytes, one request after another, and it logged nothing at all while it did.
It is two halves now. The events are listed first, from the manifests and the
live pointer, so something appears at once. Each event's size is then measured
beside it, six at a time, each row filling in as it lands — so the wait is the
largest single event rather than the sum of all of them. A size that cannot be
measured is reported as unknown rather than as zero, because "0 B" on an event
holding gigabytes is not cosmetic in a window whose whole purpose is deleting
things. Closing the window cancels the work in flight, and every listing and
sizes pass is logged with its counts and elapsed time.
The documentation
The storage-management description, the layout notes and the counts in the
guides had drifted from the code; they are corrected. PROJECT-SCOPE.md now
carries a roadmap for the next milestones: storage redundancy, output
routing, headless appliances, ABR and low latency.
What's new in v0.1.8-alpha
The encoder and the decoder can be run from a phone
Both halves of the plugin now serve an operator page on the church network: the
same interface the Raspberry Pi appliance has, out of OBS itself. One page,
polled twice a second, in the plain language of an event rather than of a video
pipeline.
- Sending side — Go live and End the broadcast, the editable event name, the
four marker buttons, and the reliability readout an operator watches
mid-event: confirmed pieces, what is waiting to send, retries, bytes sent, the
measured upload rate, the Cloudflare edge serving the bucket, and the last
error. - Receiving side — play, hold picture, catch up to now, jog, stay behind
live, ...
v0.1.11-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.11-alpha
Markers reach a control surface as they happen
The vendor events the plugin pushes carried the transport state but not the
markers, so a cue dropped at the main site reached an obs-websocket client only
on the next poll — up to five seconds later. markers and marker_labels are
now part of what the state event watches, so a cue reaches a Stream Deck's marker
button as it is dropped rather than when something else happens to change.
Nothing to configure, and nothing changes for a client that does not use it: the
same events, carrying the same document.
This is what the Companion module's generated marker buttons rely on. Every cue
the main site is configured with becomes a button of its own, and a new cue is
offered within about a second.
What's new in v0.1.10-alpha
Control it from a Stream Deck
Every command the plugin offers is now also an obs-websocket vendor request
under the vendor obs-multisite, and the plugin emits vendor events when the
state changes. Any obs-websocket client can therefore drive it — a script, an
automation system, or a Stream Deck through Bitfocus Companion.
There is nothing to switch on. obs-websocket ships with OBS 28 and later and is
enabled in Tools → WebSocket Server Settings; with it off, or absent, the plugin
logs one line and everything else carries on exactly as before.
The commands are the ones the plugin's own remote-control pages already use,
under the same names, so the two cannot drift — the list lives in one place and a
test pins it. Go live, End and the marker buttons for a main site; play, stop,
hold, resume, catch up, jog, seek, delay, markers, recordings and return-to-live
for a campus; a status request and events for everything watching.
A Companion module
There is now a purpose-built Bitfocus Companion module —
companion-module-obs-multisite
— so those commands arrive as buttons that light up: on air, held, buffering,
behind live, link offline, with variables for the same figures and two preset
banks to drag straight onto a page.
Alpha, and not in the Companion store yet. Until it is listed, Companion
loads it as a developer module — the module's README has the steps. It needs
Companion 4.0 or later, and this release of the plugin.
Tested against a real OBS, but not yet through a whole event.
What's new in v0.1.9-alpha
For v0.1.8's users: the Manage storage tool arrived in the previous
release; this one is what makes it usable on a full bucket.
Storage you can actually see and clear
Manage storage… in the encoder dock lists a room's events and lets them be
deleted — one, or everything older than a chosen number of days — with a
confirmation and a check afterwards. On a full bucket it used to sit on
"Looking…" for minutes while it walked every object of every event to add up
bytes, one request after another, and it logged nothing at all while it did.
It is two halves now. The events are listed first, from the manifests and the
live pointer, so something appears at once. Each event's size is then measured
beside it, six at a time, each row filling in as it lands — so the wait is the
largest single event rather than the sum of all of them. A size that cannot be
measured is reported as unknown rather than as zero, because "0 B" on an event
holding gigabytes is not cosmetic in a window whose whole purpose is deleting
things. Closing the window cancels the work in flight, and every listing and
sizes pass is logged with its counts and elapsed time.
The documentation
The storage-management description, the layout notes and the counts in the
guides had drifted from the code; they are corrected. PROJECT-SCOPE.md now
carries a roadmap for the next milestones: storage redundancy, output
routing, headless appliances, ABR and low latency.
What's new in v0.1.8-alpha
The encoder and the decoder can be run from a phone
Both halves of the plugin now serve an operator page on the church network: the
same interface the Raspberry Pi appliance has, out of OBS itself. One page,
polled twice a second, in the plain language of an event rather than of a video
pipeline.
- Sending side — Go live and End the broadcast, the editable event name, the
four marker buttons, and the reliability readout an operator watches
mid-event: confirmed pieces, what is waiting to send, retries, bytes sent, the
measured upload rate, the Cloudflare edge serving the bucket, and the last
error. - Receiving side — play, hold picture, catch up to now, jog, stay behind
live, a timeline that can be clicked, the recordings list, and the readout
that says how long this campus could keep playing through an outage. - Both — the log, so somebody with a phone and no access to the desk can see
why nothing is happening, and a Lock, so a tablet left on a music stand cannot
stop a broadcast by being leant on.
It is the appliance's page on purpose: somebody who has learned one should not
have to learn the other, and the words on it are the words of an event. There is
no password and no TLS, exactly as for the appliance — the building's own network
is the guard — and it is on by default on port 8080. It can be switched off
or moved to another port in Settings → Remote control in either dock, which
also shows the address to type into a phone.
Which pages exist follows the machine's role, as the docks already do: a main
site has no decoder routes at all, and a satellite none of the encoder's.
One HTTP server, three users
The appliance's small HTTP server moved into the shared core and learned to
speak Winsock, so the plugin, the relay and the player now run one
implementation rather than three. It is covered by a new test
(tests/test_http_server.cpp) that speaks real HTTP over loopback, on every
platform CI builds: routing, verbs, the static web root, keep-alive, a handler
that throws, and the refusal of a path that climbs out of the web root.
The campus player can be reached without a drive to the campus
What makes a wrong setting at a campus so expensive is that fixing it means
somebody driving there. The player now ships with the two optional tools that
remove that drive, and either can be set up from its own web page:
- ZeroTier puts the box on a private network that follows it, so it is
reachable from the office wherever it is plugged in. The network key is given
during setup — passed asZT_NETWORK_ID=…alongside the installer, or typed
at its prompt — and can be changed later under Settings → Remote access. - cloudflared publishes the operator page on a public hostname with no
port-forward and no static address. Give the installerCF_TUNNEL_TOKEN=…,
or paste the token into the same panel. - The box's ZeroTier address is printed on its screen, clearly labelled
REMOTE ACCESS IP, underneath the room's own addresses. Those are a
different thing — they work only inside the building and are what a phone in
the room types — and the label exists precisely so the two are not confused.
Both are optional and neither is required to play an event. A box with neither
installed behaves exactly as before, and its screen says nothing about remote
access at all.
Fixed since v0.1.6-alpha
The campus player leaked memory on every idle-screen redraw. The new
identity screen's FreeType text renderer opened and parsed a font file on
every redraw and never released it — one FT_Library and one font face,
abandoned each time. Not a single leak and not bounded by anything an
operator does: the idle screen redraws whenever its content changes, which
includes the box's own IP address, so even a DHCP renewal on an otherwise
quiet box would trigger it. A box left running for days between events
would have accumulated this the whole time.
Confirmed both ways before calling it fixed: a standalone harness built
against the released v0.1.6-alpha source called the renderer 3,000 times and
watched memory climb continuously, still rising when the run ended; the same
harness against this fix plateaus within two calls and stays flat for the
rest. If you have installed v0.1.6-alpha's campus player on a box you intend
to leave running, update it.
Nothing else changes in this release. v0.1.6-alpha's own notes are below.
What's new since v0.1.5-alpha
The campus player got a proper identity screen
The Raspberry Pi appliance's idle screen — ...
v0.1.10-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.10-alpha
Control it from a Stream Deck
Every command the plugin offers is now also an obs-websocket vendor request
under the vendor obs-multisite, and the plugin emits vendor events when the
state changes. Any obs-websocket client can therefore drive it — a script, an
automation system, or a Stream Deck through Bitfocus Companion.
There is nothing to switch on. obs-websocket ships with OBS 28 and later and is
enabled in Tools → WebSocket Server Settings; with it off, or absent, the plugin
logs one line and everything else carries on exactly as before.
The commands are the ones the plugin's own remote-control pages already use,
under the same names, so the two cannot drift — the list lives in one place and a
test pins it. Go live, End and the marker buttons for a main site; play, stop,
hold, resume, catch up, jog, seek, delay, markers, recordings and return-to-live
for a campus; a status request and events for everything watching.
A Companion module
There is now a purpose-built Bitfocus Companion module —
companion-module-obs-multisite
— so those commands arrive as buttons that light up: on air, held, buffering,
behind live, link offline, with variables for the same figures and two preset
banks to drag straight onto a page.
Alpha, and not in the Companion store yet. Until it is listed, Companion
loads it as a developer module — the module's README has the steps. It needs
Companion 4.0 or later, and this release of the plugin.
Tested against a real OBS, but not yet through a whole event.
What's new in v0.1.9-alpha
For v0.1.8's users: the Manage storage tool arrived in the previous
release; this one is what makes it usable on a full bucket.
Storage you can actually see and clear
Manage storage… in the encoder dock lists a room's events and lets them be
deleted — one, or everything older than a chosen number of days — with a
confirmation and a check afterwards. On a full bucket it used to sit on
"Looking…" for minutes while it walked every object of every event to add up
bytes, one request after another, and it logged nothing at all while it did.
It is two halves now. The events are listed first, from the manifests and the
live pointer, so something appears at once. Each event's size is then measured
beside it, six at a time, each row filling in as it lands — so the wait is the
largest single event rather than the sum of all of them. A size that cannot be
measured is reported as unknown rather than as zero, because "0 B" on an event
holding gigabytes is not cosmetic in a window whose whole purpose is deleting
things. Closing the window cancels the work in flight, and every listing and
sizes pass is logged with its counts and elapsed time.
The documentation
The storage-management description, the layout notes and the counts in the
guides had drifted from the code; they are corrected. PROJECT-SCOPE.md now
carries a roadmap for the next milestones: storage redundancy, output
routing, headless appliances, ABR and low latency.
What's new in v0.1.8-alpha
The encoder and the decoder can be run from a phone
Both halves of the plugin now serve an operator page on the church network: the
same interface the Raspberry Pi appliance has, out of OBS itself. One page,
polled twice a second, in the plain language of an event rather than of a video
pipeline.
- Sending side — Go live and End the broadcast, the editable event name, the
four marker buttons, and the reliability readout an operator watches
mid-event: confirmed pieces, what is waiting to send, retries, bytes sent, the
measured upload rate, the Cloudflare edge serving the bucket, and the last
error. - Receiving side — play, hold picture, catch up to now, jog, stay behind
live, a timeline that can be clicked, the recordings list, and the readout
that says how long this campus could keep playing through an outage. - Both — the log, so somebody with a phone and no access to the desk can see
why nothing is happening, and a Lock, so a tablet left on a music stand cannot
stop a broadcast by being leant on.
It is the appliance's page on purpose: somebody who has learned one should not
have to learn the other, and the words on it are the words of an event. There is
no password and no TLS, exactly as for the appliance — the building's own network
is the guard — and it is on by default on port 8080. It can be switched off
or moved to another port in Settings → Remote control in either dock, which
also shows the address to type into a phone.
Which pages exist follows the machine's role, as the docks already do: a main
site has no decoder routes at all, and a satellite none of the encoder's.
One HTTP server, three users
The appliance's small HTTP server moved into the shared core and learned to
speak Winsock, so the plugin, the relay and the player now run one
implementation rather than three. It is covered by a new test
(tests/test_http_server.cpp) that speaks real HTTP over loopback, on every
platform CI builds: routing, verbs, the static web root, keep-alive, a handler
that throws, and the refusal of a path that climbs out of the web root.
The campus player can be reached without a drive to the campus
What makes a wrong setting at a campus so expensive is that fixing it means
somebody driving there. The player now ships with the two optional tools that
remove that drive, and either can be set up from its own web page:
- ZeroTier puts the box on a private network that follows it, so it is
reachable from the office wherever it is plugged in. The network key is given
during setup — passed asZT_NETWORK_ID=…alongside the installer, or typed
at its prompt — and can be changed later under Settings → Remote access. - cloudflared publishes the operator page on a public hostname with no
port-forward and no static address. Give the installerCF_TUNNEL_TOKEN=…,
or paste the token into the same panel. - The box's ZeroTier address is printed on its screen, clearly labelled
REMOTE ACCESS IP, underneath the room's own addresses. Those are a
different thing — they work only inside the building and are what a phone in
the room types — and the label exists precisely so the two are not confused.
Both are optional and neither is required to play an event. A box with neither
installed behaves exactly as before, and its screen says nothing about remote
access at all.
Fixed since v0.1.6-alpha
The campus player leaked memory on every idle-screen redraw. The new
identity screen's FreeType text renderer opened and parsed a font file on
every redraw and never released it — one FT_Library and one font face,
abandoned each time. Not a single leak and not bounded by anything an
operator does: the idle screen redraws whenever its content changes, which
includes the box's own IP address, so even a DHCP renewal on an otherwise
quiet box would trigger it. A box left running for days between events
would have accumulated this the whole time.
Confirmed both ways before calling it fixed: a standalone harness built
against the released v0.1.6-alpha source called the renderer 3,000 times and
watched memory climb continuously, still rising when the run ended; the same
harness against this fix plateaus within two calls and stays flat for the
rest. If you have installed v0.1.6-alpha's campus player on a box you intend
to leave running, update it.
Nothing else changes in this release. v0.1.6-alpha's own notes are below.
What's new since v0.1.5-alpha
The campus player got a proper identity screen
The Raspberry Pi appliance's idle screen — the first thing a room sees — has
been rebuilt:
- A QR code to the control page. Point a phone at the screen and it opens
the operator UI in the browser. No typing, no laptop; the address is still
printed alongside for anyone who prefers it. - A five-second boot splash. On power-up the identity screen now shows for
five seconds whatever is configured — even with auto-play and a live event
already arriving — so the box visibly proves it is alive before the picture
takes over. (The box comes in a few seconds into the event; it sits minutes
behind live anyway.) - A modern look. The screen has a gradient background and anti-aliased text
rendered from the system font through FreeType, instead of the 5×7 bitmap
font. A build without FreeType falls back to th...
v0.1.9-alpha
⚠️ Alpha — read this first
This is pre-release software. A six-hour continuous soak test has been run end
to end — 3,661 segments, over 15 GB, zero retries and zero upload failures, 10
lagged frames in 658,837 — but it has still not carried a real congregation's
event. Interfaces, settings and the storage protocol may change without a
migration path. There is no warranty, no support contract and no uptime
guarantee.
If you put this in front of a congregation, do it with a tested fallback in
place, a technical person on hand, and the assumption that any given event may
have to go ahead without it.
Before you start: set a retention rule
Nothing in this project deletes anything. The plugins only write and read.
Expiry is a bucket lifecycle rule you configure once in your storage provider's
console, and without one every event you broadcast stays for ever — roughly
2.7 GB per hour at 6 Mbps.
Add a rule for the prefix events/ and another for rooms/, both deleting
objects after the same number of days. Seven days is the design default, and
the rule is also your DVR depth: a campus can timeslip back only as far as
retention allows.
Licence
This project is GPL-3.0-or-later (it moved from MIT at v0.1.5-alpha).
Releases up to and including v0.1.4-alpha were MIT, and that grant cannot be
withdrawn: anyone holding those versions keeps their MIT rights to that code.
Third-party terms are set out in COPYRIGHT.
What's new in v0.1.9-alpha
For v0.1.8's users: the Manage storage tool arrived in the previous
release; this one is what makes it usable on a full bucket.
Storage you can actually see and clear
Manage storage… in the encoder dock lists a room's events and lets them be
deleted — one, or everything older than a chosen number of days — with a
confirmation and a check afterwards. On a full bucket it used to sit on
"Looking…" for minutes while it walked every object of every event to add up
bytes, one request after another, and it logged nothing at all while it did.
It is two halves now. The events are listed first, from the manifests and the
live pointer, so something appears at once. Each event's size is then measured
beside it, six at a time, each row filling in as it lands — so the wait is the
largest single event rather than the sum of all of them. A size that cannot be
measured is reported as unknown rather than as zero, because "0 B" on an event
holding gigabytes is not cosmetic in a window whose whole purpose is deleting
things. Closing the window cancels the work in flight, and every listing and
sizes pass is logged with its counts and elapsed time.
The documentation
The storage-management description, the layout notes and the counts in the
guides had drifted from the code; they are corrected. PROJECT-SCOPE.md now
carries a roadmap for the next milestones: storage redundancy, output
routing, headless appliances, ABR and low latency.
What's new in v0.1.8-alpha
The encoder and the decoder can be run from a phone
Both halves of the plugin now serve an operator page on the church network: the
same interface the Raspberry Pi appliance has, out of OBS itself. One page,
polled twice a second, in the plain language of an event rather than of a video
pipeline.
- Sending side — Go live and End the broadcast, the editable event name, the
four marker buttons, and the reliability readout an operator watches
mid-event: confirmed pieces, what is waiting to send, retries, bytes sent, the
measured upload rate, the Cloudflare edge serving the bucket, and the last
error. - Receiving side — play, hold picture, catch up to now, jog, stay behind
live, a timeline that can be clicked, the recordings list, and the readout
that says how long this campus could keep playing through an outage. - Both — the log, so somebody with a phone and no access to the desk can see
why nothing is happening, and a Lock, so a tablet left on a music stand cannot
stop a broadcast by being leant on.
It is the appliance's page on purpose: somebody who has learned one should not
have to learn the other, and the words on it are the words of an event. There is
no password and no TLS, exactly as for the appliance — the building's own network
is the guard — and it is on by default on port 8080. It can be switched off
or moved to another port in Settings → Remote control in either dock, which
also shows the address to type into a phone.
Which pages exist follows the machine's role, as the docks already do: a main
site has no decoder routes at all, and a satellite none of the encoder's.
One HTTP server, three users
The appliance's small HTTP server moved into the shared core and learned to
speak Winsock, so the plugin, the relay and the player now run one
implementation rather than three. It is covered by a new test
(tests/test_http_server.cpp) that speaks real HTTP over loopback, on every
platform CI builds: routing, verbs, the static web root, keep-alive, a handler
that throws, and the refusal of a path that climbs out of the web root.
The campus player can be reached without a drive to the campus
What makes a wrong setting at a campus so expensive is that fixing it means
somebody driving there. The player now ships with the two optional tools that
remove that drive, and either can be set up from its own web page:
- ZeroTier puts the box on a private network that follows it, so it is
reachable from the office wherever it is plugged in. The network key is given
during setup — passed asZT_NETWORK_ID=…alongside the installer, or typed
at its prompt — and can be changed later under Settings → Remote access. - cloudflared publishes the operator page on a public hostname with no
port-forward and no static address. Give the installerCF_TUNNEL_TOKEN=…,
or paste the token into the same panel. - The box's ZeroTier address is printed on its screen, clearly labelled
REMOTE ACCESS IP, underneath the room's own addresses. Those are a
different thing — they work only inside the building and are what a phone in
the room types — and the label exists precisely so the two are not confused.
Both are optional and neither is required to play an event. A box with neither
installed behaves exactly as before, and its screen says nothing about remote
access at all.
Fixed since v0.1.6-alpha
The campus player leaked memory on every idle-screen redraw. The new
identity screen's FreeType text renderer opened and parsed a font file on
every redraw and never released it — one FT_Library and one font face,
abandoned each time. Not a single leak and not bounded by anything an
operator does: the idle screen redraws whenever its content changes, which
includes the box's own IP address, so even a DHCP renewal on an otherwise
quiet box would trigger it. A box left running for days between events
would have accumulated this the whole time.
Confirmed both ways before calling it fixed: a standalone harness built
against the released v0.1.6-alpha source called the renderer 3,000 times and
watched memory climb continuously, still rising when the run ended; the same
harness against this fix plateaus within two calls and stays flat for the
rest. If you have installed v0.1.6-alpha's campus player on a box you intend
to leave running, update it.
Nothing else changes in this release. v0.1.6-alpha's own notes are below.
What's new since v0.1.5-alpha
The campus player got a proper identity screen
The Raspberry Pi appliance's idle screen — the first thing a room sees — has
been rebuilt:
- A QR code to the control page. Point a phone at the screen and it opens
the operator UI in the browser. No typing, no laptop; the address is still
printed alongside for anyone who prefers it. - A five-second boot splash. On power-up the identity screen now shows for
five seconds whatever is configured — even with auto-play and a live event
already arriving — so the box visibly proves it is alive before the picture
takes over. (The box comes in a few seconds into the event; it sits minutes
behind live anyway.) - A modern look. The screen has a gradient background and anti-aliased text
rendered from the system font through FreeType, instead of the 5×7 bitmap
font. A build without FreeType falls back to the bitmap font, so nothing
here is a hard dependency.
The one-line installer is now reliable
curl …/install.sh | sudo bash has been hardened after GitHub's raw CDN took a
few days off, and after a branch-switch bug made an update fail without a
message:
- The installer retries its GitHub fetch and reports what is wrong instead
of stopping silently — the failure that previously looked like "nothing
happened". - It switches branches correctly (a plain fetch wrote only
FETCH_HEAD, so
the first change of branch failed). - It force-updates its tracking ref, so a shallow clone no longer rejects an
update it cannot see as a fast-forward.
The command is unchanged in shape, just sturdier:
curl -fsSL --retry 5 https://raw.githubusercontent.com/stageaudioworks/obs-multisite/main/scripts/player/install.sh | sudo bashThe documentation is restructured
The README was an 800-line wall that tried to be the pitch, the operator
manual, the appliance guide and the developer guide at once. It is now a
landing page that points at four focused guides under docs/ — the operator,
choosing a satellite and the appliance, public streaming, and building and
testing. Nothing was deleted: the long-form material was moved, and the old
links still resolve.
Decoder and relay fixes
- The decoder dock's clock was a third short. The time base was set by the
last segment listed, which for a finished recording is the partial fragment
the broadcast ended on. "Behind live", the timeline axis, the rewindable
figures and a recording's total length were all wrong by the same factor; the
es...