Skip to content

Commit ca31c0a

Browse files
Mimic Twitch dual stream display coercion for YouTube dual stream. (#6178)
* Fix dual stream display coercion and enhanced broadcasting disabling. * Fix title persistence. * Remove redundant if * YouTube mirror Twitch dual stream coercion. * Add missing streaming view changes. * Update youtube.ts --------- Co-authored-by: mhoyer-streamlabs <mhoyer@logitech.com>
1 parent 92c837d commit ca31c0a

5 files changed

Lines changed: 47 additions & 48 deletions

File tree

app/components-react/windows/go-live/useGoLiveSettings.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,7 @@ export class GoLiveSettingsModule {
644644
// Go Live window.
645645
const willDualStream = this.state.enabledPlatforms.some(
646646
(platform: TPlatform) =>
647-
this.state.getCanDualStream(platform) &&
648-
this.state.settings.platforms[platform]?.display === 'both',
647+
this.state.getCanDualStream(platform) && this.state.isDualStreaming(platform),
649648
);
650649

651650
const numTargets =
@@ -946,9 +945,7 @@ export class GoLiveSettingsModule {
946945
get nonPrimeBothDisplayPlatform(): TPlatform | null {
947946
if (this.isPrime) return null;
948947
return (
949-
this.state.enabledPlatforms.find(
950-
platform => this.state.settings.platforms[platform]?.display === 'both',
951-
) ?? null
948+
this.state.enabledPlatforms.find(platform => this.state.isDualStreaming(platform)) ?? null
952949
);
953950
}
954951

app/services/platforms/twitch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export class TwitchService
246246
// depends on whether this stream is an enhanced broadcast — deciding afterwards means the
247247
// check answers for the previous stream instead of this one.
248248
if (channelInfo) {
249-
if (this.streamingService.views.isLiveOutputEditingEnabled) {
249+
if (goLiveSettings?.liveOutputEditing) {
250250
await this.setupLiveOutputStream(goLiveSettings);
251251
} else if (channelInfo.display === 'both') {
252252
await this.setupDualStream(goLiveSettings);

app/services/platforms/youtube.ts

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,6 @@ interface IYoutubeServiceState extends IPlatformState {
3737
broadcastStatus: TBroadcastLifecycleStatus | '';
3838
settings: IYoutubeStartStreamOptions;
3939
categories: IYoutubeCategory[];
40-
backupStreamSettings?: IBackUpStreamSettings;
41-
}
42-
43-
interface IBackUpStreamSettings {
44-
service: string;
45-
key: string;
46-
server: string;
47-
streamType: 'rtmp_common' | 'rtmp_custom' | 'whip_custom';
48-
context: TDisplayType;
4940
}
5041

5142
export interface IYoutubeStartStreamOptions extends IExtraBroadcastSettings {
@@ -488,7 +479,29 @@ export class YoutubeService
488479
this.setPlatformContext('youtube');
489480
}
490481

482+
/**
483+
* Prepare the stream for live output editing
484+
* @remark Live output editing cannot dual stream. As a safety measure, if there is
485+
* any local vertical broadcast data on state, clear it
486+
*/
487+
async setupLiveOutputStream(options?: IGoLiveSettings): Promise<void> {
488+
if (!this.state.verticalStreamKey && !this.state.verticalBroadcast.id) return;
489+
490+
const destinations = this.streamingService.views.customDestinations.filter(
491+
dest => dest.streamKey !== this.state.verticalStreamKey,
492+
);
493+
494+
this.SET_VERTICAL_BROADCAST({} as IYoutubeLiveBroadcast);
495+
this.SET_VERTICAL_STREAM_KEY('');
496+
this.streamSettingsService.setGoLiveSettings({ customDestinations: destinations });
497+
}
498+
491499
async setupDualStream(goLiveSettings: IGoLiveSettings) {
500+
// Live output editing currently cannot use dual stream so guard against it
501+
if (goLiveSettings.liveOutputEditing) {
502+
return;
503+
}
504+
492505
const ytSettings = getDefined(goLiveSettings.platforms.youtube);
493506
const title = makeVerticalTitle(ytSettings.title);
494507

@@ -602,17 +615,6 @@ export class YoutubeService
602615
// setup key and platform type in the OBS settings
603616
const streamKey = stream.cdn.ingestionInfo.streamName;
604617

605-
//save user's current rtmp_common settings to restore after Go Live since they are overwritten here
606-
const currentSettings = this.streamSettingsService.settings;
607-
if (!this.state.backupStreamSettings) {
608-
this.state.backupStreamSettings = {} as IBackUpStreamSettings;
609-
}
610-
this.state.backupStreamSettings.service = currentSettings.service;
611-
this.state.backupStreamSettings.key = currentSettings.key;
612-
this.state.backupStreamSettings.server = currentSettings.server;
613-
this.state.backupStreamSettings.streamType = currentSettings.streamType;
614-
this.state.backupStreamSettings.context = !context ? 'horizontal' : context;
615-
616618
if (!this.streamingService.views.isMultiplatformMode) {
617619
// Note: This was previously changed to `rtmp_custom` for dual streaming but
618620
// it now works with `rtmp_common` as well.
@@ -627,7 +629,10 @@ export class YoutubeService
627629
);
628630
}
629631

630-
if (ytSettings.display === 'both') {
632+
// Live output editing is checked first so dual stream is never set up when live output editing is enabled.
633+
if (goLiveSettings.liveOutputEditing) {
634+
await this.setupLiveOutputStream(goLiveSettings);
635+
} else if (ytSettings.display === 'both') {
631636
try {
632637
// Prevent rate limit errors by delaying the dual stream setup by 1 second
633638
await new Promise<void>(resolve => {
@@ -684,20 +689,6 @@ export class YoutubeService
684689
this.SET_VERTICAL_BROADCAST({} as IYoutubeLiveBroadcast);
685690
this.SET_VERTICAL_STREAM_KEY('');
686691
this.streamSettingsService.setGoLiveSettings({ customDestinations: destinations });
687-
688-
//restore user's previous settings in case they were overwritten on Go Live
689-
if (this.state.backupStreamSettings) {
690-
this.streamSettingsService.setSettings(
691-
{
692-
platform: 'youtube',
693-
service: this.state.backupStreamSettings.service,
694-
key: this.state.backupStreamSettings.key,
695-
streamType: this.state.backupStreamSettings.streamType,
696-
server: this.state.backupStreamSettings.server,
697-
},
698-
this.state.backupStreamSettings.context,
699-
);
700-
}
701692
}
702693

703694
/**

app/services/streaming/streaming-view.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,21 @@ export class StreamInfoView<T extends Object> extends ViewHandler<T> {
195195
);
196196
}
197197

198+
/**
199+
* Whether a platform is set to dual stream to both displays
200+
* @remark The saved `both` display is deliberately left intact in `getSavedPlatformSettings`
201+
* because reassigning it would destroy the user's dual stream choice.
202+
* @param platform - The platform to check
203+
*/
204+
isDualStreaming(platform: TPlatform): boolean {
205+
if (this.isLiveOutputEditingEnabled) return false;
206+
return this.settings.platforms[platform]?.display === 'both';
207+
}
208+
198209
get isTwitchDualStreamEnabled() {
199210
// Twitch dual stream requires enhanced broadcasting, which is not available with live output editing
200211
// because enhanced broadcasting cannot use restream service due to api requirements
212+
// Note: redundant with the guard inside `isDualStreaming`, kept as defence in depth
201213
if (this.isLiveOutputEditingEnabled) {
202214
return false;
203215
}
@@ -209,7 +221,7 @@ export class StreamInfoView<T extends Object> extends ViewHandler<T> {
209221
return (
210222
this.settings.platforms?.twitch &&
211223
this.enabledPlatforms.includes('twitch') &&
212-
this.settings.platforms?.twitch.display === 'both'
224+
this.isDualStreaming('twitch')
213225
);
214226
}
215227

@@ -221,7 +233,7 @@ export class StreamInfoView<T extends Object> extends ViewHandler<T> {
221233
return (
222234
this.settings.platforms?.youtube &&
223235
this.enabledPlatforms.includes('youtube') &&
224-
this.settings.platforms?.youtube?.display === 'both'
236+
this.isDualStreaming('youtube')
225237
);
226238
}
227239

@@ -473,7 +485,7 @@ export class StreamInfoView<T extends Object> extends ViewHandler<T> {
473485

474486
// if the platform is set to 'both' display, add it to both horizontal and vertical
475487
// for analytics purposes
476-
if (this.settings.platforms[platform]?.display === 'both') {
488+
if (this.isDualStreaming(platform)) {
477489
displayPlatforms.vertical.push(platform);
478490
}
479491

@@ -577,8 +589,7 @@ export class StreamInfoView<T extends Object> extends ViewHandler<T> {
577589
get hasDualStream() {
578590
return this.enabledPlatforms.some(
579591
(platform: TPlatform) =>
580-
this.supports('dualStream', [platform]) &&
581-
this.settings.platforms[platform]?.display === 'both',
592+
this.supports('dualStream', [platform]) && this.isDualStreaming(platform),
582593
);
583594
}
584595

app/services/streaming/streaming.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,13 +973,13 @@ export class StreamingService
973973
}
974974

975975
// send analytics for YouTube
976-
if (settings.platforms.youtube?.enabled && settings.platforms.youtube.display === 'both') {
976+
if (settings.platforms.youtube?.enabled && this.views.isDualStreaming('youtube')) {
977977
this.usageStatisticsService.recordFeatureUsage('StreamToYouTubeBothOutputs');
978978
}
979979

980980
// send analytics for Twitch
981981
if (settings.platforms.twitch?.enabled) {
982-
if (settings.platforms.twitch.display === 'both') {
982+
if (this.views.isDualStreaming('twitch')) {
983983
this.usageStatisticsService.recordFeatureUsage('StreamToTwitchBothOutputs');
984984
} else if (this.state.enhancedBroadcasting) {
985985
// Note: use the service state because the Twitch settings stores the user's enhanced broadcasting setting

0 commit comments

Comments
 (0)