Commit 92f71e41 authored by Wei Lee's avatar Wei Lee Committed by Commit Bot

Refactor imagecapture.js for readability and extendibility

Since imagecapture.js starts to contain many things that has nothing to
do with ImageCapture, this CL refactor it as following:

1. Splits imagecapture.js into MojoConnector, DeviceOperator and
   ImageCapture to make it clearer and more extensible.
2. Avoids using singleton pattern for mojo connection.
3. For camera Hal v1, simplifies the check and remove try/catch
   mechanism.

Bug: 979104
Test: tast run DUT camera.CCA*
Test: Running above test for v1/v3 devices

Change-Id: I8b93e05a566ae81731c1e8f71cd00baa82d641f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1739035
Commit-Queue: Wei Lee <wtlee@chromium.org>
Reviewed-by: default avatarShik Chen <shik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#690181}
parent 1e7ad256
...@@ -169,7 +169,9 @@ copy("chrome_camera_app_js_models") { ...@@ -169,7 +169,9 @@ copy("chrome_camera_app_js_models") {
copy("chrome_camera_app_js_mojo") { copy("chrome_camera_app_js_mojo") {
sources = [ sources = [
"src/js/mojo/imagecapture.js", "src/js/mojo/device_operator.js",
"src/js/mojo/image_capture.js",
"src/js/mojo/mojo_connector.js",
] ]
outputs = [ outputs = [
......
...@@ -130,7 +130,9 @@ RESOURCES = \ ...@@ -130,7 +130,9 @@ RESOURCES = \
src/js/models/gallery.js \ src/js/models/gallery.js \
src/js/models/result_saver.js \ src/js/models/result_saver.js \
src/js/models/video_saver.js \ src/js/models/video_saver.js \
src/js/mojo/imagecapture.js \ src/js/mojo/device_operator.js \
src/js/mojo/image_capture.js \
src/js/mojo/mojo_connector.js \
src/js/nav.js \ src/js/nav.js \
src/js/resolution_event_broker.js \ src/js/resolution_event_broker.js \
src/js/scrollbar.js \ src/js/scrollbar.js \
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
<structure name="IDR_CAMERA_CAMERA3_DEVICE_INFO_JS" file="src/js/device/camera3_device_info.js" type="chrome_html" /> <structure name="IDR_CAMERA_CAMERA3_DEVICE_INFO_JS" file="src/js/device/camera3_device_info.js" type="chrome_html" />
<structure name="IDR_CAMERA_CAMERA_JS" file="src/js/views/camera.js" type="chrome_html" /> <structure name="IDR_CAMERA_CAMERA_JS" file="src/js/views/camera.js" type="chrome_html" />
<structure name="IDR_CAMERA_CONSTRAINTS_PREFERRER_JS" file="src/js/device/constraints_preferrer.js" type="chrome_html" /> <structure name="IDR_CAMERA_CONSTRAINTS_PREFERRER_JS" file="src/js/device/constraints_preferrer.js" type="chrome_html" />
<structure name="IDR_CAMERA_DEVICE_OPERATOR_JS" file="src/js/mojo/device_operator.js" type="chrome_html" />
<structure name="IDR_CAMERA_DEVICE_INFO_UPDATER_JS" file="src/js/device/device_info_updater.js" type="chrome_html" /> <structure name="IDR_CAMERA_DEVICE_INFO_UPDATER_JS" file="src/js/device/device_info_updater.js" type="chrome_html" />
<structure name="IDR_CAMERA_DIALOG_JS" file="src/js/views/dialog.js" type="chrome_html" /> <structure name="IDR_CAMERA_DIALOG_JS" file="src/js/views/dialog.js" type="chrome_html" />
<structure name="IDR_CAMERA_FILENAMER_JS" file="src/js/models/filenamer.js" type="chrome_html" /> <structure name="IDR_CAMERA_FILENAMER_JS" file="src/js/models/filenamer.js" type="chrome_html" />
...@@ -25,7 +26,7 @@ ...@@ -25,7 +26,7 @@
<structure name="IDR_CAMERA_GALLERY_BASE_JS" file="src/js/views/gallery_base.js" type="chrome_html" /> <structure name="IDR_CAMERA_GALLERY_BASE_JS" file="src/js/views/gallery_base.js" type="chrome_html" />
<structure name="IDR_CAMERA_GALLERY_JS" file="src/js/models/gallery.js" type="chrome_html" /> <structure name="IDR_CAMERA_GALLERY_JS" file="src/js/models/gallery.js" type="chrome_html" />
<structure name="IDR_CAMERA_GALLERYBUTTON_JS" file="src/js/gallerybutton.js" type="chrome_html" /> <structure name="IDR_CAMERA_GALLERYBUTTON_JS" file="src/js/gallerybutton.js" type="chrome_html" />
<structure name="IDR_CAMERA_IMAGECAPTURE_JS" file="src/js/mojo/imagecapture.js" type="chrome_html" /> <structure name="IDR_CAMERA_IMAGECAPTURE_JS" file="src/js/mojo/image_capture.js" type="chrome_html" />
<structure name="IDR_CAMERA_LAYOUT_JS" file="src/js/views/camera/layout.js" type="chrome_html" /> <structure name="IDR_CAMERA_LAYOUT_JS" file="src/js/views/camera/layout.js" type="chrome_html" />
<structure name="IDR_CAMERA_MAIN_CSS" file="src/css/main.css" type="chrome_html" /> <structure name="IDR_CAMERA_MAIN_CSS" file="src/css/main.css" type="chrome_html" />
<structure name="IDR_CAMERA_MAIN_HTML" file="src/views/main.html" type="chrome_html" /> <structure name="IDR_CAMERA_MAIN_HTML" file="src/views/main.html" type="chrome_html" />
...@@ -33,6 +34,7 @@ ...@@ -33,6 +34,7 @@
<structure name="IDR_CAMERA_MANIFEST" file="manifest.json" type="chrome_html" /> <structure name="IDR_CAMERA_MANIFEST" file="manifest.json" type="chrome_html" />
<structure name="IDR_CAMERA_METRICS_JS" file="src/js/metrics.js" type="chrome_html" /> <structure name="IDR_CAMERA_METRICS_JS" file="src/js/metrics.js" type="chrome_html" />
<structure name="IDR_CAMERA_MODES_JS" file="src/js/views/camera/modes.js" type="chrome_html" /> <structure name="IDR_CAMERA_MODES_JS" file="src/js/views/camera/modes.js" type="chrome_html" />
<structure name="IDR_CAMERA_MOJO_CONNECTOR_JS" file="src/js/mojo/mojo_connector.js" type="chrome_html" />
<structure name="IDR_CAMERA_NAV_JS" file="src/js/nav.js" type="chrome_html" /> <structure name="IDR_CAMERA_NAV_JS" file="src/js/nav.js" type="chrome_html" />
<structure name="IDR_CAMERA_OPTIONS_JS" file="src/js/views/camera/options.js" type="chrome_html" /> <structure name="IDR_CAMERA_OPTIONS_JS" file="src/js/views/camera/options.js" type="chrome_html" />
<structure name="IDR_CAMERA_PREVIEW_JS" file="src/js/views/camera/preview.js" type="chrome_html" /> <structure name="IDR_CAMERA_PREVIEW_JS" file="src/js/views/camera/preview.js" type="chrome_html" />
......
...@@ -14,24 +14,24 @@ js_type_check("closure_compile") { ...@@ -14,24 +14,24 @@ js_type_check("closure_compile") {
js_library("camera3_device_info") { js_library("camera3_device_info") {
deps = [ deps = [
"../mojo:imagecapture", "../mojo:image_capture",
] ]
} }
js_library("constraints_preferrer") { js_library("constraints_preferrer") {
deps = [ deps = [
":camera3_device_info",
"..:resolution_event_broker", "..:resolution_event_broker",
"..:state", "..:state",
":camera3_device_info",
"../browser_proxy:browser_proxy", "../browser_proxy:browser_proxy",
] ]
} }
js_library("device_info_updater") { js_library("device_info_updater") {
deps = [ deps = [
"..:state",
"../mojo:imagecapture",
":camera3_device_info", ":camera3_device_info",
":constraints_preferrer", ":constraints_preferrer",
"..:state",
"../mojo:mojo_connector",
] ]
} }
...@@ -96,4 +96,24 @@ cca.device.Camera3DeviceInfo = class { ...@@ -96,4 +96,24 @@ cca.device.Camera3DeviceInfo = class {
this.videoMaxFps[[w, h]] = fps; this.videoMaxFps[[w, h]] = fps;
}); });
} }
/**
* Create a Camera3DeviceInfo by given device info and the mojo device
* operator.
* @param {!MediaDeviceInfo} deviceInfo
* @param {!cca.mojo.DeviceOperator} deviceOperator
* @return {Promise<!cca.device.Camera3DeviceInfo>}
*/
static async create(deviceInfo, deviceOperator) {
const deviceId = deviceInfo.deviceId;
const facing = await deviceOperator.getCameraFacing(deviceId);
const photoResolution = await deviceOperator.getPhotoResolutions(deviceId);
const videoConfigs = await deviceOperator.getVideoConfigs(deviceId);
const supportedFpsRanges =
await deviceOperator.getSupportedFpsRanges(deviceId);
return new cca.device.Camera3DeviceInfo(
deviceInfo, facing, photoResolution, videoConfigs, supportedFpsRanges);
}
}; };
...@@ -22,9 +22,10 @@ cca.device.DeviceInfoUpdater = class { ...@@ -22,9 +22,10 @@ cca.device.DeviceInfoUpdater = class {
/** /**
* @param {cca.device.PhotoResolPreferrer} photoPreferrer * @param {cca.device.PhotoResolPreferrer} photoPreferrer
* @param {cca.device.VideoConstraintsPreferrer} videoPreferrer * @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
* @param {cca.mojo.MojoConnector} mojoConnector
* @public * @public
* */ * */
constructor(photoPreferrer, videoPreferrer) { constructor(photoPreferrer, videoPreferrer, mojoConnector) {
/** /**
* @type {cca.device.PhotoResolPreferrer} * @type {cca.device.PhotoResolPreferrer}
* @private * @private
...@@ -37,6 +38,12 @@ cca.device.DeviceInfoUpdater = class { ...@@ -37,6 +38,12 @@ cca.device.DeviceInfoUpdater = class {
*/ */
this.videoPreferrer_ = videoPreferrer; this.videoPreferrer_ = videoPreferrer;
/**
* @type {cca.mojo.MojoConnector}
* @private
*/
this.mojoConnector_ = mojoConnector;
/** /**
* Listeners to be called after new camera information is available. * Listeners to be called after new camera information is available.
* @type {!Array<function(!cca.device.DeviceInfoUpdater): Promise>} * @type {!Array<function(!cca.device.DeviceInfoUpdater): Promise>}
...@@ -142,7 +149,7 @@ cca.device.DeviceInfoUpdater = class { ...@@ -142,7 +149,7 @@ cca.device.DeviceInfoUpdater = class {
async enumerateDevices_() { async enumerateDevices_() {
const devices = (await navigator.mediaDevices.enumerateDevices()) const devices = (await navigator.mediaDevices.enumerateDevices())
.filter((device) => device.kind == 'videoinput'); .filter((device) => device.kind == 'videoinput');
if (devices.length == 0) { if (devices.length === 0) {
throw new Error('Device list empty.'); throw new Error('Device list empty.');
} }
return devices; return devices;
...@@ -158,22 +165,16 @@ cca.device.DeviceInfoUpdater = class { ...@@ -158,22 +165,16 @@ cca.device.DeviceInfoUpdater = class {
* @private * @private
*/ */
async queryMojoDevicesInfo_() { async queryMojoDevicesInfo_() {
const devices = await this.devicesInfo_; const deviceOperator = this.mojoConnector_.getDeviceOperator();
let /** ?Array<Object> */ privateInfos; if (!deviceOperator) {
try { return null;
privateInfos = await Promise.all(devices.map((d) => Promise.all([
d,
cca.mojo.getCameraFacing(d.deviceId),
cca.mojo.getPhotoResolutions(d.deviceId),
cca.mojo.getVideoConfigs(d.deviceId),
cca.mojo.getSupportedFpsRanges(d.deviceId),
])));
} catch (e) {
privateInfos = null;
} }
// Non-null version for the Closure Compiler.
const nonNullDeviceOperator = deviceOperator;
return privateInfos && const deviceInfos = await this.devicesInfo_;
privateInfos.map((info) => new cca.device.Camera3DeviceInfo(...info)); return await Promise.all(deviceInfos.map(
(d) => cca.device.Camera3DeviceInfo.create(d, nonNullDeviceOperator)));
} }
/** /**
......
...@@ -11,9 +11,17 @@ var cca = cca || {}; ...@@ -11,9 +11,17 @@ var cca = cca || {};
/** /**
* Creates the Camera App main object. * Creates the Camera App main object.
* @param {!cca.mojo.MojoConnector} mojoConnector The mojo connector which could
* be used to communicate with Chrome and video capture devices.
* @constructor * @constructor
*/ */
cca.App = function() { cca.App = function(mojoConnector) {
/**
* @type {cca.mojo.MojoConnector}
* @private
*/
this.mojoConnector_ = mojoConnector;
/** /**
* @type {cca.models.Gallery} * @type {cca.models.Gallery}
* @private * @private
...@@ -45,7 +53,7 @@ cca.App = function() { ...@@ -45,7 +53,7 @@ cca.App = function() {
* @private * @private
*/ */
this.infoUpdater_ = new cca.device.DeviceInfoUpdater( this.infoUpdater_ = new cca.device.DeviceInfoUpdater(
this.photoPreferrer_, this.videoPreferrer_); this.photoPreferrer_, this.videoPreferrer_, this.mojoConnector_);
/** /**
* @type {cca.GalleryButton} * @type {cca.GalleryButton}
...@@ -64,8 +72,8 @@ cca.App = function() { ...@@ -64,8 +72,8 @@ cca.App = function() {
* @private * @private
*/ */
this.cameraView_ = new cca.views.Camera( this.cameraView_ = new cca.views.Camera(
this.gallery_, this.infoUpdater_, this.photoPreferrer_, this.mojoConnector_, this.gallery_, this.infoUpdater_,
this.videoPreferrer_); this.photoPreferrer_, this.videoPreferrer_);
// End of properties. Seal the object. // End of properties. Seal the object.
Object.seal(this); Object.seal(this);
...@@ -200,9 +208,16 @@ cca.App.instance_ = null; ...@@ -200,9 +208,16 @@ cca.App.instance_ = null;
/** /**
* Creates the App object and starts camera stream. * Creates the App object and starts camera stream.
*/ */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', async () => {
const mojoConnector = new cca.mojo.MojoConnector();
try {
await mojoConnector.initDeviceOperator();
} catch (e) {
console.error(e);
}
if (!cca.App.instance_) { if (!cca.App.instance_) {
cca.App.instance_ = new cca.App(); cca.App.instance_ = new cca.App(mojoConnector);
} }
cca.App.instance_.start(); cca.App.instance_.start();
chrome.app.window.current().show(); chrome.app.window.current().show();
......
...@@ -6,14 +6,30 @@ import("//third_party/closure_compiler/compile_js.gni") ...@@ -6,14 +6,30 @@ import("//third_party/closure_compiler/compile_js.gni")
js_type_check("closure_compile") { js_type_check("closure_compile") {
deps = [ deps = [
":imagecapture", ":device_operator",
":image_capture",
":mojo_connector",
] ]
} }
js_library("imagecapture") { js_library("device_operator") {
deps = [ deps = [
"//media/capture/mojom:image_capture_js_library_for_compile",
"//media/capture/video/chromeos/mojom:cros_camera_js_library_for_compile", "//media/capture/video/chromeos/mojom:cros_camera_js_library_for_compile",
] ]
externs_list = [ "$externs_path/pending.js" ] externs_list = [ "$externs_path/pending.js" ]
} }
js_library("image_capture") {
deps = [
":device_operator",
"..:util",
"//media/capture/mojom:image_capture_js_library_for_compile",
]
externs_list = [ "$externs_path/pending.js" ]
}
js_library("mojo_connector") {
deps = [
"//media/capture/video/chromeos/mojom:cros_camera_js_library_for_compile",
]
}
// Copyright 2019 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.
'use strict';
/**
* Namespace for the Camera app.
*/
var cca = cca || {};
/**
* Namespace for mojo.
*/
cca.mojo = cca.mojo || {};
/**
* Type definition for cca.mojo.PhotoCapabilities.
* @extends {PhotoCapabilities}
* @record
*/
cca.mojo.PhotoCapabilities = function() {};
/** @type {Array<string>} */
cca.mojo.PhotoCapabilities.prototype.supportedEffects;
/**
* Creates the wrapper of JS image-capture and Mojo image-capture.
* @param {!MediaStreamTrack} videoTrack A video track whose still images will
* be taken.
* @param {!cca.mojo.MojoConnector} mojoConnector A mojo connector that could
* used to communicate with video capture device.
* @constructor
*/
cca.mojo.ImageCapture = function(videoTrack, mojoConnector) {
/**
* @type {string} The id of target media device.
*/
this.deviceId_ = videoTrack.getSettings().deviceId;
/**
* @type {ImageCapture} The standard ImageCapture object.
* @private
*/
this.capture_ = new ImageCapture(videoTrack);
/**
* @type {cca.mojo.MojoConnector} The mojo connector that we used to negotiate
* with the video capture device.
* @private
*/
this.mojoConnector_ = mojoConnector;
// End of properties, seal the object.
Object.seal(this);
};
/**
* Gets the photo capabilities with the available options/effects.
* @return {!Promise<!cca.mojo.PhotoCapabilities>} Promise for the result.
*/
cca.mojo.ImageCapture.prototype.getPhotoCapabilities = async function() {
const deviceOperator = this.mojoConnector_.getDeviceOperator();
const supportedEffects = [cros.mojom.Effect.NO_EFFECT];
const isPortraitModeSupported =
await deviceOperator.isPortraitModeSupported(this.deviceId_);
if (isPortraitModeSupported) {
supportedEffects.push(cros.mojom.Effect.PORTRAIT_MODE);
}
const baseCapabilities = await this.capture_.getPhotoCapabilities();
let /** !cca.mojo.PhotoCapabilities */ extendedCapabilities;
Object.assign(extendedCapabilities, baseCapabilities, {supportedEffects});
return extendedCapabilities;
};
/**
* Takes single or multiple photo(s) with the specified settings and effects.
* The amount of result photo(s) depends on the specified settings and effects,
* and the first promise in the returned array will always resolve with the
* unreprocessed photo.
* @param {!PhotoSettings} photoSettings Photo settings for ImageCapture's
* takePhoto().
* @param {!Array<cros.mojom.Effect>=} photoEffects Photo effects to be applied.
* @return {!Array<!Promise<!Blob>>} Array of promises for the result.
*/
cca.mojo.ImageCapture.prototype.takePhoto = function(
photoSettings, photoEffects = []) {
const takes = [];
if (photoEffects) {
for (const effect of photoEffects) {
const take = (async () => {
const deviceOperator = this.mojoConnector_.getDeviceOperator();
const device = deviceOperator.getDevice(this.deviceId_);
const {status, blob} = await device.setReprocessOption(effect);
if (status !== 0) {
throw new Error('Mojo image capture error: ' + status);
}
const {data, mimeType} = blob;
return new Blob([new Uint8Array(data)], {type: mimeType});
})();
takes.push(take);
}
}
takes.splice(0, 0, this.capture_.takePhoto(photoSettings));
return takes;
};
// Copyright 2019 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.
'use strict';
/**
* Namespace for the Camera app.
*/
var cca = cca || {};
/**
* Namespace for mojo.
*/
cca.mojo = cca.mojo || {};
/**
* Connector for using CrOS mojo interface.
*/
cca.mojo.MojoConnector = class {
/** @public */
constructor() {
/**
* @type {cros.mojom.CameraAppDeviceProviderRemote} An interface remote that
* used to construct the mojo interface.
* @private
*/
this.deviceProvider_ = cros.mojom.CameraAppDeviceProvider.getRemote();
/**
* @type {cca.mojo.DeviceOperator} The device operator that could
* operates video capture device through mojo interface.
* @private
*/
this.deviceOperator_ = null;
}
/**
* Enumerates the devices and construct mojo connection for each of them.
* @throws {Error} Thrown when there is any unexpected errors. Note that if it
* fails since it runs on camera hal v1 stack, no error will be thrown.
* @private
*/
async initDeviceOperator() {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(({kind}) => kind === 'videoinput');
const connectedDevices = new Map();
for (const videoDevice of videoDevices) {
const result =
await this.deviceProvider_.getCameraAppDevice(videoDevice.deviceId);
switch (result.status) {
case cros.mojom.GetCameraAppDeviceStatus.SUCCESS:
connectedDevices.set(videoDevice.deviceId, result.device);
break;
case cros.mojom.GetCameraAppDeviceStatus.ERROR_NON_V3:
return;
default:
throw new Error(`Failed to connect to: ${
videoDevice.deviceId}, error: ${result.status}`);
}
}
this.deviceOperator_ = new cca.mojo.DeviceOperator(connectedDevices);
}
/**
* Resets the mojo connection. Once it is reset, the connector should be
* initialized again before being used.
*/
async reset() {
this.deviceOperator_ = null;
try {
await this.initDeviceOperator();
} catch (e) {
console.error(e);
}
}
/**
* Gets the device operator.
* @return {?cca.mojo.DeviceOperator} The video capture device operator. For
* non-v3 devices, it returns null.
*/
getDeviceOperator() {
return this.deviceOperator_;
}
};
...@@ -16,6 +16,7 @@ cca.views = cca.views || {}; ...@@ -16,6 +16,7 @@ cca.views = cca.views || {};
/** /**
* Creates the camera-view controller. * Creates the camera-view controller.
* @param {cca.mojo.MojoConnector} mojoConnector
* @param {cca.models.ResultSaver} resultSaver * @param {cca.models.ResultSaver} resultSaver
* @param {cca.device.DeviceInfoUpdater} infoUpdater * @param {cca.device.DeviceInfoUpdater} infoUpdater
* @param {cca.device.PhotoResolPreferrer} photoPreferrer * @param {cca.device.PhotoResolPreferrer} photoPreferrer
...@@ -23,7 +24,7 @@ cca.views = cca.views || {}; ...@@ -23,7 +24,7 @@ cca.views = cca.views || {};
* @constructor * @constructor
*/ */
cca.views.Camera = function( cca.views.Camera = function(
resultSaver, infoUpdater, photoPreferrer, videoPreferrer) { mojoConnector, resultSaver, infoUpdater, photoPreferrer, videoPreferrer) {
cca.views.View.call(this, '#camera'); cca.views.View.call(this, '#camera');
/** /**
...@@ -32,6 +33,12 @@ cca.views.Camera = function( ...@@ -32,6 +33,12 @@ cca.views.Camera = function(
*/ */
this.infoUpdater_ = infoUpdater; this.infoUpdater_ = infoUpdater;
/**
* @type {cca.mojo.MojoConnector}
* @private
*/
this.mojoConnector_ = mojoConnector;
/** /**
* Layout handler for the camera view. * Layout handler for the camera view.
* @type {cca.views.camera.Layout} * @type {cca.views.camera.Layout}
...@@ -51,8 +58,8 @@ cca.views.Camera = function( ...@@ -51,8 +58,8 @@ cca.views.Camera = function(
* @type {cca.views.camera.Options} * @type {cca.views.camera.Options}
* @private * @private
*/ */
this.options_ = this.options_ = new cca.views.camera.Options(
new cca.views.camera.Options(infoUpdater, this.restart.bind(this)); infoUpdater, mojoConnector, this.restart.bind(this));
/** /**
* @type {HTMLElement} * @type {HTMLElement}
...@@ -93,8 +100,8 @@ cca.views.Camera = function( ...@@ -93,8 +100,8 @@ cca.views.Camera = function(
* @private * @private
*/ */
this.modes_ = new cca.views.camera.Modes( this.modes_ = new cca.views.camera.Modes(
photoPreferrer, videoPreferrer, this.restart.bind(this), doSavePhoto, mojoConnector, photoPreferrer, videoPreferrer, this.restart.bind(this),
createVideoSaver, doSaveVideo); doSavePhoto, createVideoSaver, doSaveVideo);
/** /**
* @type {?string} * @type {?string}
...@@ -252,11 +259,11 @@ cca.views.Camera.prototype.restart = function() { ...@@ -252,11 +259,11 @@ cca.views.Camera.prototype.restart = function() {
this.started_, this.started_,
Promise.resolve(!cca.state.get('taking') || this.endTake_()), Promise.resolve(!cca.state.get('taking') || this.endTake_()),
]) ])
.finally(() => { .finally(async () => {
// We should close all mojo connections since any communication to a // We should close all mojo connections since any communication to a
// closed stream should be avoided. // closed stream should be avoided.
cca.mojo.closeConnections();
this.preview_.stop(); this.preview_.stop();
await this.mojoConnector_.reset();
this.start_(); this.start_();
return this.started_; return this.started_;
}); });
...@@ -270,6 +277,7 @@ cca.views.Camera.prototype.restart = function() { ...@@ -270,6 +277,7 @@ cca.views.Camera.prototype.restart = function() {
*/ */
cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
let supportedModes = null; let supportedModes = null;
const deviceOperator = this.mojoConnector_.getDeviceOperator();
for (const mode of this.modes_.getModeCandidates()) { for (const mode of this.modes_.getModeCandidates()) {
try { try {
if (!deviceId) { if (!deviceId) {
...@@ -295,7 +303,10 @@ cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { ...@@ -295,7 +303,10 @@ cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
} }
for (const constraints of previewCandidates) { for (const constraints of previewCandidates) {
try { try {
const stream = await cca.mojo.getUserMedia(deviceId, constraints); if (deviceOperator) {
await deviceOperator.setFpsRange(deviceId, constraints);
}
const stream = await navigator.mediaDevices.getUserMedia(constraints);
if (!supportedModes) { if (!supportedModes) {
supportedModes = await this.modes_.getSupportedModes(stream); supportedModes = await this.modes_.getSupportedModes(stream);
if (!supportedModes.includes(mode)) { if (!supportedModes.includes(mode)) {
......
...@@ -72,6 +72,7 @@ cca.views.camera.PhotoResult; ...@@ -72,6 +72,7 @@ cca.views.camera.PhotoResult;
/** /**
* Mode controller managing capture sequence of different camera mode. * Mode controller managing capture sequence of different camera mode.
* @param {cca.mojo.MojoConnector} mojoConnector
* @param {cca.device.PhotoResolPreferrer} photoResolPreferrer * @param {cca.device.PhotoResolPreferrer} photoResolPreferrer
* @param {cca.device.VideoConstraintsPreferrer} videoPreferrer * @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
* @param {!DoSwitchMode} doSwitchMode * @param {!DoSwitchMode} doSwitchMode
...@@ -81,8 +82,8 @@ cca.views.camera.PhotoResult; ...@@ -81,8 +82,8 @@ cca.views.camera.PhotoResult;
* @constructor * @constructor
*/ */
cca.views.camera.Modes = function( cca.views.camera.Modes = function(
photoResolPreferrer, videoPreferrer, doSwitchMode, doSavePhoto, mojoConnector, photoResolPreferrer, videoPreferrer, doSwitchMode,
createVideoSaver, doSaveVideo) { doSavePhoto, createVideoSaver, doSaveVideo) {
/** /**
* @type {!DoSwitchMode} * @type {!DoSwitchMode}
* @private * @private
...@@ -146,21 +147,14 @@ cca.views.camera.Modes = function( ...@@ -146,21 +147,14 @@ cca.views.camera.Modes = function(
}, },
'portrait-mode': { 'portrait-mode': {
captureFactory: () => new cca.views.camera.Portrait( captureFactory: () => new cca.views.camera.Portrait(
this.stream_, doSavePhoto, this.captureResolution_), this.stream_, doSavePhoto, this.captureResolution_, mojoConnector),
isSupported: async (stream) => { isSupported: async (stream) => {
try { const deviceOperator = mojoConnector.getDeviceOperator();
const imageCapture = if (!deviceOperator) {
new cca.mojo.ImageCapture(stream.getVideoTracks()[0]);
const capabilities = await imageCapture.getPhotoCapabilities();
return capabilities.supportedEffects &&
capabilities.supportedEffects.includes(
cros.mojom.Effect.PORTRAIT_MODE);
} catch (e) {
// The mode is considered unsupported for given stream. This includes
// the case where underlying camera HAL is V1 causing mojo connection
// unable to work.
return false; return false;
} }
const deviceId = stream.getVideoTracks()[0].getSettings().deviceId;
return await deviceOperator.isPortraitModeSupported(deviceId);
}, },
resolutionConfig: photoResolPreferrer, resolutionConfig: photoResolPreferrer,
v1Config: cca.views.camera.Modes.getV1Constraints.bind(this, false), v1Config: cca.views.camera.Modes.getV1Constraints.bind(this, false),
...@@ -696,9 +690,11 @@ cca.views.camera.Square.prototype.cropSquare = async function(blob) { ...@@ -696,9 +690,11 @@ cca.views.camera.Square.prototype.cropSquare = async function(blob) {
* @param {MediaStream} stream * @param {MediaStream} stream
* @param {!DoSavePhoto} doSavePhoto * @param {!DoSavePhoto} doSavePhoto
* @param {?[number, number]} captureResolution * @param {?[number, number]} captureResolution
* @param {cca.mojo.MojoConnector} mojoConnector
* @constructor * @constructor
*/ */
cca.views.camera.Portrait = function(stream, doSavePhoto, captureResolution) { cca.views.camera.Portrait = function(
stream, doSavePhoto, captureResolution, mojoConnector) {
cca.views.camera.Photo.call(this, stream, doSavePhoto, captureResolution); cca.views.camera.Photo.call(this, stream, doSavePhoto, captureResolution);
/** /**
...@@ -708,6 +704,13 @@ cca.views.camera.Portrait = function(stream, doSavePhoto, captureResolution) { ...@@ -708,6 +704,13 @@ cca.views.camera.Portrait = function(stream, doSavePhoto, captureResolution) {
*/ */
this.crosImageCapture_ = null; this.crosImageCapture_ = null;
/**
* Mojo connector that used to construct CrOS ImageCapture.
* @type {cca.mojo.MojoConnector}
* @private
*/
this.mojoConnector_ = mojoConnector;
// End of properties, seal the object. // End of properties, seal the object.
Object.seal(this); Object.seal(this);
}; };
...@@ -722,8 +725,8 @@ cca.views.camera.Portrait.prototype = { ...@@ -722,8 +725,8 @@ cca.views.camera.Portrait.prototype = {
cca.views.camera.Portrait.prototype.start_ = async function() { cca.views.camera.Portrait.prototype.start_ = async function() {
if (this.crosImageCapture_ == null) { if (this.crosImageCapture_ == null) {
try { try {
this.crosImageCapture_ = this.crosImageCapture_ = new cca.mojo.ImageCapture(
new cca.mojo.ImageCapture(this.stream_.getVideoTracks()[0]); this.stream_.getVideoTracks()[0], this.mojoConnector_);
} catch (e) { } catch (e) {
cca.toast.show('error_msg_take_photo_failed'); cca.toast.show('error_msg_take_photo_failed');
throw e; throw e;
......
...@@ -19,76 +19,27 @@ cca.views = cca.views || {}; ...@@ -19,76 +19,27 @@ cca.views = cca.views || {};
*/ */
cca.views.camera = cca.views.camera || {}; cca.views.camera = cca.views.camera || {};
/**
* Video device information queried from HALv3 mojo private API.
* @param {MediaDeviceInfo} deviceInfo Information of the video device.
* @param {cros.mojom.CameraFacing} facing Camera facing of the video device.
* @param {ResolList} photoResols Supported available photo resolutions of the
* video device.
* @param {Array<[number, number, number]>} videoResolFpses Supported available
* video resolutions and maximal capture fps of the video device.
* @param {FpsRangeInfo} fpsRanges Supported fps ranges of the video device.
*/
cca.views.camera.Camera3DeviceInfo = function(
deviceInfo, facing, photoResols, videoResolFpses, fpsRanges) {
/**
* @type {string}
* @public
*/
this.deviceId = deviceInfo.deviceId;
/**
* @type {cros.mojom.CameraFacing}
* @public
*/
this.facing = facing;
/**
* @type {ResolList}
* @public
*/
this.photoResols = photoResols;
/**
* @type {ResolList}
* @public
*/
this.videoResols = [];
/**
* @type {MaxFpsInfo}
* @public
*/
this.videoMaxFps = {};
/**
* @type {FpsRangeInfo}
* @public
*/
this.fpsRanges = fpsRanges;
// End of properties, seal the object.
Object.seal(this);
videoResolFpses.filter(([, , fps]) => fps >= 24).forEach(([w, h, fps]) => {
this.videoResols.push([w, h]);
this.videoMaxFps[[w, h]] = fps;
});
};
/** /**
* Creates a controller for the options of Camera view. * Creates a controller for the options of Camera view.
* @param {cca.device.DeviceInfoUpdater} infoUpdater * @param {cca.device.DeviceInfoUpdater} infoUpdater
* @param {cca.mojo.MojoConnector} mojoConnector
* @param {function()} doSwitchDevice Callback to trigger device switching. * @param {function()} doSwitchDevice Callback to trigger device switching.
* @constructor * @constructor
*/ */
cca.views.camera.Options = function(infoUpdater, doSwitchDevice) { cca.views.camera.Options = function(
infoUpdater, mojoConnector, doSwitchDevice) {
/** /**
* @type {cca.device.DeviceInfoUpdater} * @type {cca.device.DeviceInfoUpdater}
* @private * @private
*/ */
this.infoUpdater_ = infoUpdater; this.infoUpdater_ = infoUpdater;
/**
* @type {cca.mojo.MojoConnector}
* @private
*/
this.mojoConnector_ = mojoConnector;
/** /**
* @type {function()} * @type {function()}
* @private * @private
...@@ -131,7 +82,7 @@ cca.views.camera.Options = function(infoUpdater, doSwitchDevice) { ...@@ -131,7 +82,7 @@ cca.views.camera.Options = function(infoUpdater, doSwitchDevice) {
/** /**
* Promise for querying Camera3DeviceInfo of all available video devices from * Promise for querying Camera3DeviceInfo of all available video devices from
* mojo private API. * mojo private API.
* @type {Promise<!Array<Camera3DeviceInfo>>} * @type {Promise<!Array<cca.device.Camera3DeviceInfo>>}
* @private * @private
*/ */
this.devicesPrivateInfo_ = null; this.devicesPrivateInfo_ = null;
...@@ -334,20 +285,14 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() { ...@@ -334,20 +285,14 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() {
this.devicesPrivateInfo_ = (async () => { this.devicesPrivateInfo_ = (async () => {
const devices = await this.videoDevices_; const devices = await this.videoDevices_;
try {
var privateInfos = await Promise.all(devices.map((d) => Promise.all([ const deviceOperator = this.mojoConnector_.getDeviceOperator();
d, if (!deviceOperator) {
cca.mojo.getCameraFacing(d.deviceId),
cca.mojo.getPhotoResolutions(d.deviceId),
cca.mojo.getVideoConfigs(d.deviceId),
cca.mojo.getSupportedFpsRanges(d.deviceId),
])));
} catch (e) {
cca.state.set('no-resolution-settings', true); cca.state.set('no-resolution-settings', true);
throw new Error('HALv1-api'); throw new Error('HALv1-api');
} }
return privateInfos.map( return await Promise.all(devices.map(
(info) => new cca.views.camera.Camera3DeviceInfo(...info)); (d) => cca.device.Camera3DeviceInfo.create(d, deviceOperator)));
})(); })();
(async () => { (async () => {
......
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
<script src="../js/mojo/geometry.mojom-lite.js"></script> <script src="../js/mojo/geometry.mojom-lite.js"></script>
<script src="../js/mojo/range.mojom-lite.js"></script> <script src="../js/mojo/range.mojom-lite.js"></script>
<script src="../js/mojo/camera_app.mojom-lite.js"></script> <script src="../js/mojo/camera_app.mojom-lite.js"></script>
<script src="../js/mojo/imagecapture.js"></script> <script src="../js/mojo/device_operator.js"></script>
<script src="../js/mojo/mojo_connector.js"></script>
<script src="../js/mojo/image_capture.js"></script>
<script src="../js/views/view.js"></script> <script src="../js/views/view.js"></script>
<script src="../js/views/gallery_base.js"></script> <script src="../js/views/gallery_base.js"></script>
<script src="../js/views/camera.js"></script> <script src="../js/views/camera.js"></script>
......
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