#nullable enable using System; using System.IO; using T3.Core.DataTypes; namespace T3.Core.Video; /// /// How a video stream trades decode work against seek responsiveness, chosen per operator and passed to /// . /// public enum VideoPlaybackOptimization { /// /// Software decode with a RAM frame cache: instant scrub-back / loop re-seeks and smooth playback (no /// per-frame GPU read-back), keeping the GPU free for the editor. Heavier CPU cost on large frames. /// FastSeeking = 0, /// /// Zero-copy GPU (hardware) decode, no cache: smoothest continuous playback — best for large/4K frames — but /// random seeks re-decode the GOP. Falls back to software when the codec/GPU profile is unsupported. /// PlaybackPerformance = 1, } /// /// The latest frame and status for one video stream, returned by /// . is true when this call uploaded a /// new frame; drives the export-gated Playback.OpNotReady. /// public readonly record struct VideoFrameResult( bool Produced, Texture2D? Texture, float Duration, bool HasCompleted, bool IsReady, string? ErrorMessage); /// /// Process-wide video-playback service. The implementation lives in the operator-loaded video assembly; this /// Core interface lets operators (and, later, the editor) reach it without depending on FFmpeg. One instance /// owns every decode stream and its frame cache. Obtain it via . /// public interface IVideoPlaybackEngine { /// /// Drives the stream identified by toward /// and returns its latest frame and status. The stream's decoder is created on first request and re-opened /// if changes. /// VideoFrameResult RequestFrame(Guid streamId, string absolutePath, double requestedSeconds, bool loop, bool renderingToFile, VideoPlaybackOptimization optimization); /// Releases the stream's decoder and cache. Call when the owning operator is disposed. void ReleaseStream(Guid streamId); } /// /// Holds the process-wide . The video assembly sets it when it first loads; /// it stays null in a project that never uses video (the video assembly is never loaded). /// public static class VideoPlayback { public static IVideoPlaybackEngine? Engine { get; set; } /// Sibling-file suffix that marks a generated proxy (clipclip.proxy.mov). Proxy codecs /// (ProRes/HAP/…) all mux to MOV, so a single suffix identifies every proxy for lookup and cleanup. public const string ProxySuffix = ".proxy.mov"; /// The sibling proxy path for a source video (clip.mp4clip.proxy.mov). Shared by /// proxy generation and the engine's substitution so both agree. public static string GetProxyPath(string sourcePath) => Path.Combine(Path.GetDirectoryName(sourcePath) ?? ".", Path.GetFileNameWithoutExtension(sourcePath) + ProxySuffix); }