using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using UnityEditor.Recorder; using UnityEditor.Recorder.Encoder; using UnityEngine; namespace UnityEditor.Recorder.Examples { /// /// The settings of the FFmpeg Encoder. /// /// /// This class is sealed because users shouldn't inherit from it. Instead, create a new encoder along with its settings class. /// [DisplayName("FFmpeg Encoder")] [Serializable] [EncoderSettings(typeof(FFmpegEncoder))] public sealed class FFmpegEncoderSettings : IEncoderSettings, IEquatable { [SerializeField] string ffmpegPath; public string FFMpegPath => ffmpegPath; /// /// The output format of the FFmpeg encoder. /// public enum OutputFormat { [InspectorName("H.264 Default")] H264Default, [InspectorName("H.264 NVIDIA")] H264Nvidia, [InspectorName("H.264 Lossless 420")] H264Lossless420, [InspectorName("H.264 Lossless 444")] H264Lossless444, [InspectorName("H.265 HEVC Default")] HevcDefault, [InspectorName("H.265 HEVC NVIDIA")] HevcNvidia, [InspectorName("Apple ProRes 4444 XQ (ap4x)")] ProRes4444XQ, [InspectorName("Apple ProRes 4444 (ap4h)")] ProRes4444, [InspectorName("Apple ProRes 422 HQ (apch)")] ProRes422HQ, [InspectorName("Apple ProRes 422 (apcn)")] ProRes422, [InspectorName("Apple ProRes 422 LT (apcs)")] ProRes422LT, [InspectorName("Apple ProRes 422 Proxy (apco)")] ProRes422Proxy, [InspectorName("VP8 (WebM)")] VP8Default, [InspectorName("VP9 (WebM)")] VP9Default, } /// /// The format of the encoder. /// public OutputFormat Format { get => outputFormat; set => outputFormat = value; } [SerializeField] OutputFormat outputFormat; /// string IEncoderSettings.Extension { get { switch (Format) { case OutputFormat.H264Default: case OutputFormat.H264Nvidia: case OutputFormat.H264Lossless420: case OutputFormat.H264Lossless444: case OutputFormat.HevcDefault: case OutputFormat.HevcNvidia: return "mp4"; case OutputFormat.ProRes4444XQ: case OutputFormat.ProRes4444: case OutputFormat.ProRes422HQ: case OutputFormat.ProRes422: case OutputFormat.ProRes422LT: case OutputFormat.ProRes422Proxy: return "mov"; case OutputFormat.VP8Default: case OutputFormat.VP9Default: return "webm"; } return ".mp4"; } } public string GetOptions() { return Format switch { OutputFormat.H264Default => "-c:v libx264 -preset veryslow -crf 17 -tune film -pix_fmt yuv420p", OutputFormat.H264Nvidia => "-c:v h264_nvenc -pix_fmt yuv420p -rc constqp -qmin 17 -qmax 51 -qp 24 -preset p7 -tune hq -rc-lookahead 4 -profile:v high", OutputFormat.H264Lossless420 => "-c:v libx264 -pix_fmt yuv420p -crf 0", OutputFormat.H264Lossless444 => "-c:v libx264 -pix_fmt yuv444p -crf 0", OutputFormat.HevcDefault => "-c:v libx265 -pix_fmt yuv420p", OutputFormat.HevcNvidia => "-c:v hevc_nvenc -pix_fmt yuv420p -preset p7 -tune hq", OutputFormat.ProRes4444XQ => "-c:v prores_ks -profile:v 5 -vendor apl0 -pix_fmt yuva444p10le", OutputFormat.ProRes4444 => "-c:v prores_ks -profile:v 4 -vendor apl0 -pix_fmt yuva444p10le", OutputFormat.ProRes422HQ => "-c:v prores_ks -profile:v 3 -vendor apl0 -pix_fmt yuv422p10le", OutputFormat.ProRes422 => "-c:v prores_ks -profile:v 2 -vendor apl0 -pix_fmt yuv422p10le", OutputFormat.ProRes422LT => "-c:v prores_ks -profile:v 1 -vendor apl0 -pix_fmt yuv422p10le", OutputFormat.ProRes422Proxy => "-c:v prores_ks -profile:v 0 -vendor apl0 -pix_fmt yuv422p10le", OutputFormat.VP8Default => "-c:v libvpx -pix_fmt yuv420p -crf 6", OutputFormat.VP9Default => "-c:v libvpx-vp9 -pix_fmt yuv420p -crf 25 -b:v 0", _ => null }; } public string GetPixelFormat(bool inputContainsAlpha) { var codecFormatSupportsTransparency = CodecFormatSupportsAlphaChannel(Format); var willIncludeAlpha = inputContainsAlpha && codecFormatSupportsTransparency; return willIncludeAlpha ? "argb" : "rgb24"; } /// bool IEncoderSettings.CanCaptureAlpha => CodecFormatSupportsAlphaChannel(Format); /// bool IEncoderSettings.CanCaptureAudio => true; /// /// Indicates whether the requested ProRes codec format can encode an alpha channel or not. /// /// The ProRes codec format to check. /// True if the specified codec can encode an alpha channel, False otherwise. internal bool CodecFormatSupportsAlphaChannel(OutputFormat format) { return format == OutputFormat.ProRes4444XQ || format == OutputFormat.ProRes4444; } /// TextureFormat IEncoderSettings.GetTextureFormat(bool inputContainsAlpha) { var codecFormatSupportsTransparency = CodecFormatSupportsAlphaChannel(Format); var willIncludeAlpha = inputContainsAlpha && codecFormatSupportsTransparency; return willIncludeAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24; } /// void IEncoderSettings.ValidateRecording(RecordingContext ctx, List errors, List warnings) { if (!File.Exists(FFMpegPath)) errors.Add($"Cannot find the FFMPEG encoder at path: {FFMpegPath}"); // Is the codec format supported? if (!IsOutputFormatSupported(Format)) errors.Add($"Format '{Format}' is not supported on this platform."); if (ctx.doCaptureAlpha && !CodecFormatSupportsAlphaChannel(Format)) errors.Add($"Format '{Format}' does not support transparency."); if (ctx.frameRateMode == FrameRatePlayback.Variable) errors.Add($"This encoder does not support Variable frame rate playback. Please consider using Constant frame rate instead."); } /// public bool SupportsCurrentPlatform() { return true; } /// /// Indicates whether the specified ProRes codec format is supported on the current operating system or not. /// /// The ProRes codec format to check. /// True if the specified output format is supported on the current operating system, False otherwise /// /// On Windows, all formats are available. /// internal static bool IsOutputFormatSupported(OutputFormat toCheck) { return true; } /// bool IEquatable.Equals(FFmpegEncoderSettings other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return outputFormat == other.outputFormat; } /// /// Compares the current object with another one. /// /// The object to compare with the current one. /// True if the two objects are equal, false otherwise. public override bool Equals(object obj) { return ReferenceEquals(this, obj) || obj is FFmpegEncoderSettings other && ((IEquatable) this).Equals(other); } /// /// Returns a hash code of all serialized fields. /// /// The hash code. public override int GetHashCode() { return HashCode.Combine((int)outputFormat); } } }