/* * 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 } }