Fix CCA query resolution API not available on HALv1 device bug
Bug: 965933
Test: On HALv1 device the CCA function normally without resolution
settings menu. On HALv3 device the CCA is able to capture with specified
resolution from resolution settings menu.
Change-Id: If22961c55a052a7a6061c62569d88aca69e332d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1626063
Commit-Queue: Kuo Jen Wei <inker@chromium.org>
Reviewed-by: Sheng-hao Tsao <shenghao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663002}
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index 06f339a..f4f76d6 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -1025,6 +1025,10 @@
background-image: url(../images/settings_timer_duration.svg);
}
+body.no-resolution-settings #settings-resolution {
+ display: none;
+}
+
#settings-resolution .icon {
background-image: url(../images/settings_resolution.svg);
}
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
index 3c29ad2..baf23e0 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -240,9 +240,16 @@
cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
let supportedModes = null;
for (const mode of this.modes_.getModeCandidates()) {
- const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1];
- for (const [[width, height], previewCandidates] of this.modes_
- .getResolutionCandidates(mode, deviceId, previewRs)) {
+ try {
+ const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1];
+ var resolCandidates =
+ this.modes_.getResolutionCandidates(mode, deviceId, previewRs);
+ } catch (e) {
+ // Assume the exception here is thrown from error of HALv1 not support
+ // resolution query, fallback to use v1 constraints-candidates.
+ resolCandidates = this.modes_.getResolutionCandidatesV1(mode, deviceId);
+ }
+ for (const [captureResolution, previewCandidates] of resolCandidates) {
for (const constraints of previewCandidates) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
@@ -256,7 +263,8 @@
await this.preview_.start(stream);
this.facingMode_ = this.options_.updateValues(constraints, stream);
await this.modes_.updateModeSelectionUI(supportedModes);
- await this.modes_.updateMode(mode, stream, deviceId, width, height);
+ await this.modes_.updateMode(
+ mode, stream, deviceId, captureResolution);
cca.nav.close('warning', 'no-camera');
return true;
} catch (e) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index ba1a4ceb..ea3878a 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -63,18 +63,10 @@
this.modesGroup_ = document.querySelector('#modes-group');
/**
- * Captured resolution width.
- * @type {number}
+ * @type {?[number, number]}
* @private
*/
- this.captureWidth_ = 0;
-
- /**
- * Captured resolution height.
- * @type {number}
- * @private
- */
- this.captureHeight_ = 0;
+ this.captureResolution_ = null;
/**
* Mode classname and related functions and attributes.
@@ -87,28 +79,28 @@
new cca.views.camera.Video(this.stream_, this.doSavePicture_),
isSupported: async () => true,
resolutionConfig: videoResolPreferrer,
+ v1Config: cca.views.camera.Modes.videoConstraits,
nextMode: 'photo-mode',
},
'photo-mode': {
captureFactory: () => new cca.views.camera.Photo(
- this.stream_, this.doSavePicture_, this.captureWidth_,
- this.captureHeight_),
+ this.stream_, this.doSavePicture_, this.captureResolution_),
isSupported: async () => true,
resolutionConfig: photoResolPreferrer,
+ v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'square-mode',
},
'square-mode': {
captureFactory: () => new cca.views.camera.Square(
- this.stream_, this.doSavePicture_, this.captureWidth_,
- this.captureHeight_),
+ this.stream_, this.doSavePicture_, this.captureResolution_),
isSupported: async () => true,
resolutionConfig: photoResolPreferrer,
+ v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'portrait-mode',
},
'portrait-mode': {
captureFactory: () => new cca.views.camera.Portrait(
- this.stream_, this.doSavePicture_, this.captureWidth_,
- this.captureHeight_),
+ this.stream_, this.doSavePicture_, this.captureResolution_),
isSupported: async (stream) => {
try {
const imageCapture =
@@ -125,6 +117,7 @@
}
},
resolutionConfig: photoResolPreferrer,
+ v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'video-mode',
},
};
@@ -169,6 +162,50 @@
};
/**
+ * Returns a set of available video constraints for HALv1 device.
+ * @param {?string} deviceId Id of video device.
+ * @return {Array<Object>} Result of constraints-candidates.
+ */
+cca.views.camera.Modes.videoConstraits = function(deviceId) {
+ return [
+ {
+ aspectRatio: {ideal: 1.7777777778},
+ width: {min: 1280},
+ frameRate: {min: 24},
+ },
+ {
+ width: {min: 640},
+ frameRate: {min: 24},
+ },
+ ].map((constraint) => {
+ constraint.deviceId = {exact: deviceId};
+ return {audio: true, video: constraint};
+ });
+};
+
+/**
+ * Returns a set of available photo constraints for HALv1 device.
+ * @param {?string} deviceId Id of video device.
+ * @return {Array<Object>} Result of constraints-candidates.
+ */
+cca.views.camera.Modes.photoConstraits = function(deviceId) {
+ return [
+ {
+ aspectRatio: {ideal: 1.3333333333},
+ width: {min: 1280},
+ frameRate: {min: 24},
+ },
+ {
+ width: {min: 640},
+ frameRate: {min: 24},
+ },
+ ].map((constraint) => {
+ constraint.deviceId = {exact: deviceId};
+ return {audio: false, video: constraint};
+ });
+};
+
+/**
* Switches mode to either video-recording or photo-taking.
* @param {string} mode Class name of the switching mode.
* @private
@@ -205,7 +242,7 @@
* @param {string} mode
* @param {string} deviceId
* @param {ResolList} previewResolutions
- * @return {Array<[number, number, Array<Object>]>} Result capture resolution
+ * @return {Array<[?[number, number], Array<Object>]>} Result capture resolution
* width, height and constraints-candidates for its preview.
*/
cca.views.camera.Modes.prototype.getResolutionCandidates = function(
@@ -215,6 +252,20 @@
};
/**
+ * Gets capture resolution and its corresponding preview constraints for the
+ * given mode on camera HALv1 device.
+ * @param {string} mode
+ * @param {string} deviceId
+ * @return {Array<[?[number, number], Array<Object>]>} Result capture resolution
+ * width, height and constraints-candidates for its preview.
+ */
+cca.views.camera.Modes.prototype.getResolutionCandidatesV1 = function(
+ mode, deviceId) {
+ return this.allModes_[mode].v1Config(deviceId).map(
+ (constraints) => [null, [constraints]]);
+};
+
+/**
* Gets supported modes for video device of the given stream.
* @param {MediaStream} stream Stream of the video device.
* @return {Array<string>} Names of all supported mode for the video device.
@@ -249,33 +300,33 @@
* @param {string} mode Classname of mode to be updated.
* @param {MediaStream} stream Stream of the new switching mode.
* @param {string} deviceId Device id of currently working video device.
- * @param {number} captureWidth Capturing resolution width.
- * @param {number} captureHeight Capturing resolution height.
+ * @param {?[number, number]} captureResolution Capturing resolution width and
+ * height.
*/
cca.views.camera.Modes.prototype.updateMode =
- async function(mode, stream, deviceId, captureWidth, captureHeight) {
+ async function(mode, stream, deviceId, captureResolution) {
if (this.current != null) {
await this.current.stopCapture();
}
this.updateModeUI_(mode);
this.stream_ = stream;
- this.captureWidth_ = captureWidth;
- this.captureHeight_ = captureHeight;
+ this.captureResolution_ = captureResolution;
this.current = this.allModes_[mode].captureFactory();
- this.allModes_[mode].resolutionConfig.updateCurrentResolution(
- deviceId, captureWidth, captureHeight);
+ if (this.captureResolution_) {
+ this.allModes_[mode].resolutionConfig.updateCurrentResolution(
+ deviceId, ...this.captureResolution_);
+ }
};
/**
* Base class for controlling capture sequence in different camera modes.
* @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth Capturing resolution width.
- * @param {number} captureHeight Capturing resolution height.
+ * @param {?[number, number]} captureResolution Capturing resolution width and
+ * height.
* @constructor
*/
-cca.views.camera.Mode = function(
- stream, doSavePicture, captureWidth, captureHeight) {
+cca.views.camera.Mode = function(stream, doSavePicture, captureResolution) {
/**
* Stream of current mode.
* @type {?Promise}
@@ -291,16 +342,12 @@
this.doSavePicture_ = doSavePicture;
/**
- * @type {number}
+ * Width, height of capture resolution. May be null on device not supporting
+ * setting resolution.
+ * @type {?[number, number]}
* @private
*/
- this.captureWidth_ = captureWidth;
-
- /**
- * @type {number}
- * @private
- */
- this.captureHeight_ = captureHeight;
+ this.captureResolution_ = captureResolution;
/**
* Promise for ongoing capture operation.
@@ -351,7 +398,7 @@
* @constructor
*/
cca.views.camera.Video = function(stream, doSavePicture) {
- cca.views.camera.Mode.call(this, stream, doSavePicture, -1, -1);
+ cca.views.camera.Mode.call(this, stream, doSavePicture, null);
/**
* Promise for play start sound delay.
@@ -491,14 +538,11 @@
* Photo mode capture controller.
* @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
* @constructor
*/
-cca.views.camera.Photo = function(
- stream, doSavePicture, captureWidth, captureHeight) {
- cca.views.camera.Mode.call(
- this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Photo = function(stream, doSavePicture, captureResolution) {
+ cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
/**
* ImageCapture object to capture still photos.
@@ -544,10 +588,18 @@
* @private
*/
cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() {
- const photoSettings = {
- imageWidth: this.captureWidth_,
- imageHeight: this.captureHeight_,
- };
+ if (this.captureResolution_) {
+ var photoSettings = {
+ imageWidth: this.captureResolution_[0],
+ imageHeight: this.captureResolution_[1],
+ };
+ } else {
+ const caps = await this.imageCapture_.getPhotoCapabilities();
+ photoSettings = {
+ imageWidth: caps.imageWidth.max,
+ imageHeight: caps.imageHeight.max,
+ };
+ }
return await this.imageCapture_.takePhoto(photoSettings);
};
@@ -555,14 +607,11 @@
* Square mode capture controller.
* @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
* @constructor
*/
-cca.views.camera.Square = function(
- stream, doSavePicture, captureWidth, captureHeight) {
- cca.views.camera.Photo.call(
- this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Square = function(stream, doSavePicture, captureResolution) {
+ cca.views.camera.Photo.call(this, stream, doSavePicture, captureResolution);
/**
* Picture saving callback from parent.
@@ -618,14 +667,11 @@
* Portrait mode capture controller.
* @param {MediaStream} stream
* @param {function(?Blob, boolean): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
* @constructor
*/
-cca.views.camera.Portrait = function(
- stream, doSavePicture, captureWidth, captureHeight) {
- cca.views.camera.Mode.call(
- this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Portrait = function(stream, doSavePicture, captureResolution) {
+ cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
/**
* ImageCapture object to capture still photos.
@@ -655,10 +701,18 @@
throw e;
}
}
- const photoSettings = {
- imageWidth: this.captureWidth_,
- imageHeight: this.captureHeight_,
- };
+ if (this.captureResolution_) {
+ var photoSettings = {
+ imageWidth: this.captureResolution_[0],
+ imageHeight: this.captureResolution_[1],
+ };
+ } else {
+ const caps = await this.imageCapture_.getPhotoCapabilities();
+ photoSettings = {
+ imageWidth: caps.imageWidth.max,
+ imageHeight: caps.imageHeight.max,
+ };
+ }
try {
var [reference, portrait] = this.crosImageCapture_.takePhoto(
photoSettings, [cros.mojom.Effect.PORTRAIT_MODE]);
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
index ec8ccff..341d4670 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
@@ -278,17 +278,29 @@
this.refreshingVideoDeviceIds_ = false;
});
- this.deviceResolutions_ = this.videoDevices_.then((devices) => {
- return Promise.all(devices.map((d) => Promise.all([
- d,
- cca.mojo.getPhotoResolutions(d.deviceId),
- cca.mojo.getVideoConfigs(d.deviceId)
- .then((v) => v.filter(([, , fps]) => fps >= 24).map(([w,
- h]) => [w, h])),
- ])));
- });
+ this.deviceResolutions_ =
+ this.videoDevices_
+ .then((devices) => {
+ return Promise.all(devices.map((d) => Promise.all([
+ d,
+ cca.mojo.getPhotoResolutions(d.deviceId),
+ cca.mojo.getVideoConfigs(d.deviceId)
+ .then(
+ (v) => v.filter(([, , fps]) => fps >= 24)
+ .map(([w, h]) => [w, h])),
+ ])));
+ })
+ .catch((e) => {
+ cca.state.set('no-resolution-settings', true);
+ throw e;
+ });
- this.deviceResolutions_.then((deviceResolutions) => {
+ (async () => {
+ try {
+ var deviceResolutions = await this.deviceResolutions_;
+ } catch (e) {
+ return;
+ }
let frontSetting = null;
let backSetting = null;
let externalSettings = [];
@@ -314,7 +326,7 @@
frontSetting && [frontSetting[0], frontSetting[2]],
backSetting && [backSetting[0], backSetting[2]],
externalSettings.map(([deviceId, , videoRs]) => [deviceId, videoRs]));
- });
+ })();
};
/**
@@ -348,6 +360,8 @@
* @async
* @param {string} deviceId Device id of the video device.
* @return {[ResolList, ResolList]} Supported photo and video resolutions.
+ * @throws {Error} May fail on HALv1 device without capability of querying
+ * supported resolutions.
*/
cca.views.camera.Options.prototype.getDeviceResolutions =
async function(deviceId) {