Files
ADOFAIRenderer/Util/FFmpeg/FFmpegProcess.cs
T

130 lines
3.9 KiB
C#

using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using Unity.Collections;
namespace ADOFAIRenderer.Util.FFmpeg
{
public class FFmpegProcess : IDisposable
{
public static string FFmpegPath = "";
private Process? _ffmpegProcess;
private Stream? _inputStream;
public bool IsEnd => _ffmpegProcess is { HasExited: true };
protected void Execute(string command)
{
if (string.IsNullOrEmpty(command))
{
throw new ArgumentException("Command is empty");
}
if (string.IsNullOrEmpty(FFmpegPath) || !File.Exists(FFmpegPath))
{
throw new FileNotFoundException($"ffmpeg binary not found at '{FFmpegPath}'");
}
_ffmpegProcess = new Process();
_ffmpegProcess.StartInfo.FileName = FFmpegPath;
_ffmpegProcess.StartInfo.Arguments = command;
_ffmpegProcess.StartInfo.UseShellExecute = false;
_ffmpegProcess.StartInfo.RedirectStandardInput = true;
_ffmpegProcess.StartInfo.CreateNoWindow = true;
_ffmpegProcess.Start();
_inputStream = _ffmpegProcess.StandardInput.BaseStream;
}
public void SendDataFromBuffer(byte[] buffer, int length = -1)
{
if (_inputStream == null) return;
var writeLength = length < 0 ? buffer.Length : length;
_inputStream.Write(buffer, 0, writeLength);
_inputStream.Flush();
}
public void SendDataFromNativeBuffer(IntPtr nativePtr, int length)
{
if (_inputStream == null) return;
StreamUtil.WriteNativePtrToStream(nativePtr, length, _inputStream);
_inputStream.Flush();
}
public void Close()
{
if (_ffmpegProcess == null || _ffmpegProcess.HasExited || _inputStream == null) return;
try
{
_inputStream.Flush();
_inputStream.Close();
// Allow FFmpeg to finish encoding and exit gracefully without any timeout to prevent video corruption
_ffmpegProcess.WaitForExit();
}
catch (Exception)
{
// Ignore any issues during termination
}
finally
{
_ffmpegProcess.Dispose();
_ffmpegProcess = null;
_inputStream = null;
}
}
public void Kill(int timeoutMs = 0)
{
try
{
if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
{
_ffmpegProcess.Kill();
if (timeoutMs > 0)
{
_ffmpegProcess.WaitForExit(timeoutMs);
}
}
}
catch (Exception)
{
// Ignore process already exited or access denied errors during cleanup
}
finally
{
try
{
_inputStream?.Close();
}
catch
{
// Ignore
}
_ffmpegProcess?.Dispose();
_ffmpegProcess = null;
_inputStream = null;
}
}
public void Dispose()
{
Kill();
}
public static byte[] RentRawBytes(int size)
{
var array = ArrayPool<byte>.Shared.Rent(size);
return array;
}
protected static byte[] ToRawBytes<T>(NativeArray<T> arr) where T : struct
{
var val = new NativeSlice<T>(arr).SliceConvert<byte>();
var array = new byte[val.Length];
val.CopyTo(array);
return array;
}
}
}