Advanced
OBS Integration
Learn how Segra leverages ObsKit.NET and OBS Studio for high-performance game recording
Segra's recording engine is built on top of Open Broadcaster Software (OBS) using ObsKit.NET, a modern C# wrapper that provides type-safe access to OBS's powerful recording capabilities.
Architecture Overview
Segra uses a layered architecture to interact with OBS:
Segra Application
↓
ObsKit.NET (C# Wrapper)
↓
OBS Studio (libobs)
↓
Hardware Encoders (NVENC/AMF/QSV)Core Components
OBSService - Backend/Recorder/OBSService.cs
The OBSService class is the central hub for all recording operations. It manages:
- OBS context initialization and shutdown
- Video encoder configuration (NVENC, AMF, QSV, x264)
- Audio source management (microphones, desktop audio, game audio)
- Scene composition with game capture and display capture
- Recording outputs (sessions, replay buffer, hybrid mode)
ObsKit.NET Integration
ObsKit.NET provides a fluent, type-safe API for interacting with OBS. Segra uses it for:
- Video/audio encoder creation
- Source management (GameCapture, MonitorCapture, AudioInputCapture)
- Output handling (RecordingOutput, ReplayBuffer)
- .NET events for OBS callbacks (
Hooked/UnhookedonGameCapture,Saved/StoppedonReplayBuffer,StoppedonRecordingOutput) — replaces the older rawConnectSignal(OutputSignal.*)handles - Global hotkey registration via
Obs.RegisterHotkey(...)(see Hotkeys)
Segra tracks the minimum ObsKit.NET version it needs in Segra.csproj.
GPU Vendor Detection
Segra automatically detects your GPU vendor (NVIDIA, AMD, Intel) to select the optimal hardware encoder:
- NVIDIA: NVENC (H.264/HEVC)
- AMD: AMF (H.264/HEVC)
- Intel: QSV (H.264/HEVC)
- Fallback: x264 (software encoding)
Initialization Process
When Segra starts, it initializes OBS through several stages:
Verifies that obs.dll exists in the application directory. If missing, Segra downloads the appropriate OBS version from Segra's CDN. Current builds bundle OBS 30.1.1, 31.1.2, 32.1.2, 32.2.1, and 32.2.2 with FFmpeg 9.0.1; the automatic default (when selectedOBSVersion is null) is the latest non-beta build.
Creates an OBS context with the fluent API:
_obsContext = Obs.Initialize(config =>
{
config
.WithLocale("en-US")
.WithDataPath("./data/libobs/")
.WithModulePath("./obs-plugins/64bit/", "./data/obs-plugins/%module%/")
.WithVideo(v => v.Resolution(1920, 1080).Fps(60))
.WithAudio(a => a.WithSampleRate(44100).WithSpeakers(SpeakerLayout.Stereo))
.WithLogging((level, message) => { /* Log processing */ });
});Queries OBS for available video encoders and stores them in application state for the settings UI.
Begins monitoring Windows processes for game launches using WMI event watchers.
Recording Pipeline
Scene Composition
Segra creates a layered scene with multiple capture sources:
Layer Order (bottom to top):
- Display Capture (fallback of last resort)
- Window Capture via WGC (fallback used when the game hook can't attach)
- Game Capture (primary layer when hooked)
- Audio sources (microphones, desktop audio, game audio)
Game Capture Hooking
Segra uses OBS's game capture plugin to hook directly into games:
GameCaptureSource = new GameCapture("gameplay", GameCapture.CaptureMode.SpecificWindow);
GameCaptureSource.SetWindow($"*:*:{fileName}");
// Subscribe to hook events
GameCaptureSource.Hooked += OnGameCaptureHookedEvent;
GameCaptureSource.Unhooked += OnGameCaptureUnhookedEvent;Some applications are blacklisted by OBS and cannot be captured using game capture:
explorer.exe,chrome.exe,discord.exe,firefox.exe- Steam, Battle.net, Origin launchers
- Windows system apps
Segra automatically falls back to display capture for these applications.
Coexisting with OBS Studio
Only one application can hold a game's capture hook at a time. Segra avoids fighting OBS Studio or Streamlabs for it:
- At recording start, Segra skips Game Capture and uses Window or Display Capture when either of these is true:
- OBS Studio (
obs64) is running and is streaming or recording. Segra checks the newest session log in%APPDATA%\obs-studio\logs\for the==== Streaming Start ====and==== Recording Start ====markers. - Another application already holds the game's hook (for example OBS Studio's preview or Streamlabs). Segra detects this through the
CaptureHook_Pipe<pid>named pipe.
- OBS Studio (
- When you tab back into the recorded game, Segra waits 2 seconds and then:
- Releases its game capture and switches to Window or Display Capture if OBS Studio is now streaming or recording.
- Retries Game Capture if OBS Studio isn't active and the hook is free. If the retry times out, Segra tries again on the next tab-in.
Audio Routing
Segra supports three audio output modes:
Records all desktop audio sources (default behavior)
Records only game audio using OBS's capture_audio feature
Records game audio and Discord voice chat separately
When game capture hooks successfully, Segra automatically:
- Mutes desktop audio sources
- Unmutes game audio and Discord sources (if enabled)
- Switches back to desktop audio when the game unhooks
Encoder Configuration
Segra configures encoders based on user settings:
videoEncoderSettings.Set("rate_control", "CBR");
videoEncoderSettings.Set("bitrate", targetBitrateKbps);
videoEncoderSettings.Set("max_bitrate", targetBitrateKbps);videoEncoderSettings.Set("rate_control", "VBR");
videoEncoderSettings.Set("bitrate", minBitrateKbps);
videoEncoderSettings.Set("max_bitrate", maxBitrateKbps);videoEncoderSettings.Set("rate_control", "CQP");
videoEncoderSettings.Set("cqp", cqLevel);
videoEncoderSettings.Set("qp", cqLevel);Recording Modes
Segra supports three recording modes, each using different OBS output types:
Session Recording
Uses RecordingOutput to record the entire gaming session to an MP4 file. Segra always attempts the Hybrid MP4 muxer (crash-resilient, chapter markers; OBS 30.2+) and transparently falls back to the plain ffmpeg muxer when the running OBS build doesn't register mp4_output:
_output = new RecordingOutput("simple_output", videoOutputPath);
try
{
_output.SetFormat(RecordingFormat.HybridMp4);
}
catch (NotSupportedException)
{
// OBS build didn't register mp4_output; the output is already
// a working ffmpeg_muxer recorder, no further fallback needed.
}
_output.WithVideoEncoder(_videoEncoder);
_output.WithAudioEncoder(_audioEncoder, track: 0);
_output.Stopped += OnOutputStopped;
_output.Start();Replay Buffer
Uses ReplayBuffer to maintain a rolling buffer in memory:
_bufferOutput = new ReplayBuffer(
"replay_buffer_output",
Settings.Instance.ReplayBufferDuration,
Settings.Instance.ReplayBufferMaxSize
);
_bufferOutput.SetDirectory(bufferDir);
// Handle replay saved and unexpected stops via .NET events
_bufferOutput.Saved += OnReplaySaved; // e.Path is populated on the callback
_bufferOutput.Stopped += OnOutputStopped; // e.Code is an ObsOutputStopCode
_bufferOutput.Start();
// Save the buffer when user presses hotkey
_bufferOutput.Save();Saves for network / slow storage back-stop on an internal timeout of up to 15 minutes so a stalled mux doesn't leave the replay buffer permanently in a half-saved state.
Hybrid Mode
Runs both session recording and replay buffer simultaneously, allowing:
- Full session recording with bookmarks
- Instant replay saving during gameplay
- Separate file organization
Separate Audio Tracks
Segra records multiple audio tracks using OBS's multi-track support. The upper bound is probed from the loaded OBS build at startup (ObsAudioTrackLimit.Probe()):
All audio sources combined (always present)
Individual sources (microphones, game audio, Discord). Stock OBS allows 5 separate sources (6 tracks total, matching its MAX_OUTPUT_AUDIO_ENCODERS). Segra's bundled OBS (32.2.2-segra and later) raises the cap; the exact number is published as appState.maxAudioTracks.
// Assign source to multiple tracks
uint mixersMask = (1u << 0) | (1u << (trackIndex + 1));
allAudioSources[i].AudioMixers = mixersMask;
// Create encoder for each track
for (int t = 0; t < trackCount; t++)
{
var audioEncoder = AudioEncoder.CreateAac(encoderName, 128, t);
_audioEncoders.Add(audioEncoder);
}Separate audio tracks enable advanced editing workflows where you can independently control game audio, microphone, and Discord volumes in post-production.
Display and Window Capture
Segra layers three video sources into every scene:
Segra polls once per second while a fallback is in effect and swaps between Window and Display capture as the window's captureability changes.
// Display fallback (bottom layer)
_displaySource = MonitorCapture.FromMonitor(monitorIndex, "display");
// Window fallback (middle layer, WGC)
_windowSource = WindowCapture.FromWindow(windowSpec, "window");
// Game capture (top layer, primary)
_gameCaptureSource = new GameCapture("gameplay", GameCapture.CaptureMode.SpecificWindow);Resource Management
Segra follows strict resource disposal patterns to prevent memory leaks:
Gracefully stop recording/replay buffer with 30-second timeout
Remove and dispose all scene items and the scene itself
Release game capture, display capture, and audio sources
Encoders are automatically disposed by ObsKit.NET when outputs stop
Never dispose OBS resources while they are still in use. Always stop outputs before disposing sources and encoders.
Log Processing
OBS log messages are processed asynchronously using channels to prevent blocking:
private static readonly Channel<(int level, string message)> _logChannel =
Channel.CreateUnbounded<(int, string)>(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = false
});
// OBS logging callback (non-blocking)
.WithLogging((level, message) =>
{
_logChannel.Writer.TryWrite(((int)level, message));
})
// Background processing
await foreach (var (level, formattedMessage) in _logChannel.Reader.ReadAllAsync())
{
Log.Information($"{(ObsLogLevel)level}: {formattedMessage}");
}This prevents OBS's internal logging thread from being blocked by Serilog's file I/O operations.