/*
* VideoKit
* Copyright © 2026 Yusuf Olokoba. All Rights Reserved.
*/
#nullable enable
namespace VideoKit.Sources {
using System;
using UnityEngine;
using Clocks;
using Internal;
///
/// Media source for generating images from the screen.
///
public sealed class ScreenSource : IDisposable {
#region --Client API--
///
/// Texture source used to create pixel buffers from the screen texture.
///
public readonly TextureSource textureSource;
///
/// Control number of successive frames to skip while generating images.
/// This is very useful for GIF recording, which typically has a lower framerate appearance.
///
public int frameSkip;
///
/// Create a screen source.
///
/// Image width.
/// Image height.
/// Handler to receive images.
/// Clock for generating image timestamps.
/// Whether to use` LateUpdate` instead of end of frame for capturing frames. See #52.
public ScreenSource(
int width,
int height,
Action handler,
IClock? clock = null,
bool useLateUpdate = false
) {
// Create texture source
this.clock = clock;
this.descriptor = new(width, height, RenderTextureFormat.ARGBHalf, 0);
this.textureSource = new(width, height, handler);
// Listen for frame events
if (useLateUpdate)
VideoKitEvents.Instance.onLateUpdate += OnFrame;
else
VideoKitEvents.Instance.onFrame += OnFrame;
}
///
/// Stop the screen source and release resources.
///
public void Dispose() {
// Stop listening for events
var events = VideoKitEvents.OptionalInstance;
if (events != null) {
events.onLateUpdate -= OnFrame;
events.onFrame -= OnFrame;
}
// Teardown
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;
// Capture screen
var screenBuffer = RenderTexture.GetTemporary(
Screen.width,
Screen.height,
0,
RenderTextureFormat.ARGBHalf
);
var frameBuffer = RenderTexture.GetTemporary(descriptor);
ScreenCapture.CaptureScreenshotIntoRenderTexture(screenBuffer);
Graphics.Blit(
screenBuffer,
frameBuffer,
SystemInfo.graphicsUVStartsAtTop ? new Vector2(1, -1) : Vector2.one,
SystemInfo.graphicsUVStartsAtTop ? Vector2.up : Vector2.zero
);
// Append
textureSource.Append(frameBuffer, clock?.timestamp ?? 0L);
RenderTexture.ReleaseTemporary(frameBuffer);
RenderTexture.ReleaseTemporary(screenBuffer);
}
#endregion
#region --Deprecated--
[Obsolete(@"Deprecated in VideoKit 0.0.23. Use the ScreenSource(width, height, handler, clock) constructor instead.", false)]
public ScreenSource(
MediaRecorder recorder,
IClock? clock = null,
bool useLateUpdate = false
) : this(recorder.width, recorder.height, recorder.Append, clock, useLateUpdate) { }
#endregion
}
}