/*
* VideoKit
* Copyright © 2025 Yusuf Olokoba. All Rights Reserved.
*/
#nullable enable
namespace VideoKit.Sources {
using System;
using UnityEngine;
using Clocks;
using Internal;
///
/// Media source for generating pixel buffers from one or more scene cameras.
///
public sealed class CameraSource : IDisposable {
#region --Client API--
///
/// Cameras being recorded from.
///
public readonly Camera[] cameras;
///
/// Texture source used to create pixel buffers from the camera texture.
///
public readonly TextureSource textureSource;
///
/// 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 source.
///
/// Pixel buffer width.
/// Pixel buffer height.
/// Game camera to capture pixel buffers from.
/// Handler to receive pixel buffers.
/// Clock for generating pixel buffer timestamps.
public CameraSource (
int width,
int height,
Camera camera,
Action handler,
IClock? clock = null
) : this(width, height, new [] { camera }, handler, clock) { }
///
/// Create a camera source.
///
/// Pixel buffer width.
/// Pixel buffer height.
/// Game cameras to capture pixel buffers from.
/// Handler to receive pixel buffers.
/// Clock for generating pixel buffer timestamps.
public CameraSource (
int width,
int height,
Camera[] cameras,
Action handler,
IClock? clock = null
) {
Array.Sort(cameras, (a, b) => (int)(100 * (a.depth - b.depth)));
this.cameras = cameras;
this.clock = clock;
this.descriptor = new(width, height, RenderTextureFormat.ARGBHalf, 24) {
sRGB = true,
msaaSamples = Mathf.Max(QualitySettings.antiAliasing, 1)
};
this.textureSource = new TextureSource(width, height, handler);
VideoKitEvents.Instance.onFrame += OnFrame;
}
///
/// Stop the media source and release resources.
///
public void Dispose () {
VideoKitEvents.Instance.onFrame -= OnFrame;
textureSource.Dispose();
}
#endregion
#region --Operations--
private readonly IClock? clock;
private readonly RenderTextureDescriptor descriptor;
private int frameIdx;
private void OnFrame () {
// Check frame index
if (frameIdx++ % (frameSkip + 1) != 0)
return;
// Clear framebuffer
var frameBuffer = RenderTexture.GetTemporary(descriptor);
var prevActive = RenderTexture.active;
RenderTexture.active = frameBuffer;
GL.Clear(true, true, Color.clear);
RenderTexture.active = prevActive;
// Render cameras
for (var i = 0; i < cameras.Length; i++) {
var camera = cameras[i];
var prevTarget = camera.targetTexture;
camera.targetTexture = frameBuffer;
camera.Render();
camera.targetTexture = prevTarget;
}
// Append
textureSource.Append(frameBuffer, clock?.timestamp ?? 0L);
// Release framebuffer
RenderTexture.ReleaseTemporary(frameBuffer);
}
#endregion
#region --Deprecated--
[Obsolete(@"Deprecated in VideoKit 0.0.23. Use the CameraSource(width, height, cameras, handler, clock) constructor instead.", false)]
public CameraSource (
MediaRecorder recorder,
params Camera[] cameras
) : this(recorder.width, recorder.height, cameras, recorder.Append) { }
[Obsolete(@"Deprecated in VideoKit 0.0.23. Use the CameraSource(width, height, cameras, handler, clock) constructor instead.", false)]
public CameraSource (
MediaRecorder recorder,
IClock? clock,
params Camera[] cameras
) : this(recorder.width, recorder.height, cameras, recorder.Append, clock) { }
#endregion
}
}