Commit a05125d4 authored by Kuo Jen Wei's avatar Kuo Jen Wei Committed by Commit Bot

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: default avatarSheng-hao Tsao <shenghao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663002}
parent 0adc1e40
......@@ -1025,6 +1025,10 @@ body._10sec .description span[i18n-content=label_timer_10s],
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);
}
......
......@@ -240,9 +240,16 @@ cca.views.Camera.prototype.stop_ = function() {
cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
let supportedModes = null;
for (const mode of this.modes_.getModeCandidates()) {
try {
const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1];
for (const [[width, height], previewCandidates] of this.modes_
.getResolutionCandidates(mode, deviceId, previewRs)) {
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 @@ cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
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) {
......
......@@ -63,18 +63,10 @@ cca.views.camera.Modes = function(
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 @@ cca.views.camera.Modes = function(
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 @@ cca.views.camera.Modes = function(
}
},
resolutionConfig: photoResolPreferrer,
v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'video-mode',
},
};
......@@ -168,6 +161,50 @@ cca.views.camera.Modes.prototype.updateModeUI_ = function(mode) {
});
};
/**
* 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.
......@@ -205,7 +242,7 @@ cca.views.camera.Modes.prototype.getModeCandidates = function() {
* @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(
......@@ -214,6 +251,20 @@ cca.views.camera.Modes.prototype.getResolutionCandidates = function(
deviceId, previewResolutions);
};
/**
* 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.
......@@ -249,33 +300,33 @@ cca.views.camera.Modes.prototype.updateModeSelectionUI = function(
* @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();
if (this.captureResolution_) {
this.allModes_[mode].resolutionConfig.updateCurrentResolution(
deviceId, captureWidth, captureHeight);
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 @@ cca.views.camera.Mode = function(
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 @@ cca.views.camera.Mode.prototype.stop_ = function() {};
* @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 @@ cca.views.camera.Video.prototype.createVideoBlob_ = function() {
* 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 @@ cca.views.camera.Photo.prototype.start_ = async function() {
* @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 @@ cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() {
* 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 @@ cca.views.camera.Square.prototype.cropSquare = function(blob) {
* 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 @@ cca.views.camera.Portrait.prototype.start_ = async function() {
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]);
......
......@@ -278,17 +278,29 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() {
this.refreshingVideoDeviceIds_ = false;
});
this.deviceResolutions_ = this.videoDevices_.then((devices) => {
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])),
.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 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() {
frontSetting && [frontSetting[0], frontSetting[2]],
backSetting && [backSetting[0], backSetting[2]],
externalSettings.map(([deviceId, , videoRs]) => [deviceId, videoRs]));
});
})();
};
/**
......@@ -348,6 +360,8 @@ cca.views.camera.Options.prototype.videoDeviceIds = function() {
* @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) {
......
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