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;
}
}
}
}
}
}