Make CallView use CallFeed

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-03-07 08:13:35 +01:00
parent 62e9d7f46b
commit bb13dc49a6
No known key found for this signature in database
GPG key ID: 9760693FDD98A790
5 changed files with 185 additions and 68 deletions

View file

@ -18,50 +18,81 @@ import classnames from 'classnames';
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import React, {createRef} from 'react';
import SettingsStore from "../../../settings/SettingsStore";
export enum VideoFeedType {
Local,
Remote,
}
import { CallFeed, CallFeedEvent } from 'matrix-js-sdk/src/webrtc/callFeed';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { logger } from 'matrix-js-sdk/src/logger';
import MemberAvatar from "../avatars/MemberAvatar"
import CallHandler from '../../../CallHandler';
interface IProps {
call: MatrixCall,
type: VideoFeedType,
feed: CallFeed,
// Whether this call view is for picture-in-pictue mode
// otherwise, it's the larger call view when viewing the room the call is in.
// This is sort of a proxy for a number of things but we currently have no
// need to control those things separately, so this is simpler.
pipMode?: boolean;
// a callback which is called when the video element is resized
// due to a change in video metadata
onResize?: (e: Event) => void,
}
export default class VideoFeed extends React.Component<IProps> {
interface IState {
audioOnly: boolean;
}
export default class VideoFeed extends React.Component<IProps, IState> {
private vid = createRef<HTMLVideoElement>();
componentDidMount() {
this.vid.current.addEventListener('resize', this.onResize);
this.setVideoElement();
constructor(props: IProps) {
super(props);
this.state = {
audioOnly: this.props.feed.isAudioOnly(),
};
}
componentDidUpdate(prevProps) {
if (this.props.call !== prevProps.call) {
this.setVideoElement();
componentDidMount() {
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
if (!this.vid.current) return;
// A note on calling methods on media elements:
// We used to have queues per media element to serialise all calls on those elements.
// The reason given for this was that load() and play() were racing. However, we now
// never call load() explicitly so this seems unnecessary. However, serialising every
// operation was causing bugs where video would not resume because some play command
// had got stuck and all media operations were queued up behind it. If necessary, we
// should serialise the ones that need to be serialised but then be able to interrupt
// them with another load() which will cancel the pending one, but since we don't call
// load() explicitly, it shouldn't be a problem. - Dave
this.vid.current.srcObject = this.props.feed.stream;
this.vid.current.autoplay = true;
this.vid.current.muted = true;
try {
this.vid.current.play();
} catch (e) {
logger.info("Failed to play video element with feed", this.props.feed, e);
}
}
componentWillUnmount() {
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
if (!this.vid.current) return;
this.vid.current.removeEventListener('resize', this.onResize);
this.vid.current.pause();
this.vid.current.srcObject = null;
}
private setVideoElement() {
if (this.props.type === VideoFeedType.Local) {
this.props.call.setLocalVideoElement(this.vid.current);
} else {
this.props.call.setRemoteVideoElement(this.vid.current);
}
onNewStream = (newStream: MediaStream) => {
this.setState({ audioOnly: this.props.feed.isAudioOnly()});
if (!this.vid.current) return;
this.vid.current.srcObject = newStream;
}
onResize = (e) => {
if (this.props.onResize) {
if (this.props.onResize && !this.props.feed.isLocal()) {
this.props.onResize(e);
}
};
@ -69,14 +100,35 @@ export default class VideoFeed extends React.Component<IProps> {
render() {
const videoClasses = {
mx_VideoFeed: true,
mx_VideoFeed_local: this.props.type === VideoFeedType.Local,
mx_VideoFeed_remote: this.props.type === VideoFeedType.Remote,
mx_VideoFeed_local: this.props.feed.isLocal(),
mx_VideoFeed_remote: !this.props.feed.isLocal(),
mx_VideoFeed_voice: this.state.audioOnly,
mx_VideoFeed_video: !this.state.audioOnly,
mx_VideoFeed_mirror: (
this.props.type === VideoFeedType.Local &&
this.props.feed.isLocal() &&
SettingsStore.getValue('VideoView.flipVideoHorizontally')
),
};
return <video className={classnames(videoClasses)} ref={this.vid} />;
if (this.state.audioOnly) {
const callRoomId = CallHandler.roomIdForCall(this.props.call);
const callRoom = MatrixClientPeg.get().getRoom(callRoomId);
const member = callRoom.getMember(this.props.feed.userId);
const avatarSize = this.props.pipMode ? 76 : 160;
return (
<div className={classnames(videoClasses)} >
<MemberAvatar
member={member}
height={avatarSize}
width={avatarSize}
/>
</div>
);
} else {
return (
<video className={classnames(videoClasses)} ref={this.vid} />
);
}
}
}