Skip to content

Commit

Permalink
obs-webrtc: Add Simulcast Support
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean-Der committed Sep 18, 2024
1 parent a289581 commit 4fab92b
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 41 deletions.
3 changes: 3 additions & 0 deletions UI/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,9 @@ Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Enable stream dump to FLV
Basic.Settings.Stream.MultitrackVideoConfigOverride="Config Override (JSON)"
Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Enable Config Override"
Basic.Settings.Stream.MultitrackVideoLabel="Multitrack Video"
Basic.Settings.Stream.SimulcastLabel="Simulcast"
Basic.Settings.Stream.SimulcastInfo="Simulcast allows you to encode and send multiple video qualities. No information about your computer and software setup will be sent."
Basic.Settings.Stream.SimulcastTotalLayers="Total Layers"
Basic.Settings.Stream.AdvancedOptions="Advanced Options"

# basic mode 'output' settings
Expand Down
85 changes: 85 additions & 0 deletions UI/forms/OBSBasicSettings.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,91 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="simulcastGroupBox">
<property name="title">
<string>Basic.Settings.Stream.SimulcastLabel</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_35">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_33">
<item>
<spacer name="horizontalSpacer_33">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>170</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="simulcastInfo">
<property name="text">
<string>Basic.Settings.Stream.SimulcastInfo</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_39">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="simulcastTotalLayersLabel">
<property name="text">
<string>Basic.Settings.Stream.SimulcastTotalLayers</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_34" stretch="0,0">
<item>
<widget class="QSpinBox" name="simulcastTotalLayers">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="serviceAdvancedOptionsGroupBox">
<property name="title">
Expand Down
73 changes: 73 additions & 0 deletions UI/window-basic-main-outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ void SimpleOutput::LoadStreamingPreset_Lossy(const char *encoderId)
if (!videoStreaming)
throw "Failed to create video streaming encoder (simple output)";
obs_encoder_release(videoStreaming);

CreateSimulcastEncoders(encoderId);
}

/* mistakes have been made to lead us to this. */
Expand Down Expand Up @@ -895,9 +897,14 @@ void SimpleOutput::Update()
default:
obs_encoder_set_preferred_video_format(videoStreaming,
VIDEO_FORMAT_NV12);
for (auto enc : simulcastEncoders)
obs_encoder_set_preferred_video_format(
enc, VIDEO_FORMAT_NV12);
}

obs_encoder_update(videoStreaming, videoSettings);
SimulcastEncodersUpdate(videoSettings, videoBitrate);

obs_encoder_update(audioStreaming, audioSettings);
obs_encoder_update(audioArchive, audioSettings);
}
Expand Down Expand Up @@ -1209,6 +1216,9 @@ SimpleOutput::SetupStreaming(obs_service_t *service,
}

obs_output_set_video_encoder(streamOutput, videoStreaming);
for (size_t i = 0; i < simulcastEncoders.size(); i++)
obs_output_set_video_encoder2(
streamOutput, simulcastEncoders[i], i + 1);
obs_output_set_audio_encoder(streamOutput, audioStreaming, 0);
obs_output_set_service(streamOutput, service);
return true;
Expand Down Expand Up @@ -1762,6 +1772,8 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
"(advanced output)";
obs_encoder_release(videoStreaming);

CreateSimulcastEncoders(streamEncoder);

const char *rate_control = obs_data_get_string(
useStreamEncoder ? streamEncSettings : recordEncSettings,
"rate_control");
Expand Down Expand Up @@ -1882,6 +1894,8 @@ void AdvancedOutput::UpdateStreamSettings()
}

obs_encoder_update(videoStreaming, settings);
SimulcastEncodersUpdate(settings,
obs_data_get_int(settings, "bitrate"));
}

inline void AdvancedOutput::UpdateRecordingSettings()
Expand Down Expand Up @@ -2367,6 +2381,9 @@ AdvancedOutput::SetupStreaming(obs_service_t *service,
}

obs_output_set_video_encoder(streamOutput, videoStreaming);
for (size_t i = 0; i < simulcastEncoders.size(); i++)
obs_output_set_video_encoder2(
streamOutput, simulcastEncoders[i], i + 1);
obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);

if (!is_multitrack_output) {
Expand Down Expand Up @@ -2916,3 +2933,59 @@ BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main)
{
return new AdvancedOutput(main);
}

void BasicOutputHandler::CreateSimulcastEncoders(const char *encoderId)
{
int rescaleFilter =
config_get_int(main->Config(), "AdvOut", "RescaleFilter");
if (rescaleFilter == OBS_SCALE_DISABLE) {
rescaleFilter = OBS_SCALE_BICUBIC;
}

std::string encoder_name = "simulcast_0";
auto simulcastTotalLayers = config_get_int(main->Config(), "Stream1",
"SimulcastTotalLayers");
auto widthStep =
video_output_get_width(obs_get_video()) / simulcastTotalLayers;
auto heightStep =
video_output_get_height(obs_get_video()) / simulcastTotalLayers;

for (auto i = simulcastTotalLayers - 1; i > 0; i--) {
uint32_t width = widthStep * i;
width -= width % 2;

uint32_t height = heightStep * i;
height -= height % 2;

encoder_name[encoder_name.size() - 1] = to_string(i).at(0);
auto simulcast_encoder = obs_video_encoder_create(
encoderId, encoder_name.c_str(), nullptr, nullptr);

if (simulcast_encoder) {
obs_encoder_set_video(simulcast_encoder,
obs_get_video());
obs_encoder_set_scaled_size(simulcast_encoder, width,
height);
obs_encoder_set_gpu_scale_type(
simulcast_encoder,
(obs_scale_type)rescaleFilter);
simulcastEncoders.push_back(simulcast_encoder);
obs_encoder_release(simulcast_encoder);
} else {
blog(LOG_WARNING,
"Failed to create video streaming simulcast encoders (BasicOutputHandler)");
}
}
}

void BasicOutputHandler::SimulcastEncodersUpdate(obs_data_t *videoSettings,
int videoBitrate)
{
auto bitrateStep =
videoBitrate / static_cast<int>(simulcastEncoders.size() + 1);
for (auto &simulcastEncoder : simulcastEncoders) {
videoBitrate -= bitrateStep;
obs_data_set_int(videoSettings, "bitrate", videoBitrate);
obs_encoder_update(simulcastEncoder, videoSettings);
}
}
5 changes: 5 additions & 0 deletions UI/window-basic-main-outputs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ struct BasicOutputHandler {
obs_scene_t *vCamSourceScene = nullptr;
obs_sceneitem_t *vCamSourceSceneItem = nullptr;

std::vector<OBSEncoder> simulcastEncoders;

std::string outputType;
std::string lastError;

Expand Down Expand Up @@ -105,6 +107,9 @@ struct BasicOutputHandler {
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,
std::function<void(std::optional<bool>)> continuation);
OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig();
void CreateSimulcastEncoders(const char *encoderId);
void SimulcastEncodersUpdate(obs_data_t *videoSettings,
int videoBitrate);
};

BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);
Expand Down
25 changes: 22 additions & 3 deletions UI/window-basic-settings-stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ void OBSBasicSettings::LoadStream1Settings()
{
bool ignoreRecommended =
config_get_bool(main->Config(), "Stream1", "IgnoreRecommended");
int simulcastTotalLayers = config_get_int(main->Config(), "Stream1",
"SimulcastTotalLayers");

obs_service_t *service_obj = main->GetService();
const char *type = obs_service_get_type(service_obj);
Expand Down Expand Up @@ -224,10 +226,13 @@ void OBSBasicSettings::LoadStream1Settings()
if (use_custom_server)
ui->serviceCustomServer->setText(server);

if (is_whip)
if (is_whip) {
ui->key->setText(bearer_token);
else
ui->simulcastGroupBox->show();
} else {
ui->key->setText(key);
ui->simulcastGroupBox->hide();
}

ServiceChanged(true);

Expand All @@ -241,6 +246,7 @@ void OBSBasicSettings::LoadStream1Settings()
ui->streamPage->setEnabled(!streamActive);

ui->ignoreRecommended->setChecked(ignoreRecommended);
ui->simulcastTotalLayers->setValue(simulcastTotalLayers);

loading = false;

Expand Down Expand Up @@ -365,6 +371,11 @@ void OBSBasicSettings::SaveStream1Settings()

SaveCheckBox(ui->ignoreRecommended, "Stream1", "IgnoreRecommended");

auto oldSimulcastTotalLayers = config_get_int(main->Config(), "Stream1",
"SimulcastTotalLayers");
SaveSpinBox(ui->simulcastTotalLayers, "Stream1",
"SimulcastTotalLayers");

auto oldMultitrackVideoSetting = config_get_bool(
main->Config(), "Stream1", "EnableMultitrackVideo");

Expand Down Expand Up @@ -404,7 +415,9 @@ void OBSBasicSettings::SaveStream1Settings()
SaveText(ui->multitrackVideoConfigOverride, "Stream1",
"MultitrackVideoConfigOverride");

if (oldMultitrackVideoSetting != ui->enableMultitrackVideo->isChecked())
if (oldMultitrackVideoSetting !=
ui->enableMultitrackVideo->isChecked() ||
oldSimulcastTotalLayers != ui->simulcastTotalLayers->value())
main->ResetOutputs();

SwapMultiTrack(QT_TO_UTF8(protocol));
Expand Down Expand Up @@ -661,6 +674,12 @@ void OBSBasicSettings::on_service_currentIndexChanged(int idx)
} else {
SwapMultiTrack(QT_TO_UTF8(protocol));
}

if (IsWHIP()) {
ui->simulcastGroupBox->show();
} else {
ui->simulcastGroupBox->hide();
}
}

void OBSBasicSettings::on_customServer_textChanged(const QString &)
Expand Down
1 change: 1 addition & 0 deletions UI/window-basic-settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->authUsername, EDIT_CHANGED, STREAM1_CHANGED);
HookWidget(ui->authPw, EDIT_CHANGED, STREAM1_CHANGED);
HookWidget(ui->ignoreRecommended, CHECK_CHANGED, STREAM1_CHANGED);
HookWidget(ui->simulcastTotalLayers, SCROLL_CHANGED, STREAM1_CHANGED);
HookWidget(ui->enableMultitrackVideo, CHECK_CHANGED, STREAM1_CHANGED);
HookWidget(ui->multitrackVideoMaximumAggregateBitrateAuto, CHECK_CHANGED, STREAM1_CHANGED);
HookWidget(ui->multitrackVideoMaximumAggregateBitrate, SCROLL_CHANGED, STREAM1_CHANGED);
Expand Down
1 change: 1 addition & 0 deletions plugins/obs-webrtc/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ Service.BearerToken="Bearer Token"

Error.InvalidSDP="WHIP server responded with invalid SDP: %1"
Error.NoRemoteDescription="Failed to set remote description: %1"
Error.SimulcastLayersRejected="WHIP server only accepted %1 simulcast layers"
Loading

0 comments on commit 4fab92b

Please sign in to comment.