Qt: add recording settings dialog

This commit is contained in:
Megamouse 2026-03-29 09:31:35 +02:00
parent aa7cf5ea15
commit bd2b2c2747
10 changed files with 845 additions and 9 deletions

View File

@ -393,7 +393,7 @@ namespace cfg
void set(const s64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}
@ -484,7 +484,7 @@ namespace cfg
void set(const f64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<float_type>(value);
}
@ -571,7 +571,7 @@ namespace cfg
void set(const u64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}

View File

@ -13,13 +13,13 @@ struct cfg_recording final : cfg::node
node_video(cfg::node* _this) : cfg::node(_this, "Video") {}
cfg::uint<0, 60> framerate{this, "Framerate", 30};
cfg::uint<0, 7680> width{this, "Width", 1280};
cfg::uint<0, 4320> height{this, "Height", 720};
cfg::uint<640, 7680> width{this, "Width", 1280};
cfg::uint<360, 4320> height{this, "Height", 720};
cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P
cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4
cfg::uint<0, 25000000> video_bps{this, "Video Bitrate", 4000000};
cfg::uint<0, 5> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<0, 20> gop_size{this, "Group of Pictures Size", 12};
cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000};
cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30};
} video{ this };
@ -28,7 +28,7 @@ struct cfg_recording final : cfg::node
node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {}
cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC
cfg::uint<0, 25000000> audio_bps{this, "Audio Bitrate", 320000};
cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000};
} audio{ this };

View File

@ -386,6 +386,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_pad_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recording_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_patch_creator_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -689,6 +692,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pad_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recording_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_patch_creator_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -868,6 +874,7 @@
<ClCompile Include="rpcs3qt\osk_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\pad_led_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\pad_motion_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\recording_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\patch_creator_dialog.cpp" />
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
@ -1157,6 +1164,7 @@
<ClInclude Include="QTGeneratedFiles\ui_pad_led_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_motion_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_recording_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_patch_creator_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_patch_manager_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_settings_dialog.h" />
@ -1624,6 +1632,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\pad_device_info.h" />
<ClInclude Include="rpcs3qt\permissions.h" />
<ClInclude Include="rpcs3qt\progress_indicator.h" />
@ -2196,6 +2214,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\shortcut_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>

View File

@ -291,6 +291,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pad_settings_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recording_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recording_settings_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_rsx_debugger.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -441,6 +447,9 @@
<ClCompile Include="rpcs3qt\pad_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\recording_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\log_frame.cpp">
<Filter>Gui\log</Filter>
</ClCompile>
@ -1304,6 +1313,9 @@
<ClInclude Include="QTGeneratedFiles\ui_pad_led_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_recording_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
@ -1555,6 +1567,9 @@
<CustomBuild Include="rpcs3qt\settings_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\welcome_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
@ -1597,6 +1612,9 @@
<CustomBuild Include="rpcs3qt\pad_led_settings_dialog.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\log_frame.h">
<Filter>Gui\log</Filter>
</CustomBuild>

View File

@ -83,6 +83,7 @@ add_library(rpcs3_ui STATIC
qt_video_source.cpp
raw_mouse_settings_dialog.cpp
register_editor_dialog.cpp
recording_settings_dialog.cpp
recvmessage_dialog_frame.cpp
render_creator.cpp
rpcn_settings_dialog.cpp
@ -135,6 +136,7 @@ add_library(rpcs3_ui STATIC
patch_creator_dialog.ui
patch_manager_dialog.ui
ps_move_tracker_dialog.ui
recording_settings_dialog.ui
settings_dialog.ui
shortcut_dialog.ui
welcome_dialog.ui

View File

@ -46,6 +46,7 @@
#include "welcome_dialog.h"
#include "music_player_dialog.h"
#include "sound_effect_manager_dialog.h"
#include "recording_settings_dialog.h"
#include <thread>
#include <unordered_set>
@ -3130,6 +3131,12 @@ void main_window::CreateConnects()
dlg->show();
});
connect(ui->actionRecording, &QAction::triggered, this, [this]
{
recording_settings_dialog* dlg = new recording_settings_dialog(this);
dlg->open();
});
connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this]
{
cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings);

View File

@ -290,6 +290,7 @@
<addaction name="separator"/>
<addaction name="confShortcutsAct"/>
<addaction name="actionManage_SoundEffects"/>
<addaction name="actionRecording"/>
<addaction name="separator"/>
<addaction name="confAutopauseManagerAct"/>
</widget>
@ -1536,6 +1537,11 @@
<string>Play Hover Music</string>
</property>
</action>
<action name="actionRecording">
<property name="text">
<string>Recording</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -0,0 +1,455 @@
#include "stdafx.h"
#include "recording_settings_dialog.h"
#include "ui_recording_settings_dialog.h"
#include <QPushButton>
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
LOG_CHANNEL(cfg_log, "CFG");
static std::vector<const AVCodec*> get_video_codecs(const AVOutputFormat* fmt)
{
std::vector<const AVCodec*> codecs;
void* opaque = nullptr;
while (const AVCodec* codec = av_codec_iterate(&opaque))
{
if (!codec->pix_fmts)
continue;
if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL)
continue;
if (codec->type != AVMediaType::AVMEDIA_TYPE_VIDEO)
continue;
switch (codec->id)
{
case AV_CODEC_ID_H264:
case AV_CODEC_ID_HEVC:
case AV_CODEC_ID_MPEG4:
case AV_CODEC_ID_AV1:
break;
default:
continue;
}
if (!av_codec_is_encoder(codec))
continue;
if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1)
continue;
codecs.push_back(codec);
}
return codecs;
}
static std::vector<const AVCodec*> get_audio_codecs(const AVOutputFormat* fmt)
{
std::vector<const AVCodec*> codecs;
void* opaque = nullptr;
while (const AVCodec* codec = av_codec_iterate(&opaque))
{
if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL)
continue;
if (codec->type != AVMediaType::AVMEDIA_TYPE_AUDIO)
continue;
if (!av_codec_is_encoder(codec))
continue;
if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1)
continue;
codecs.push_back(codec);
}
return codecs;
}
recording_settings_dialog::recording_settings_dialog(QWidget* parent)
: QDialog(parent), ui(new Ui::recording_settings_dialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
if (!g_cfg_recording.load())
{
cfg_log.notice("Could not load recording config. Using defaults.");
}
ui->combo_presets->addItem(tr("720p 30fps"), static_cast<int>(quality_preset::_720p_30));
ui->combo_presets->addItem(tr("720p 60fps"), static_cast<int>(quality_preset::_720p_60));
ui->combo_presets->addItem(tr("1080p 30fps"), static_cast<int>(quality_preset::_1080p_30));
ui->combo_presets->addItem(tr("1080p 60fps"), static_cast<int>(quality_preset::_1080p_60));
ui->combo_presets->addItem(tr("1440p 30fps"), static_cast<int>(quality_preset::_1440p_30));
ui->combo_presets->addItem(tr("1440p 60fps"), static_cast<int>(quality_preset::_1440p_60));
ui->combo_presets->addItem(tr("2160p 30fps"), static_cast<int>(quality_preset::_2160p_30));
ui->combo_presets->addItem(tr("2160p 60fps"), static_cast<int>(quality_preset::_2160p_60));
ui->combo_presets->addItem(tr("Custom"), static_cast<int>(quality_preset::custom));
connect(ui->combo_presets, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_presets->itemData(index);
if (var.canConvert<int>())
{
const quality_preset preset = static_cast<quality_preset>(var.toInt());
select_preset(preset, g_cfg_recording);
update_ui();
}
});
ui->combo_resolution->addItem("360p", QVariant::fromValue(QPair<int, int>(640, 360)));
ui->combo_resolution->addItem("480p", QVariant::fromValue(QPair<int, int>(854, 480)));
ui->combo_resolution->addItem("720p", QVariant::fromValue(QPair<int, int>(1280, 720)));
ui->combo_resolution->addItem("1080p", QVariant::fromValue(QPair<int, int>(1920, 1080)));
ui->combo_resolution->addItem("1440p", QVariant::fromValue(QPair<int, int>(2560, 1440)));
ui->combo_resolution->addItem("2160p", QVariant::fromValue(QPair<int, int>(3840, 2160)));
connect(ui->combo_resolution, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_resolution->itemData(index);
if (var.canConvert<QPair<int, int>>())
{
const QPair<int, int> size = var.value<QPair<int, int>>();
g_cfg_recording.video.width.set(size.first);
g_cfg_recording.video.height.set(size.second);
update_preset();
}
});
const AVOutputFormat* fmt = av_guess_format("mp4", nullptr, nullptr);
m_video_codecs = get_video_codecs(fmt);
m_audio_codecs = get_audio_codecs(fmt);
for (const AVCodec* codec : m_video_codecs)
{
if (!codec) continue;
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
ui->combo_video_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
}
for (const AVCodec* codec : m_audio_codecs)
{
if (!codec) continue;
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
ui->combo_audio_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
}
connect(ui->combo_video_codec, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_video_codec->itemData(index);
if (var.canConvert<int>())
{
const int codec_id = var.toInt();
g_cfg_recording.video.video_codec.set(codec_id);
update_preset();
}
});
connect(ui->combo_audio_codec, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_audio_codec->itemData(index);
if (var.canConvert<int>())
{
const int codec_id = var.toInt();
g_cfg_recording.audio.audio_codec.set(codec_id);
update_preset();
}
});
ui->combo_framerate->addItem("30", 30);
ui->combo_framerate->addItem("60", 60);
connect(ui->combo_framerate, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_framerate->itemData(index);
if (var.canConvert<int>())
{
const int fps = var.toInt();
g_cfg_recording.video.framerate.set(fps);
update_preset();
}
});
ui->spinbox_video_bitrate->setSingleStep(1);
ui->spinbox_video_bitrate->setMinimum(g_cfg_recording.video.video_bps.min);
ui->spinbox_video_bitrate->setMaximum(g_cfg_recording.video.video_bps.max);
connect(ui->spinbox_video_bitrate, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.video_bps.set(value);
update_preset();
});
ui->spinbox_audio_bitrate->setSingleStep(1);
ui->spinbox_audio_bitrate->setMinimum(g_cfg_recording.audio.audio_bps.min);
ui->spinbox_audio_bitrate->setMaximum(g_cfg_recording.audio.audio_bps.max);
connect(ui->spinbox_audio_bitrate, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.audio.audio_bps.set(value);
update_preset();
});
ui->spinbox_gop_size->setSingleStep(1);
ui->spinbox_gop_size->setMinimum(g_cfg_recording.video.gop_size.min);
ui->spinbox_gop_size->setMaximum(g_cfg_recording.video.gop_size.max);
connect(ui->spinbox_gop_size, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.gop_size.set(value);
update_preset();
});
ui->spinbox_max_b_frames->setSingleStep(1);
ui->spinbox_max_b_frames->setMinimum(g_cfg_recording.video.max_b_frames.min);
ui->spinbox_max_b_frames->setMaximum(g_cfg_recording.video.max_b_frames.max);
connect(ui->spinbox_max_b_frames, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.max_b_frames.set(value);
update_preset();
});
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Save))
{
g_cfg_recording.save();
accept();
}
else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel))
{
reject();
}
else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults))
{
g_cfg_recording.from_default();
update_ui();
update_preset();
}
});
connect(this, &QDialog::rejected, this, []()
{
if (!g_cfg_recording.load())
{
cfg_log.notice("Could not load recording config. Using defaults.");
}
});
update_ui();
update_preset();
}
recording_settings_dialog::~recording_settings_dialog()
{
}
void recording_settings_dialog::update_preset()
{
const quality_preset preset = current_preset();
ui->combo_presets->setCurrentIndex(ui->combo_presets->findData(static_cast<int>(preset)));
}
void recording_settings_dialog::update_ui()
{
ui->combo_resolution->blockSignals(true);
ui->combo_framerate->blockSignals(true);
ui->combo_video_codec->blockSignals(true);
ui->combo_audio_codec->blockSignals(true);
ui->spinbox_video_bitrate->blockSignals(true);
ui->spinbox_audio_bitrate->blockSignals(true);
ui->spinbox_gop_size->blockSignals(true);
ui->spinbox_max_b_frames->blockSignals(true);
ui->combo_resolution->setCurrentIndex(ui->combo_resolution->findData(QVariant::fromValue(QPair<int, int>(g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get()))));
ui->combo_framerate->setCurrentIndex(ui->combo_framerate->findData(static_cast<int>(g_cfg_recording.video.framerate.get())));
ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast<int>(g_cfg_recording.video.video_codec.get())));
ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast<int>(g_cfg_recording.audio.audio_codec.get())));
ui->spinbox_video_bitrate->setValue(g_cfg_recording.video.video_bps);
ui->spinbox_audio_bitrate->setValue(g_cfg_recording.audio.audio_bps);
ui->spinbox_gop_size->setValue(g_cfg_recording.video.gop_size);
ui->spinbox_max_b_frames->setValue(g_cfg_recording.video.max_b_frames);
ui->combo_resolution->blockSignals(false);
ui->combo_framerate->blockSignals(false);
ui->combo_video_codec->blockSignals(false);
ui->combo_audio_codec->blockSignals(false);
ui->spinbox_video_bitrate->blockSignals(false);
ui->spinbox_audio_bitrate->blockSignals(false);
ui->spinbox_gop_size->blockSignals(false);
ui->spinbox_max_b_frames->blockSignals(false);
const auto get_codec_name = [](const std::vector<const AVCodec*>& codecs, u32 id)
{
for (const AVCodec* codec : codecs)
{
if (codec && codec->id == static_cast<AVCodecID>(id))
{
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
return name;
}
}
return std::string();
};
ui->label_info_keys->setText(
tr("Resolution:") + "\n" +
tr("Framerate:") + "\n" +
tr("Video Codec:") + "\n" +
tr("Video Bitrate:") + "\n" +
tr("Audio Codec:") + "\n" +
tr("Audio Bitrate:") + "\n" +
tr("Gop-Size:") + "\n" +
tr("Max B-Frames:")
);
ui->label_info_values->setText(QString::fromStdString(
fmt::format("%d x %d\n%d fps\n%s\n%d\n%s\n%d\n%d\n%d",
g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get(),
g_cfg_recording.video.framerate.get(),
get_codec_name(m_video_codecs, g_cfg_recording.video.video_codec.get()),
g_cfg_recording.video.video_bps.get(),
get_codec_name(m_audio_codecs, g_cfg_recording.audio.audio_codec.get()),
g_cfg_recording.audio.audio_bps.get(),
g_cfg_recording.video.gop_size.get(),
g_cfg_recording.video.max_b_frames.get()
)
));
}
void recording_settings_dialog::select_preset(quality_preset preset, cfg_recording& cfg)
{
if (preset == quality_preset::custom)
{
return;
}
cfg.audio.audio_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_AAC));
cfg.audio.audio_bps.set(192'000); // 192 kbps
cfg.video.video_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_MPEG4));
cfg.video.pixel_format.set(static_cast<u32>(::AV_PIX_FMT_YUV420P));
switch (preset)
{
case quality_preset::_720p_30:
case quality_preset::_720p_60:
cfg.video.width.set(1280);
cfg.video.height.set(720);
break;
case quality_preset::_1080p_30:
case quality_preset::_1080p_60:
cfg.video.width.set(1920);
cfg.video.height.set(1080);
break;
case quality_preset::_1440p_30:
case quality_preset::_1440p_60:
cfg.video.width.set(2560);
cfg.video.height.set(1440);
break;
case quality_preset::_2160p_30:
case quality_preset::_2160p_60:
cfg.video.width.set(3840);
cfg.video.height.set(2160);
break;
case quality_preset::custom:
break;
}
switch (preset)
{
case quality_preset::_720p_30:
case quality_preset::_1080p_30:
case quality_preset::_1440p_30:
case quality_preset::_2160p_30:
cfg.video.framerate.set(30);
break;
case quality_preset::_720p_60:
case quality_preset::_1080p_60:
case quality_preset::_1440p_60:
case quality_preset::_2160p_60:
cfg.video.framerate.set(60);
break;
case quality_preset::custom:
break;
}
switch (preset)
{
case quality_preset::_720p_30:
cfg.video.video_bps.set(4'000'000);
break;
case quality_preset::_720p_60:
cfg.video.video_bps.set(6'000'000);
break;
case quality_preset::_1080p_30:
cfg.video.video_bps.set(8'000'000);
break;
case quality_preset::_1080p_60:
cfg.video.video_bps.set(12'000'000);
break;
case quality_preset::_1440p_30:
cfg.video.video_bps.set(16'000'000);
break;
case quality_preset::_1440p_60:
cfg.video.video_bps.set(24'000'000);
break;
case quality_preset::_2160p_30:
cfg.video.video_bps.set(40'000'000);
break;
case quality_preset::_2160p_60:
cfg.video.video_bps.set(60'000'000);
break;
case quality_preset::custom:
break;
}
cfg.video.gop_size.set(cfg.video.framerate.get());
cfg.video.max_b_frames.set(2);
}
recording_settings_dialog::quality_preset recording_settings_dialog::current_preset()
{
for (u32 i = 0; i < static_cast<u32>(quality_preset::custom); i++)
{
const quality_preset preset = static_cast<quality_preset>(i);
cfg_recording cfg;
select_preset(preset, cfg);
if (g_cfg_recording.video.framerate.get() == cfg.video.framerate.get() &&
g_cfg_recording.video.width.get() == cfg.video.width.get() &&
g_cfg_recording.video.height.get() == cfg.video.height.get() &&
g_cfg_recording.video.pixel_format.get() == cfg.video.pixel_format.get() &&
g_cfg_recording.video.video_codec.get() == cfg.video.video_codec.get() &&
g_cfg_recording.video.video_bps.get() == cfg.video.video_bps.get() &&
g_cfg_recording.video.max_b_frames.get() == cfg.video.max_b_frames.get() &&
g_cfg_recording.video.gop_size.get() == cfg.video.gop_size.get() &&
g_cfg_recording.audio.audio_codec.get() == cfg.audio.audio_codec.get() &&
g_cfg_recording.audio.audio_bps.get() == cfg.audio.audio_bps.get())
{
return preset;
}
}
return quality_preset::custom;
}

View File

@ -0,0 +1,47 @@
#pragma once
#include "util/types.hpp"
#include "Emu/Io/recording_config.h"
#include <QDialog>
namespace Ui
{
class recording_settings_dialog;
}
struct AVCodec;
class recording_settings_dialog : public QDialog
{
Q_OBJECT
public:
recording_settings_dialog(QWidget* parent = nullptr);
virtual ~recording_settings_dialog();
private:
enum class quality_preset
{
_720p_30,
_720p_60,
_1080p_30,
_1080p_60,
_1440p_30,
_1440p_60,
_2160p_30,
_2160p_60,
custom
};
void update_preset();
void update_ui();
static void select_preset(quality_preset preset, cfg_recording& cfg);
static quality_preset current_preset();
Ui::recording_settings_dialog* ui;
std::vector<const AVCodec*> m_video_codecs;
std::vector<const AVCodec*> m_audio_codecs;
};

View File

@ -0,0 +1,273 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>recording_settings_dialog</class>
<widget class="QDialog" name="recording_settings_dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>734</height>
</rect>
</property>
<property name="windowTitle">
<string>Recording Settings</string>
</property>
<layout class="QVBoxLayout" name="main_layout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="presets_tab">
<attribute name="title">
<string>Presets</string>
</attribute>
<layout class="QVBoxLayout" name="layout_presets_tab">
<item>
<widget class="QGroupBox" name="gb_presets">
<property name="title">
<string>Preset</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_presets">
<item>
<widget class="QComboBox" name="combo_presets"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_info">
<property name="title">
<string>Info</string>
</property>
<layout class="QHBoxLayout" name="layout_gb_info">
<item>
<widget class="QLabel" name="label_info_keys">
<property name="text">
<string>Keys</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_info_values">
<property name="text">
<string>Values</string>
</property>
</widget>
</item>
<item>
<spacer name="info_spacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer_presets_tab">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="advanced_tab">
<attribute name="title">
<string>Advanced</string>
</attribute>
<layout class="QVBoxLayout" name="layout_advanced_tab">
<item>
<widget class="QGroupBox" name="gb_advanced_video">
<property name="title">
<string>Video</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_advanced_video">
<item>
<widget class="QGroupBox" name="gb_video_codec">
<property name="title">
<string>Codec</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_video_codec">
<item>
<widget class="QComboBox" name="combo_video_codec"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_resolution">
<property name="title">
<string>Resolution</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_resolution">
<item>
<widget class="QComboBox" name="combo_resolution"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_framerate">
<property name="title">
<string>Framerate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_framerate">
<item>
<widget class="QComboBox" name="combo_framerate"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_video_bitrate">
<property name="title">
<string>Bitrate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_video_bitrate">
<item>
<widget class="QSpinBox" name="spinbox_video_bitrate"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_gop_size">
<property name="title">
<string>Group of Pictures Size</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_gop_size">
<item>
<widget class="QSpinBox" name="spinbox_gop_size"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_max_b_frames">
<property name="title">
<string>Max. B-Frames</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_max_b_frames">
<item>
<widget class="QSpinBox" name="spinbox_max_b_frames"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_advanced_audio">
<property name="title">
<string>Audio</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_advanced_audio">
<item>
<widget class="QGroupBox" name="gb_audio_codec">
<property name="title">
<string>Codec</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_audio_codec">
<item>
<widget class="QComboBox" name="combo_audio_codec"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_audio_bitrate">
<property name="title">
<string>Bitrate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_audio_bitrate">
<item>
<widget class="QSpinBox" name="spinbox_audio_bitrate"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer_advanced_tab">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>recording_settings_dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>recording_settings_dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>