Browse Source

frontend: Allow selecting additional canvases for multitrack

Dennis Sädtler 11 months ago
parent
commit
0552062390

+ 3 - 0
frontend/data/locale/en-US.ini

@@ -994,6 +994,7 @@ 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.MultitrackVideoExtraCanvas="Additional Canvas"
 Basic.Settings.Stream.AdvancedOptions="Advanced Options"
 
 # basic mode 'output' settings
@@ -1576,6 +1577,7 @@ ConfigDownload.WarningMessageTitle="Warning"
 FailedToStartStream.MissingConfigURL="No config URL available for the current service"
 FailedToStartStream.NoCustomRTMPURLInSettings="Custom RTMP URL not specified"
 FailedToStartStream.InvalidCustomConfig="Invalid custom config"
+FailedToStartStream.MissingCanvas="A configured extra canvas is missing"
 FailedToStartStream.FailedToCreateMultitrackVideoService="Failed to create multitrack video service"
 FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video RTMP output"
 FailedToStartStream.EncoderNotAvailable="NVENC not available.\n\nFailed to find encoder type '%1'"
@@ -1589,6 +1591,7 @@ FailedToStartStream.WarningUnknownStatus="Received unknown status value '%1'"
 FailedToStartStream.WarningRetryNonMultitrackVideo="\n<br><br>\nDo you want to continue streaming without %1?"
 FailedToStartStream.WarningRetry="\n<br><br>\nDo you want to continue streaming?"
 FailedToStartStream.MissingEncoderConfigs="Go live config did not include encoder configurations"
+FailedToStartStream.InvalidEncoderConfig="Go live config included an invalid encoder configuration"
 FailedToStartStream.StatusMissingHTML="Go live request returned an unspecified error"
 FailedToStartStream.NoConfigSupplied="Missing config"
 MultitrackVideo.Info="%1 automatically optimizes your settings to encode and send multiple video qualities. Selecting this option will send %2 information about your computer and software setup."

+ 38 - 24
frontend/forms/OBSBasicSettings.ui

@@ -1907,6 +1907,29 @@
                    <property name="fieldGrowthPolicy">
                     <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
                    </property>
+                   <item row="0" column="0">
+                    <spacer name="horizontalSpacer_30">
+                     <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 row="0" column="1">
+                    <widget class="QCheckBox" name="enableMultitrackVideo">
+                     <property name="text">
+                      <string>Basic.Settings.Stream.EnableMultitrackVideo</string>
+                     </property>
+                    </widget>
+                   </item>
                    <item row="1" column="0">
                     <widget class="QLabel" name="multitrackVideoMaximumAggregateBitrateLabel">
                      <property name="text">
@@ -1993,7 +2016,14 @@
                      </item>
                     </layout>
                    </item>
-                   <item row="3" column="0">
+                   <item row="3" column="1">
+                    <widget class="QComboBox" name="multitrackVideoAdditionalCanvas">
+                     <property name="placeholderText">
+                      <string>None</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="4" column="0">
                     <spacer name="horizontalSpacer_32">
                      <property name="orientation">
                       <enum>Qt::Horizontal</enum>
@@ -2009,28 +2039,28 @@
                      </property>
                     </spacer>
                    </item>
-                   <item row="3" column="1">
+                   <item row="4" column="1">
                     <widget class="QCheckBox" name="multitrackVideoStreamDumpEnable">
                      <property name="text">
                       <string>Basic.Settings.Stream.MultitrackVideoStreamDumpEnable</string>
                      </property>
                     </widget>
                    </item>
-                   <item row="4" column="1">
+                   <item row="5" column="1">
                     <widget class="QCheckBox" name="multitrackVideoConfigOverrideEnable">
                      <property name="text">
                       <string>Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable</string>
                      </property>
                     </widget>
                    </item>
-                   <item row="5" column="0">
+                   <item row="6" column="0">
                     <widget class="QLabel" name="multitrackVideoConfigOverrideLabel">
                      <property name="text">
                       <string>Basic.Settings.Stream.MultitrackVideoConfigOverride</string>
                      </property>
                     </widget>
                    </item>
-                   <item row="5" column="1">
+                   <item row="6" column="1">
                     <widget class="QPlainTextEdit" name="multitrackVideoConfigOverride">
                      <property name="sizePolicy">
                       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@@ -2040,29 +2070,13 @@
                      </property>
                     </widget>
                    </item>
-                   <item row="0" column="1">
-                    <widget class="QCheckBox" name="enableMultitrackVideo">
+                   <item row="3" column="0">
+                    <widget class="QLabel" name="multitrackVideoAdditionalCanvasLabel">
                      <property name="text">
-                      <string>Basic.Settings.Stream.EnableMultitrackVideo</string>
+                      <string>Basic.Settings.Stream.MultitrackVideoExtraCanvas</string>
                      </property>
                     </widget>
                    </item>
-                   <item row="0" column="0">
-                    <spacer name="horizontalSpacer_30">
-                     <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>
                   </layout>
                  </item>
                 </layout>

+ 2 - 0
frontend/settings/OBSBasicSettings.cpp

@@ -393,6 +393,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
 	HookWidget(ui->multitrackVideoStreamDumpEnable,            CHECK_CHANGED,  STREAM1_CHANGED);
 	HookWidget(ui->multitrackVideoConfigOverrideEnable,        CHECK_CHANGED,  STREAM1_CHANGED);
 	HookWidget(ui->multitrackVideoConfigOverride,              TEXT_CHANGED,   STREAM1_CHANGED);
+	HookWidget(ui->multitrackVideoAdditionalCanvas,            COMBO_CHANGED,   STREAM1_CHANGED);
 	HookWidget(ui->outputMode,           COMBO_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->simpleOutputPath,     EDIT_CHANGED,   OUTPUTS_CHANGED);
 	HookWidget(ui->simpleNoSpace,        CHECK_CHANGED,  OUTPUTS_CHANGED);
@@ -5621,6 +5622,7 @@ void OBSBasicSettings::UpdateMultitrackVideo()
 							      ui->enableMultitrackVideo->isChecked());
 	ui->multitrackVideoMaximumVideoTracks->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked() &&
 							  !ui->multitrackVideoMaximumVideoTracksAuto->isChecked());
+	ui->multitrackVideoAdditionalCanvas->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked());
 
 	ui->multitrackVideoStreamDumpEnable->setVisible(available && MultitrackVideoDeveloperModeEnabled());
 	ui->multitrackVideoConfigOverrideEnable->setVisible(available && MultitrackVideoDeveloperModeEnabled());

+ 19 - 0
frontend/settings/OBSBasicSettings_Stream.cpp

@@ -170,6 +170,24 @@ void OBSBasicSettings::LoadStream1Settings()
 				config_get_string(main->Config(), "Stream1", "MultitrackVideoConfigOverride"))
 				.c_str());
 
+	ui->multitrackVideoAdditionalCanvas->clear();
+	ui->multitrackVideoAdditionalCanvas->addItem(QTStr("None"));
+	for (const auto &canvas : main->GetCanvases()) {
+		if (obs_canvas_get_flags(canvas) & EPHEMERAL)
+			continue;
+
+		ui->multitrackVideoAdditionalCanvas->addItem(obs_canvas_get_name(canvas), obs_canvas_get_uuid(canvas));
+	}
+
+	if (config_has_user_value(main->Config(), "Stream1", "MultitrackExtraCanvas")) {
+		/* Currently we only support one canvas, so the value will just be one UUID. */
+		const std::string_view uuid = config_get_string(main->Config(), "Stream1", "MultitrackExtraCanvas");
+		if (!uuid.empty()) {
+			int idx = ui->multitrackVideoAdditionalCanvas->findData(uuid.data());
+			ui->multitrackVideoAdditionalCanvas->setCurrentIndex(idx);
+		}
+	}
+
 	UpdateServerList();
 
 	if (is_rtmp_common) {
@@ -335,6 +353,7 @@ void OBSBasicSettings::SaveStream1Settings()
 	SaveCheckBox(ui->multitrackVideoStreamDumpEnable, "Stream1", "MultitrackVideoStreamDumpEnabled");
 	SaveCheckBox(ui->multitrackVideoConfigOverrideEnable, "Stream1", "MultitrackVideoConfigOverrideEnabled");
 	SaveText(ui->multitrackVideoConfigOverride, "Stream1", "MultitrackVideoConfigOverride");
+	SaveComboData(ui->multitrackVideoAdditionalCanvas, "Stream1", "MultitrackExtraCanvas");
 
 	if (oldMultitrackVideoSetting != ui->enableMultitrackVideo->isChecked())
 		main->ResetOutputs();

+ 8 - 1
frontend/utility/BasicOutputHandler.cpp

@@ -457,6 +457,12 @@ std::shared_future<void> BasicOutputHandler::SetupMultitrackVideo(obs_service_t
 		custom_config = DeserializeConfigText(
 			config_get_string(main->Config(), "Stream1", "MultitrackVideoConfigOverride"));
 
+	std::optional<QString> extraCanvasUUID;
+	const char *uuid = config_get_string(main->Config(), "Stream1", "MultitrackExtraCanvas");
+	if (uuid && *uuid) {
+		extraCanvasUUID = uuid;
+	}
+
 	OBSDataAutoRelease settings = obs_service_get_settings(service);
 	QString key = obs_data_get_string(settings, "key");
 
@@ -532,7 +538,8 @@ std::shared_future<void> BasicOutputHandler::SetupMultitrackVideo(obs_service_t
 			multitrackVideo->PrepareStreaming(main, service_name.c_str(), service, custom_rtmp_url, key,
 							  audio_encoder_id.c_str(), maximum_aggregate_bitrate,
 							  maximum_video_tracks, custom_config, stream_dump_config,
-							  main_audio_mixer, vod_track_mixer, use_rtmps);
+							  main_audio_mixer, vod_track_mixer, use_rtmps,
+							  extraCanvasUUID);
 		} catch (const MultitrackVideoError &error_) {
 			error.emplace(error_);
 		}

+ 12 - 7
frontend/utility/GoLiveAPI_PostData.cpp

@@ -6,7 +6,8 @@
 #include <nlohmann/json.hpp>
 
 GoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,
-					const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled)
+					const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled,
+					const std::vector<OBSCanvasAutoRelease> &canvases)
 {
 	GoLiveApi::PostData post_data{};
 	post_data.service = "IVS";
@@ -49,13 +50,17 @@ GoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<u
 	preferences.vod_track_audio = vod_track_enabled;
 
 	obs_video_info ovi;
-	if (obs_get_video_info(&ovi)) {
+	if (obs_get_video_info(&ovi))
 		preferences.composition_gpu_index = ovi.adapter;
-		preferences.canvases.emplace_back(GoLiveApi::Canvas{ovi.output_width,
-								    ovi.output_height,
-								    ovi.base_width,
-								    ovi.base_height,
-								    {ovi.fps_num, ovi.fps_den}});
+
+	for (const auto &canvas : canvases) {
+		if (obs_canvas_get_video_info(canvas, &ovi)) {
+			preferences.canvases.emplace_back(GoLiveApi::Canvas{ovi.output_width,
+									    ovi.output_height,
+									    ovi.base_width,
+									    ovi.base_height,
+									    {ovi.fps_num, ovi.fps_den}});
+		}
 	}
 
 	obs_audio_info2 oai2;

+ 2 - 1
frontend/utility/GoLiveAPI_PostData.hpp

@@ -9,4 +9,5 @@
 #include <optional>
 
 GoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,
-					const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled);
+					const std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled,
+					const std::vector<OBSCanvasAutoRelease> &canvases);

+ 57 - 21
frontend/utility/MultitrackVideoOutput.cpp

@@ -222,7 +222,8 @@ static bool encoder_available(const char *type)
 }
 
 static OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer, size_t encoder_index,
-						  const GoLiveApi::VideoEncoderConfiguration &encoder_config)
+						  const GoLiveApi::VideoEncoderConfiguration &encoder_config,
+						  const OBSCanvasAutoRelease &canvas)
 {
 	auto encoder_type = encoder_config.type.c_str();
 	if (!encoder_available(encoder_type)) {
@@ -292,10 +293,10 @@ static OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer, size_t enco
 		throw MultitrackVideoError::warning(
 			QTStr("FailedToStartStream.FailedToCreateVideoEncoder").arg(name_buffer->array, encoder_type));
 	}
-	obs_encoder_set_video(video_encoder, obs_get_video());
+	obs_encoder_set_video(video_encoder, obs_canvas_get_video(canvas));
 
 	obs_video_info ovi;
-	if (!obs_get_video_info(&ovi)) {
+	if (!obs_canvas_get_video_info(canvas, &ovi)) {
 		blog(LOG_WARNING, "Failed to get obs_video_info while creating encoder %zu", encoder_index);
 		throw MultitrackVideoError::warning(
 			QTStr("FailedToStartStream.FailedToGetOBSVideoInfo").arg(name_buffer->array, encoder_type));
@@ -329,18 +330,17 @@ static OBSOutputs SetupOBSOutput(QWidget *parent, const QString &multitrack_vide
 				 std::vector<OBSEncoderAutoRelease> &audio_encoders,
 				 std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
 				 const char *audio_encoder_id, size_t main_audio_mixer,
-				 std::optional<size_t> vod_track_mixer);
+				 std::optional<size_t> vod_track_mixer,
+				 const std::vector<OBSCanvasAutoRelease> &canvases);
 static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, obs_output_t *output, OBSSignal &start,
 				OBSSignal &stop);
 
-void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *service_name, obs_service_t *service,
-					     const std::optional<std::string> &rtmp_url, const QString &stream_key,
-					     const char *audio_encoder_id,
-					     std::optional<uint32_t> maximum_aggregate_bitrate,
-					     std::optional<uint32_t> maximum_video_tracks,
-					     std::optional<std::string> custom_config,
-					     obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer,
-					     std::optional<size_t> vod_track_mixer, std::optional<bool> use_rtmps)
+void MultitrackVideoOutput::PrepareStreaming(
+	QWidget *parent, const char *service_name, obs_service_t *service, const std::optional<std::string> &rtmp_url,
+	const QString &stream_key, const char *audio_encoder_id, std::optional<uint32_t> maximum_aggregate_bitrate,
+	std::optional<uint32_t> maximum_video_tracks, std::optional<std::string> custom_config,
+	obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,
+	std::optional<bool> use_rtmps, std::optional<QString> extra_canvas)
 {
 	{
 		const std::lock_guard<std::mutex> current_lock{current_mutex};
@@ -366,6 +366,25 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 
 	auto auto_config_url_data = auto_config_url.toUtf8();
 
+	std::vector<OBSCanvasAutoRelease> canvases;
+
+	canvases.emplace_back(obs_get_main_canvas());
+	if (extra_canvas) {
+		obs_canvas_t *canvas = obs_get_canvas_by_uuid(extra_canvas->toUtf8().constData());
+		if (!canvas) {
+			throw MultitrackVideoError::critical(QTStr("FailedToStartStream.MissingCanvas"));
+		}
+		canvases.emplace_back(canvas);
+	}
+
+	std::string canvasNames;
+	for (const auto &canvas : canvases) {
+		if (!canvasNames.empty())
+			canvasNames += ", ";
+
+		canvasNames += obs_canvas_get_name(canvas);
+	}
+
 	DStr vod_track_info_storage;
 	if (vod_track_mixer.has_value())
 		dstr_printf(vod_track_info_storage, "Yes (mixer: %zu)", vod_track_mixer.value());
@@ -379,13 +398,14 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 	     "    max aggregate bitrate: %s (%" PRIu32 ")\n"
 	     "    max video tracks:      %s (%" PRIu32 ")\n"
 	     "    custom rtmp url:       %s ('%s')\n"
-	     "    vod track:             %s",
+	     "    vod track:             %s\n"
+	     "    canvases:              %s",
 	     is_custom_config ? "Yes" : "No", !auto_config_url.isEmpty() ? auto_config_url_data.constData() : "(null)",
 	     service_name, maximum_aggregate_bitrate.has_value() ? "Set" : "Auto",
 	     maximum_aggregate_bitrate.value_or(0), maximum_video_tracks.has_value() ? "Set" : "Auto",
 	     maximum_video_tracks.value_or(0), rtmp_url.has_value() ? "Yes" : "No",
 	     rtmp_url.has_value() ? rtmp_url->c_str() : "",
-	     vod_track_info_storage->array ? vod_track_info_storage->array : "No");
+	     vod_track_info_storage->array ? vod_track_info_storage->array : "No", canvasNames.c_str());
 
 	const bool custom_config_only = auto_config_url.isEmpty() && MultitrackVideoDeveloperModeEnabled() &&
 					custom_config.has_value() &&
@@ -393,7 +413,7 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 
 	if (!custom_config_only) {
 		auto go_live_post = constructGoLivePost(stream_key, maximum_aggregate_bitrate, maximum_video_tracks,
-							vod_track_mixer.has_value());
+							vod_track_mixer.has_value(), canvases);
 
 		go_live_config = DownloadGoLiveConfig(parent, auto_config_url, go_live_post, multitrack_video_name);
 	}
@@ -436,7 +456,7 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 	std::shared_ptr<obs_encoder_group_t> video_encoder_group;
 	auto outputs = SetupOBSOutput(parent, multitrack_video_name, dump_stream_to_file_config, output_config,
 				      audio_encoders, video_encoder_group, audio_encoder_id, main_audio_mixer,
-				      vod_track_mixer);
+				      vod_track_mixer, canvases);
 	auto output = std::move(outputs.output);
 	auto recording_output = std::move(outputs.recording_output);
 	if (!output)
@@ -474,6 +494,11 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 			recording_audio_encoders.emplace_back(obs_encoder_get_ref(encoder));
 		}
 
+		std::vector<OBSCanvasAutoRelease> recording_canvases(canvases.size());
+		for (const auto &canvas : canvases) {
+			recording_canvases.emplace_back(obs_canvas_get_ref(canvas));
+		}
+
 		{
 			const std::lock_guard current_stream_dump_lock{current_stream_dump_mutex};
 			current_stream_dump.emplace(OBSOutputObjects{
@@ -483,6 +508,7 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 				nullptr,
 				std::move(start_recording),
 				std::move(stop_recording),
+				std::move(recording_canvases),
 			});
 		}
 	}
@@ -495,6 +521,7 @@ void MultitrackVideoOutput::PrepareStreaming(QWidget *parent, const char *servic
 		std::move(multitrack_video_service),
 		std::move(start_streaming),
 		std::move(stop_streaming),
+		std::move(canvases),
 	});
 }
 
@@ -622,7 +649,7 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings(QWidget *parent, config_t
 
 static bool create_video_encoders(const GoLiveApi::Config &go_live_config,
 				  std::shared_ptr<obs_encoder_group_t> &video_encoder_group, obs_output_t *output,
-				  obs_output_t *recording_output)
+				  obs_output_t *recording_output, const std::vector<OBSCanvasAutoRelease> &canvases)
 {
 	DStr video_encoder_name_buffer;
 	if (go_live_config.encoder_configurations.empty()) {
@@ -634,9 +661,17 @@ static bool create_video_encoders(const GoLiveApi::Config &go_live_config,
 	if (!encoder_group)
 		return false;
 
+	auto max_canvas_idx = canvases.size() - 1;
+
 	for (size_t i = 0; i < go_live_config.encoder_configurations.size(); i++) {
-		auto encoder =
-			create_video_encoder(video_encoder_name_buffer, i, go_live_config.encoder_configurations[i]);
+		auto &config = go_live_config.encoder_configurations[i];
+		if (config.canvas_index > max_canvas_idx) {
+			blog(LOG_ERROR, "MultitrackVideoOutput: Invalid canvas index: %u", config.canvas_index);
+			throw MultitrackVideoError::warning(QTStr("FailedToStartStream.InvalidEncoderConfig"));
+		}
+
+		auto &canvas = canvases[config.canvas_index];
+		auto encoder = create_video_encoder(video_encoder_name_buffer, i, config, canvas);
 		if (!encoder)
 			return false;
 
@@ -797,14 +832,15 @@ static OBSOutputs SetupOBSOutput(QWidget *parent, const QString &multitrack_vide
 				 std::vector<OBSEncoderAutoRelease> &audio_encoders,
 				 std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
 				 const char *audio_encoder_id, size_t main_audio_mixer,
-				 std::optional<size_t> vod_track_mixer)
+				 std::optional<size_t> vod_track_mixer,
+				 const std::vector<OBSCanvasAutoRelease> &canvases)
 {
 	auto output = create_output();
 	OBSOutputAutoRelease recording_output;
 	if (dump_stream_to_file_config)
 		recording_output = create_recording_output(dump_stream_to_file_config);
 
-	if (!create_video_encoders(go_live_config, video_encoder_group, output, recording_output))
+	if (!create_video_encoders(go_live_config, video_encoder_group, output, recording_output, canvases))
 		return {nullptr, nullptr};
 
 	std::vector<speaker_layout> requested_speaker_layouts;

+ 3 - 1
frontend/utility/MultitrackVideoOutput.hpp

@@ -27,7 +27,8 @@ public:
 			      const char *audio_encoder_id, std::optional<uint32_t> maximum_aggregate_bitrate,
 			      std::optional<uint32_t> maximum_video_tracks, std::optional<std::string> custom_config,
 			      obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer,
-			      std::optional<size_t> vod_track_mixer, std::optional<bool> use_rtmps);
+			      std::optional<size_t> vod_track_mixer, std::optional<bool> use_rtmps,
+			      std::optional<QString> extra_canvas);
 	signal_handler_t *StreamingSignalHandler();
 	void StartedStreaming();
 	void StopStreaming();
@@ -49,6 +50,7 @@ private:
 		std::vector<OBSEncoderAutoRelease> audio_encoders_;
 		OBSServiceAutoRelease multitrack_video_service_;
 		OBSSignal start_signal, stop_signal;
+		std::vector<OBSCanvasAutoRelease> canvases;
 	};
 
 	std::optional<OBSOutputObjects> take_current();

+ 3 - 1
frontend/wizards/AutoConfigStreamPage.cpp

@@ -173,8 +173,10 @@ bool AutoConfigStreamPage::validatePage()
 		wiz->testMultitrackVideo = ui->useMultitrackVideo->isChecked();
 
 		if (wiz->testMultitrackVideo) {
+			std::vector<OBSCanvasAutoRelease> canvases;
+			canvases.emplace_back(obs_get_main_canvas());
 			auto postData = constructGoLivePost(QString::fromStdString(wiz->key), std::nullopt,
-							    std::nullopt, false);
+							    std::nullopt, false, canvases);
 
 			OBSDataAutoRelease service_settings = obs_service_get_settings(service);
 			auto multitrack_video_name = QTStr("Basic.Settings.Stream.MultitrackVideoLabel");