Make CallView use CallFeed
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
62e9d7f46b
commit
bb13dc49a6
5 changed files with 185 additions and 68 deletions
|
@ -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} />
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue