/*
* VideoKit
* Copyright © 2026 Yusuf Olokoba. All Rights Reserved.
*/
#nullable enable
namespace VideoKit.Sources {
using System;
using Clocks;
using UI;
///
/// Media source for generating pixel buffers from a camera device.
///
internal sealed class CameraViewSource : IDisposable {
#region --Client API--
///
/// Control number of successive camera frames to skip while recording.
/// This is very useful for GIF recording, which typically has a lower framerate appearance.
///
public int frameSkip;
///
/// Create a camera device source.
///
/// Camera view to capture pixel buffers from.
///
/// Clock for generating pixel buffer timestamps.
/// Raised when the camera preview is not running.
public CameraViewSource(
VideoKitCameraView view,
Action handler,
IClock? clock = null
) {
if (view.texture == null)
throw new ArgumentException("Cannot create camera view source because camera manager is not running");
this.handler = handler;
this.clock = clock;
this.view = view;
view.OnPixelBuffer += OnPixelBuffer;
}
///
/// Stop the camera view source and release resources.
///
public void Dispose() => view.OnPixelBuffer -= OnPixelBuffer;
#endregion
#region --Operations--
private readonly Action handler;
private readonly IClock? clock;
private readonly VideoKitCameraView view;
private int frameIdx;
private void OnPixelBuffer(PixelBuffer pixelBuffer) {
if (frameIdx++ % (frameSkip + 1) != 0)
return;
using var outputBuffer = new PixelBuffer(
width: pixelBuffer.width,
height: pixelBuffer.height,
format: pixelBuffer.format,
data: pixelBuffer.data,
rowStride: pixelBuffer.rowStride,
timestamp: clock?.timestamp ?? 0L,
mirrored: pixelBuffer.verticallyMirrored
);
handler(outputBuffer);
}
#endregion
}
}