Skip to content

Commit

Permalink
Wait until video is ready before capture (GoogleChromeLabs#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
andsouto authored and dandv committed Oct 28, 2017
1 parent f629515 commit 722b923
Showing 1 changed file with 36 additions and 38 deletions.
74 changes: 36 additions & 38 deletions src/imagecapture.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ if (typeof ImageCapture === 'undefined') {
// MediaStream constructor not available until Chrome 55 - https://www.chromestatus.com/feature/5912172546752512
this._previewStream = new MediaStream([videoStreamTrack]);
this.videoElement = document.createElement('video');
this.videoElementPlaying = new Promise(resolve => {
this.videoElement.addEventListener('playing', resolve);
});
this.videoElement.src = URL.createObjectURL(this._previewStream);
this.videoElement.muted = true;
this.videoElement.play(); // required by Firefox
Expand All @@ -59,7 +62,9 @@ if (typeof ImageCapture === 'undefined') {

/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-getphotocapabilities
* @return {Promise<PhotoCapabilities>} Fulfilled promise with [PhotoCapabilities](https://www.w3.org/TR/image-capture/#idl-def-photocapabilities) object on success, rejected promise on failure
* @return {Promise<PhotoCapabilities>} Fulfilled promise with
* [PhotoCapabilities](https://www.w3.org/TR/image-capture/#idl-def-photocapabilities)
* object on success, rejected promise on failure
*/
getPhotoCapabilities() {
return new Promise(function executorGPC(resolve, reject) {
Expand Down Expand Up @@ -97,63 +102,56 @@ if (typeof ImageCapture === 'undefined') {
/**
* TODO
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-takephoto
* @return {Promise<Blob>} Fulfilled promise with [Blob](https://www.w3.org/TR/FileAPI/#blob) argument on success; rejected promise on failure
* @return {Promise<Blob>} Fulfilled promise with [Blob](https://www.w3.org/TR/FileAPI/#blob)
* argument on success; rejected promise on failure
*/
takePhoto() {
let self = this;
return new Promise(function executorTP(resolve, reject) {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState === 'live') {
// -- however, checking for `live` alone doesn't guarantee the video is ready
if (self.videoElement.videoWidth) {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
self.canvasElement.toBlob(blob => {
resolve(blob);
});
} catch (error) {
reject(new DOMException('UnknownError'));
}
} else {
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
self.canvasElement.toBlob(resolve);
} catch (error) {
reject(new DOMException('UnknownError'));
}
} else {
reject(new DOMException('InvalidStateError'));
}
});
});
}

/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-grabframe
* @return {Promise<ImageBitmap>} Fulfilled promise with [ImageBitmap](https://www.w3.org/TR/html51/webappapis.html#webappapis-images) argument on success; rejected promise on failure
* @return {Promise<ImageBitmap>} Fulfilled promise with
* [ImageBitmap](https://www.w3.org/TR/html51/webappapis.html#webappapis-images)
* argument on success; rejected promise on failure
*/
grabFrame() {
let self = this;
return new Promise(function executorGF(resolve, reject) {
if (self._videoStreamTrack.readyState === 'live') {
if (self.videoElement.videoWidth) {
try {
// videoWidth is available after videoElement.onloadedmetadata fires
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
// The video has an image after videoElement.oncanplay triggers
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
// TODO polyfill https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapFactories/createImageBitmap for IE
resolve(window.createImageBitmap(self.canvasElement));
} catch (error) {
reject(new DOMException('UnknownError'));
}
} else {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
// TODO polyfill https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapFactories/createImageBitmap for IE
resolve(window.createImageBitmap(self.canvasElement));
} catch (error) {
reject(new DOMException('UnknownError'));
}
} else {
reject(new DOMException('InvalidStateError'));
}
});
});
}

};
}

0 comments on commit 722b923

Please sign in to comment.