Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Resolution (supports output higher than your display resolution)
- Frame rate (FPS)
- Camera settings (Camera2, ReeCamera, None or Default)
- Encoding (h.264, HEVC, AV1)
- Output files saved to:
```
Beat Saber/Renders/Finished
Expand All @@ -37,4 +38,3 @@ From there, you can:

*(The mod will not run unless using `-fpfc` or the first person flying controller)*
- Ensure that the replay files being played are not broken or your replay playback mod is broken, this mod does not handle the replays themselves
- Audio is currently **NOT** recorded from the game, and is instead muxed in from the beatmaps ogg/egg file
32 changes: 24 additions & 8 deletions RenderMod/Render/EncoderHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using IPA.Utilities;
using System.Diagnostics;
using System.IO;
using Debug = UnityEngine.Debug;

namespace RenderMod.Render
{
Expand All @@ -23,7 +25,7 @@ public static string SelectBestEncoder()

if (IsEncoderUsable("hevc_qsv", encodersOutput))
return "hevc_qsv";

return "libx265";
}

Expand All @@ -48,7 +50,7 @@ public static string SelectBestEncoder()

if (IsEncoderUsable("h264_qsv", encodersOutput))
return "h264_qsv";

return "libx264";
}

Expand Down Expand Up @@ -77,12 +79,14 @@ private static string GetEncodersOutput()
return string.Empty;
}
}

private static bool IsEncoderUsable(string encoder, string encodersOutput)
{
if (!encodersOutput.Contains(encoder))
{
Debug.LogError($"{encoder} is unavailable!");
return false;

}
return TestEncoder(encoder);
}

Expand All @@ -95,7 +99,7 @@ public static bool TestEncoder(string encoder)
proc.StartInfo.Arguments =
$"-hide_banner -loglevel error " +
$"-f lavfi -i testsrc=size=1280x720:rate=30 " +
$"-c:v {encoder} -t 1 -f null -";
$"-vf format=yuv420p -c:v {encoder} -t 1 -f null -";

proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.UseShellExecute = false;
Expand Down Expand Up @@ -130,7 +134,11 @@ public static (string encoder, string args) BuildEncoderArgs(string encoder)
switch (ReplayRenderSettings.Preset)
{
case QualityPreset.Low:
if (encoder.Contains("nvenc"))
if (encoder.Contains("av1_nvenc"))
{
presetArgs = $"-preset fast -rc vbr {bitrateArg}";
}
else if (encoder.Contains("nvenc"))
{
presetArgs = $"-preset fast -rc vbr_hq -cq 28 {bitrateArg}";
}
Expand All @@ -153,7 +161,11 @@ public static (string encoder, string args) BuildEncoderArgs(string encoder)
break;

case QualityPreset.Medium:
if (encoder.Contains("nvenc"))
if (encoder.Contains("av1_nvenc"))
{
presetArgs = $"-preset medium -rc vbr {bitrateArg}";
}
else if (encoder.Contains("nvenc"))
{
presetArgs = $"-preset medium -rc vbr_hq -cq 23 {bitrateArg}";
}
Expand All @@ -176,7 +188,11 @@ public static (string encoder, string args) BuildEncoderArgs(string encoder)
break;

case QualityPreset.High:
if (encoder.Contains("nvenc"))
if (encoder.Contains("av1_nvenc"))
{
presetArgs = $"-preset slow -rc vbr -tune hq {bitrateArg}";
}
else if (encoder.Contains("nvenc"))
{
presetArgs = $"-preset slow -rc vbr_hq -cq 18 {bitrateArg}";
}
Expand Down
26 changes: 21 additions & 5 deletions RenderMod/Render/RenderManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ public static void StartAudioCapture()
currentState = RenderState.Audio;
if (beatleaderRender)
{
_log.Notice("Starting video render for BeatLeader replay");
_log.Notice("Starting audio render for BeatLeader replay");
BeatLeaderWarningPatch.TargetMethod()?.Invoke(BeatLeaderWarningPatch.instance, null);
}
else
{
_log.Notice("Starting video render for ScoreSaber replay");
_log.Notice("Starting audio render for ScoreSaber replay");
ScoreSaberWarningPatch.TargetMethod()?.Invoke(ScoreSaberWarningPatch.instance, null);
}
}
Expand Down Expand Up @@ -105,14 +105,19 @@ public static void StopRender()

try
{
_log.Notice($"Muxing raw video stream to mp4...");
FFmpegPipe.RemuxRawStreamToMp4(latestVideoFile, tempVideoMp4, ReplayRenderSettings.FPS);
_log.Notice($"Raw video stream remuxed to MP4: {tempVideoMp4}");


_log.Notice($"Muxing audio and video into a final mp4 file...");
FFmpegPipe.AddAudioToMp4(tempVideoMp4, latestAudioFile, finalMp4, ReplayRenderSettings.AudioBitrateKbps);
_log.Notice($"Final muxed MP4 created: {finalMp4}");

Process.Start("explorer.exe", $"/select,\"{finalMp4}\"");
DingPlayer.shouldPlayDing = true;
//deleting temp files
File.Delete(latestVideoFile);
File.Delete(latestAudioFile);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -151,11 +156,22 @@ internal static void SceneChange(Scene scene1, Scene scene2)
case RenderState.None:
break;
case RenderState.Video:

if (scene2.name.ToLower() != "gamecore")
{
_log.Notice("Exiting gameplay scene, starting audio capture in 2 seconds");
// leaving gameplay (render end)
Task.Delay(2000).ContinueWith(t => StartAudioCapture());
var codec = ReplayRenderSettings.VideoCodec;
if (codec == "av1") //have to delay audio capture a bit more when using av1, ffmpeg takes longer to finish doing its av1 shit
{
_log.Notice("AV1 detected, waiting 15 seconds instead of 2...");
Task.Delay(15000).ContinueWith(t => StartAudioCapture()); //ideally wait for ffmpeg exit and then delay by 2 seconds, fml
}
else
{
Task.Delay(2000).ContinueWith(t => StartAudioCapture());
}

}
break;
case RenderState.Audio:
Expand All @@ -177,4 +193,4 @@ internal static void SceneChange(Scene scene1, Scene scene2)
}
}
}
}
}