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], ...@@ -1025,6 +1025,10 @@ body._10sec .description span[i18n-content=label_timer_10s],
background-image: url(../images/settings_timer_duration.svg); background-image: url(../images/settings_timer_duration.svg);
} }
body.no-resolution-settings #settings-resolution {
display: none;
}
#settings-resolution .icon { #settings-resolution .icon {
background-image: url(../images/settings_resolution.svg); background-image: url(../images/settings_resolution.svg);
} }
......
...@@ -240,9 +240,16 @@ cca.views.Camera.prototype.stop_ = function() { ...@@ -240,9 +240,16 @@ cca.views.Camera.prototype.stop_ = function() {
cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
let supportedModes = null; let supportedModes = null;
for (const mode of this.modes_.getModeCandidates()) { for (const mode of this.modes_.getModeCandidates()) {
const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1]; try {
for (const [[width, height], previewCandidates] of this.modes_ const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1];
.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) { for (const constraints of previewCandidates) {
try { try {
const stream = await navigator.mediaDevices.getUserMedia(constraints); const stream = await navigator.mediaDevices.getUserMedia(constraints);
...@@ -256,7 +263,8 @@ cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { ...@@ -256,7 +263,8 @@ cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
await this.preview_.start(stream); await this.preview_.start(stream);
this.facingMode_ = this.options_.updateValues(constraints, stream); this.facingMode_ = this.options_.updateValues(constraints, stream);
await this.modes_.updateModeSelectionUI(supportedModes); 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'); cca.nav.close('warning', 'no-camera');
return true; return true;
} catch (e) { } catch (e) {
......
...@@ -63,18 +63,10 @@ cca.views.camera.Modes = function( ...@@ -63,18 +63,10 @@ cca.views.camera.Modes = function(
this.modesGroup_ = document.querySelector('#modes-group'); this.modesGroup_ = document.querySelector('#modes-group');
/** /**
* Captured resolution width. * @type {?[number, number]}
* @type {number}
* @private * @private
*/ */
this.captureWidth_ = 0; this.captureResolution_ = null;
/**
* Captured resolution height.
* @type {number}
* @private
*/
this.captureHeight_ = 0;
/** /**
* Mode classname and related functions and attributes. * Mode classname and related functions and attributes.
...@@ -87,28 +79,28 @@ cca.views.camera.Modes = function( ...@@ -87,28 +79,28 @@ cca.views.camera.Modes = function(
new cca.views.camera.Video(this.stream_, this.doSavePicture_), new cca.views.camera.Video(this.stream_, this.doSavePicture_),
isSupported: async () => true, isSupported: async () => true,
resolutionConfig: videoResolPreferrer, resolutionConfig: videoResolPreferrer,
v1Config: cca.views.camera.Modes.videoConstraits,
nextMode: 'photo-mode', nextMode: 'photo-mode',
}, },
'photo-mode': { 'photo-mode': {
captureFactory: () => new cca.views.camera.Photo( captureFactory: () => new cca.views.camera.Photo(
this.stream_, this.doSavePicture_, this.captureWidth_, this.stream_, this.doSavePicture_, this.captureResolution_),
this.captureHeight_),
isSupported: async () => true, isSupported: async () => true,
resolutionConfig: photoResolPreferrer, resolutionConfig: photoResolPreferrer,
v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'square-mode', nextMode: 'square-mode',
}, },
'square-mode': { 'square-mode': {
captureFactory: () => new cca.views.camera.Square( captureFactory: () => new cca.views.camera.Square(
this.stream_, this.doSavePicture_, this.captureWidth_, this.stream_, this.doSavePicture_, this.captureResolution_),
this.captureHeight_),
isSupported: async () => true, isSupported: async () => true,
resolutionConfig: photoResolPreferrer, resolutionConfig: photoResolPreferrer,
v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'portrait-mode', nextMode: 'portrait-mode',
}, },
'portrait-mode': { 'portrait-mode': {
captureFactory: () => new cca.views.camera.Portrait( captureFactory: () => new cca.views.camera.Portrait(
this.stream_, this.doSavePicture_, this.captureWidth_, this.stream_, this.doSavePicture_, this.captureResolution_),
this.captureHeight_),
isSupported: async (stream) => { isSupported: async (stream) => {
try { try {
const imageCapture = const imageCapture =
...@@ -125,6 +117,7 @@ cca.views.camera.Modes = function( ...@@ -125,6 +117,7 @@ cca.views.camera.Modes = function(
} }
}, },
resolutionConfig: photoResolPreferrer, resolutionConfig: photoResolPreferrer,
v1Config: cca.views.camera.Modes.photoConstraits,
nextMode: 'video-mode', nextMode: 'video-mode',
}, },
}; };
...@@ -168,6 +161,50 @@ cca.views.camera.Modes.prototype.updateModeUI_ = function(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. * Switches mode to either video-recording or photo-taking.
* @param {string} mode Class name of the switching mode. * @param {string} mode Class name of the switching mode.
...@@ -205,7 +242,7 @@ cca.views.camera.Modes.prototype.getModeCandidates = function() { ...@@ -205,7 +242,7 @@ cca.views.camera.Modes.prototype.getModeCandidates = function() {
* @param {string} mode * @param {string} mode
* @param {string} deviceId * @param {string} deviceId
* @param {ResolList} previewResolutions * @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. * width, height and constraints-candidates for its preview.
*/ */
cca.views.camera.Modes.prototype.getResolutionCandidates = function( cca.views.camera.Modes.prototype.getResolutionCandidates = function(
...@@ -214,6 +251,20 @@ cca.views.camera.Modes.prototype.getResolutionCandidates = function( ...@@ -214,6 +251,20 @@ cca.views.camera.Modes.prototype.getResolutionCandidates = function(
deviceId, previewResolutions); 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. * Gets supported modes for video device of the given stream.
* @param {MediaStream} stream Stream of the video device. * @param {MediaStream} stream Stream of the video device.
...@@ -249,33 +300,33 @@ cca.views.camera.Modes.prototype.updateModeSelectionUI = function( ...@@ -249,33 +300,33 @@ cca.views.camera.Modes.prototype.updateModeSelectionUI = function(
* @param {string} mode Classname of mode to be updated. * @param {string} mode Classname of mode to be updated.
* @param {MediaStream} stream Stream of the new switching mode. * @param {MediaStream} stream Stream of the new switching mode.
* @param {string} deviceId Device id of currently working video device. * @param {string} deviceId Device id of currently working video device.
* @param {number} captureWidth Capturing resolution width. * @param {?[number, number]} captureResolution Capturing resolution width and
* @param {number} captureHeight Capturing resolution height. * height.
*/ */
cca.views.camera.Modes.prototype.updateMode = cca.views.camera.Modes.prototype.updateMode =
async function(mode, stream, deviceId, captureWidth, captureHeight) { async function(mode, stream, deviceId, captureResolution) {
if (this.current != null) { if (this.current != null) {
await this.current.stopCapture(); await this.current.stopCapture();
} }
this.updateModeUI_(mode); this.updateModeUI_(mode);
this.stream_ = stream; this.stream_ = stream;
this.captureWidth_ = captureWidth; this.captureResolution_ = captureResolution;
this.captureHeight_ = captureHeight;
this.current = this.allModes_[mode].captureFactory(); this.current = this.allModes_[mode].captureFactory();
this.allModes_[mode].resolutionConfig.updateCurrentResolution( if (this.captureResolution_) {
deviceId, captureWidth, captureHeight); this.allModes_[mode].resolutionConfig.updateCurrentResolution(
deviceId, ...this.captureResolution_);
}
}; };
/** /**
* Base class for controlling capture sequence in different camera modes. * Base class for controlling capture sequence in different camera modes.
* @param {MediaStream} stream * @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture * @param {function(?Blob, boolean, string): Promise} doSavePicture
* @param {number} captureWidth Capturing resolution width. * @param {?[number, number]} captureResolution Capturing resolution width and
* @param {number} captureHeight Capturing resolution height. * height.
* @constructor * @constructor
*/ */
cca.views.camera.Mode = function( cca.views.camera.Mode = function(stream, doSavePicture, captureResolution) {
stream, doSavePicture, captureWidth, captureHeight) {
/** /**
* Stream of current mode. * Stream of current mode.
* @type {?Promise} * @type {?Promise}
...@@ -291,16 +342,12 @@ cca.views.camera.Mode = function( ...@@ -291,16 +342,12 @@ cca.views.camera.Mode = function(
this.doSavePicture_ = doSavePicture; this.doSavePicture_ = doSavePicture;
/** /**
* @type {number} * Width, height of capture resolution. May be null on device not supporting
* setting resolution.
* @type {?[number, number]}
* @private * @private
*/ */
this.captureWidth_ = captureWidth; this.captureResolution_ = captureResolution;
/**
* @type {number}
* @private
*/
this.captureHeight_ = captureHeight;
/** /**
* Promise for ongoing capture operation. * Promise for ongoing capture operation.
...@@ -351,7 +398,7 @@ cca.views.camera.Mode.prototype.stop_ = function() {}; ...@@ -351,7 +398,7 @@ cca.views.camera.Mode.prototype.stop_ = function() {};
* @constructor * @constructor
*/ */
cca.views.camera.Video = function(stream, doSavePicture) { 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. * Promise for play start sound delay.
...@@ -491,14 +538,11 @@ cca.views.camera.Video.prototype.createVideoBlob_ = function() { ...@@ -491,14 +538,11 @@ cca.views.camera.Video.prototype.createVideoBlob_ = function() {
* Photo mode capture controller. * Photo mode capture controller.
* @param {MediaStream} stream * @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture * @param {function(?Blob, boolean, string): Promise} doSavePicture
* @param {number} captureWidth * @param {?[number, number]} captureResolution
* @param {number} captureHeight
* @constructor * @constructor
*/ */
cca.views.camera.Photo = function( cca.views.camera.Photo = function(stream, doSavePicture, captureResolution) {
stream, doSavePicture, captureWidth, captureHeight) { cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
cca.views.camera.Mode.call(
this, stream, doSavePicture, captureWidth, captureHeight);
/** /**
* ImageCapture object to capture still photos. * ImageCapture object to capture still photos.
...@@ -544,10 +588,18 @@ cca.views.camera.Photo.prototype.start_ = async function() { ...@@ -544,10 +588,18 @@ cca.views.camera.Photo.prototype.start_ = async function() {
* @private * @private
*/ */
cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() { cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() {
const photoSettings = { if (this.captureResolution_) {
imageWidth: this.captureWidth_, var photoSettings = {
imageHeight: this.captureHeight_, 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); return await this.imageCapture_.takePhoto(photoSettings);
}; };
...@@ -555,14 +607,11 @@ cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() { ...@@ -555,14 +607,11 @@ cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() {
* Square mode capture controller. * Square mode capture controller.
* @param {MediaStream} stream * @param {MediaStream} stream
* @param {function(?Blob, boolean, string): Promise} doSavePicture * @param {function(?Blob, boolean, string): Promise} doSavePicture
* @param {number} captureWidth * @param {?[number, number]} captureResolution
* @param {number} captureHeight
* @constructor * @constructor
*/ */
cca.views.camera.Square = function( cca.views.camera.Square = function(stream, doSavePicture, captureResolution) {
stream, doSavePicture, captureWidth, captureHeight) { cca.views.camera.Photo.call(this, stream, doSavePicture, captureResolution);
cca.views.camera.Photo.call(
this, stream, doSavePicture, captureWidth, captureHeight);
/** /**
* Picture saving callback from parent. * Picture saving callback from parent.
...@@ -618,14 +667,11 @@ cca.views.camera.Square.prototype.cropSquare = function(blob) { ...@@ -618,14 +667,11 @@ cca.views.camera.Square.prototype.cropSquare = function(blob) {
* Portrait mode capture controller. * Portrait mode capture controller.
* @param {MediaStream} stream * @param {MediaStream} stream
* @param {function(?Blob, boolean): Promise} doSavePicture * @param {function(?Blob, boolean): Promise} doSavePicture
* @param {number} captureWidth * @param {?[number, number]} captureResolution
* @param {number} captureHeight
* @constructor * @constructor
*/ */
cca.views.camera.Portrait = function( cca.views.camera.Portrait = function(stream, doSavePicture, captureResolution) {
stream, doSavePicture, captureWidth, captureHeight) { cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
cca.views.camera.Mode.call(
this, stream, doSavePicture, captureWidth, captureHeight);
/** /**
* ImageCapture object to capture still photos. * ImageCapture object to capture still photos.
...@@ -655,10 +701,18 @@ cca.views.camera.Portrait.prototype.start_ = async function() { ...@@ -655,10 +701,18 @@ cca.views.camera.Portrait.prototype.start_ = async function() {
throw e; throw e;
} }
} }
const photoSettings = { if (this.captureResolution_) {
imageWidth: this.captureWidth_, var photoSettings = {
imageHeight: this.captureHeight_, 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 { try {
var [reference, portrait] = this.crosImageCapture_.takePhoto( var [reference, portrait] = this.crosImageCapture_.takePhoto(
photoSettings, [cros.mojom.Effect.PORTRAIT_MODE]); photoSettings, [cros.mojom.Effect.PORTRAIT_MODE]);
......
...@@ -278,17 +278,29 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() { ...@@ -278,17 +278,29 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() {
this.refreshingVideoDeviceIds_ = false; this.refreshingVideoDeviceIds_ = false;
}); });
this.deviceResolutions_ = this.videoDevices_.then((devices) => { this.deviceResolutions_ =
return Promise.all(devices.map((d) => Promise.all([ this.videoDevices_
d, .then((devices) => {
cca.mojo.getPhotoResolutions(d.deviceId), return Promise.all(devices.map((d) => Promise.all([
cca.mojo.getVideoConfigs(d.deviceId) d,
.then((v) => v.filter(([, , fps]) => fps >= 24).map(([w, cca.mojo.getPhotoResolutions(d.deviceId),
h]) => [w, h])), cca.mojo.getVideoConfigs(d.deviceId)
]))); .then(
}); (v) => v.filter(([, , fps]) => fps >= 24)
.map(([w, h]) => [w, h])),
this.deviceResolutions_.then((deviceResolutions) => { ])));
})
.catch((e) => {
cca.state.set('no-resolution-settings', true);
throw e;
});
(async () => {
try {
var deviceResolutions = await this.deviceResolutions_;
} catch (e) {
return;
}
let frontSetting = null; let frontSetting = null;
let backSetting = null; let backSetting = null;
let externalSettings = []; let externalSettings = [];
...@@ -314,7 +326,7 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() { ...@@ -314,7 +326,7 @@ cca.views.camera.Options.prototype.maybeRefreshVideoDeviceIds_ = function() {
frontSetting && [frontSetting[0], frontSetting[2]], frontSetting && [frontSetting[0], frontSetting[2]],
backSetting && [backSetting[0], backSetting[2]], backSetting && [backSetting[0], backSetting[2]],
externalSettings.map(([deviceId, , videoRs]) => [deviceId, videoRs])); externalSettings.map(([deviceId, , videoRs]) => [deviceId, videoRs]));
}); })();
}; };
/** /**
...@@ -348,6 +360,8 @@ cca.views.camera.Options.prototype.videoDeviceIds = function() { ...@@ -348,6 +360,8 @@ cca.views.camera.Options.prototype.videoDeviceIds = function() {
* @async * @async
* @param {string} deviceId Device id of the video device. * @param {string} deviceId Device id of the video device.
* @return {[ResolList, ResolList]} Supported photo and video resolutions. * @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 = cca.views.camera.Options.prototype.getDeviceResolutions =
async function(deviceId) { 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