Commit aa8afe0f authored by Naoki Fukino's avatar Naoki Fukino Committed by Commit Bot

Files app: Abort generating thumbnail of unseekable video.

In some vide files, there is no I-frame after the first frame.
In this case, seeking to some position may end up loading entire video.
To avoid the issue in Image Loader, we should abort loading video file
when we don't receive 'canplay' event for a while.

Bug: 777521
Test: Manually tested using the video on the issue.
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Icb37b457d7753c46592894993a235840d0c92fd7
Reviewed-on: https://chromium-review.googlesource.com/888233
Commit-Queue: Naoki Fukino <fukino@chromium.org>
Reviewed-by: default avatarTatsuhisa Yamaguchi <yamaguchi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532315}
parent 31233e09
...@@ -111,6 +111,14 @@ function ImageRequest(id, cache, piexLoader, request, callback) { ...@@ -111,6 +111,14 @@ function ImageRequest(id, cache, piexLoader, request, callback) {
*/ */
ImageRequest.VIDEO_THUMBNAIL_POSITION = 3; // [sec] ImageRequest.VIDEO_THUMBNAIL_POSITION = 3; // [sec]
/**
* The maximum milliseconds to load video. If loading video exceeds the limit,
* we give up generating video thumbnail and free the consumed memory.
* @const
* @type {number}
*/
ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO = 3000;
/** /**
* Returns ID of the request. * Returns ID of the request.
* @return {string} Request ID. * @return {string} Request ID.
...@@ -299,20 +307,34 @@ ImageRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) { ...@@ -299,20 +307,34 @@ ImageRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) {
* @private * @private
*/ */
ImageRequest.prototype.createVideoThumbnailUrl_ = function(url) { ImageRequest.prototype.createVideoThumbnailUrl_ = function(url) {
var video = document.createElement('video'); const video = document.createElement('video');
return new Promise(function(resolve, reject) { return Promise
video.addEventListener('canplay', resolve); .race([
video.addEventListener('error', reject); new Promise((resolve, reject) => {
video.currentTime = ImageRequest.VIDEO_THUMBNAIL_POSITION; video.addEventListener('canplay', resolve);
video.preload = 'auto'; video.addEventListener('error', reject);
video.src = url; video.currentTime = ImageRequest.VIDEO_THUMBNAIL_POSITION;
}).then(function() { video.preload = 'auto';
var canvas = document.createElement('canvas'); video.src = url;
canvas.width = video.videoWidth; }),
canvas.height = video.videoHeight; new Promise((resolve) => {
canvas.getContext('2d').drawImage(video, 0, 0); setTimeout(resolve, ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO);
return canvas.toDataURL(); }).then(() => {
}); // If we don't receive 'canplay' event after 3 seconds have passed for
// some reason (e.g. unseekable video), we give up generating
// thumbnail.
video.src =
''; // Make sure to stop loading remaining part of the video.
throw new Error('Seeking video failed.');
})
])
.then(() => {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
return canvas.toDataURL();
});
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment