Core Services
Clip Service
FFmpeg-based clip creation service that extracts and concatenates video segments from recordings
The ClipService handles creating clips from recorded sessions and replay buffers. It uses FFmpeg to extract segments, apply encoding settings, and concatenate multiple selections into a single output file.
Overview
Implemented in Backend/Media/ClipService.cs, the service provides:
- Extract multiple segments from different source videos
- Concatenate segments into a single clip
- Re-encode with configurable quality settings
- Hardware and software encoding support
- Progress tracking and cancellation
- Automatic metadata generation
Core Methods
CreateClips
Creates a clip from one or more video segments.
public static async Task CreateClips(List<Segment> segments, bool createSeparateClips = false)List of video segments to include in the clip. MessageService.HandleCreateClip populates each segment from the referenced content, so callers only supply Id, ContentId, StartTime, EndTime, and optional audio overrides — the fields below are what ClipService.CreateClips sees after resolution.
Id: Client-generated segment IDContentId: ID of the source content (looked up inAppState.Content)Type: Content type (Session,Buffer,Clip, orHighlight), copied from the sourceGame: Game name, copied from the sourceFileName: Source video filename (without extension), copied from the sourceFilePath: Full path to source video, copied from the sourceStartTime: Start time in secondsEndTime: End time in secondsTitle: Optional clip title, copied from the sourceIgdbId: Optional IGDB game ID, copied from the sourceMutedAudioTracks: Optional list of audio track indexes to muteAudioTrackVolumes: Optional dictionary of per-track volume multipliers
When true, each segment is exported as its own standalone clip instead of being concatenated. Triggered by sending OutputMode: "separate" on the CreateClip WebSocket call.
Process Flow:
- Validates selections and calculates total duration
- Extracts each segment to a temporary file
- Concatenates temporary files (if multiple selections)
- Generates metadata, thumbnail, and waveform
- Adds clip to content library
- Cleans up temporary files
Progress Events:
Sends ClipProgress messages to the frontend with:
id: Unique clip operation IDprogress: Progress percentage (0-100, or -1 for error)segments: The original segments listerror: Error message (only when progress = -1)
var segments = new List<Segment>
{
new Segment
{
Id = 1,
Game = "Valorant",
FileName = "2026-03-03_14-30-00",
Type = "Session",
StartTime = 45.5,
EndTime = 78.2,
Title = "Ace Round"
}
};
await ClipService.CreateClips(segments);var segments = new List<Segment>
{
new Segment
{
Id = 1,
Game = "Valorant",
FileName = "2026-03-03_14-30-00",
Type = "Session",
StartTime = 45.5,
EndTime = 78.2
},
new Segment
{
Id = 2,
Game = "Valorant",
FileName = "2026-03-03_15-00-00",
Type = "Buffer",
StartTime = 10.0,
EndTime = 25.0
}
};
await ClipService.CreateClips(segments);CancelClip
Cancels an in-progress clip operation.
public static async void CancelClip(int clipId)The unique ID of the clip operation to cancel (provided in ClipProgress messages)
Behavior:
- Kills all active FFmpeg processes associated with the clip ID
- Removes processes from tracking
- Sends completion message to frontend
- Cleanup of temporary files is handled by the original CreateClips task
// From frontend message handler
int clipId = 123456789;
ClipService.CancelClip(clipId);Cancellation is immediate and forces FFmpeg process termination. Partial output files are automatically cleaned up.
Encoding Configuration
The service uses settings from Settings.Instance to configure encoding:
Encoder Selection
"gpu": Use hardware acceleration"cpu": Use software encoding
Codec Selection
"h264": H.264/AVC"h265": H.265/HEVC"av1": AV1
Hardware Encoder Settings
NVIDIA (NVENC):
CQ level for NVENC (lower = higher quality, typical range: 15-35)
NVENC preset. For H.264/HEVC NVENC: slow, medium, fast. The legacy presets (hp, hq, bd, ll, llhq, llhp, lossless, losslesshp) were removed because FFmpeg 9 rejects them, and migration 0020_remove_legacy_nvenc_presets maps saved values to slow, fast, or medium. For AV1 NVENC: p1 (fastest) through p7 (slowest, best quality).
FFmpeg arguments: -cq {quality} -preset {preset}
AMD (AMF):
QP level for AMF (lower = higher quality, typical range: 15-35)
AMF usage mode: quality, transcoding, lowlatency, ultralowlatency
FFmpeg arguments: -rc cqp -qp_i {quality} -qp_p {quality} -usage {preset}
Intel (QuickSync):
Global quality for QSV ICQ mode (lower = higher quality, typical range: 15-35)
QSV preset: fast, medium, or slow
FFmpeg arguments: -global_quality {quality} -preset {preset}
Software Encoder Settings
CRF value for libx264/libx265 (lower = higher quality, typical range: 18-28)
x264/x265 preset: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
FFmpeg arguments: -crf {quality} -preset {preset}
Additional Settings
Target frame rate (0 = keep source frame rate)
AAC audio bitrate (e.g., "128k", "192k", "256k", "320k")
Codec Matrix
The service automatically selects the appropriate codec based on encoder and codec settings:
If GPU vendor detection fails, the service automatically falls back to CPU encoding with a warning log message.
Hardware-Accelerated Decoding
Clip extraction runs the source recording through a hardware-accelerated decode path whenever possible. ClipService.BuildHwDecodeArgs picks the flags based on the source codec (detected via FFmpegService.DetectVideoCodec) and the detected GPU vendor:
If the hardware decode run fails and the clip wasn't cancelled, Segra automatically retries the same segment with software decoding. This mainly speeds up clip creation from AV1 recordings on supported GPUs.
FFmpeg Command Examples
Single Clip Extraction
ffmpeg -y -hwaccel auto -ss 45.5 -t 32.7 \
-i "input.mp4" \
-c:v h264_nvenc -preset p4 -cq 20 -r 60 \
-c:a aac -b:a 192k \
-movflags +faststart \
"output.mp4"Multi-Clip Concatenation
ffmpeg -y -f concat -safe 0 \
-i "concat_list.txt" \
-c copy \
-movflags +faststart \
"output.mp4"Where concat_list.txt contains (paths are written with forward slashes by FFmpegService.BuildConcatListLine):
file 'C:/Temp/clip123.mp4'
file 'C:/Temp/clip456.mp4'The pure -c copy concat is only used when the resulting clip keeps a single mixed audio track. When Keep separate audio tracks is enabled, Segra concatenates with -c:v copy -c:a aac -b:a {ClipAudioQuality} so the per-track audio is re-encoded into the final container.
File Organization
Clips are saved to:
ContentFolder/Clips/{GameName}/{Timestamp}.mp4Example:
C:/Users/User/Videos/Segra/Clips/Valorant/2026-03-03_14-45-30.mp4Earlier versions of Segra used lower-case folder names (clips/). They are still recognized as legacy locations, but new content is written to the names shown above. See Backend/Shared/FolderNames.cs for the full set of canonical folder names.
Each clip includes:
- Video file:
{timestamp}.mp4 - Metadata:
{contentId}.json(game, title, timestamps, IGDB ID,compressedflag) - Thumbnail:
{contentId}.jpeg - Waveform:
{contentId}.peaks.json
Metadata, thumbnails, and waveforms are keyed by the content's stable Id (see migration 0014_id_keyed_sidecars), so renaming a video no longer has to move its sidecars and titles can't collide across game folders.
Progress Tracking
The service tracks progress at multiple stages:
Progress is weighted by clip duration:
double currentProgress = (processedDuration + (progress * clipDuration)) / totalDuration * 95;Error Handling
The service includes comprehensive error handling:
Validation Errors
- Empty selections list
- Zero or negative total duration
- FFmpeg not found
- Missing source files
Extraction Errors
- Failed to create temp clip file
- FFmpeg process errors
- Cancellation during extraction
Concatenation Errors
- Failed to create output file
- File not ready for metadata generation
Cleanup
All temporary files are cleaned up in the finally block:
- Individual clip segments
- Concat list file
- Partial output files (on error)
Errors are logged with full stack traces and reported to the frontend via ClipProgress messages with progress: -1 and an error description.
Thread Safety
The service uses a ProcessLock object to ensure thread-safe access to the active FFmpeg process dictionary:
lock (ProcessLock)
{
ActiveFFmpegProcesses[clipId] = process;
}This prevents race conditions when:
- Adding processes during extraction
- Removing processes after completion
- Cancelling clips from frontend
Integration Points
- FFmpegService: Executes FFmpeg with progress callbacks
- ContentService: Generates thumbnails, metadata, and waveforms
- SettingsService: Loads clips into content library
- StorageService: Sanitizes game names for folder organization
- MessageService: Sends progress updates to frontend