SHA256
Add Audio Config
This commit is contained in:
7 files changed
+332
-121
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -387,7 +387,7 @@ namespace ADOFAIRenderer.Core
|
|||||||
|
|
||||||
private IEnumerator SetupPlay()
|
private IEnumerator SetupPlay()
|
||||||
{
|
{
|
||||||
if (Configuration.RenderAudio && GetOutputExtension(Configuration.EncodingConfig!.Codec) != ".mp4")
|
if (Configuration.RenderAudio && GetOutputExtension(Configuration.VideoConfig!.Codec) != ".mp4")
|
||||||
{
|
{
|
||||||
Configuration.AudioSampleRate = 48000;
|
Configuration.AudioSampleRate = 48000;
|
||||||
}
|
}
|
||||||
@@ -475,7 +475,7 @@ namespace ADOFAIRenderer.Core
|
|||||||
var renderFPS = Configuration.RenderFPS;
|
var renderFPS = Configuration.RenderFPS;
|
||||||
var videoFPS = Configuration.VideoFPS;
|
var videoFPS = Configuration.VideoFPS;
|
||||||
|
|
||||||
var selectedCodec = Configuration.EncodingConfig?.Codec ?? Codec.H264;
|
var selectedCodec = Configuration.VideoConfig?.Codec ?? Codec.H264;
|
||||||
var ext = GetOutputExtension(selectedCodec);
|
var ext = GetOutputExtension(selectedCodec);
|
||||||
|
|
||||||
var startTime = DateTime.Now;
|
var startTime = DateTime.Now;
|
||||||
@@ -493,12 +493,12 @@ namespace ADOFAIRenderer.Core
|
|||||||
Height = Configuration.RenderHeight,
|
Height = Configuration.RenderHeight,
|
||||||
FPS = effectiveVideoFps,
|
FPS = effectiveVideoFps,
|
||||||
Codec = selectedCodec,
|
Codec = selectedCodec,
|
||||||
Mode = Configuration.EncodingConfig!.RateControlMode,
|
Mode = Configuration.VideoConfig!.RateControlMode,
|
||||||
QualityValue = Configuration.EncodingConfig.QP,
|
QualityValue = Configuration.VideoConfig.QP,
|
||||||
TargetBitrate = Configuration.EncodingConfig.Bitrate,
|
TargetBitrate = Configuration.VideoConfig.Bitrate,
|
||||||
MaxBitrate = Configuration.EncodingConfig.MaxBitrate,
|
MaxBitrate = Configuration.VideoConfig.MaxBitrate,
|
||||||
GopSize = Configuration.EncodingConfig.KeyframeInterval,
|
GopSize = Configuration.VideoConfig.KeyframeInterval,
|
||||||
ForceSoftware = Configuration.EncodingConfig.ForceSoftwareEncoder,
|
ForceSoftware = Configuration.VideoConfig.ForceSoftwareEncoder,
|
||||||
Audio = Configuration.RenderAudio ? new FFmpegNativeEncoder.AudioSettings
|
Audio = Configuration.RenderAudio ? new FFmpegNativeEncoder.AudioSettings
|
||||||
{
|
{
|
||||||
SampleRate = Configuration.AudioSampleRate,
|
SampleRate = Configuration.AudioSampleRate,
|
||||||
|
|||||||
+31
-5
@@ -75,20 +75,46 @@ namespace ADOFAIRenderer.Data
|
|||||||
var configToken = jObject["Config"];
|
var configToken = jObject["Config"];
|
||||||
if (configToken is JObject configObj)
|
if (configToken is JObject configObj)
|
||||||
{
|
{
|
||||||
|
// 1. legacy EncodingConfig -> VideoConfig
|
||||||
|
if (configObj["EncodingConfig"] is JObject oldEncodingConfigObj)
|
||||||
|
{
|
||||||
|
configObj["VideoConfig"] ??= oldEncodingConfigObj;
|
||||||
|
configObj.Remove("EncodingConfig");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. legacy top-level Codec -> VideoConfig.Codec
|
||||||
var legacyCodecToken = configObj["Codec"];
|
var legacyCodecToken = configObj["Codec"];
|
||||||
if (legacyCodecToken != null)
|
if (legacyCodecToken != null)
|
||||||
{
|
{
|
||||||
var encodingConfigToken = configObj["EncodingConfig"];
|
if (configObj["VideoConfig"] == null)
|
||||||
if (encodingConfigToken == null)
|
|
||||||
{
|
{
|
||||||
configObj["EncodingConfig"] = new JObject();
|
configObj["VideoConfig"] = new JObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
var encodingConfigObj = (JObject)configObj["EncodingConfig"]!;
|
var videoConfigObj = (JObject)configObj["VideoConfig"]!;
|
||||||
encodingConfigObj["Codec"] ??= legacyCodecToken;
|
videoConfigObj["Codec"] ??= legacyCodecToken;
|
||||||
|
|
||||||
configObj.Remove("Codec");
|
configObj.Remove("Codec");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. legacy top-level Audio properties -> AudioConfig
|
||||||
|
if (configObj["AudioConfig"] == null)
|
||||||
|
{
|
||||||
|
var audioConfigObj = new JObject();
|
||||||
|
if (configObj["RenderAudio"] != null)
|
||||||
|
audioConfigObj["RenderAudio"] = configObj["RenderAudio"];
|
||||||
|
if (configObj["AudioSampleRate"] != null)
|
||||||
|
audioConfigObj["AudioSampleRate"] = configObj["AudioSampleRate"];
|
||||||
|
if (configObj["AudioChannels"] != null)
|
||||||
|
audioConfigObj["AudioChannels"] = configObj["AudioChannels"];
|
||||||
|
if (configObj["AudioBitrate"] != null)
|
||||||
|
audioConfigObj["AudioBitrate"] = configObj["AudioBitrate"];
|
||||||
|
|
||||||
|
if (audioConfigObj.HasValues)
|
||||||
|
{
|
||||||
|
configObj["AudioConfig"] = audioConfigObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var preset = jObject.ToObject<RenderPreset>();
|
var preset = jObject.ToObject<RenderPreset>();
|
||||||
|
|||||||
+36
-6
@@ -10,20 +10,42 @@ namespace ADOFAIRenderer.Data
|
|||||||
public int RenderWidth { get; set; }
|
public int RenderWidth { get; set; }
|
||||||
public int RenderHeight { get; set; }
|
public int RenderHeight { get; set; }
|
||||||
|
|
||||||
public EncodingConfig? EncodingConfig { get; set; }
|
public VideoEncodingConfig? VideoConfig { get; set; }
|
||||||
|
public AudioEncodingConfig? AudioConfig { get; set; }
|
||||||
|
|
||||||
public double DelayAfterLevelEnd { get; set; }
|
public double DelayAfterLevelEnd { get; set; }
|
||||||
public bool ShowJudgementText { get; set; }
|
public bool ShowJudgementText { get; set; }
|
||||||
|
|
||||||
public int? RandomSeed { get; set; } = null;
|
public int? RandomSeed { get; set; } = null;
|
||||||
public bool ProcessAudio { get; set; }
|
public bool ProcessAudio { get; set; }
|
||||||
public bool RenderAudio { get; set; } = true;
|
|
||||||
public int AudioSampleRate { get; set; } = 44100;
|
// 하위 호환 및 편의를 위한 접근자
|
||||||
public int AudioChannels { get; set; } = 2;
|
public bool RenderAudio
|
||||||
public int AudioBitrate { get; set; } = 256000;
|
{
|
||||||
|
get => AudioConfig?.RenderAudio ?? true;
|
||||||
|
set => (AudioConfig ??= new AudioEncodingConfig()).RenderAudio = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AudioSampleRate
|
||||||
|
{
|
||||||
|
get => AudioConfig?.AudioSampleRate ?? 44100;
|
||||||
|
set { (AudioConfig ??= new AudioEncodingConfig()).AudioSampleRate = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AudioChannels
|
||||||
|
{
|
||||||
|
get => AudioConfig?.AudioChannels ?? 2;
|
||||||
|
set { (AudioConfig ??= new AudioEncodingConfig()).AudioChannels = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AudioBitrate
|
||||||
|
{
|
||||||
|
get => AudioConfig?.AudioBitrate ?? 256000;
|
||||||
|
set { (AudioConfig ??= new AudioEncodingConfig()).AudioBitrate = value; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EncodingConfig
|
public class VideoEncodingConfig
|
||||||
{
|
{
|
||||||
public Codec Codec { get; set; } = Codec.H264;
|
public Codec Codec { get; set; } = Codec.H264;
|
||||||
public RateControlMode RateControlMode { get; set; }
|
public RateControlMode RateControlMode { get; set; }
|
||||||
@@ -35,4 +57,12 @@ namespace ADOFAIRenderer.Data
|
|||||||
public double KeyframeInterval { get; set; }
|
public double KeyframeInterval { get; set; }
|
||||||
public bool ForceSoftwareEncoder { get; set; }
|
public bool ForceSoftwareEncoder { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AudioEncodingConfig
|
||||||
|
{
|
||||||
|
public bool RenderAudio { get; set; } = true;
|
||||||
|
public int AudioSampleRate { get; set; } = 44100;
|
||||||
|
public int AudioChannels { get; set; } = 2;
|
||||||
|
public int AudioBitrate { get; set; } = 256000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+257
-102
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using ADOFAIRenderer.Core;
|
using ADOFAIRenderer.Core;
|
||||||
using ADOFAIRenderer.Data;
|
using ADOFAIRenderer.Data;
|
||||||
using ADOFAIRenderer.Util.FFmpeg;
|
using ADOFAIRenderer.Util.FFmpeg;
|
||||||
@@ -17,8 +18,11 @@ namespace ADOFAIRenderer.UI
|
|||||||
private DoubleInputValidator _doubleValidator = null!;
|
private DoubleInputValidator _doubleValidator = null!;
|
||||||
|
|
||||||
private Button _goRenderButton = null!;
|
private Button _goRenderButton = null!;
|
||||||
private Button _encodingConfigButton = null!;
|
private Button _videoEncodingConfigButton = null!;
|
||||||
private VisualElement _encodingConfigContainer = null!;
|
private VisualElement _videoEncodingConfigContainer = null!;
|
||||||
|
|
||||||
|
private Button _audioEncodingConfigButton = null!;
|
||||||
|
private VisualElement _audioEncodingConfigContainer = null!;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
@@ -48,15 +52,26 @@ namespace ADOFAIRenderer.UI
|
|||||||
_goRenderButton = _root.Q<Button>("GoRenderButton");
|
_goRenderButton = _root.Q<Button>("GoRenderButton");
|
||||||
_goRenderButton?.RegisterCallback<ClickEvent>(_ => TryGoRender());
|
_goRenderButton?.RegisterCallback<ClickEvent>(_ => TryGoRender());
|
||||||
|
|
||||||
_encodingConfigButton = _root.Q<Button>("EncodingSettingsButton");
|
_videoEncodingConfigButton = _root.Q<Button>("VideoEncodingSettingsButton");
|
||||||
if (_encodingConfigButton != null)
|
if (_videoEncodingConfigButton != null)
|
||||||
{
|
{
|
||||||
Melon<RenderMod>.Logger.Msg($"[UI] EncodingSettingsButton 발견: pickingMode={_encodingConfigButton.pickingMode}, enabledSelf={_encodingConfigButton.enabledSelf}");
|
Melon<RenderMod>.Logger.Msg($"[UI] VideoEncodingSettingsButton 발견: pickingMode={_videoEncodingConfigButton.pickingMode}, enabledSelf={_videoEncodingConfigButton.enabledSelf}");
|
||||||
_encodingConfigButton.RegisterCallback<ClickEvent>(_ => OpenEncodingConfig());
|
_videoEncodingConfigButton.RegisterCallback<ClickEvent>(_ => OpenVideoEncodingConfig());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Melon<RenderMod>.Logger.Error("Button 'EncodingSettingsButton'을(를) 찾을 수 없습니다.");
|
Melon<RenderMod>.Logger.Error("Button 'VideoEncodingSettingsButton'을(를) 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioEncodingConfigButton = _root.Q<Button>("AudioEncodingSettingsButton");
|
||||||
|
if (_audioEncodingConfigButton != null)
|
||||||
|
{
|
||||||
|
Melon<RenderMod>.Logger.Msg($"[UI] AudioEncodingSettingsButton 발견: pickingMode={_audioEncodingConfigButton.pickingMode}, enabledSelf={_audioEncodingConfigButton.enabledSelf}");
|
||||||
|
_audioEncodingConfigButton.RegisterCallback<ClickEvent>(_ => OpenAudioEncodingConfig());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Melon<RenderMod>.Logger.Error("Button 'AudioEncodingSettingsButton'을(를) 찾을 수 없습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var presetSettingsButton = _root.Q<Button>("PresetSettingsButton");
|
var presetSettingsButton = _root.Q<Button>("PresetSettingsButton");
|
||||||
@@ -79,31 +94,31 @@ namespace ADOFAIRenderer.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenEncodingConfig()
|
private void OpenVideoEncodingConfig()
|
||||||
{
|
{
|
||||||
var config = RenderManager.Instance.Configuration;
|
var config = RenderManager.Instance.Configuration;
|
||||||
config.EncodingConfig ??= GenerateEncodingConfig();
|
config.VideoConfig ??= GenerateEncodingConfig();
|
||||||
|
|
||||||
var tempConfig = new EncodingConfig
|
var tempConfig = new VideoEncodingConfig
|
||||||
{
|
{
|
||||||
Codec = config.EncodingConfig.Codec,
|
Codec = config.VideoConfig.Codec,
|
||||||
RateControlMode = config.EncodingConfig.RateControlMode,
|
RateControlMode = config.VideoConfig.RateControlMode,
|
||||||
Bitrate = config.EncodingConfig.Bitrate,
|
Bitrate = config.VideoConfig.Bitrate,
|
||||||
MaxBitrate = config.EncodingConfig.MaxBitrate,
|
MaxBitrate = config.VideoConfig.MaxBitrate,
|
||||||
QP = config.EncodingConfig.QP,
|
QP = config.VideoConfig.QP,
|
||||||
KeyframeInterval = config.EncodingConfig.KeyframeInterval,
|
KeyframeInterval = config.VideoConfig.KeyframeInterval,
|
||||||
ForceSoftwareEncoder = config.EncodingConfig.ForceSoftwareEncoder
|
ForceSoftwareEncoder = config.VideoConfig.ForceSoftwareEncoder
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_root.Contains(_encodingConfigContainer))
|
if (_root.Contains(_videoEncodingConfigContainer))
|
||||||
{
|
{
|
||||||
_root.Remove(_encodingConfigContainer);
|
_root.Remove(_videoEncodingConfigContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 전체 화면 컨테이너 생성 및 배경 적용
|
// 1. 전체 화면 컨테이너 생성 및 배경 적용
|
||||||
_encodingConfigContainer = new VisualElement
|
_videoEncodingConfigContainer = new VisualElement
|
||||||
{
|
{
|
||||||
name = "EncodingSettingsContainer",
|
name = "VideoEncodingSettingsContainer",
|
||||||
style =
|
style =
|
||||||
{
|
{
|
||||||
position = Position.Absolute,
|
position = Position.Absolute,
|
||||||
@@ -119,80 +134,66 @@ namespace ADOFAIRenderer.UI
|
|||||||
|
|
||||||
// 2. 타이틀 로우
|
// 2. 타이틀 로우
|
||||||
var titleRow = CreateRow();
|
var titleRow = CreateRow();
|
||||||
var title = new Label("인코딩 설정")
|
var titleLabel = new Label("비디오 인코딩 설정")
|
||||||
{
|
{
|
||||||
style =
|
style =
|
||||||
{
|
{
|
||||||
fontSize = 36,
|
fontSize = 24,
|
||||||
unityFontStyleAndWeight = FontStyle.Bold,
|
color = new StyleColor(Color.white)
|
||||||
color = new StyleColor(Color.white),
|
|
||||||
unityTextAlign = TextAnchor.MiddleCenter,
|
|
||||||
width = 770, // 360 * 2 + 50 (여백)에 맞춤
|
|
||||||
height = StyleKeyword.Auto
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
titleRow.Add(title);
|
titleRow.Add(titleLabel);
|
||||||
_encodingConfigContainer.Add(titleRow);
|
_videoEncodingConfigContainer.Add(titleRow);
|
||||||
|
|
||||||
// 2.5. 코덱 로우 (라벨 + 드롭다운)
|
// 3. 코덱 로우
|
||||||
var codecRow = CreateRow();
|
var codecRow = CreateRow();
|
||||||
var codecLabel = CreateFormLabel("비디오 코덱");
|
var codecLabel = CreateFormLabel("코덱");
|
||||||
var codecDropdown = new DropdownField { label = "" };
|
var codecChoices = new List<string>(System.Enum.GetNames(typeof(Codec)));
|
||||||
foreach (Codec codecVal in System.Enum.GetValues(typeof(Codec)))
|
var codecDropdown = new DropdownField(codecChoices, tempConfig.Codec.ToString());
|
||||||
{
|
|
||||||
codecDropdown.choices.Add(codecVal.ToString());
|
|
||||||
}
|
|
||||||
codecDropdown.value = tempConfig.Codec.ToString();
|
|
||||||
|
|
||||||
codecRow.Add(codecLabel);
|
codecRow.Add(codecLabel);
|
||||||
codecRow.Add(codecDropdown);
|
codecRow.Add(codecDropdown);
|
||||||
_encodingConfigContainer.Add(codecRow);
|
_videoEncodingConfigContainer.Add(codecRow);
|
||||||
|
|
||||||
// 3. 전송률 모드 로우 (라벨 + 드롭다운)
|
// 4. 모드 로우
|
||||||
var modeRow = CreateRow();
|
var modeRow = CreateRow();
|
||||||
var modeLabel = CreateFormLabel("전송률 제어 모드");
|
var modeLabel = CreateFormLabel("모드");
|
||||||
var modeDropdown = new DropdownField { label = "" };
|
var modeChoices = new List<string>(System.Enum.GetNames(typeof(RateControlMode)));
|
||||||
foreach (RateControlMode modeVal in System.Enum.GetValues(typeof(RateControlMode)))
|
var modeDropdown = new DropdownField(modeChoices, tempConfig.RateControlMode.ToString());
|
||||||
{
|
|
||||||
modeDropdown.choices.Add(modeVal.ToString());
|
|
||||||
}
|
|
||||||
modeDropdown.value = tempConfig.RateControlMode.ToString();
|
|
||||||
|
|
||||||
modeRow.Add(modeLabel);
|
modeRow.Add(modeLabel);
|
||||||
modeRow.Add(modeDropdown);
|
modeRow.Add(modeDropdown);
|
||||||
_encodingConfigContainer.Add(modeRow);
|
_videoEncodingConfigContainer.Add(modeRow);
|
||||||
|
|
||||||
// 4. Bitrate 로우 (라벨 + 입력)
|
// 5. 비트레이트 로우
|
||||||
var bitrateRow = CreateRow();
|
var bitrateRow = CreateRow();
|
||||||
var bitrateLabel = CreateFormLabel("대상 비트레이트 (kbps)");
|
var bitrateLabel = CreateFormLabel("비트레이트(kbps)");
|
||||||
var bitrateField = new IntegerField { name = "EncodingBitrate", label = "", value = tempConfig.Bitrate };
|
var bitrateField = new IntegerField { name = "EncodingBitrate", label = "", value = tempConfig.Bitrate };
|
||||||
bitrateRow.Add(bitrateLabel);
|
bitrateRow.Add(bitrateLabel);
|
||||||
bitrateRow.Add(bitrateField);
|
bitrateRow.Add(bitrateField);
|
||||||
_encodingConfigContainer.Add(bitrateRow);
|
_videoEncodingConfigContainer.Add(bitrateRow);
|
||||||
|
|
||||||
// 5. Max Bitrate 로우 (라벨 + 입력)
|
// 5.5. 최대 비트레이트 로우 (VBR 전용)
|
||||||
var maxBitrateRow = CreateRow();
|
var maxBitrateRow = CreateRow();
|
||||||
var maxBitrateLabel = CreateFormLabel("최대 비트레이트 (kbps)");
|
var maxBitrateLabel = CreateFormLabel("최대 비트레이트(kbps)");
|
||||||
var maxBitrateField = new IntegerField { name = "EncodingMaxBitrate", label = "", value = tempConfig.MaxBitrate };
|
var maxBitrateField = new IntegerField { name = "EncodingMaxBitrate", label = "", value = tempConfig.MaxBitrate };
|
||||||
maxBitrateRow.Add(maxBitrateLabel);
|
maxBitrateRow.Add(maxBitrateLabel);
|
||||||
maxBitrateRow.Add(maxBitrateField);
|
maxBitrateRow.Add(maxBitrateField);
|
||||||
_encodingConfigContainer.Add(maxBitrateRow);
|
_videoEncodingConfigContainer.Add(maxBitrateRow);
|
||||||
|
|
||||||
// 6. QP 로우 (라벨 + 입력)
|
// 6. QP 로우
|
||||||
var qpRow = CreateRow();
|
var qpRow = CreateRow();
|
||||||
var qpLabel = CreateFormLabel("QP 품질 값 (0 ~ 51)");
|
var qpLabel = CreateFormLabel("품질 (QP)");
|
||||||
var qpField = new IntegerField { name = "EncodingQP", label = "", value = tempConfig.QP };
|
var qpField = new IntegerField { name = "EncodingQP", label = "", value = tempConfig.QP };
|
||||||
qpRow.Add(qpLabel);
|
qpRow.Add(qpLabel);
|
||||||
qpRow.Add(qpField);
|
qpRow.Add(qpField);
|
||||||
_encodingConfigContainer.Add(qpRow);
|
_videoEncodingConfigContainer.Add(qpRow);
|
||||||
|
|
||||||
// 6.5. 키프레임 간격 로우 (라벨 + 입력)
|
// 6.5. 키프레임 간격 로우 (초 단위)
|
||||||
var keyframeRow = CreateRow();
|
var keyframeRow = CreateRow();
|
||||||
var keyframeLabel = CreateFormLabel("키프레임 간격 (초)");
|
var keyframeLabel = CreateFormLabel("키프레임 간격(초)");
|
||||||
var keyframeField = new DoubleField { name = "EncodingKeyframeInterval", label = "", value = tempConfig.KeyframeInterval };
|
var keyframeField = new DoubleField { name = "EncodingKeyframeInterval", label = "", value = tempConfig.KeyframeInterval };
|
||||||
keyframeRow.Add(keyframeLabel);
|
keyframeRow.Add(keyframeLabel);
|
||||||
keyframeRow.Add(keyframeField);
|
keyframeRow.Add(keyframeField);
|
||||||
_encodingConfigContainer.Add(keyframeRow);
|
_videoEncodingConfigContainer.Add(keyframeRow);
|
||||||
|
|
||||||
// 6.75. 강제 소프트웨어 인코더 로우 (라벨 + 토글)
|
// 6.75. 강제 소프트웨어 인코더 로우 (라벨 + 토글)
|
||||||
var forceSoftwareRow = CreateRow();
|
var forceSoftwareRow = CreateRow();
|
||||||
@@ -200,53 +201,46 @@ namespace ADOFAIRenderer.UI
|
|||||||
var forceSoftwareToggle = new Toggle { name = "ForceSoftwareEncoder", label = "", value = tempConfig.ForceSoftwareEncoder };
|
var forceSoftwareToggle = new Toggle { name = "ForceSoftwareEncoder", label = "", value = tempConfig.ForceSoftwareEncoder };
|
||||||
forceSoftwareRow.Add(forceSoftwareLabel);
|
forceSoftwareRow.Add(forceSoftwareLabel);
|
||||||
forceSoftwareRow.Add(forceSoftwareToggle);
|
forceSoftwareRow.Add(forceSoftwareToggle);
|
||||||
_encodingConfigContainer.Add(forceSoftwareRow);
|
_videoEncodingConfigContainer.Add(forceSoftwareRow);
|
||||||
|
|
||||||
// 7. QP = 0 (무손실) 경고 라벨 로우 생성 (ui-row 클래스를 상속받아 NanumSquareNeo 폰트가 적용되도록 처리)
|
// 7. QP = 0 (무손실) 경고 라벨 로우 생성
|
||||||
var warningRow = CreateRow();
|
var warningRow = CreateRow();
|
||||||
warningRow.style.height = StyleKeyword.Auto;
|
warningRow.style.height = StyleKeyword.Auto;
|
||||||
warningRow.style.maxHeight = StyleKeyword.Null;
|
warningRow.style.maxHeight = StyleKeyword.Null;
|
||||||
warningRow.style.minHeight = StyleKeyword.Null;
|
warningRow.style.minHeight = StyleKeyword.Null;
|
||||||
|
|
||||||
var warningLabel = new Label("※ QP 0 (무손실)은 일부 하드웨어/코덱에서 지원되지 않을 수 있으며, 인코딩 실패 시 CPU 모드로 폴백됩니다.")
|
var warningLabel = new Label("주의: QP를 0으로 설정하면 무손실 인코딩이 적용되어 용량이 매우 커질 수 있습니다.")
|
||||||
|
{
|
||||||
|
style =
|
||||||
{
|
{
|
||||||
style =
|
color = new StyleColor(new Color(1.0f, 0.8f, 0.2f)),
|
||||||
{
|
fontSize = 12,
|
||||||
fontSize = 20, // 30px에서 20px로 오버라이드
|
unityTextAlign = TextAnchor.MiddleCenter,
|
||||||
color = new StyleColor(new Color(0.9f, 0.6f, 0.2f)),
|
whiteSpace = WhiteSpace.Normal
|
||||||
whiteSpace = WhiteSpace.Normal,
|
}
|
||||||
width = 720, // 360px에서 720px로 오버라이드
|
};
|
||||||
height = StyleKeyword.Auto,
|
|
||||||
unityTextAlign = TextAnchor.MiddleCenter,
|
|
||||||
marginTop = 10,
|
|
||||||
marginBottom = 10
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
warningRow.Add(warningLabel);
|
warningRow.Add(warningLabel);
|
||||||
_encodingConfigContainer.Add(warningRow);
|
_videoEncodingConfigContainer.Add(warningRow);
|
||||||
|
|
||||||
// 7.5. 유효성 검사기 설정 (기존에 작성된 validator 재사용 및 이전 값 복원)
|
var encodingValidator = new IntegerInputValidator(_videoEncodingConfigContainer);
|
||||||
var encodingValidator = new IntegerInputValidator(_encodingConfigContainer);
|
|
||||||
encodingValidator
|
encodingValidator
|
||||||
.AddLimits("EncodingBitrate", new RangeIntegerLimit(100, 1000000))
|
.AddLimits("EncodingBitrate", new PositiveOnlyIntegerLimit(), new RangeIntegerLimit(1, 1000000))
|
||||||
.AddLimits("EncodingMaxBitrate", new RangeIntegerLimit(100, 1000000))
|
.AddLimits("EncodingMaxBitrate", new PositiveOnlyIntegerLimit(), new RangeIntegerLimit(1, 1000000))
|
||||||
.AddLimits("EncodingQP", new RangeIntegerLimit(0, 51));
|
.AddLimits("EncodingQP", new RangeIntegerLimit(0, 51));
|
||||||
encodingValidator.SetupValidation();
|
encodingValidator.SetupValidation();
|
||||||
|
|
||||||
var encodingDoubleValidator = new DoubleInputValidator(_encodingConfigContainer);
|
var doubleValidator = new DoubleInputValidator(_videoEncodingConfigContainer);
|
||||||
encodingDoubleValidator
|
doubleValidator.AddLimits("EncodingKeyframeInterval", new PositiveOnlyDoubleLimit(), new RangeDoubleLimit(0.1, 60.0));
|
||||||
.AddLimits("EncodingKeyframeInterval", new PositiveOnlyDoubleLimit(), new RangeDoubleLimit(0.01, 1000.0));
|
doubleValidator.SetupValidation();
|
||||||
encodingDoubleValidator.SetupValidation();
|
|
||||||
|
|
||||||
modeDropdown.RegisterValueChangedCallback(evt => UpdateVisibility(evt.newValue));
|
modeDropdown.RegisterValueChangedCallback(evt => UpdateVisibility(evt.newValue));
|
||||||
qpField.RegisterValueChangedCallback(evt =>
|
qpField.RegisterValueChangedCallback(evt =>
|
||||||
{
|
{
|
||||||
warningRow.style.display = (modeDropdown.value == "CQP" && evt.newValue == 0) ? DisplayStyle.Flex : DisplayStyle.None;
|
warningRow.style.display = modeDropdown.value == RateControlMode.CQP.ToString() && evt.newValue == 0
|
||||||
|
? DisplayStyle.Flex
|
||||||
|
: DisplayStyle.None;
|
||||||
});
|
});
|
||||||
|
|
||||||
UpdateVisibility(modeDropdown.value);
|
|
||||||
|
|
||||||
// 8. 제어 버튼 로우 (취소 / 저장)
|
// 8. 제어 버튼 로우 (취소 / 저장)
|
||||||
var buttonRow = CreateRow();
|
var buttonRow = CreateRow();
|
||||||
var cancelBtn = new Button { text = "취소" };
|
var cancelBtn = new Button { text = "취소" };
|
||||||
@@ -254,15 +248,17 @@ namespace ADOFAIRenderer.UI
|
|||||||
|
|
||||||
buttonRow.Add(cancelBtn);
|
buttonRow.Add(cancelBtn);
|
||||||
buttonRow.Add(saveBtn);
|
buttonRow.Add(saveBtn);
|
||||||
_encodingConfigContainer.Add(buttonRow);
|
_videoEncodingConfigContainer.Add(buttonRow);
|
||||||
|
|
||||||
cancelBtn.RegisterCallback<ClickEvent>(_ =>
|
cancelBtn.RegisterCallback<ClickEvent>(_ =>
|
||||||
{
|
{
|
||||||
ToggleScreens(false);
|
ToggleScreens(false);
|
||||||
_root.Remove(_encodingConfigContainer);
|
_root.Remove(_videoEncodingConfigContainer);
|
||||||
_encodingConfigContainer = null!;
|
_videoEncodingConfigContainer = null!;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
UpdateVisibility(modeDropdown.value);
|
||||||
|
|
||||||
saveBtn.RegisterCallback<ClickEvent>(_ =>
|
saveBtn.RegisterCallback<ClickEvent>(_ =>
|
||||||
{
|
{
|
||||||
if (System.Enum.TryParse<RateControlMode>(modeDropdown.value, out var mode))
|
if (System.Enum.TryParse<RateControlMode>(modeDropdown.value, out var mode))
|
||||||
@@ -284,14 +280,14 @@ namespace ADOFAIRenderer.UI
|
|||||||
tempConfig.MaxBitrate = tempConfig.Bitrate;
|
tempConfig.MaxBitrate = tempConfig.Bitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.EncodingConfig = tempConfig;
|
config.VideoConfig = tempConfig;
|
||||||
|
|
||||||
ToggleScreens(false);
|
ToggleScreens(false);
|
||||||
_root.Remove(_encodingConfigContainer);
|
_root.Remove(_videoEncodingConfigContainer);
|
||||||
_encodingConfigContainer = null!;
|
_videoEncodingConfigContainer = null!;
|
||||||
});
|
});
|
||||||
|
|
||||||
_root.Add(_encodingConfigContainer);
|
_root.Add(_videoEncodingConfigContainer);
|
||||||
|
|
||||||
// 화면 전환 활성화
|
// 화면 전환 활성화
|
||||||
ToggleScreens(true);
|
ToggleScreens(true);
|
||||||
@@ -304,7 +300,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
{
|
{
|
||||||
_root[0].style.display = showEncoding ? DisplayStyle.None : DisplayStyle.Flex;
|
_root[0].style.display = showEncoding ? DisplayStyle.None : DisplayStyle.Flex;
|
||||||
}
|
}
|
||||||
_encodingConfigContainer.style.display = showEncoding ? DisplayStyle.Flex : DisplayStyle.None;
|
_videoEncodingConfigContainer.style.display = showEncoding ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 가시성 필터링 업데이트 (행 단위로 보임/숨김 제어)
|
// 가시성 필터링 업데이트 (행 단위로 보임/숨김 제어)
|
||||||
@@ -316,7 +312,155 @@ namespace ADOFAIRenderer.UI
|
|||||||
qpRow.style.display = mode == RateControlMode.CQP ? DisplayStyle.Flex : DisplayStyle.None;
|
qpRow.style.display = mode == RateControlMode.CQP ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
warningRow.style.display = mode == RateControlMode.CQP && qpField.value == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
warningRow.style.display = mode == RateControlMode.CQP && qpField.value == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenAudioEncodingConfig()
|
||||||
|
{
|
||||||
|
var config = RenderManager.Instance.Configuration;
|
||||||
|
config.AudioConfig ??= GenerateAudioEncodingConfig();
|
||||||
|
|
||||||
|
var tempAudioConfig = new AudioEncodingConfig
|
||||||
|
{
|
||||||
|
RenderAudio = config.AudioConfig.RenderAudio,
|
||||||
|
AudioSampleRate = config.AudioConfig.AudioSampleRate,
|
||||||
|
AudioChannels = config.AudioConfig.AudioChannels,
|
||||||
|
AudioBitrate = config.AudioConfig.AudioBitrate
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_root.Contains(_audioEncodingConfigContainer))
|
||||||
|
{
|
||||||
|
_root.Remove(_audioEncodingConfigContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioEncodingConfigContainer = new VisualElement
|
||||||
|
{
|
||||||
|
name = "AudioEncodingSettingsContainer",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
position = Position.Absolute,
|
||||||
|
width = Length.Percent(100),
|
||||||
|
height = Length.Percent(100),
|
||||||
|
left = 0,
|
||||||
|
top = 0,
|
||||||
|
backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f)),
|
||||||
|
justifyContent = Justify.Center,
|
||||||
|
alignItems = Align.Center
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. 타이틀 로우
|
||||||
|
var titleRow = CreateRow();
|
||||||
|
var titleLabel = new Label("오디오 인코딩 설정")
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
fontSize = 24,
|
||||||
|
color = new StyleColor(Color.white)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
titleRow.Add(titleLabel);
|
||||||
|
_audioEncodingConfigContainer.Add(titleRow);
|
||||||
|
|
||||||
|
// 2. 오디오 렌더링 활성화 로우 (라벨 + 토글)
|
||||||
|
var renderAudioRow = CreateRow();
|
||||||
|
var renderAudioLabel = CreateFormLabel("오디오 렌더링 활성화");
|
||||||
|
var renderAudioToggle = new Toggle { name = "RenderAudioToggle", label = "", value = tempAudioConfig.RenderAudio };
|
||||||
|
renderAudioRow.Add(renderAudioLabel);
|
||||||
|
renderAudioRow.Add(renderAudioToggle);
|
||||||
|
_audioEncodingConfigContainer.Add(renderAudioRow);
|
||||||
|
|
||||||
|
// 3. 샘플 레이트 로우
|
||||||
|
var sampleRateRow = CreateRow();
|
||||||
|
var sampleRateLabel = CreateFormLabel("샘플 레이트(Hz)");
|
||||||
|
var sampleRateDropdown = new DropdownField(
|
||||||
|
new List<string> { "44100", "48000" },
|
||||||
|
tempAudioConfig.AudioSampleRate.ToString()
|
||||||
|
);
|
||||||
|
sampleRateRow.Add(sampleRateLabel);
|
||||||
|
sampleRateRow.Add(sampleRateDropdown);
|
||||||
|
_audioEncodingConfigContainer.Add(sampleRateRow);
|
||||||
|
|
||||||
|
// 4. 채널 로우
|
||||||
|
var channelsRow = CreateRow();
|
||||||
|
var channelsLabel = CreateFormLabel("오디오 채널");
|
||||||
|
var channelsDropdown = new DropdownField(
|
||||||
|
new List<string> { "1 (Mono)", "2 (Stereo)" },
|
||||||
|
tempAudioConfig.AudioChannels == 1 ? "1 (Mono)" : "2 (Stereo)"
|
||||||
|
);
|
||||||
|
channelsRow.Add(channelsLabel);
|
||||||
|
channelsRow.Add(channelsDropdown);
|
||||||
|
_audioEncodingConfigContainer.Add(channelsRow);
|
||||||
|
|
||||||
|
// 5. 비트레이트 로우 (kbps 단위 입력)
|
||||||
|
var bitrateRow = CreateRow();
|
||||||
|
var bitrateLabel = CreateFormLabel("오디오 비트레이트(kbps)");
|
||||||
|
var bitrateField = new IntegerField { name = "AudioBitrateKbps", label = "", value = tempAudioConfig.AudioBitrate / 1000 };
|
||||||
|
bitrateRow.Add(bitrateLabel);
|
||||||
|
bitrateRow.Add(bitrateField);
|
||||||
|
_audioEncodingConfigContainer.Add(bitrateRow);
|
||||||
|
|
||||||
|
var audioValidator = new IntegerInputValidator(_audioEncodingConfigContainer);
|
||||||
|
audioValidator.AddLimits("AudioBitrateKbps", new PositiveOnlyIntegerLimit(), new RangeIntegerLimit(32, 1024));
|
||||||
|
audioValidator.SetupValidation();
|
||||||
|
|
||||||
|
// 가시성 업데이트 (오디오 렌더링 비활성화 시 하위 설정 숨김)
|
||||||
|
renderAudioToggle.RegisterValueChangedCallback(evt => UpdateAudioVisibility(evt.newValue));
|
||||||
|
UpdateAudioVisibility(tempAudioConfig.RenderAudio);
|
||||||
|
|
||||||
|
// 6. 제어 버튼 로우 (취소 / 저장)
|
||||||
|
var buttonRow = CreateRow();
|
||||||
|
var cancelBtn = new Button { text = "취소" };
|
||||||
|
var saveBtn = new Button { text = "저장" };
|
||||||
|
|
||||||
|
buttonRow.Add(cancelBtn);
|
||||||
|
buttonRow.Add(saveBtn);
|
||||||
|
_audioEncodingConfigContainer.Add(buttonRow);
|
||||||
|
|
||||||
|
cancelBtn.RegisterCallback<ClickEvent>(_ =>
|
||||||
|
{
|
||||||
|
ToggleAudioScreens(false);
|
||||||
|
_root.Remove(_audioEncodingConfigContainer);
|
||||||
|
_audioEncodingConfigContainer = null!;
|
||||||
|
});
|
||||||
|
|
||||||
|
saveBtn.RegisterCallback<ClickEvent>(_ =>
|
||||||
|
{
|
||||||
|
tempAudioConfig.RenderAudio = renderAudioToggle.value;
|
||||||
|
|
||||||
|
if (int.TryParse(sampleRateDropdown.value, out var sampleRate))
|
||||||
|
{
|
||||||
|
tempAudioConfig.AudioSampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempAudioConfig.AudioChannels = channelsDropdown.value.StartsWith("1") ? 1 : 2;
|
||||||
|
tempAudioConfig.AudioBitrate = bitrateField.value * 1000;
|
||||||
|
|
||||||
|
config.AudioConfig = tempAudioConfig;
|
||||||
|
|
||||||
|
ToggleAudioScreens(false);
|
||||||
|
_root.Remove(_audioEncodingConfigContainer);
|
||||||
|
_audioEncodingConfigContainer = null!;
|
||||||
|
});
|
||||||
|
|
||||||
|
_root.Add(_audioEncodingConfigContainer);
|
||||||
|
ToggleAudioScreens(true);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void ToggleAudioScreens(bool showAudio)
|
||||||
|
{
|
||||||
|
if (_root.childCount > 0)
|
||||||
|
{
|
||||||
|
_root[0].style.display = showAudio ? DisplayStyle.None : DisplayStyle.Flex;
|
||||||
|
}
|
||||||
|
_audioEncodingConfigContainer.style.display = showAudio ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateAudioVisibility(bool isAudioEnabled)
|
||||||
|
{
|
||||||
|
sampleRateRow.style.display = isAudioEnabled ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
channelsRow.style.display = isAudioEnabled ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
bitrateRow.style.display = isAudioEnabled ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryGoRender()
|
private void TryGoRender()
|
||||||
@@ -353,7 +497,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
else
|
else
|
||||||
config.RandomSeed = null;
|
config.RandomSeed = null;
|
||||||
|
|
||||||
RenderManager.Instance.Configuration.EncodingConfig ??= GenerateEncodingConfig();
|
RenderManager.Instance.Configuration.VideoConfig ??= GenerateEncodingConfig();
|
||||||
|
|
||||||
RenderManager.Instance.SetupCaptureDirectory();
|
RenderManager.Instance.SetupCaptureDirectory();
|
||||||
RenderManager.Instance.SetupGameScene();
|
RenderManager.Instance.SetupGameScene();
|
||||||
@@ -776,7 +920,7 @@ namespace ADOFAIRenderer.UI
|
|||||||
currentConfig.RandomSeed = null;
|
currentConfig.RandomSeed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentConfig.EncodingConfig = RenderManager.Instance.Configuration.EncodingConfig ?? GenerateEncodingConfig();
|
currentConfig.VideoConfig = RenderManager.Instance.Configuration.VideoConfig ?? GenerateEncodingConfig();
|
||||||
|
|
||||||
PresetManager.SavePreset(presetName, currentConfig);
|
PresetManager.SavePreset(presetName, currentConfig);
|
||||||
nameField.value = string.Empty;
|
nameField.value = string.Empty;
|
||||||
@@ -856,9 +1000,9 @@ namespace ADOFAIRenderer.UI
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncodingConfig GenerateEncodingConfig()
|
private VideoEncodingConfig GenerateEncodingConfig()
|
||||||
{
|
{
|
||||||
return new EncodingConfig
|
return new VideoEncodingConfig
|
||||||
{
|
{
|
||||||
Codec = Codec.H264,
|
Codec = Codec.H264,
|
||||||
RateControlMode = RateControlMode.CBR,
|
RateControlMode = RateControlMode.CBR,
|
||||||
@@ -867,5 +1011,16 @@ namespace ADOFAIRenderer.UI
|
|||||||
ForceSoftwareEncoder = false
|
ForceSoftwareEncoder = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AudioEncodingConfig GenerateAudioEncodingConfig()
|
||||||
|
{
|
||||||
|
return new AudioEncodingConfig
|
||||||
|
{
|
||||||
|
RenderAudio = true,
|
||||||
|
AudioSampleRate = 44100,
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioBitrate = 256000
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user