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