using System.Collections; using System.IO; using Meta.XR; using UnityEngine; using UnityEngine.UI; public class PassthroughCameraRecorder : MonoBehaviour { [Header("Camera")] [SerializeField] private PassthroughCameraAccess cameraAccess; [SerializeField] private RawImage optionalPreview; [Header("Recording")] [SerializeField, Range(0.2f, 10f)] private float savedFramesPerSecond = 2f; [SerializeField, Range(1, 100)] private int jpegQuality = 75; private Texture2D snapshotTexture; private Vector2Int snapshotResolution; private string cameraDirectory; private double nextCaptureTime; private int frameIndex; private IEnumerator Start() { while (XRSessionLogger.Instance == null || string.IsNullOrEmpty(XRSessionLogger.Instance.SessionDirectory)) yield return null; cameraDirectory = Path.Combine(XRSessionLogger.Instance.SessionDirectory, "camera"); Directory.CreateDirectory(cameraDirectory); if (cameraAccess == null) { Debug.LogError("PassthroughCameraRecorder: cameraAccess is not assigned."); yield break; } while (!cameraAccess.IsPlaying) yield return null; if (optionalPreview != null) optionalPreview.texture = cameraAccess.GetTexture(); nextCaptureTime = Time.realtimeSinceStartupAsDouble; Debug.Log("Passthrough camera recorder is ready."); } private void Update() { if (cameraAccess == null || !cameraAccess.IsPlaying || XRSessionLogger.Instance == null || string.IsNullOrEmpty(cameraDirectory)) return; if (!cameraAccess.IsUpdatedThisFrame) return; double now = Time.realtimeSinceStartupAsDouble; if (now < nextCaptureTime) return; nextCaptureTime = now + (1.0 / savedFramesPerSecond); CaptureFrame(); } private void CaptureFrame() { Vector2Int resolution = cameraAccess.CurrentResolution; if (resolution.x <= 0 || resolution.y <= 0) return; EnsureSnapshotTexture(resolution); var pixels = cameraAccess.GetColors(); if (!pixels.IsCreated || pixels.Length == 0) return; snapshotTexture.SetPixelData(pixels, 0); snapshotTexture.Apply(false, false); byte[] jpg = snapshotTexture.EncodeToJPG(jpegQuality); string filename = $"frame_{frameIndex:D6}.jpg"; string fullPath = Path.Combine(cameraDirectory, filename); File.WriteAllBytes(fullPath, jpg); Pose cameraPose = cameraAccess.GetCameraPose(); var intrinsics = cameraAccess.Intrinsics; XRSessionLogger.Instance.LogCameraFrame( cameraAccess.Timestamp, Path.Combine("camera", filename).Replace("\\", "/"), cameraPose, resolution, intrinsics.FocalLength, intrinsics.PrincipalPoint); frameIndex++; } private void EnsureSnapshotTexture(Vector2Int resolution) { if (snapshotTexture != null && snapshotResolution == resolution) return; if (snapshotTexture != null) Destroy(snapshotTexture); snapshotResolution = resolution; snapshotTexture = new Texture2D( resolution.x, resolution.y, TextureFormat.RGBA32, false); } private void OnDestroy() { if (snapshotTexture != null) Destroy(snapshotTexture); } }