Merge pull request #5430 from matrix-org/jryans/fix-encrypted-videos

Fix encrypted video playback in Chrome-based browsers
This commit is contained in:
J. Ryan Stinnett 2020-11-19 13:59:35 +00:00 committed by GitHub
commit dea4fd661a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

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 = {
@ -71,7 +73,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
} }
_getContentUrl(): string|null { private getContentUrl(): string|null {
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined) { if (content.file !== undefined) {
return this.state.decryptedUrl; return this.state.decryptedUrl;
@ -80,7 +82,12 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
} }
_getThumbUrl(): string|null { private hasContentUrl(): boolean {
const url = this.getContentUrl();
return url && !url.startsWith("data:");
}
private getThumbUrl(): string|null {
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined) { if (content.file !== undefined) {
return this.state.decryptedThumbnailUrl; return this.state.decryptedThumbnailUrl;
@ -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,
}); });
@ -142,8 +152,8 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
} }
} }
async _videoOnPlay() { 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();
} }
@ -195,8 +208,8 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
); );
} }
const contentUrl = this._getContentUrl(); const contentUrl = this.getContentUrl();
const thumbUrl = this._getThumbUrl(); const thumbUrl = this.getThumbUrl();
let height = null; let height = null;
let width = null; let width = null;
let poster = null; let poster = null;
@ -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.bind(this)}> 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>