title: Pre-Event Recording Using C# .NET: Circular Buffer Capture description: Record webcam and IP camera video with pre-event circular buffer in C# .NET. Save video before and after motion detection events with Video Capture SDK .Net.
Pre-Event Recording Using C# .Net: Circular Buffer Video Capture¶
Pre-event recording allows your application to continuously buffer video and audio in memory and save event clips that include footage from before the trigger occurred. Use it to record webcam video, capture IP camera streams, or save RTSP footage with motion detection triggers — essential for surveillance, security, and monitoring applications where capturing what happened before an event is critical.
Key Features of Video Capture SDK .Net Pre-Event Recording¶
- Configurable buffer duration: Buffer the last 5 to 120+ seconds of video in memory
- Automatic post-event recording: Continue recording for a configurable duration after the trigger
- Multiple output formats: MP4 (default), MPEG-TS (crash-safe), MKV
- Extend on re-trigger: If an event recurs during recording, the timer resets without creating a new file
- Multiple independent outputs: Add multiple pre-event recording outputs per pipeline
- GPU-accelerated encoding: Leverages NVENC, QSV, and AMF hardware encoders when available
- Event notifications: Receive callbacks when recording starts and stops
How Pre-Event Recording Works¶
graph LR;
A[Camera Source] --> B[VideoCaptureCoreX];
B --> C[Preview Window];
B --> D[Pre-Event Buffer in Memory];
D -- "Trigger Event" --> E[Event Clip File]; VideoCaptureCoreXcaptures video from a camera (webcam, IP camera, etc.) and displays a preview- Encoded frames are continuously stored in a circular buffer in memory
- When you call
TriggerPreEventRecording(), the buffer is flushed to a file - Live frames continue recording for the configured post-event duration
- Recording stops automatically and the system returns to buffering mode
Implementation Example¶
Info
For a complete working project with XAML and all dependencies, see the Pre-Event Recording VideoCaptureCoreX Demo.
WPF Application with Camera, Motion Detection, and Pre-Event Recording¶
The following code is based on the Pre-Event Recording WPF demo. It supports both local camera and RTSP IP camera sources, with motion detection for automatic recording triggers.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using VisioForge.Core;
using VisioForge.Core.Types.Events;
using VisioForge.Core.Types.VideoProcessing;
using VisioForge.Core.Types.X.Output;
using VisioForge.Core.Types.X.PreEventRecording;
using VisioForge.Core.Types.X.Sources;
using VisioForge.Core.Types.X.VideoEncoders;
using VisioForge.Core.Types.X.AudioEncoders;
using VisioForge.Core.VideoCaptureX;
public partial class MainWindow : Window, IDisposable
{
private VideoCaptureCoreX VideoCapture1;
private string _outputFolder;
private System.Timers.Timer _statusTimer;
private async void BtStart_Click(object sender, RoutedEventArgs e)
{
_outputFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyVideos),
"PreEventRecording");
Directory.CreateDirectory(_outputFolder);
// Create VideoCaptureCoreX with video preview
VideoCapture1 = new VideoCaptureCoreX(VideoView1);
VideoCapture1.OnError += (s, args) => Log($"[Error] {args.Message}");
// Configure video source — camera or RTSP
bool useRtsp = false; // Set to true for RTSP IP camera
if (useRtsp)
{
var rtspSettings = await RTSPSourceSettings.CreateAsync(
new Uri("rtsp://192.168.1.21:554/Streaming/Channels/101"),
login: "admin",
password: "password",
audioEnabled: true);
VideoCapture1.Video_Source = rtspSettings;
VideoCapture1.Audio_Record = true;
}
else
{
// Local camera
var device = (await DeviceEnumerator.Shared.VideoSourcesAsync()).FirstOrDefault();
VideoCapture1.Video_Source = new VideoCaptureDeviceSourceSettings(device);
// Local audio device
var audioDevice = (await DeviceEnumerator.Shared.AudioSourcesAsync()).FirstOrDefault();
if (audioDevice != null)
{
VideoCapture1.Audio_Source = audioDevice.CreateSourceSettingsVC(null);
VideoCapture1.Audio_Record = true;
}
}
// Enable motion detection for automatic triggering
VideoCapture1.Motion_Detection = new MotionDetectionExSettings
{
ProcessorType = MotionProcessorType.None,
DetectorType = MotionDetectorType.TwoFramesDifference,
DifferenceThreshold = 15,
SuppressNoise = true
};
VideoCapture1.OnMotionDetection += VideoCapture1_OnMotionDetection;
// Add pre-event recording output with explicit encoders
var preEventSettings = new PreEventRecordingSettings
{
PreEventDuration = TimeSpan.FromSeconds(10),
PostEventDuration = TimeSpan.FromSeconds(5)
};
var preEventOutput = new PreEventRecordingOutput(
settings: preEventSettings,
videoEnc: new OpenH264EncoderSettings(),
audioEnc: new VOAACEncoderSettings());
VideoCapture1.Outputs_Add(preEventOutput);
// Subscribe to recording events
VideoCapture1.OnPreEventRecordingStarted += (s, args) =>
{
Log($"Recording started: {args.Filename}");
Dispatcher.Invoke(() => lbRecFile.Text = $"File: {args.Filename}");
};
VideoCapture1.OnPreEventRecordingStopped += (s, args) =>
{
Log($"Recording stopped: {args.Filename}");
};
// Start capture — preview and buffering begin
await VideoCapture1.StartAsync();
// Start status timer
_statusTimer = new System.Timers.Timer(500);
_statusTimer.Elapsed += (s, args) => UpdateStatus();
_statusTimer.Start();
Log("Started. Buffering...");
}
// Motion detection handler: auto-trigger recording on motion
private void VideoCapture1_OnMotionDetection(object sender, MotionDetectionExEventArgs e)
{
if (VideoCapture1 == null) return;
bool isMotion = e.LevelPercent >= 5;
if (!isMotion) return;
var state = VideoCapture1.GetPreEventRecordingState(0);
if (state == PreEventRecordingState.Buffering)
{
var filename = Path.Combine(_outputFolder,
$"motion_{DateTime.Now:yyyyMMdd_HHmmss}.mp4");
VideoCapture1.TriggerPreEventRecording(0, filename);
Log($"Motion triggered recording: {filename}");
}
else if (state == PreEventRecordingState.Recording ||
state == PreEventRecordingState.PostEventRecording)
{
// Motion still active — extend the recording
VideoCapture1.ExtendPreEventRecording(0);
}
}
// Manual trigger button
private void BtTrigger_Click(object sender, RoutedEventArgs e)
{
if (VideoCapture1 == null) return;
var filename = Path.Combine(_outputFolder,
$"event_{DateTime.Now:yyyyMMdd_HHmmss}.mp4");
VideoCapture1.TriggerPreEventRecording(0, filename);
Log($"Trigger recording: {filename}");
}
// Manual stop recording button
private void BtStopRec_Click(object sender, RoutedEventArgs e)
{
VideoCapture1?.StopPreEventRecording(0);
Log("Recording stopped manually.");
}
// Extend recording button
private void BtExtend_Click(object sender, RoutedEventArgs e)
{
VideoCapture1?.ExtendPreEventRecording(0);
Log("Post-event timer extended.");
}
// Monitor status periodically
private void UpdateStatus()
{
if (VideoCapture1 == null) return;
var state = VideoCapture1.GetPreEventRecordingState(0);
Dispatcher.Invoke(() => lbState.Text = $"State: {state}");
}
// Stop and clean up
private async void BtStop_Click(object sender, RoutedEventArgs e)
{
_statusTimer?.Stop();
_statusTimer?.Dispose();
if (VideoCapture1 != null)
{
VideoCapture1.OnMotionDetection -= VideoCapture1_OnMotionDetection;
await VideoCapture1.StopAsync();
await VideoCapture1.DisposeAsync();
VideoCapture1 = null;
}
Log("Stopped.");
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_statusTimer?.Stop();
_statusTimer?.Dispose();
VideoCapture1?.DisposeAsync().GetAwaiter().GetResult();
VisioForgeX.DestroySDK();
}
}
MPEG-TS Output for Crash Safety¶
For unattended or headless deployments, use MPEG-TS output to ensure recordings are always playable even if the process crashes:
// Use the factory method for MPEG-TS output
var preEventOutput = PreEventRecordingOutput.CreateMPEGTS(
settings: new PreEventRecordingSettings
{
PreEventDuration = TimeSpan.FromSeconds(30),
PostEventDuration = TimeSpan.FromSeconds(10)
});
core.Outputs_Add(preEventOutput);
// Trigger with .ts extension
core.TriggerPreEventRecording(0, "/recordings/event_001.ts");
MP4 vs MPEG-TS
MP4 files require a finalization step (writing the moov atom). If the process crashes during recording, the MP4 file may be unplayable. MPEG-TS does not have this requirement and is always playable, making it the recommended format for surveillance applications running unattended.
MKV Output¶
var preEventOutput = PreEventRecordingOutput.CreateMKV(
settings: new PreEventRecordingSettings
{
PreEventDuration = TimeSpan.FromSeconds(30),
PostEventDuration = TimeSpan.FromSeconds(10)
});
core.Outputs_Add(preEventOutput);
core.TriggerPreEventRecording(0, "/recordings/event_001.mkv");
API Reference¶
PreEventRecordingOutput¶
| Constructor / Factory | Description |
|---|---|
new PreEventRecordingOutput(settings, videoEnc, audioEnc) | MP4 output with default or custom encoders |
PreEventRecordingOutput.CreateMPEGTS(settings, videoEnc, audioEnc) | MPEG-TS output (crash-safe) |
PreEventRecordingOutput.CreateMKV(settings, videoEnc, audioEnc) | MKV output |
VideoCaptureCoreX Methods¶
| Method | Description |
|---|---|
TriggerPreEventRecording(int index, string filename) | Flush buffer to file and start recording. index is the zero-based pre-event output index. |
TriggerPreEventRecordingAsync(int index, string filename) | Async version of TriggerPreEventRecording. |
ExtendPreEventRecording(int index) | Reset the post-event timer. Call when the trigger condition persists. |
StopPreEventRecording(int index) | Manually stop recording and return to buffering. |
IsPreEventRecording(int index) | Returns true if the output is currently recording. |
GetPreEventRecordingState(int index) | Returns the current PreEventRecordingState. |
VideoCaptureCoreX Properties¶
| Property | Type | Description |
|---|---|---|
PreEventRecording_Count | int | Number of configured pre-event recording outputs. |
VideoCaptureCoreX Events¶
| Event | Description |
|---|---|
OnPreEventRecordingStarted | Fired when a pre-event recording begins. Includes filename and actual pre-event duration. |
OnPreEventRecordingStopped | Fired when a recording finishes (timer expired or manual stop). |
Available Encoders¶
Video encoders:
- OpenH264 (software, cross-platform)
- Intel QSV H264 (hardware)
- NVIDIA NVENC H264 (hardware)
- AMD AMF H264 (hardware)
- Intel QSV HEVC (hardware)
- NVIDIA NVENC HEVC (hardware)
- AMD AMF H265 (hardware)
Audio encoders:
- MP3
- VO-AAC
- AVENC AAC
- MF AAC (Windows only)
Native Dependencies¶
Major SDK package (managed):
<PackageReference Include="VisioForge.DotNet.Core.VideoCaptureX" Version="15.x.x" />
Native dependencies for Windows x64:
<PackageReference Include="VisioForge.DotNet.Core.Redist.VideoCapture.x64" Version="15.x.x" />
For alternative platforms (macOS, Linux, Android, iOS), use the corresponding native dependency packages. See the Deployment Guide for details.
Cross-Platform Compatibility¶
Pre-event recording is available on all platforms supported by Video Capture SDK .Net:
- Windows (x86, x64, ARM64)
- macOS (x64, ARM64)
- Linux (x64, ARM64)
- Android
- iOS
Platform availability depends on GStreamer muxer and encoder support. Hardware-accelerated encoders (NVENC, QSV, AMF) are available on platforms with compatible GPU hardware.