better error handling in video decoding

This commit is contained in:
Bruno Windels 2021-03-10 13:40:11 +01:00
parent e8c8455f43
commit a672b0c78a
2 changed files with 50 additions and 10 deletions

View File

@ -106,11 +106,16 @@ export class BaseMediaTile extends MessageTile {
get error() { get error() {
if (this._error) { if (this._error) {
return `Could not decrypt media: ${this._error.message}`; return `Could not load media: ${this._error.message}`;
} }
return null; return null;
} }
setViewError(err) {
this._error = err;
this.emitChange("error");
}
async _loadEncryptedFile(file) { async _loadEncryptedFile(file) {
const blob = await this._mediaRepository.downloadEncryptedFile(file, true); const blob = await this._mediaRepository.downloadEncryptedFile(file, true);
if (this.isDisposed) { if (this.isDisposed) {

View File

@ -15,10 +15,11 @@ limitations under the License.
*/ */
import {BaseMediaView} from "./BaseMediaView.js"; import {BaseMediaView} from "./BaseMediaView.js";
import {domEventAsPromise} from "../../../../dom/utils.js";
export class VideoView extends BaseMediaView { export class VideoView extends BaseMediaView {
renderMedia(t, vm) { renderMedia(t) {
return t.video({ const video = t.video({
// provide empty data url if video is not decrypted yet. // provide empty data url if video is not decrypted yet.
// Chrome/Electron need this to enable the play button. // Chrome/Electron need this to enable the play button.
src: vm => vm.videoUrl || `data:${vm.mimeType},`, src: vm => vm.videoUrl || `data:${vm.mimeType},`,
@ -26,13 +27,47 @@ export class VideoView extends BaseMediaView {
controls: true, controls: true,
preload: "none", preload: "none",
poster: vm => vm.thumbnailUrl, poster: vm => vm.thumbnailUrl,
onPlay: async evt => { onPlay: this._onPlay.bind(this),
if (!vm.videoUrl) { style: vm => `max-width: ${vm.width}px; max-height: ${vm.height}px;${vm.isPending ? "z-index: -1": ""}`
await vm.loadVideo();
evt.target.play();
}
},
style: `max-width: ${vm.width}px; max-height: ${vm.height}px;`
}); });
video.addEventListener("error", this._onError.bind(this));
return video;
}
async _onPlay(evt) {
const vm = this.value;
// download and decrypt the video if needed,
if (!vm.videoUrl) {
try {
const video = evt.target;
// this will trigger the src to update
await vm.loadVideo();
// important to only listen for this after src has changed,
// or we get the error for the placeholder data url
const loadPromise = domEventAsPromise(video, "loadeddata");
// now, reload the video and play
video.load();
await loadPromise;
video.play();
} catch (err) {/* errors are already caught in error event handler */}
}
}
_onError(evt) {
const vm = this.value;
const video = evt.target;
const err = video.error;
if (err instanceof window.MediaError && err.code === 4) {
if (!video.src.startsWith("data:")) {
vm.setViewError(new Error(`this browser does not support videos of type ${vm.mimeType}.`));
} else {
// ignore placeholder url failing to load
return;
}
} else {
vm.setViewError(err);
}
} }
} }