Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,42 @@ All notable changes will land here. Format loosely follows

Nothing yet.

## [0.1.8] - VOD-audio session race fix

**VOD audio could go live Source-Only with duplicate sessions.** On a
stream with VOD audio enabled, Twitch Inspector sometimes showed several
short sessions for a single go-live, the first one Transmuxed
(Source-Only) instead of Transcoded, and viewers hit a playback error.

Root cause: the egress supervisor wakes every ~2s and, for a Twitch
destination with VOD audio on, asks Twitch's API to allocate an IVS
session, then points egress at the URL it returns. The old code fired a
fresh GetClientConfiguration call on every tick until one came back. When
Twitch answered slower than the 2s tick, the calls stacked. Each one
allocated its own IVS session and rewrote the destination's override URL,
and each rewrite restarted egress, so a single OBS stream reached Twitch
as a string of brief sessions. Whichever session won the race decided
whether the stream came up Transcoded or Source-Only.

Two guards, both per-destination so multistreaming is unaffected:

- A single-flight latch keeps exactly one session fetch in flight at a
time. It re-checks the override under its lock, so a fetch that just
finished can't let a duplicate slip in right behind it.

- A session epoch, bumped on every publisher disconnect, records which
publisher session a fetch belongs to. A request that returns after OBS
disconnected, whose IVS token is now bound to a dead session, is
discarded instead of being written into the next stream. The apply check
and the disconnect both touch the override under the same lock, so they
cannot interleave.

The `/obs/multitrack-config` proxy path (Enhanced Broadcasting via the
registered OBS service, and the experimental VOD+EB Launch button) was
never affected: it runs inside OBS's blocking config request, before the
stream starts, so it always sets the override for the session about to
begin. Non-Twitch destinations do not use this path at all.

## [0.1.7] - Tour + VOD audio UX fixes

**App tour overlay step updated.** The tour bubble for the Overlay tab
Expand Down Expand Up @@ -902,7 +938,8 @@ See `0.1.0` below for the full feature list.
port pre-flight, and process / RSS sampler have Windows-specific paths.
- No automated release pipeline yet. Build from source per the README.

[Unreleased]: https://github.com/Soulhackzlol/InstantClone/compare/v0.1.7...HEAD
[Unreleased]: https://github.com/Soulhackzlol/InstantClone/compare/v0.1.8...HEAD
[0.1.8]: https://github.com/Soulhackzlol/InstantClone/compare/v0.1.7...v0.1.8
[0.1.7]: https://github.com/Soulhackzlol/InstantClone/compare/v0.1.6...v0.1.7
[0.1.6]: https://github.com/Soulhackzlol/InstantClone/compare/v0.1.0...v0.1.6
[0.1.0]: https://github.com/Soulhackzlol/InstantClone/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "instantclone"
version = "0.1.7"
version = "0.1.8"
edition = "2021"
authors = ["s1moscs"]
license = "GPL-3.0-only"
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ No npm. No submodules. No platform SDKs. The dashboard HTML is minified + gzippe

## Status

**Daily-driver ready on Windows.** I use it on my own streams. CI runs fmt + clippy (with `-D warnings`) + 185 tests on every push, and a tagged commit auto-builds + publishes a release artifact with a `SHA256SUMS.txt` checksum file alongside (no code-signing certificate yet, so the OS may warn on first launch).
**Daily-driver ready on Windows.** I use it on my own streams. CI runs fmt + clippy (with `-D warnings`) + 197 tests on every push, and a tagged commit auto-builds + publishes a release artifact with a `SHA256SUMS.txt` checksum file alongside (no code-signing certificate yet, so the OS may warn on first launch).

**What's solid**

Expand All @@ -303,8 +303,9 @@ No npm. No submodules. No platform SDKs. The dashboard HTML is minified + gzippe
**What's rough, honestly**

- **Windows only.** macOS / Linux aren't tested or packaged. Several modules (tray, port pre-flight, RSS sampler) have Windows-specific code paths that need parallel implementations.
- **Multi-hour streams unproven.** Longest validated real-world stream is ~30 minutes. The supervisor + keepalive + ack logic is designed to handle indefinite sessions but nobody has stress-tested an 8-hour run.
- **Sync disk I/O on the async hot path** for ring append. Page cache absorbs it at typical stream rates, but a flush stall could freeze other tasks. `spawn_blocking` is on the v0.2 list.
- **Multi-hour streams lightly validated.** Longest single run is ~5 hours, with roughly ~10 hours cumulative across test sessions. The supervisor + keepalive + ack logic is designed to handle indefinite sessions; an 8+ hour daily-driver run still wants more mileage.
- **Transcoded ladder isn't guaranteed without EB.** Only Twitch Partners get a transcode slot every time. Affiliates get one opportunistically (capacity-dependent, and far less likely above ~6 Mbps); everyone else stays Source-Only. Above ~8 Mbps under Source-Only, some viewers' hardware decoders fail with Error #1000. This is Twitch's allocation behaviour, not the proxy: OBS's native Twitch preset auto-caps bitrate to stay transcode-eligible, but the Custom-server path (which InstantClone has to be) skips that cap. For a guaranteed ladder use Enhanced Broadcasting (or the VOD+EB Launch button); otherwise keep bitrate near ~6000 Kbps.
- **Sync disk I/O on the ring-append hot path, by choice.** The buffered write lands in the OS page cache in microseconds and the kernel flushes in the background, so the page cache is already acting as the async buffer, and the index and the bytes advance under one lock so a reader never sees a tag whose bytes aren't on disk yet. The tail risk is a writeback stall under memory or slow-disk pressure, which on the current-thread runtime would briefly freeze egress too, not just ingest. Moving the write off-thread adds per-tag overhead and reopens a write-vs-index consistency window, so it isn't worth it for typical hardware. If low-end disks ever become a priority the real lever is the runtime (separate the disk-blocking ingest from egress), not async writes. A someday-maybe I'll revisit, not a blocker.
- **A handful of `unwrap()` on lock guards.** Fine because `panic = "abort"` means a poison condition can't propagate, but still on the cleanup list.
- **Hand-rolled HTTP server.** Smaller binary than `hyper`, but I now own the entire HTTP CVE surface. Worth re-evaluating if the surface grows.

Expand Down
Loading