Fix encrypted video playback in Chrome-based browsers

For Chrome-based browsers, it seems we need to set some non-empty `src` URI for
the video element's play button to be enabled, so this crafts an empty `data`
URI and ensures playing is triggered once the real content has been fetched.

Fixes https://github.com/vector-im/element-web/issues/15694
Regressed by https://github.com/matrix-org/matrix-react-sdk/pull/5352
This commit is contained in:
J. Ryan Stinnett 2020-11-19 13:26:14 +00:00
parent f7e2d70ddf
commit 3e85b6d085

View file

@ -39,6 +39,8 @@ interface IState {
} }
export default class MVideoBody extends React.PureComponent<IProps, IState> { export default class MVideoBody extends React.PureComponent<IProps, IState> {
private videoRef = React.createRef<HTMLVideoElement>();
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -80,6 +82,11 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
} }
private hasContentUrl(): boolean {
const url = this.getContentUrl();
return url && !url.startsWith("data:");
}
private getThumbUrl(): string|null { private getThumbUrl(): string|null {
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined) { if (content.file !== undefined) {
@ -118,7 +125,10 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} else { } else {
console.log("NOT preloading video"); console.log("NOT preloading video");
this.setState({ this.setState({
decryptedUrl: null, // For Chrome and Electron, we need to set some non-empty `src` to
// enable the play button. Firefox does not seem to care either
// way, so it's fine to do for all browsers.
decryptedUrl: `data:${content?.info?.mimetype},`,
decryptedThumbnailUrl: thumbnailUrl, decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: null, decryptedBlob: null,
}); });
@ -143,7 +153,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
private videoOnPlay = async () => { private videoOnPlay = async () => {
if (this.getContentUrl() || this.state.fetchingData || this.state.error) { if (this.hasContentUrl() || this.state.fetchingData || this.state.error) {
// We have the file, we are fetching the file, or there is an error. // We have the file, we are fetching the file, or there is an error.
return; return;
} }
@ -164,6 +174,9 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
decryptedUrl: contentUrl, decryptedUrl: contentUrl,
decryptedBlob: decryptedBlob, decryptedBlob: decryptedBlob,
fetchingData: false, fetchingData: false,
}, () => {
if (!this.videoRef.current) return;
this.videoRef.current.play();
}); });
this.props.onHeightChanged(); this.props.onHeightChanged();
} }
@ -215,9 +228,20 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
return ( return (
<span className="mx_MVideoBody"> <span className="mx_MVideoBody">
<video className="mx_MVideoBody" src={contentUrl} title={content.body} <video
controls preload={preload} muted={autoplay} autoPlay={autoplay} className="mx_MVideoBody"
height={height} width={width} poster={poster} onPlay={this.videoOnPlay}> ref={this.videoRef}
src={contentUrl}
title={content.body}
controls
preload={preload}
muted={autoplay}
autoPlay={autoplay}
height={height}
width={width}
poster={poster}
onPlay={this.videoOnPlay}
>
</video> </video>
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} /> <MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
</span> </span>