Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only clone streams on Safari #2450

Merged
merged 1 commit into from
Jun 10, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions src/webrtc/groupCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,33 @@ function getCallUserId(call: MatrixCall): string | null {
return call.getOpponentMember()?.userId || call.invitee || null;
}

/**
* Returns a call feed for passing to a new call in the group call. The media
* This could be either return the passed feed as-is or a clone, depending on the
* platform.
* @returns CallFeed
*/
function feedForNewCallFromFeed(feed: CallFeed): CallFeed {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);

// Safari can't send a MediaStream to multiple sources, so we clone it,
// however cloning mediastreams on Chrome appears to cause the audio renderer
// to become unstable and hang: https://github.com/vector-im/element-call/issues/267
// It's a bit arbitrary what we do for other browsers: I've made Safari the special
// case on a somewhat arbitrary basis.
// To retest later to see if this hack is still necessary:
// * In Safari, you should be able to have a group call with 2 other people and both
// of them see your video stream (either desktop or mobile Safari)
// * In Chrome, you should be able to enter a call and then go to youtube and play
// a video (both desktop & Android Chrome, although in Android you may have to
// open YouTube in incognito mode to avoid being redirected to the app.)
if (isSafari) {
return feed.clone();
}

return feed;
}

export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventHandlerMap> {
// Config
public activeSpeakerInterval = 1000;
Expand Down Expand Up @@ -552,7 +579,9 @@ export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventH
);

// TODO: handle errors
await Promise.all(this.calls.map(call => call.pushLocalFeed(this.localScreenshareFeed.clone())));
await Promise.all(this.calls.map(call => call.pushLocalFeed(
feedForNewCallFromFeed(this.localScreenshareFeed),
)));

await this.sendMemberStateEvent();

Expand Down Expand Up @@ -626,8 +655,7 @@ export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventH
this.addCall(newCall);
}

// Safari can't send a MediaStream to multiple sources, so clone it
newCall.answerWithCallFeeds(this.getLocalFeeds().map((feed) => feed.clone()));
newCall.answerWithCallFeeds(this.getLocalFeeds().map((feed) => feedForNewCallFromFeed(feed)));
};

/**
Expand Down Expand Up @@ -787,9 +815,8 @@ export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventH
(feed) => feed.purpose === SDPStreamMetadataPurpose.Screenshare);

try {
// Safari can't send a MediaStream to multiple sources, so clone it
await newCall.placeCallWithCallFeeds(
this.getLocalFeeds().map(feed => feed.clone()),
this.getLocalFeeds().map(feed => feedForNewCallFromFeed(feed)),
requestScreenshareFeed,
);
} catch (e) {
Expand Down