Add support for up to 4 feeds

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-05-07 21:34:56 +02:00
parent 198722eb41
commit 1f27354439
No known key found for this signature in database
GPG key ID: 9760693FDD98A790
3 changed files with 71 additions and 35 deletions

View file

@ -21,7 +21,7 @@ limitations under the License.
} }
.mx_VideoFeed_remote { .mx_VideoFeed_primary {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
@ -33,7 +33,7 @@ limitations under the License.
} }
} }
.mx_VideoFeed_local { .mx_VideoFeed_secondary {
max-width: 25%; max-width: 25%;
max-height: 25%; max-height: 25%;
position: absolute; position: absolute;
@ -47,6 +47,34 @@ limitations under the License.
} }
} }
.mx_VideoFeed_tertiary {
max-width: 25%;
max-height: 25%;
position: absolute;
right: 10px;
bottom: 10px;
z-index: 100;
border-radius: 4px;
&.mx_VideoFeed_video {
background-color: transparent;
}
}
.mx_VideoFeed_quaternary {
max-width: 25%;
max-height: 25%;
position: absolute;
left: 10px;
top: 10px;
z-index: 100;
border-radius: 4px;
&.mx_VideoFeed_video {
background-color: transparent;
}
}
.mx_VideoFeed_mirror { .mx_VideoFeed_mirror {
transform: scale(-1, 1); transform: scale(-1, 1);
} }

View file

@ -33,6 +33,13 @@ import DialpadContextMenu from '../context_menus/DialpadContextMenu';
import { CallFeed } from 'matrix-js-sdk/src/webrtc/callFeed'; import { CallFeed } from 'matrix-js-sdk/src/webrtc/callFeed';
import {replaceableComponent} from "../../../utils/replaceableComponent"; import {replaceableComponent} from "../../../utils/replaceableComponent";
const FEED_CLASS_NAMES = [
"mx_VideoFeed_primary",
"mx_VideoFeed_secondary",
"mx_VideoFeed_tertiary",
"mx_VideoFeed_quaternary",
];
interface IProps { interface IProps {
// The call for us to display // The call for us to display
call: MatrixCall, call: MatrixCall,
@ -371,6 +378,34 @@ export default class CallView extends React.Component<IProps, IState> {
this.props.call.transferToCall(transfereeCall); this.props.call.transferToCall(transfereeCall);
} }
private renderFeeds(feeds: Array<CallFeed>, offset = 0) {
const sortedFeeds = [...feeds].sort((a, b) => {
if (b.purpose === SDPStreamMetadataPurpose.Screenshare && !b.isLocal()) return 1;
if (a.isLocal() && !b.isLocal()) return 1;
return 0;
});
return sortedFeeds.map((feed, i) => {
i += offset;
// TODO: Later the CallView should probably be reworked to support
// any number of feeds but now we can't render more than 4 feeds
if (i >= 4) return;
// Here we check to hide local audio feeds to achieve the same UI/UX
// as before. But once again this might be subject to change
if (feed.isVideoMuted() && feed.isLocal()) return;
return (
<VideoFeed
key={feed.stream.id}
className={FEED_CLASS_NAMES[i]}
feed={feed}
call={this.props.call}
pipMode={this.props.pipMode}
onResize={this.props.onResize}
/>
);
});
}
public render() { public render() {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const callRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call); const callRoomId = CallHandler.sharedInstance().roomIdForCall(this.props.call);
@ -594,20 +629,8 @@ export default class CallView extends React.Component<IProps, IState> {
mx_CallView_voice: true, mx_CallView_voice: true,
}); });
const feeds = this.props.call.getLocalFeeds().map((feed, i) => { // We pass offset of one to avoid a feed being rendered as primary
// Here we check to hide local audio feeds to achieve the same UI/UX const feeds = this.renderFeeds(this.props.call.getLocalFeeds(), 1);
// as before. But once again this might be subject to change
if (feed.isVideoMuted()) return;
return (
<VideoFeed
key={i}
feed={feed}
call={this.props.call}
pipMode={this.props.pipMode}
onResize={this.props.onResize}
/>
);
});
// Saying "Connecting" here isn't really true, but the best thing // Saying "Connecting" here isn't really true, but the best thing
// I can come up with, but this might be subject to change as well // I can come up with, but this might be subject to change as well
@ -631,23 +654,7 @@ export default class CallView extends React.Component<IProps, IState> {
mx_CallView_video: true, mx_CallView_video: true,
}); });
// TODO: Later the CallView should probably be reworked to support const feeds = this.renderFeeds(this.state.feeds);
// any number of feeds but now we can always expect there to be two
// feeds. This is because the js-sdk ignores any new incoming streams
const feeds = this.state.feeds.map((feed, i) => {
// Here we check to hide local audio feeds to achieve the same UI/UX
// as before. But once again this might be subject to change
if (feed.isVideoMuted() && feed.isLocal()) return;
return (
<VideoFeed
key={i}
feed={feed}
call={this.props.call}
pipMode={this.props.pipMode}
onResize={this.props.onResize}
/>
);
});
contentView = <div className={containerClasses} ref={this.contentRef} onMouseMove={this.onMouseMove}> contentView = <div className={containerClasses} ref={this.contentRef} onMouseMove={this.onMouseMove}>
{feeds} {feeds}

View file

@ -37,6 +37,8 @@ interface IProps {
// a callback which is called when the video element is resized // a callback which is called when the video element is resized
// due to a change in video metadata // due to a change in video metadata
onResize?: (e: Event) => void, onResize?: (e: Event) => void,
className: string,
} }
interface IState { interface IState {
@ -121,8 +123,7 @@ export default class VideoFeed extends React.Component<IProps, IState> {
render() { render() {
const videoClasses = { const videoClasses = {
mx_VideoFeed: true, mx_VideoFeed: true,
mx_VideoFeed_local: this.props.feed.isLocal(), [this.props.className]: true,
mx_VideoFeed_remote: !this.props.feed.isLocal(),
mx_VideoFeed_voice: this.state.videoMuted, mx_VideoFeed_voice: this.state.videoMuted,
mx_VideoFeed_video: !this.state.videoMuted, mx_VideoFeed_video: !this.state.videoMuted,
mx_VideoFeed_mirror: ( mx_VideoFeed_mirror: (