diff --git a/obs/CMakeLists.txt b/obs/CMakeLists.txt
index e48c36d02cfc68f3d52e260e40c6861c666a283d..189d5cb44aeb0971a0df97884272ef4ab77ba5c8 100644
--- a/obs/CMakeLists.txt
+++ b/obs/CMakeLists.txt
@@ -189,6 +189,7 @@ endif()
target_link_libraries(obs
libobs
+ libff
Qt5::Widgets
Qt5::Network
${obs_PLATFORM_LIBRARIES})
diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini
index 6745a30d767311b4ae5f39fd9241366a33564c66..48e6bc44cefd56f0d9cad1219ffe967178b7c9b3 100644
--- a/obs/data/locale/en-US.ini
+++ b/obs/data/locale/en-US.ini
@@ -276,9 +276,17 @@ Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Use stream encoder)"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Common recording formats"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="All Files"
Basic.Settings.Output.Adv.FFmpeg.SavePathURL="File path or URL"
-Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Encoder (blank=default)"
+Basic.Settings.Output.Adv.FFmpeg.Format="Container Format"
+Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Audio"
+Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video"
+Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Default Format"
+Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Container Format Description"
+Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Audio/Video Codec guessed from File path or URL"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Default Encoder"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Disable Encoder"
+Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Encoder"
Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video Encoder Settings (if any)"
-Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder (blank=default)"
+Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder"
Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audio Encoder Settings (if any)"
# basic mode 'video' settings
diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui
index 65813850ab83993075370ff9600ba353bfd73243..f516e9ae99d5859899b4bef22662ee26b31c3779 100644
--- a/obs/forms/OBSBasicSettings.ui
+++ b/obs/forms/OBSBasicSettings.ui
@@ -1241,13 +1241,20 @@
-
+
+
+ Basic.Settings.Output.Adv.FFmpeg.Format
+
+
+
+ -
Basic.Settings.Output.VideoBitrate
- -
+
-
0
@@ -1260,34 +1267,60 @@
- -
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::RightToLeft
+
+
+ Basic.Settings.Output.Adv.Rescale
+
+
+
+ -
+
+
+ false
+
+
+ true
+
+
+
+ -
Basic.Settings.Output.Adv.FFmpeg.VEncoder
- -
-
+
-
+
- -
+
-
Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings
- -
+
-
- -
+
-
Basic.Settings.Output.AudioBitrate
- -
+
-
32
@@ -1303,60 +1336,14 @@
- -
+
-
Basic.Settings.Output.Adv.AudioTrack
- -
-
-
- Basic.Settings.Output.Adv.FFmpeg.AEncoder
-
-
-
- -
-
-
- -
-
-
- Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Qt::RightToLeft
-
-
- Basic.Settings.Output.Adv.Rescale
-
-
-
- -
-
-
- false
-
-
- true
-
-
-
- -
@@ -1411,6 +1398,43 @@
+ -
+
+
+ Basic.Settings.Output.Adv.FFmpeg.AEncoder
+
+
+
+ -
+
+
+ Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Basic.Settings.Output.Adv.FFmpeg.FormatDesc
+
+
+
+ -
+
+
diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp
index 8c0232e91c5aa6caf81ce535d746caee3f84398a..ad2979f06da907486f4624845659ee61826b6ac6 100644
--- a/obs/window-basic-main-outputs.cpp
+++ b/obs/window-basic-main-outputs.cpp
@@ -495,8 +495,14 @@ inline void AdvancedOutput::SetupFFmpeg()
"FFRescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"FFRescaleRes");
+ const char *formatName = config_get_string(main->Config(), "AdvOut",
+ "FFFormat");
+ const char *mimeType = config_get_string(main->Config(), "AdvOut",
+ "FFFormatMimeType");
const char *vEncoder = config_get_string(main->Config(), "AdvOut",
"FFVEncoder");
+ int vEncoderId = config_get_int(main->Config(), "AdvOut",
+ "FFVEncoderId");
const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
"FFVCustom");
int aBitrate = config_get_int(main->Config(), "AdvOut",
@@ -505,16 +511,22 @@ inline void AdvancedOutput::SetupFFmpeg()
"FFAudioTrack");
const char *aEncoder = config_get_string(main->Config(), "AdvOut",
"FFAEncoder");
+ int aEncoderId = config_get_int(main->Config(), "AdvOut",
+ "FFAEncoderId");
const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
"FFACustom");
obs_data_t *settings = obs_data_create();
obs_data_set_string(settings, "url", url);
+ obs_data_set_string(settings, "format_name", formatName);
+ obs_data_set_string(settings, "format_mime_type", mimeType);
obs_data_set_int(settings, "video_bitrate", vBitrate);
obs_data_set_string(settings, "video_encoder", vEncoder);
+ obs_data_set_int(settings, "video_encoder_id", vEncoderId);
obs_data_set_string(settings, "video_settings", vEncCustom);
obs_data_set_int(settings, "audio_bitrate", aBitrate);
obs_data_set_string(settings, "audio_encoder", aEncoder);
+ obs_data_set_int(settings, "audio_encoder_id", aEncoderId);
obs_data_set_string(settings, "audio_settings", aEncCustom);
if (rescale && rescaleRes && *rescaleRes) {
diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp
index b9abc6dac61353117fa60791c4a29288174f3003..05f7138d766315fcc86fcbb2788374b33262d8c8 100644
--- a/obs/window-basic-settings.cpp
+++ b/obs/window-basic-settings.cpp
@@ -25,6 +25,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "obs-app.hpp"
#include "platform.hpp"
@@ -37,6 +40,51 @@
using namespace std;
+// Used for QVariant in codec comboboxes
+namespace {
+struct FormatDesc {
+ const char *name = nullptr;
+ const char *mimeType = nullptr;
+ const ff_format_desc *desc = nullptr;
+
+ inline FormatDesc() = default;
+ inline FormatDesc(const char *name, const char *mimeType,
+ const ff_format_desc *desc = nullptr)
+ : name(name), mimeType(mimeType), desc(desc) {}
+
+ bool operator==(const FormatDesc &f) const
+ {
+ if ((name == nullptr) ^ (f.name == nullptr))
+ return false;
+ if (name != nullptr && strcmp(name, f.name) != 0)
+ return false;
+ if ((mimeType == nullptr) ^ (f.mimeType == nullptr))
+ return false;
+ if (mimeType != nullptr && strcmp(mimeType, f.mimeType) != 0)
+ return false;
+ return true;
+ }
+};
+struct CodecDesc {
+ const char *name = nullptr;
+ int id = 0;
+
+ inline CodecDesc() = default;
+ inline CodecDesc(const char *name, int id) : name(name), id(id) {}
+
+ bool operator==(const CodecDesc &codecDesc) const
+ {
+ if ((name == nullptr) ^ (codecDesc.name == nullptr))
+ return false;
+ if (id != codecDesc.id)
+ return false;
+ return name == nullptr || strcmp(name, codecDesc.name) == 0;
+ }
+};
+}
+Q_DECLARE_METATYPE(FormatDesc)
+Q_DECLARE_METATYPE(CodecDesc)
+
/* parses "[width]x[height]", string, i.e. 1024x768 */
static bool ConvertResText(const char *res, uint32_t &cx, uint32_t &cy)
{
@@ -102,6 +150,40 @@ static inline QString GetComboData(QComboBox *combo)
return combo->itemData(idx).toString();
}
+static int FindEncoder(QComboBox *combo, const char *name, int id)
+{
+ CodecDesc codecDesc(name, id);
+ for(int i = 0; i < combo->count(); i++) {
+ QVariant v = combo->itemData(i);
+ if (!v.isNull()) {
+ if (codecDesc == v.value()) {
+ return i;
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+static CodecDesc GetDefaultCodecDesc(const ff_format_desc *formatDesc,
+ ff_codec_type codecType)
+{
+ int id = 0;
+ switch (codecType) {
+ case FF_CODEC_AUDIO:
+ id = ff_format_desc_audio(formatDesc);
+ break;
+ case FF_CODEC_VIDEO:
+ id = ff_format_desc_video(formatDesc);
+ break;
+ default:
+ return CodecDesc();
+ }
+
+ return CodecDesc(ff_format_desc_get_default_name(formatDesc, codecType),
+ id);
+}
+
void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
const char *slot)
{
@@ -173,17 +255,18 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->advOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFURL, EDIT_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutFFFormat, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
- HookWidget(ui->advOutFFVEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutFFVEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFVCfg, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFABitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
- HookWidget(ui->advOutFFAEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutFFAEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFACfg, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack1Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack1Name, EDIT_CHANGED, OUTPUTS_CHANGED);
@@ -219,9 +302,13 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
//Apply button disabled until change.
EnableApplyButton(false);
+ // Initialize libff library
+ ff_init();
+
LoadServiceTypes();
LoadEncoderTypes();
LoadColorRanges();
+ LoadFormats();
LoadSettings(false);
}
@@ -320,6 +407,122 @@ void OBSBasicSettings::LoadColorRanges()
ui->colorRange->addItem(CS_FULL_STR, "Full");
}
+#define AV_FORMAT_DEFAULT_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.FormatDefault")
+#define AUDIO_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.FormatAudio")
+#define VIDEO_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.FormatVideo")
+
+void OBSBasicSettings::LoadFormats()
+{
+ formats.reset(ff_format_supported());
+ const ff_format_desc *format = formats.get();
+
+ while(format != nullptr) {
+ bool audio = ff_format_desc_has_audio(format);
+ bool video = ff_format_desc_has_video(format);
+ FormatDesc formatDesc(ff_format_desc_name(format),
+ ff_format_desc_mime_type(format),
+ format);
+ if (audio || video) {
+ QString itemText(ff_format_desc_name(format));
+ if (audio ^ video)
+ itemText += QString(" (%1)").arg(
+ audio ? AUDIO_STR : VIDEO_STR);
+
+ ui->advOutFFFormat->addItem(itemText,
+ qVariantFromValue(formatDesc));
+ }
+
+ format = ff_format_desc_next(format);
+ }
+
+ ui->advOutFFFormat->model()->sort(0);
+
+ ui->advOutFFFormat->insertItem(0, AV_FORMAT_DEFAULT_STR);
+}
+
+static void AddCodec(QComboBox *combo, const ff_codec_desc *codec_desc)
+{
+ QString itemText(ff_codec_desc_name(codec_desc));
+ if (ff_codec_desc_is_alias(codec_desc))
+ itemText += QString(" (%1)").arg(
+ ff_codec_desc_base_name(codec_desc));
+
+ CodecDesc cd(ff_codec_desc_name(codec_desc),
+ ff_codec_desc_id(codec_desc));
+
+ combo->addItem(itemText, qVariantFromValue(cd));
+}
+
+#define AV_ENCODER_DEFAULT_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault")
+
+static void AddDefaultCodec(QComboBox *combo, const ff_format_desc *formatDesc,
+ ff_codec_type codecType)
+{
+ CodecDesc cd = GetDefaultCodecDesc(formatDesc, codecType);
+
+ int existingIdx = FindEncoder(combo, cd.name, cd.id);
+ if (existingIdx >= 0)
+ combo->removeItem(existingIdx);
+
+ combo->addItem(QString("%1 (%2)").arg(cd.name, AV_ENCODER_DEFAULT_STR),
+ qVariantFromValue(cd));
+}
+
+#define AV_ENCODER_DISABLE_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable")
+
+void OBSBasicSettings::ReloadCodecs(const ff_format_desc *formatDesc)
+{
+ ui->advOutFFAEncoder->blockSignals(true);
+ ui->advOutFFVEncoder->blockSignals(true);
+ ui->advOutFFAEncoder->clear();
+ ui->advOutFFVEncoder->clear();
+
+ if (formatDesc == nullptr)
+ return;
+
+ OBSFFCodecDesc codecDescs(ff_codec_supported(formatDesc));
+
+ const ff_codec_desc *codec = codecDescs.get();
+
+ while(codec != nullptr) {
+ switch (ff_codec_desc_type(codec)) {
+ case FF_CODEC_AUDIO:
+ AddCodec(ui->advOutFFAEncoder, codec);
+ break;
+ case FF_CODEC_VIDEO:
+ AddCodec(ui->advOutFFVEncoder, codec);
+ break;
+ default:
+ break;
+ }
+
+ codec = ff_codec_desc_next(codec);
+ }
+
+ if (ff_format_desc_has_audio(formatDesc))
+ AddDefaultCodec(ui->advOutFFAEncoder, formatDesc,
+ FF_CODEC_AUDIO);
+ if (ff_format_desc_has_video(formatDesc))
+ AddDefaultCodec(ui->advOutFFVEncoder, formatDesc,
+ FF_CODEC_VIDEO);
+
+ ui->advOutFFAEncoder->model()->sort(0);
+ ui->advOutFFVEncoder->model()->sort(0);
+
+ QVariant disable = qVariantFromValue(CodecDesc());
+
+ ui->advOutFFAEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
+ ui->advOutFFVEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);
+
+ ui->advOutFFAEncoder->blockSignals(false);
+ ui->advOutFFVEncoder->blockSignals(false);
+}
+
void OBSBasicSettings::LoadLanguageList()
{
const char *currentLang = App()->GetLocale();
@@ -783,9 +986,38 @@ void OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties()
SetComboByValue(ui->advOutRecEncoder, encoder);
}
+static void SelectFormat(QComboBox *combo, const char *name,
+ const char *mimeType)
+{
+ FormatDesc formatDesc(name, mimeType);
+
+ for(int i = 0; i < combo->count(); i++) {
+ QVariant v = combo->itemData(i);
+ if (!v.isNull()) {
+ if (formatDesc == v.value()) {
+ combo->setCurrentIndex(i);
+ return;
+ }
+ }
+ }
+
+ combo->setCurrentIndex(0);
+}
+
+static void SelectEncoder(QComboBox *combo, const char *name, int id)
+{
+ int idx = FindEncoder(combo, name, id);
+ if (idx >= 0)
+ combo->setCurrentIndex(idx);
+}
+
void OBSBasicSettings::LoadAdvOutputFFmpegSettings()
{
const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
+ const char *format = config_get_string(main->Config(), "AdvOut",
+ "FFFormat");
+ const char *mimeType = config_get_string(main->Config(), "AdvOut",
+ "FFFormatMimeType");
int videoBitrate = config_get_int(main->Config(), "AdvOut",
"FFVBitrate");
bool rescale = config_get_bool(main->Config(), "AdvOut",
@@ -794,6 +1026,8 @@ void OBSBasicSettings::LoadAdvOutputFFmpegSettings()
"FFRescaleRes");
const char *vEncoder = config_get_string(main->Config(), "AdvOut",
"FFVEncoder");
+ int vEncoderId = config_get_int(main->Config(), "AdvOut",
+ "FFVEncoderId");
const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
"FFVCustom");
int audioBitrate = config_get_int(main->Config(), "AdvOut",
@@ -802,18 +1036,21 @@ void OBSBasicSettings::LoadAdvOutputFFmpegSettings()
"FFAudioTrack");
const char *aEncoder = config_get_string(main->Config(), "AdvOut",
"FFAEncoder");
+ int aEncoderId = config_get_int(main->Config(), "AdvOut",
+ "FFAEncoderId");
const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
"FFACustom");
ui->advOutFFURL->setText(url);
+ SelectFormat(ui->advOutFFFormat, format, mimeType);
ui->advOutFFVBitrate->setValue(videoBitrate);
ui->advOutFFUseRescale->setChecked(rescale);
ui->advOutFFRescale->setEnabled(rescale);
ui->advOutFFRescale->setCurrentText(rescaleRes);
- ui->advOutFFVEncoder->setText(vEncoder);
+ SelectEncoder(ui->advOutFFVEncoder, vEncoder, vEncoderId);
ui->advOutFFVCfg->setText(vEncCustom);
ui->advOutFFABitrate->setValue(audioBitrate);
- ui->advOutFFAEncoder->setText(aEncoder);
+ SelectEncoder(ui->advOutFFAEncoder, aEncoder, aEncoderId);
ui->advOutFFACfg->setText(aEncCustom);
switch (audioTrack) {
@@ -887,6 +1124,34 @@ void OBSBasicSettings::LoadOutputSettings()
loading = false;
}
+void OBSBasicSettings::SetAdvOutputFFmpegEnablement(
+ ff_codec_type encoderType, bool enabled,
+ bool enableEncoder)
+{
+ bool rescale = config_get_bool(main->Config(), "AdvOut",
+ "FFRescale");
+
+ switch (encoderType) {
+ case FF_CODEC_VIDEO:
+ ui->advOutFFVBitrate->setEnabled(enabled);
+ ui->advOutFFUseRescale->setEnabled(enabled);
+ ui->advOutFFRescale->setEnabled(enabled && rescale);
+ ui->advOutFFVEncoder->setEnabled(enabled || enableEncoder);
+ ui->advOutFFVCfg->setEnabled(enabled);
+ break;
+ case FF_CODEC_AUDIO:
+ ui->advOutFFABitrate->setEnabled(enabled);
+ ui->advOutFFAEncoder->setEnabled(enabled || enableEncoder);
+ ui->advOutFFACfg->setEnabled(enabled);
+ ui->advOutFFTrack1->setEnabled(enabled);
+ ui->advOutFFTrack2->setEnabled(enabled);
+ ui->advOutFFTrack3->setEnabled(enabled);
+ ui->advOutFFTrack4->setEnabled(enabled);
+ default:
+ break;
+ }
+}
+
static inline void LoadListValue(QComboBox *widget, const char *text,
const char *val)
{
@@ -1147,6 +1412,38 @@ static void SaveTrackIndex(config_t *config, const char *section,
else if (check4->isChecked()) config_set_int(config, section, name, 4);
}
+void OBSBasicSettings::SaveFormat(QComboBox *combo)
+{
+ QVariant v = combo->currentData();
+ if (!v.isNull()) {
+ FormatDesc desc = v.value();
+ config_set_string(main->Config(), "AdvOut", "FFFormat",
+ desc.name);
+ config_set_string(main->Config(), "AdvOut", "FFFormatMimeType",
+ desc.mimeType);
+ } else {
+ config_set_string(main->Config(), "AdvOut", "FFFormat",
+ nullptr);
+ config_set_string(main->Config(), "AdvOut", "FFFormatMimeType",
+ nullptr);
+ }
+}
+
+void OBSBasicSettings::SaveEncoder(QComboBox *combo, const char *section,
+ const char *value)
+{
+ QVariant v = combo->currentData();
+ CodecDesc cd;
+ if (!v.isNull())
+ cd = v.value();
+ config_set_int(main->Config(), section,
+ QT_TO_UTF8(QString("%1Id").arg(value)), cd.id);
+ if (cd.id != 0)
+ config_set_string(main->Config(), section, value, cd.name);
+ else
+ config_set_string(main->Config(), section, value, nullptr);
+}
+
void OBSBasicSettings::SaveOutputSettings()
{
config_set_string(main->Config(), "Output", "Mode",
@@ -1190,13 +1487,14 @@ void OBSBasicSettings::SaveOutputSettings()
ui->advOutRecTrack3, ui->advOutRecTrack4);
SaveEdit(ui->advOutFFURL, "AdvOut", "FFURL");
+ SaveFormat(ui->advOutFFFormat);
SaveSpinBox(ui->advOutFFVBitrate, "AdvOut", "FFVBitrate");
SaveCheckBox(ui->advOutFFUseRescale, "AdvOut", "FFRescale");
SaveCombo(ui->advOutFFRescale, "AdvOut", "FFRescaleRes");
- SaveEdit(ui->advOutFFVEncoder, "AdvOut", "FFVEncoder");
+ SaveEncoder(ui->advOutFFVEncoder, "AdvOut", "FFVEncoder");
SaveEdit(ui->advOutFFVCfg, "AdvOut", "FFVCustom");
SaveSpinBox(ui->advOutFFABitrate, "AdvOut", "FFABitrate");
- SaveEdit(ui->advOutFFAEncoder, "AdvOut", "FFAEncoder");
+ SaveEncoder(ui->advOutFFAEncoder, "AdvOut", "FFAEncoder");
SaveEdit(ui->advOutFFACfg, "AdvOut", "FFACustom");
SaveTrackIndex(main->Config(), "AdvOut", "FFAudioTrack",
ui->advOutFFTrack1, ui->advOutFFTrack2,
@@ -1442,6 +1740,59 @@ void OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx)
}
}
+#define DEFAULT_CONTAINER_STR \
+ QTStr("Basic.Settings.Output.Adv.FFmpeg.FormatDescDef")
+
+void OBSBasicSettings::on_advOutFFFormat_currentIndexChanged(int idx)
+{
+ const QVariant itemDataVariant = ui->advOutFFFormat->itemData(idx);
+
+ if (!itemDataVariant.isNull()) {
+ FormatDesc desc = itemDataVariant.value();
+ SetAdvOutputFFmpegEnablement(FF_CODEC_AUDIO,
+ ff_format_desc_has_audio(desc.desc),
+ false);
+ SetAdvOutputFFmpegEnablement(FF_CODEC_VIDEO,
+ ff_format_desc_has_video(desc.desc),
+ false);
+ ReloadCodecs(desc.desc);
+ ui->advOutFFFormatDesc->setText(ff_format_desc_long_name(
+ desc.desc));
+
+ CodecDesc defaultAudioCodecDesc =
+ GetDefaultCodecDesc(desc.desc, FF_CODEC_AUDIO);
+ CodecDesc defaultVideoCodecDesc =
+ GetDefaultCodecDesc(desc.desc, FF_CODEC_VIDEO);
+ SelectEncoder(ui->advOutFFAEncoder, defaultAudioCodecDesc.name,
+ defaultAudioCodecDesc.id);
+ SelectEncoder(ui->advOutFFVEncoder, defaultVideoCodecDesc.name,
+ defaultVideoCodecDesc.id);
+ } else {
+ ReloadCodecs(nullptr);
+ ui->advOutFFFormatDesc->setText(DEFAULT_CONTAINER_STR);
+ }
+}
+
+void OBSBasicSettings::on_advOutFFAEncoder_currentIndexChanged(int idx)
+{
+ const QVariant itemDataVariant = ui->advOutFFAEncoder->itemData(idx);
+ if (!itemDataVariant.isNull()) {
+ CodecDesc desc = itemDataVariant.value();
+ SetAdvOutputFFmpegEnablement(FF_CODEC_AUDIO,
+ desc.id != 0 || desc.name != nullptr, true);
+ }
+}
+
+void OBSBasicSettings::on_advOutFFVEncoder_currentIndexChanged(int idx)
+{
+ const QVariant itemDataVariant = ui->advOutFFVEncoder->itemData(idx);
+ if (!itemDataVariant.isNull()) {
+ CodecDesc desc = itemDataVariant.value();
+ SetAdvOutputFFmpegEnablement(FF_CODEC_VIDEO,
+ desc.id != 0 || desc.name != nullptr, true);
+ }
+}
+
#define INVALID_RES_STR "Basic.Settings.Video.InvalidResolution"
static bool ValidResolutions(Ui::OBSBasicSettings *ui)
diff --git a/obs/window-basic-settings.hpp b/obs/window-basic-settings.hpp
index b36e8e75b6d9e8c2b31608814c53b38408a76945..f6da637e1beb20ea27a436e1b72eee7cdbefc912 100644
--- a/obs/window-basic-settings.hpp
+++ b/obs/window-basic-settings.hpp
@@ -23,6 +23,8 @@
#include
#include
+#include
+
#include
class OBSBasic;
@@ -32,6 +34,23 @@ class OBSPropertiesView;
#include "ui_OBSBasicSettings.h"
+class OBSFFDeleter
+{
+public:
+ void operator()(const ff_format_desc *format)
+ {
+ ff_format_desc_free(format);
+ }
+ void operator()(const ff_codec_desc *codec)
+ {
+ ff_codec_desc_free(codec);
+ }
+};
+using OBSFFCodecDesc = std::unique_ptr;
+using OBSFFFormatDesc = std::unique_ptr;
+
class OBSBasicSettings : public QDialog {
Q_OBJECT
@@ -49,6 +68,8 @@ private:
bool loading = true;
std::string savedTheme;
+ OBSFFFormatDesc formats;
+
OBSPropertiesView *streamProperties = nullptr;
OBSPropertiesView *streamEncoderProps = nullptr;
OBSPropertiesView *recordEncoderProps = nullptr;
@@ -63,6 +84,9 @@ private:
const char *value);
void SaveSpinBox(QSpinBox *widget, const char *section,
const char *value);
+ void SaveFormat(QComboBox *combo);
+ void SaveEncoder(QComboBox *combo, const char *section,
+ const char *value);
inline bool Changed() const
{
@@ -93,6 +117,8 @@ private:
void LoadServiceTypes();
void LoadEncoderTypes();
void LoadColorRanges();
+ void LoadFormats();
+ void ReloadCodecs(const ff_format_desc *formatDesc);
void LoadGeneralSettings();
void LoadStream1Settings();
@@ -117,6 +143,9 @@ private:
void LoadAdvOutputRecordingEncoderProperties();
void LoadAdvOutputFFmpegSettings();
void LoadAdvOutputAudioSettings();
+ void SetAdvOutputFFmpegEnablement(
+ ff_codec_type encoderType, bool enabled,
+ bool enableEncode = false);
/* audio */
void LoadListValues(QComboBox *widget, obs_property_t *prop,
@@ -154,6 +183,9 @@ private slots:
void on_advOutFFPathBrowse_clicked();
void on_advOutEncoder_currentIndexChanged(int idx);
void on_advOutRecEncoder_currentIndexChanged(int idx);
+ void on_advOutFFFormat_currentIndexChanged(int idx);
+ void on_advOutFFAEncoder_currentIndexChanged(int idx);
+ void on_advOutFFVEncoder_currentIndexChanged(int idx);
void on_baseResolution_editTextChanged(const QString &text);