using System; using System.Collections.Generic; using System.Linq; using UnityEditor.Recorder.Input; using UnityEngine; namespace UnityEditor.Recorder { /// /// Main class to use the Recorder framework via script. /// Controls recording states like start and stop. /// public class RecorderController { static HashSet resolutionUserType = new HashSet { typeof(GameViewInputSettings), typeof(CameraInputSettings), typeof(RenderTextureSamplerSettings), typeof(AOVCameraInputSettings) }; readonly SceneHook m_SceneHook; internal List m_RecordingSessions; readonly RecorderControllerSettings m_Settings; /// /// Current settings used by this RecorderControler. /// public RecorderControllerSettings Settings { get { return m_Settings; } } /// /// The constructor of the RecorderController. /// /// The settings to be used by this RecorderController. public RecorderController(RecorderControllerSettings settings) { m_Settings = settings; m_SceneHook = new SceneHook(Guid.NewGuid().ToString()); } /// /// Prepares the recording context. /// To start recording once you've called this method, you must call . /// /// /// Sets up the internal data for the recording session and pauses the simulation to ensure a proper synchronization between the Recorder and the Unity Editor. /// /// When the settings are null. /// When there are multiple recorders and accumulation is enabled for one of them. public void PrepareRecording() { if (!Application.isPlaying) throw new Exception("You can only call the PrepareRecording method in Play mode."); if (RecorderOptions.VerboseMode) Debug.Log("Prepare Recording."); if (m_Settings == null) throw new NullReferenceException("Can start recording without prefs"); SceneHook.PrepareSessionRoot(); m_RecordingSessions = new List(); int numberOfSubframeRecorder = 0; int numberOfRecorderEnabled = 0; ValidateRecorderNames(); ValidateRecorderResolutions(); if (m_Settings.InvalidContextBecauseOfAccumulation()) { throw new InvalidOperationException("You can only use one active Recorder at a time when you capture accumulation."); } foreach (var recorderSetting in m_Settings.RecorderSettings) { if (recorderSetting == null) { if (RecorderOptions.VerboseMode) Debug.Log("Ignoring unknown recorder."); continue; } m_Settings.ApplyGlobalSetting(recorderSetting); if (recorderSetting.IsInvalid()) { if (RecorderOptions.VerboseMode) Debug.Log("Ignoring invalid recorder '" + recorderSetting.name + "'"); continue; } if (!recorderSetting.Enabled) { if (RecorderOptions.VerboseMode) Debug.Log("Ignoring disabled recorder '" + recorderSetting.name + "'"); continue; } if (recorderSetting.Enabled) { numberOfRecorderEnabled++; } // Validate that only one recorder support enable capture SubFrames if (UnityHelpers.CaptureAccumulation(recorderSetting)) { numberOfSubframeRecorder++; if (numberOfSubframeRecorder >= 1 && numberOfRecorderEnabled > 1) { Debug.LogError("You can only use one active Recorder at a time when you capture accumulation."); continue; } } var session = m_SceneHook.CreateRecorderSessionWithRecorderComponent(recorderSetting); m_RecordingSessions.Add(session); } } /// /// Starts the recording (works only in Play mode). /// To use this method, you must first have called to set up the recording context. /// Also ensure that you've finished loading any additional Scene data required before you start recording. /// /// false if an error occured. The console usually contains logs about the errors. /// If not in Playmode. /// If settings is null. public bool StartRecording() { if (!Application.isPlaying) throw new Exception("You can only call the StartRecording method in Play mode."); if (IsRecording()) { if (RecorderOptions.VerboseMode) Debug.Log("Recording was already started."); return false; } if (RecorderOptions.VerboseMode) Debug.Log("Start Recording."); // The controller successfully starts recording if all the sessions were correctly created and at least one session begun recording properly var allSessionsCreated = m_RecordingSessions.All(r => r.SessionCreated()); var atLeastOneSuccessfulRecording = false; foreach (var r in m_RecordingSessions) atLeastOneSuccessfulRecording |= r.BeginRecording(); var success = m_RecordingSessions.Any() && allSessionsCreated && atLeastOneSuccessfulRecording; return success; } /// /// Use this method to know if all recorders are done recording. /// A recording stops: /// 1. The settings is set to a time (or frame) interval and the end time (or last frame) was reached. /// 2. Calling the StopRecording method. /// 3. Exiting Playmode. /// /// True if at least one recording is still active. false otherwise. /// /// /// public bool IsRecording() { return m_RecordingSessions != null && m_RecordingSessions.Any(r => r.isRecording); } /// /// Stops all active recordings. /// Most recordings will create the recorded file once stopped. /// If the settings is using Manual recording mode. then the only way to stop recording is by calling this method or by exiting Playmode. /// /// public void StopRecording() { if (RecorderOptions.VerboseMode) Debug.Log("Stop Recording."); if (m_RecordingSessions != null) { foreach (var session in m_RecordingSessions) { session.Dispose(); if (session.recorderComponent != null) UnityEngine.Object.Destroy(session.recorderComponent); } m_RecordingSessions = null; } } internal IEnumerable GetRecordingSessions() { return m_SceneHook.GetRecordingSessions(); } internal void ValidateRecorderNames() { var outputPaths = new Dictionary(); foreach (var recSettings in m_Settings.RecorderSettings) { var path = recSettings.FileNameGenerator.BuildAbsolutePath(null); if (outputPaths.ContainsKey(path)) { var matchingRecorder = outputPaths[path]; if (!matchingRecorder.Enabled || !recSettings.Enabled) continue; recSettings.IsOutputNameDuplicate = true; matchingRecorder.IsOutputNameDuplicate = true; } else { if (!recSettings.Enabled) continue; recSettings.IsOutputNameDuplicate = false; outputPaths.Add(path, recSettings); } } } internal void ValidateRecorderResolutions() { var resSet = new ValueTuple?(); var contradictory = false; foreach (var recSettings in m_Settings.RecorderSettings.Where(x => x.Enabled).OfType().Where(x => resolutionUserType.Contains(x.ImageInputType))) { var val = new ValueTuple(recSettings.OutputHeight, recSettings.OutputWidth); if (resSet.HasValue && resSet.Value != val) { contradictory = true; break; } resSet = val; } foreach (var recSettings in m_Settings.RecorderSettings.Where(x => x.Enabled).OfType().Where(x => resolutionUserType.Contains(x.ImageInputType))) { recSettings.IsOutputResolutionContradictory = contradictory; } // Disabled recorders never report errors foreach (var recSettings in m_Settings.RecorderSettings) { if (recSettings is RecorderSettings.IResolutionUser res) { if (!resolutionUserType.Contains(res.ImageInputType) || recSettings.Enabled == false) { res.IsOutputResolutionContradictory = false; } } } } } }