frontend: Fix null settings and DBR for custom configs

When using custom RTMP output with a JSON config that omits
encoder settings, settings.dump() produces "null" which
obs_data_create_from_json cannot parse. Fall back to
obs_data_create() for null/non-object settings so encoders
use their defaults.

Additionally, when bitrate_interpolation_points is absent,
the code unconditionally set interpolation_table_data as
empty arrays. This caused build_dbr_interpolation_table to
wipe the default linear table, leaving DBR enabled but
unable to adjust bitrates during congestion. Only set the
interpolation data when all encoders provide it, otherwise
fall back to the default linear 0-to-max interpolation.
This commit is contained in:
Alex Luccisano
2026-05-25 11:54:34 -04:00
committed by Ryan Foster
parent 83ed914ecf
commit af77628900

View File

@@ -16,6 +16,7 @@
#include <QUrl>
#include <QUrlQuery>
#include <algorithm>
#include <cinttypes>
// Codec profile strings
@@ -238,7 +239,14 @@ static OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer, size_t enco
dstr_printf(name_buffer, "multitrack video video encoder %zu", encoder_index);
OBSDataAutoRelease encoder_settings = obs_data_create_from_json(encoder_config.settings.dump().c_str());
// settings.dump() produces "null" when the config omits encoder settings, which
// obs_data_create_from_json cannot parse. Fall back to empty settings so the encoder uses its defaults.
OBSDataAutoRelease encoder_settings =
encoder_config.settings.is_object() ? obs_data_create_from_json(encoder_config.settings.dump().c_str())
: obs_data_create();
if (!encoder_config.settings.is_object()) {
blog(LOG_INFO, "Encoder %zu has no settings, using defaults", encoder_index);
}
/* VAAPI-based encoders unfortunately use an integer for "profile". Until a string-based "profile" can be used with
* VAAPI, find the corresponding integer value and update the settings with an integer-based "profile".
@@ -613,10 +621,11 @@ static bool create_video_encoders(const GoLiveApi::Config &go_live_config,
obs_output_set_video_encoder2(recording_output, encoder, i);
auto &data = go_live_config.encoder_configurations[i].bitrate_interpolation_points;
if (data.has_value())
if (data.has_value()) {
bitrate_interpolation_array.push_back(*data);
else
} else {
bitrate_interpolation_array.push_back(json::array());
}
}
video_encoder_group = encoder_group;
@@ -675,7 +684,15 @@ static void create_audio_encoders(const GoLiveApi::Config &go_live_config,
for (size_t i = 0; i < configs.size(); i++) {
dstr_printf(encoder_name_buffer, "%s %zu", name_prefix, i);
OBSDataAutoRelease settings = obs_data_create_from_json(configs[i].settings.dump().c_str());
// settings.dump() produces "null" when the config omits encoder settings,
// which obs_data_create_from_json cannot parse. Fall back to empty settings.
OBSDataAutoRelease settings =
configs[i].settings.is_object()
? obs_data_create_from_json(configs[i].settings.dump().c_str())
: obs_data_create();
if (!configs[i].settings.is_object()) {
blog(LOG_INFO, "Audio encoder %zu has no settings, using defaults", i);
}
OBSEncoderAutoRelease audio_encoder =
create_audio_encoder(encoder_name_buffer->array, audio_encoder_id, settings, mixer_idx);
@@ -782,7 +799,18 @@ static OBSOutputs SetupOBSOutput(QWidget *parent, const QString &multitrack_vide
return {nullptr, nullptr};
OBSDataAutoRelease settings = obs_output_get_settings(output);
obs_data_set_string(settings, "interpolation_table_data", bitrate_interpolation_array.dump().c_str());
// Only set interpolation_table_data if every encoder has interpolation points. Partial data would
// fail validation in the output plugin and disable DBR. Omitting it lets the output use its default
// linear interpolation (proportional scaling from 0 to max bitrate).
bool has_interpolation_data = std::all_of(bitrate_interpolation_array.begin(),
bitrate_interpolation_array.end(),
[](const json &arr) { return !arr.empty(); });
if (has_interpolation_data) {
obs_data_set_string(settings, "interpolation_table_data", bitrate_interpolation_array.dump().c_str());
} else {
blog(LOG_INFO,
"Interpolation data missing for one or more encoders, using default linear interpolation");
}
obs_output_update(output, settings);
std::vector<speaker_layout> requested_speaker_layouts;