image-capture
Version:
MediaStream ImageCapture polyfill: takePhoto(), grabFrame() and more
165 lines (152 loc) • 6.58 kB
JavaScript
/**
* MediaStream ImageCapture polyfill
*
* @license
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export let ImageCapture = window.ImageCapture;
if (typeof ImageCapture === 'undefined') {
ImageCapture = class {
/**
* TODO https://www.w3.org/TR/image-capture/#constructors
*
* @param {MediaStreamTrack} videoStreamTrack - A MediaStreamTrack of the 'video' kind
*/
constructor(videoStreamTrack) {
if (videoStreamTrack.kind !== 'video')
throw new DOMException('NotSupportedError');
this._videoStreamTrack = videoStreamTrack;
if (!('readyState' in this._videoStreamTrack)) {
// Polyfill for Firefox
this._videoStreamTrack.readyState = 'live';
}
// 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);
});
if (HTMLMediaElement) {
this.videoElement.srcObject = this._previewStream; // Safari 11 doesn't allow use of createObjectURL for MediaStream
} else {
this.videoElement.src = URL.createObjectURL(this._previewStream);
}
this.videoElement.muted = true;
this.videoElement.setAttribute('playsinline', ''); // Required by Safari on iOS 11. See https://webkit.org/blog/6784
this.videoElement.play();
this.canvasElement = document.createElement('canvas');
// TODO Firefox has https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
this.canvas2dContext = this.canvasElement.getContext('2d');
}
/**
* https://w3c.github.io/mediacapture-image/index.html#dom-imagecapture-videostreamtrack
* @return {MediaStreamTrack} The MediaStreamTrack passed into the constructor
*/
get videoStreamTrack() {
return this._videoStreamTrack;
}
/**
* 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
*/
getPhotoCapabilities() {
return new Promise(function executorGPC(resolve, reject) {
// TODO see https://github.com/w3c/mediacapture-image/issues/97
const MediaSettingsRange = {
current: 0, min: 0, max: 0,
};
resolve({
exposureCompensation: MediaSettingsRange,
exposureMode: 'none',
fillLightMode: 'none',
focusMode: 'none',
imageHeight: MediaSettingsRange,
imageWidth: MediaSettingsRange,
iso: MediaSettingsRange,
redEyeReduction: false,
whiteBalanceMode: 'none',
zoom: MediaSettingsRange,
});
reject(new DOMException('OperationError'));
});
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-setoptions
* @param {Object} photoSettings - Photo settings dictionary, https://www.w3.org/TR/image-capture/#idl-def-photosettings
* @return {Promise<void>} Fulfilled promise on success, rejected promise on failure
*/
setOptions(photoSettings = {}) {
return new Promise(function executorSO(resolve, reject) {
// TODO
});
}
/**
* 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
*/
takePhoto() {
const 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') {
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'));
}
});
});
}
/**
* 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
*/
grabFrame() {
const self = this;
return new Promise(function executorGF(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') {
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'));
}
});
});
}
};
}
window.ImageCapture = ImageCapture;