// FFmpegOut - FFmpeg video encoding plugin for Unity // https://github.com/keijiro/KlakNDI using UnityEngine; using UnityEngine.Rendering; using System.Collections.Generic; namespace FFmpegOut { public sealed class FFmpegSession : System.IDisposable { #region Factory methods public static FFmpegSession Create( string name, int width, int height, float frameRate, int bitRate, FFmpegPreset preset ) { name += System.DateTime.Now.ToString(" yyyy MMdd HHmmss"); var path = name.Replace(" ", "_") + preset.GetSuffix(); return CreateWithOutputPath(path, width, height, frameRate, bitRate, preset); } public static FFmpegSession CreateWithOutputPath( string outputPath, int width, int height, float frameRate, int bitRate, FFmpegPreset preset ) { Debug.Log("I'm" + bitRate); return new FFmpegSession( "-y -f rawvideo -vcodec rawvideo -pixel_format rgba" + " -colorspace bt709" + " -video_size " + width + "x" + height + " -framerate " + frameRate + " -loglevel warning -i - " + preset.GetOptions() + " -b:v " + (bitRate * 75) + "k" + " \"" + "C:/Users/pikmi/Downloads/temp.mp4" + "\"" //hijacked this. basically i dont want some fancy output file name (which is what the old code did) so it's just temp now lol ); } public static FFmpegSession CreateWithArguments(string arguments) { return new FFmpegSession(arguments); } #endregion #region Public properties and members public void PushFrame(Texture source) { if (_pipe != null) { ProcessQueue(); if (source != null) QueueFrame(source); } } public void CompletePushFrames() { _pipe?.SyncFrameData(); } public void Close() { if (_pipe != null) { var error = _pipe.CloseAndGetOutput(); if (!string.IsNullOrEmpty(error)) Debug.LogWarning( "FFmpeg returned with warning/error messages. " + "See the following lines for details:\n" + error ); _pipe.Dispose(); _pipe = null; } if (_blitMaterial != null) { UnityEngine.Object.Destroy(_blitMaterial); _blitMaterial = null; } } public void Dispose() { Close(); } #endregion #region Private objects and constructor/destructor FFmpegPipe _pipe; Material _blitMaterial; FFmpegSession(string arguments) { if (!FFmpegPipe.IsAvailable) Debug.LogWarning( "Failed to initialize an FFmpeg session due to missing " + "executable file. Please check FFmpeg installation." ); else if (!UnityEngine.SystemInfo.supportsAsyncGPUReadback) Debug.LogWarning( "Failed to initialize an FFmpeg session due to lack of " + "async GPU readback support. Please try changing " + "graphics API to readback-enabled one." ); else _pipe = new FFmpegPipe(arguments); } ~FFmpegSession() { if (_pipe != null) Debug.LogError( "An unfinalized FFmpegCapture object was detected. " + "It should be explicitly closed or disposed " + "before being garbage-collected." ); } #endregion #region Frame readback queue List _readbackQueue = new List(4); void QueueFrame(Texture source) { if (_readbackQueue.Count > 6) { Debug.LogWarning("Too many GPU readback requests."); return; } // Lazy initialization of the preprocessing blit shader if (_blitMaterial == null) { var shader = Shader.Find("Hidden/FFmpegOut/Preprocess"); _blitMaterial = new Material(shader); } // Blit to a temporary texture and request readback on it. var rt = RenderTexture.GetTemporary (source.width, source.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(source, rt, _blitMaterial, 0); _readbackQueue.Add(AsyncGPUReadback.Request(rt)); RenderTexture.ReleaseTemporary(rt); } void ProcessQueue() { while (_readbackQueue.Count > 0) { // Check if the first entry in the queue is completed. if (!_readbackQueue[0].done) { // Detect out-of-order case (the second entry in the queue // is completed before the first entry). if (_readbackQueue.Count > 1 && _readbackQueue[1].done) { // We can't allow the out-of-order case, so force it to // be completed now. _readbackQueue[0].WaitForCompletion(); } else { // Nothing to do with the queue. break; } } // Retrieve the first entry in the queue. var req = _readbackQueue[0]; _readbackQueue.RemoveAt(0); // Error detection if (req.hasError) { Debug.LogWarning("GPU readback error was detected."); continue; } // Feed the frame to the FFmpeg pipe. _pipe.PushFrameData(req.GetData()); } } #endregion } }