Multistream Manager is a Windows desktop app (Avalonia + .NET 9) for local RTMP ingest, multi-destination restreaming, metadata management, OAuth login handling, and optional social announcement posting.
It acts as a control center between:
- Your encoder (
OBS/other RTMP-capable software) - A local RTMP server (
rtmp://localhost:1935/live/demo) - Destination platforms (YouTube, Twitch, Kick, and other RTMP targets from
services.json) - Optional social posting (X, Facebook Pages, Discord webhooks)
- What the App Does
- Getting Started
- High-Level Architecture
- Project Structure
- Core Workflows (How It Works)
- Authentication and Token Lifecycle
- Streaming Pipeline and ffmpeg
- Metadata and Category Handling
- Social AutoPoster
- Themes and UI Behavior
- Persistence and Storage
- Configuration Files
- Prerequisites
- Setup and First Run
- Build and Run
- Installer (WiX)
- Troubleshooting
- Known Limitations / Notes
For a concise first-run guide, see:
docs/GETTING_STARTED.mddocs/DESIGN_AND_ARCHITECTURE.md
Main capabilities:
- Maintains a local RTMP ingest server and status polling.
- Lets you choose stream destinations and store their stream keys.
- Creates/updates platform broadcast sessions where applicable.
- Starts one ffmpeg forwarding process per active destination.
- Lets you manage stream metadata (title, thumbnail, category).
- Supports OAuth login for YouTube, Twitch, Kick, X, Facebook.
- Persists settings, service selections, metadata, webhooks, and encrypted tokens.
- Can auto-post social announcements when stream starts.
- Provides live logs and per-output ffmpeg stderr output.
- Supports multiple built-in themes with a runtime theme selector.
The app follows an MVVM-centered structure with service-driven state:
- Views (
.axaml): UI markup only. - ViewModels: Bindable UI state + commands.
- Services: Application logic, persistence, auth, stream orchestration.
StateService: central state owner and persistence boundary.
Dependency injection is configured in SSMM_UI\App.axaml.cs and services/viewmodels are primarily registered as singletons.
Application startup is in SSMM_UI\Program.cs:
- Sets
ffmpeg.RootPath = "Dependencies" - Starts Avalonia app lifetime.
Important folders/files:
SSMM_UI\Program.cs- app entrypoint.SSMM_UI\App.axaml.cs- DI setup, main window initialization.SSMM_UI\MainWindow.axaml- shell layout (menu, tabs, side panels, theme selector).SSMM_UI\Services\StateService.cs- central state, token events, persistence.SSMM_UI\Services\StreamService.cs- ffmpeg process management + stream start/stop.SSMM_UI\Services\BroadCastService.cs- YouTube/Twitch/Kick broadcast creation logic.SSMM_UI\Services\CentralAuthService.cs- OAuth coordination and auto-login logic.SSMM_UI\Services\ThemeService.cs- theme registration/application/persistence.SSMM_UI\Poster\SocialPoster.cs- social post dispatch logic.SSMM_UI\RTMP\RTMPServer.cs- local RTMP/HTTP-FLV/HLS admin server host.WixPackage\- MSI packaging and shortcut definitions.
LeftSideBarViewModelbinds available RTMP service groups loaded byStateServicefromservices.json.- User selects a service.
DialogService.ShowServerDetailsAsync(...)opens server/key dialog and stores aSelectedServiceinStateService.SelectedServicesToStream.
- Login actions in
LoginViewModelandSocialPosterLoginViewModelcallCentralAuthService. StateServicestores tokens in encrypted files and updates in-memory auth dictionary.- UI statuses refresh from
StateService.OnAuthObjectsUpdated.
MetaDataViewModeledits title/category/thumbnail.- Metadata is kept in
StateServiceand serialized to roaming storage.
-
StreamControlViewModel.StartStream():- Validates active selected outputs.
- For YouTube/Twitch/Kick, asks
BroadCastServiceto prepare upstream broadcast metadata/session details. - Calls
StreamService.StartStream(...).
-
StreamService.StartStream(...):- Builds ffmpeg args for each active output.
- Starts one ffmpeg process per output.
- Captures process references and makes them visible in output logs.
StreamControlViewModel.StopStreamsCommandcallsStreamService.StopStreams().- Existing ffmpeg processes are killed and output viewmodels disposed.
Auth providers in use:
- Stream providers:
YouTube,Twitch,Kick - Social providers:
X,Facebook
Key mechanics:
- Tokens implement a common auth token interface (
IAuthToken). StateServicestores tokens encrypted via DPAPI (SecureStorage).StateServiceemits:OnTokenAddedOnTokenRemovedOnAuthObjectsClearedOnAuthObjectsUpdated
AvailableAuthProvidersis exposed as aReadOnlyObservableCollectionfor reactive UI binding.
Token purge:
- “Purge all” removes token files + clears in-memory state.
- “Purge specific token” removes one provider token from both storage and in-memory state.
- Login status labels are derived from current state (not stale snapshots).
- App starts local RTMP server (
RTMPServer.StartSrv()) on startup throughStreamServiceconstructor. - Ingest endpoint is expected at:
rtmp://localhost:1935/live/demo
- Runtime expects ffmpeg tools in
SSMM_UI\Dependencies. Program.cssets ffmpeg root path accordingly.ffmpeg,ffprobe, and related DLLs are included as copied dependencies in project file.
- One ffmpeg process per selected active destination.
- ffmpeg args are built based on service recommended settings where available.
- Current default video codec behavior uses stream copy (
-c:v copy) fallback. - Process stderr is captured and surfaced in UI through
OutputViewModel.
PollService reports:
- RTMP stream alive status (via ffmpeg probe logic against local ingest URL).
- Server status (via HTTPS check to local admin endpoint
https://localhost:7000/ui/).
Metadata (MetaDataViewModel + MetaDataService):
- Title editing.
- YouTube category selection from
youtube_categories.json. - Thumbnail image selection via file picker.
- Twitch category selection via
SearchView.
Platform-specific updates:
- YouTube: broadcast title/category + optional thumbnail upload.
- Twitch: title/category update through Twitch API integration.
- Kick: title/category automation via puppeteering workflow.
SocialPosterViewModel controls:
- Enable/disable auto-post on stream start.
- Destination toggles: X, Facebook, Discord.
- Optional custom social message.
SocialPoster behavior:
- Resolves currently active stream platforms through
PostMaster. - Builds announcement text (
SocialPostTemplate). - Posts to enabled destinations when possible:
- X API
- Facebook Pages API
- Discord webhooks from configured list
- Returns aggregate success/failure details (
SocialPostResult) for logging.
Theme system:
- Implemented in
ThemeService. - Persisted in
UserSettings.ThemeKey. - Applied by replacing merged resource dictionaries at runtime.
Included themes:
- Midnight Neon
- Cyberpunk Pulse
- Vaporwave Drift
- Crimson Core
- Aurora Glass
- Oceanic Storm
- Solar Flare
- Acid Reactor
- Sunrise Glow
UI theme controls:
- Header
ComboBoxinMainWindowfor direct theme selection. - Theme selection is synchronized through
SelectedTheme+ThemeOption.IsSelected.
Storage helper root:
%APPDATA%\GWAP Technologies\Multistream Manager\...
Persisted data includes:
Settings\UserSettings.jsonServices\Serialized_Services.jsonMetadata\MetaData_State.jsonWebHooks\Webhooks.jsonSettings\WindowSettings.jsonTokens\*Token.json(encrypted)
On main window close (MainWindow.axaml.cs):
- Services, settings, webhooks, and window position are serialized.
-
SSMM_UI\services.json
RTMP service definitions and recommended streaming constraints. -
SSMM_UI\youtube_categories.json
YouTube categories loaded into metadata UI. -
SSMM_UI\Dependencies\*
ffmpeg binaries and supporting native DLLs.
- Windows (desktop target)
- .NET SDK 9.x
- ffmpeg/ffprobe binaries present in
SSMM_UI\Dependencies - Valid API credentials/config for enabled OAuth providers
- OBS Studio (or another RTMP-capable encoder) for local ingest feed
- Ensure dependencies:
- Restore NuGet packages (
dotnet restore). - Verify
Dependenciesfolder contains ffmpeg binaries.
- Configure encoder:
- In OBS: Stream Service = Custom
- Server:
rtmp://localhost:1935/live/ - Stream key:
demo
-
Start app and authenticate providers as needed.
-
Add desired destinations in left sidebar and set stream keys.
-
Set metadata and optional social posting preferences.
-
Click Start Receiving in Inspection panel to preview local ingest.
-
Click Start Stream to begin forwarding to active destinations.
From repository root:
dotnet build .\SSMM_UI\SSMM_UI.csproj -nologo
dotnet run --project .\SSMM_UI\SSMM_UI.csprojSolution build:
dotnet build .\SSMM_UI.sln -nologoInstaller project:
WixPackage\WixPackage.wixproj
Notes:
- Pre-build target publishes desktop app and harvests files with Heat.
- MSI output name is
MultistreamManager. - Desktop and Start Menu shortcuts target
MultistreamManager.exe. - Shortcuts explicitly use app icon via
WixPackage\Shortcuts.wxs(AppShortcutIcon).
Build MSI from repo root:
dotnet build .\WixPackage\WixPackage.wixproj -nologo -p:SolutionDir="C:\path\to\SSMM_UI\"- Verify URI uses new assembly host:
avares://MultistreamManager/...
- Ensure
Assets\MainIcon.pngis included asAvaloniaResource.
- Confirm encoder is pushing to
rtmp://localhost:1935/live/demo. - Confirm local RTMP service is running and no port conflict on
1935.
- Verify token is valid and YouTube service object exists.
- Check logs for lifecycle transition retries.
- Confirm ingest stream became active before transition timeout.
- Verify destination toggle enabled.
- Confirm corresponding token/webhook exists.
- Check log output for provider-specific error reason.
- Current implementation uses central token events + read-only provider collection, so login status and purge dialogs should react immediately. If stale status appears, check subscribers to
OnAuthObjectsUpdated.
- Some provider integrations are partial/stubbed (example: Trovo/Facebook broadcast creation stubs in
BroadCastService). PollServicecurrently runs continuous loops without explicit stop/dispose path for cancellation source.- Some exception handling paths log-and-continue rather than fail-fast (legacy behavior).
- Current project contains an older
ReadMe\ReadMe.txt; thisREADME.mdis intended to be the primary, comprehensive documentation moving forward.