blob: f48daae1c7597c82e5ef288d01cd0f4da034f99f [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* 'cr-camera' is a Polymer element used to take a picture from the
* user webcam to use as a Chrome OS profile picture.
*/
import '//resources/cr_elements/shared_style_css.m.js';
import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
import '//resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js';
import '//resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
import './icons.html.js';
import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './cr_camera.html.js';
import * as webcamUtils from './webcam_utils.js';
Polymer({
is: 'cr-camera',
_template: getTemplate(),
properties: {
/** Strings provided by host */
takePhotoLabel: String,
captureVideoLabel: String,
switchModeToCameraLabel: String,
switchModeToVideoLabel: String,
/** True if video mode is enabled. */
videoModeEnabled: {
type: Boolean,
value: false,
},
/**
* True if currently in video mode.
* @private {boolean}
*/
videomode: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
/**
* True when the camera is actually streaming video. May be false even when
* the camera is present and shown, but still initializing.
* @private {boolean}
*/
cameraOnline_: {
type: Boolean,
value: false,
},
},
/** @private {boolean} */
cameraStartInProgress_: false,
/** @private {boolean} */
cameraCaptureInProgress_: false,
/** @override */
attached() {
this.$.cameraVideo.addEventListener('canplay', function() {
this.$.userImageStreamCrop.classList.add('preview');
this.cameraOnline_ = true;
this.focusTakePhotoButton();
}.bind(this));
this.startCamera();
},
/** @override */
detached() {
this.stopCamera();
},
/** Only focuses the button if it's not disabled. */
focusTakePhotoButton() {
if (this.cameraOnline_) {
this.$.takePhoto.focus();
}
},
/**
* Performs photo capture from the live camera stream. A 'photo-taken' event
* will be fired as soon as captured photo is available, with the
* 'photoDataURL' property containing the photo encoded as a data URL.
*/
async takePhoto() {
if (!this.cameraOnline_ || this.cameraCaptureInProgress_) {
return;
}
this.cameraCaptureInProgress_ = true;
this.$.userImageStreamCrop.classList.remove('preview');
this.$.userImageStreamCrop.classList.add('capture');
const numFrames = this.videomode ?
webcamUtils.CAPTURE_DURATION_MS / webcamUtils.CAPTURE_INTERVAL_MS :
1;
try {
const frames = await webcamUtils.captureFrames(
/** @type {!HTMLVideoElement} */ (this.$.cameraVideo),
this.getCaptureSize_(), webcamUtils.CAPTURE_INTERVAL_MS, numFrames);
const photoDataUrl = webcamUtils.convertFramesToPng(frames);
this.fire('photo-taken', {photoDataUrl});
} catch (e) {
console.error(e);
}
this.$.userImageStreamCrop.classList.remove('capture');
this.cameraCaptureInProgress_ = false;
},
/** Tries to start the camera stream capture. */
startCamera() {
this.stopCamera();
this.cameraStartInProgress_ = true;
const successCallback = function(stream) {
if (this.cameraStartInProgress_) {
this.$.cameraVideo.srcObject = stream;
this.cameraStream_ = stream;
} else {
webcamUtils.stopMediaTracks(stream);
}
this.cameraStartInProgress_ = false;
}.bind(this);
const errorCallback = function() {
this.cameraOnline_ = false;
this.cameraStartInProgress_ = false;
}.bind(this);
navigator.webkitGetUserMedia(
{video: webcamUtils.kDefaultVideoConstraints}, successCallback,
errorCallback);
},
/** Stops the camera stream capture if it's currently active. */
stopCamera() {
this.$.userImageStreamCrop.classList.remove('preview');
this.cameraOnline_ = false;
this.$.cameraVideo.srcObject = null;
webcamUtils.stopMediaTracks(this.cameraStream_);
this.cameraStream_ = null;
// Cancel any pending getUserMedia() checks.
this.cameraStartInProgress_ = false;
},
/**
* Get the correct capture size for single photo or video mode.
* @return {{height: number, width: number}}
* @private
*/
getCaptureSize_() {
if (this.videomode) {
/** Reduce capture size when in video mode. */
return {
width: webcamUtils.CAPTURE_SIZE.width / 2,
height: webcamUtils.CAPTURE_SIZE.height / 2,
};
}
return webcamUtils.CAPTURE_SIZE;
},
/**
* Switch between photo and video mode.
* @private
*/
onTapSwitchMode_() {
this.videomode = !this.videomode;
this.fire('switch-mode', this.videomode);
},
/**
* @return {string}
* @private
*/
getTakePhotoIcon_() {
return this.videomode ? 'cr-picture:videocam-shutter-icon' :
'cr-picture:camera-shutter-icon';
},
/**
* Returns the label to use for take photo button.
* @return {string}
* @private
*/
getTakePhotoLabel_(videomode, photoLabel, videoLabel) {
return videomode ? videoLabel : photoLabel;
},
/**
* @return {string}
* @private
*/
getSwitchModeIcon_() {
return this.videomode ? 'cr-picture:camera-alt-icon' :
'cr-picture:videocam-icon';
},
/**
* Returns the label to use for switch mode button.
* @return {string}
* @private
*/
getSwitchModeLabel_(videomode, cameraLabel, videoLabel) {
return videomode ? cameraLabel : videoLabel;
},
});