SHA256
Add Render Cleanup Logic
This commit is contained in:
1 parent
459b2f3b0e
commit
f4062912a0
3 files changed
+143
-7
No files matched your search
+83
-3
@@ -88,6 +88,8 @@ namespace ADOFAIRenderer.Core
|
||||
public long wonFrame = -1;
|
||||
|
||||
// Private Instance Fields
|
||||
private VideoFFmpeg? _activeVideoFFmpeg;
|
||||
private readonly List<VideoFFmpeg> _allVideoFFmpegInstances = new List<VideoFFmpeg>();
|
||||
private int _originalInputOffset;
|
||||
private GameObject? _debugMonitorObj;
|
||||
private GameObject? _escapeHandlerObj;
|
||||
@@ -99,6 +101,49 @@ namespace ADOFAIRenderer.Core
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CleanUpActiveRender();
|
||||
}
|
||||
|
||||
public void CleanUpActiveRender()
|
||||
{
|
||||
isRendering = isCapturing = false;
|
||||
isRenderCancelled = true;
|
||||
|
||||
if (_activeVideoFFmpeg != null)
|
||||
{
|
||||
Melon<RenderMod>.Logger.Msg("게임 종료 또는 RenderManager 파괴 감지: FFmpeg 렌더 프로세스를 강제 종료합니다.");
|
||||
_activeVideoFFmpeg.Kill();
|
||||
_activeVideoFFmpeg = null;
|
||||
}
|
||||
|
||||
lock (_allVideoFFmpegInstances)
|
||||
{
|
||||
if (_allVideoFFmpegInstances.Count > 0)
|
||||
{
|
||||
Melon<RenderMod>.Logger.Msg($"게임 종료 감지: 생성된 모든 FFmpeg 프로세스({_allVideoFFmpegInstances.Count}개)를 강제 수거 및 종료합니다.");
|
||||
foreach (var instance in _allVideoFFmpegInstances)
|
||||
{
|
||||
try
|
||||
{
|
||||
instance.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
_allVideoFFmpegInstances.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (RenderThread is { IsAlive: true })
|
||||
{
|
||||
RenderThread.Join(200);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_renderSetup && ADOBase.lm)
|
||||
@@ -405,13 +450,19 @@ namespace ADOFAIRenderer.Core
|
||||
|
||||
RenderThread = new Thread(() =>
|
||||
{
|
||||
VideoFFmpeg? videoFFmpeg = null;
|
||||
try
|
||||
{
|
||||
var effectiveVideoFps = Math.Min(videoFps, targetFps);
|
||||
var videoFFmpeg = new VideoFFmpeg(Configuration.RenderWidth, Configuration.RenderHeight, effectiveVideoFps, videoFps, selectedCodec);
|
||||
videoFFmpeg = new VideoFFmpeg(Configuration.RenderWidth, Configuration.RenderHeight, effectiveVideoFps, videoFps, selectedCodec);
|
||||
|
||||
SetupCaptureDirectory();
|
||||
|
||||
_activeVideoFFmpeg = videoFFmpeg;
|
||||
lock (_allVideoFFmpegInstances)
|
||||
{
|
||||
_allVideoFFmpegInstances.Add(videoFFmpeg);
|
||||
}
|
||||
videoFFmpeg.Start(_currentOutPath, bitrate);
|
||||
|
||||
while (true)
|
||||
@@ -464,12 +515,35 @@ namespace ADOFAIRenderer.Core
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
videoFFmpeg.Close();
|
||||
endSuccessful = true;
|
||||
if (isRenderCancelled)
|
||||
{
|
||||
videoFFmpeg.Kill();
|
||||
}
|
||||
else
|
||||
{
|
||||
videoFFmpeg.Close();
|
||||
}
|
||||
|
||||
lock (_allVideoFFmpegInstances)
|
||||
{
|
||||
_allVideoFFmpegInstances.Remove(videoFFmpeg);
|
||||
}
|
||||
|
||||
_activeVideoFFmpeg = null;
|
||||
endSuccessful = !isRenderCancelled;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error($"FFmpeg 렌더링 스레드 오류: {ex}");
|
||||
_activeVideoFFmpeg?.Kill();
|
||||
if (videoFFmpeg != null)
|
||||
{
|
||||
lock (_allVideoFFmpegInstances)
|
||||
{
|
||||
_allVideoFFmpegInstances.Remove(videoFFmpeg);
|
||||
}
|
||||
}
|
||||
_activeVideoFFmpeg = null;
|
||||
}
|
||||
})
|
||||
{
|
||||
@@ -511,6 +585,12 @@ namespace ADOFAIRenderer.Core
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error("렌더링이 실패했거나 취소되었습니다. 상태 및 리소스를 안전하게 청소합니다.");
|
||||
|
||||
if (_activeVideoFFmpeg != null)
|
||||
{
|
||||
_activeVideoFFmpeg.Kill();
|
||||
_activeVideoFFmpeg = null;
|
||||
}
|
||||
|
||||
// 1. 메모리 누수 방지: 큐에 렌트된 임시 프레임 버퍼 풀 반환 및 클리어
|
||||
lock (ImageQueue)
|
||||
{
|
||||
|
||||
@@ -46,5 +46,12 @@ namespace ADOFAIRenderer
|
||||
var newModule = EventSystem.current.gameObject.AddComponent<StandaloneInputModule>();
|
||||
newModule.enabled = true;
|
||||
}
|
||||
|
||||
public override void OnApplicationQuit()
|
||||
{
|
||||
if (RenderManager.Instance == null) return;
|
||||
Melon<RenderMod>.Logger.Msg("MelonLoader 종료 감지: RenderManager의 활성 프로세스를 강제 수거 및 종료합니다.");
|
||||
RenderManager.Instance.CleanUpActiveRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Unity.Collections;
|
||||
|
||||
namespace ADOFAIRenderer.Util.FFmpeg
|
||||
{
|
||||
public class FFmpegProcess
|
||||
public class FFmpegProcess : IDisposable
|
||||
{
|
||||
public static string FFmpegPath = "";
|
||||
|
||||
@@ -47,9 +47,58 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
||||
public void Close()
|
||||
{
|
||||
if (_ffmpegProcess == null || _ffmpegProcess.HasExited || _inputStream == null) return;
|
||||
_inputStream.Flush();
|
||||
_inputStream.Close();
|
||||
_ffmpegProcess.WaitForExit();
|
||||
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()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
|
||||
{
|
||||
_ffmpegProcess.Kill();
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
Reference in new issue
Block a user