Commit dc85603d authored by Wei Lee's avatar Wei Lee Committed by Commit Bot

CCA: Add photo-capture-shutter perf event and put facing into event name

When taking photo, there are two stages:
1. Click shutter button -> Shutter is done
2. Shutter is done -> Captured image is returned and save

We already have 'photo-taking' event to measure (1) + (2) and
'photo-capture-post-processing' for (2). It will be helpful to have a
'photo-capture-shutter' for (1) as well.

In addition, to measure performance for front camera and back camera
respectively, we can add events with facing information in their names.
Therefore, the ones without facing information are the average metrics
and the ones with facing information are results for specific facing.

Bug: b/149454553
Test: tast run [DUT] camera.CCAUIPerf
Change-Id: I5889ddde4df7c1b3ca4a71ba1c1c0f1628af2fff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087501Reviewed-by: default avatarShik Chen <shik@chromium.org>
Reviewed-by: default avatarKuo Jen Wei <inker@chromium.org>
Commit-Queue: Wei Lee <wtlee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749134}
parent eb931527
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import {browserProxy} from './browser_proxy/browser_proxy.js'; import {browserProxy} from './browser_proxy/browser_proxy.js';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import {Intent} from './intent.js'; import {Intent} from './intent.js';
// eslint-disable-next-line no-unused-vars
import {PerfEvent} from './perf.js';
import * as state from './state.js'; import * as state from './state.js';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import {Facing} from './type.js'; import {Facing} from './type.js';
...@@ -141,15 +143,16 @@ function captureType(facingMode, length, resolution, intentResult) { ...@@ -141,15 +143,16 @@ function captureType(facingMode, length, resolution, intentResult) {
/** /**
* Returns event builder for the metrics type: perf. * Returns event builder for the metrics type: perf.
* @param {string} event The target event type. * @param {PerfEvent} event The target event type.
* @param {number} duration The duration of the event in ms. * @param {number} duration The duration of the event in ms.
* @param {Object=} extras Optional information for the event. * @param {Object=} extras Optional information for the event.
* @return {!analytics.EventBuilder} * @return {!analytics.EventBuilder}
*/ */
function perfType(event, duration, extras = {}) { function perfType(event, duration, extras = {}) {
const {resolution = ''} = extras; const {resolution = '', facing = ''} = extras;
return base.category('perf') return base.category('perf')
.action(event) .action(event)
.label(facing)
// Round the duration here since GA expects that the value is an integer. // Round the duration here since GA expects that the value is an integer.
// Reference: https://support.google.com/analytics/answer/1033068 // Reference: https://support.google.com/analytics/answer/1033068
.value(Math.round(duration)) .value(Math.round(duration))
......
...@@ -12,6 +12,7 @@ import {PerfInformation} from './type.js'; ...@@ -12,6 +12,7 @@ import {PerfInformation} from './type.js';
*/ */
export const PerfEvent = { export const PerfEvent = {
PHOTO_TAKING: 'photo-taking', PHOTO_TAKING: 'photo-taking',
PHOTO_CAPTURE_SHUTTER: 'photo-capture-shutter',
PHOTO_CAPTURE_POST_PROCESSING: 'photo-capture-post-processing', PHOTO_CAPTURE_POST_PROCESSING: 'photo-capture-post-processing',
VIDEO_CAPTURE_POST_PROCESSING: 'video-capture-post-processing', VIDEO_CAPTURE_POST_PROCESSING: 'video-capture-post-processing',
PORTRAIT_MODE_CAPTURE_POST_PROCESSING: PORTRAIT_MODE_CAPTURE_POST_PROCESSING:
......
...@@ -263,7 +263,8 @@ export class Camera extends View { ...@@ -263,7 +263,8 @@ export class Camera extends View {
console.error(e); console.error(e);
} finally { } finally {
this.take_ = null; this.take_ = null;
state.set(state.State.TAKING, false, {hasError}); state.set(
state.State.TAKING, false, {hasError, facing: this.facingMode_});
this.focus(); // Refocus the visible shutter button for ChromeVox. this.focus(); // Refocus the visible shutter button for ChromeVox.
} }
})(); })();
...@@ -406,7 +407,8 @@ export class Camera extends View { ...@@ -406,7 +407,8 @@ export class Camera extends View {
await this.preview_.start(stream); await this.preview_.start(stream);
this.facingMode_ = await this.options_.updateValues(stream); this.facingMode_ = await this.options_.updateValues(stream);
await this.modes_.updateModeSelectionUI(deviceId); await this.modes_.updateModeSelectionUI(deviceId);
await this.modes_.updateMode(mode, stream, deviceId, captureR); await this.modes_.updateMode(
mode, stream, this.facingMode_, deviceId, captureR);
nav.close(ViewName.WARNING, 'no-camera'); nav.close(ViewName.WARNING, 'no-camera');
return true; return true;
} catch (e) { } catch (e) {
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
// found in the LICENSE file. // found in the LICENSE file.
import {assert, assertInstanceof} from '../../chrome_util.js'; import {assert, assertInstanceof} from '../../chrome_util.js';
import {CaptureCandidate, // eslint-disable-line no-unused-vars import {
CaptureCandidate, // eslint-disable-line no-unused-vars
ConstraintsPreferrer, // eslint-disable-line no-unused-vars ConstraintsPreferrer, // eslint-disable-line no-unused-vars
PhotoConstraintsPreferrer, // eslint-disable-line no-unused-vars PhotoConstraintsPreferrer, // eslint-disable-line no-unused-vars
VideoConstraintsPreferrer, // eslint-disable-line no-unused-vars VideoConstraintsPreferrer, // eslint-disable-line no-unused-vars
...@@ -18,10 +19,14 @@ import {PerfEvent} from '../../perf.js'; ...@@ -18,10 +19,14 @@ import {PerfEvent} from '../../perf.js';
import * as sound from '../../sound.js'; import * as sound from '../../sound.js';
import * as state from '../../state.js'; import * as state from '../../state.js';
import * as toast from '../../toast.js'; import * as toast from '../../toast.js';
import {Resolution, import {
Resolution,
ResolutionList, // eslint-disable-line no-unused-vars ResolutionList, // eslint-disable-line no-unused-vars
} from '../../type.js'; } from '../../type.js';
import {Mode} from '../../type.js'; import {
Facing, // eslint-disable-line no-unused-vars
Mode,
} from '../../type.js';
import * as util from '../../util.js'; import * as util from '../../util.js';
import {RecordTime} from './recordtime.js'; import {RecordTime} from './recordtime.js';
...@@ -175,6 +180,13 @@ export class Modes { ...@@ -175,6 +180,13 @@ export class Modes {
*/ */
this.stream_ = null; this.stream_ = null;
/**
* Camera facing of current mode.
* @type {!Facing}
* @private
*/
this.facing_ = Facing.UNKNOWN;
/** /**
* @type {!HTMLElement} * @type {!HTMLElement}
* @private * @private
...@@ -231,8 +243,8 @@ export class Modes { ...@@ -231,8 +243,8 @@ export class Modes {
this.allModes_ = { this.allModes_ = {
[Mode.VIDEO]: { [Mode.VIDEO]: {
captureFactory: () => new Video( captureFactory: () => new Video(
assertInstanceof(this.stream_, MediaStream), createVideoSaver, assertInstanceof(this.stream_, MediaStream), this.facing_,
doSaveVideo), createVideoSaver, doSaveVideo),
isSupported: async () => true, isSupported: async () => true,
constraintsPreferrer: videoPreferrer, constraintsPreferrer: videoPreferrer,
getV1Constraints: getV1Constraints.bind(this, true), getV1Constraints: getV1Constraints.bind(this, true),
...@@ -241,8 +253,8 @@ export class Modes { ...@@ -241,8 +253,8 @@ export class Modes {
}, },
[Mode.PHOTO]: { [Mode.PHOTO]: {
captureFactory: () => new Photo( captureFactory: () => new Photo(
assertInstanceof(this.stream_, MediaStream), doSavePhoto, assertInstanceof(this.stream_, MediaStream), this.facing_,
this.captureResolution_, playShutterEffect), doSavePhoto, this.captureResolution_, playShutterEffect),
isSupported: async () => true, isSupported: async () => true,
constraintsPreferrer: photoPreferrer, constraintsPreferrer: photoPreferrer,
getV1Constraints: getV1Constraints.bind(this, false), getV1Constraints: getV1Constraints.bind(this, false),
...@@ -251,8 +263,8 @@ export class Modes { ...@@ -251,8 +263,8 @@ export class Modes {
}, },
[Mode.SQUARE]: { [Mode.SQUARE]: {
captureFactory: () => new Square( captureFactory: () => new Square(
assertInstanceof(this.stream_, MediaStream), doSavePhoto, assertInstanceof(this.stream_, MediaStream), this.facing_,
this.captureResolution_, playShutterEffect), doSavePhoto, this.captureResolution_, playShutterEffect),
isSupported: async () => true, isSupported: async () => true,
constraintsPreferrer: photoPreferrer, constraintsPreferrer: photoPreferrer,
getV1Constraints: getV1Constraints.bind(this, false), getV1Constraints: getV1Constraints.bind(this, false),
...@@ -261,8 +273,8 @@ export class Modes { ...@@ -261,8 +273,8 @@ export class Modes {
}, },
[Mode.PORTRAIT]: { [Mode.PORTRAIT]: {
captureFactory: () => new Portrait( captureFactory: () => new Portrait(
assertInstanceof(this.stream_, MediaStream), doSavePhoto, assertInstanceof(this.stream_, MediaStream), this.facing_,
this.captureResolution_, playShutterEffect), doSavePhoto, this.captureResolution_, playShutterEffect),
isSupported: async (deviceId) => { isSupported: async (deviceId) => {
if (deviceId === null) { if (deviceId === null) {
return false; return false;
...@@ -430,17 +442,19 @@ export class Modes { ...@@ -430,17 +442,19 @@ export class Modes {
* Creates and updates new current mode object. * Creates and updates new current mode object.
* @param {!Mode} mode Classname of mode to be updated. * @param {!Mode} 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 {!Facing} facing Camera facing of the current mode.
* @param {?string} deviceId Device id of currently working video device. * @param {?string} deviceId Device id of currently working video device.
* @param {?Resolution} captureResolution Capturing resolution width and * @param {?Resolution} captureResolution Capturing resolution width and
* height. * height.
* @return {!Promise} * @return {!Promise}
*/ */
async updateMode(mode, stream, deviceId, captureResolution) { async updateMode(mode, stream, facing, 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.facing_ = facing;
this.captureResolution_ = captureResolution; this.captureResolution_ = captureResolution;
this.current = this.allModes_[mode].captureFactory(); this.current = this.allModes_[mode].captureFactory();
if (deviceId && this.captureResolution_) { if (deviceId && this.captureResolution_) {
...@@ -493,10 +507,11 @@ export class Modes { ...@@ -493,10 +507,11 @@ export class Modes {
class ModeBase { class ModeBase {
/** /**
* @param {!MediaStream} stream * @param {!MediaStream} stream
* @param {!Facing} facing
* @param {?Resolution} captureResolution Capturing resolution width and * @param {?Resolution} captureResolution Capturing resolution width and
* height. * height.
*/ */
constructor(stream, captureResolution) { constructor(stream, facing, captureResolution) {
/** /**
* Stream of current mode. * Stream of current mode.
* @type {!MediaStream} * @type {!MediaStream}
...@@ -504,6 +519,13 @@ class ModeBase { ...@@ -504,6 +519,13 @@ class ModeBase {
*/ */
this.stream_ = stream; this.stream_ = stream;
/**
* Camera facing of current mode.
* @type {!Facing}
* @protected
*/
this.facing_ = facing;
/** /**
* Capture resolution. May be null on device not support of setting * Capture resolution. May be null on device not support of setting
* resolution. * resolution.
...@@ -579,11 +601,12 @@ const VIDEO_MIMETYPE = 'video/x-matroska;codecs=avc1'; ...@@ -579,11 +601,12 @@ const VIDEO_MIMETYPE = 'video/x-matroska;codecs=avc1';
class Video extends ModeBase { class Video extends ModeBase {
/** /**
* @param {!MediaStream} stream * @param {!MediaStream} stream
* @param {!Facing} facing
* @param {!CreateVideoSaver} createVideoSaver * @param {!CreateVideoSaver} createVideoSaver
* @param {!DoSaveVideo} doSaveVideo * @param {!DoSaveVideo} doSaveVideo
*/ */
constructor(stream, createVideoSaver, doSaveVideo) { constructor(stream, facing, createVideoSaver, doSaveVideo) {
super(stream, null); super(stream, facing, null);
/** /**
* @type {!CreateVideoSaver} * @type {!CreateVideoSaver}
...@@ -662,7 +685,9 @@ class Video extends ModeBase { ...@@ -662,7 +685,9 @@ class Video extends ModeBase {
try { try {
await this.doSaveVideo_( await this.doSaveVideo_(
{resolution, duration, videoSaver}, (new Filenamer()).newVideoName()); {resolution, duration, videoSaver}, (new Filenamer()).newVideoName());
state.set(PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {resolution}); state.set(
PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false,
{resolution, facing: this.facing_});
} catch (e) { } catch (e) {
state.set( state.set(
PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true}); PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true});
...@@ -725,12 +750,14 @@ class Video extends ModeBase { ...@@ -725,12 +750,14 @@ class Video extends ModeBase {
class Photo extends ModeBase { class Photo extends ModeBase {
/** /**
* @param {!MediaStream} stream * @param {!MediaStream} stream
* @param {!Facing} facing
* @param {!DoSavePhoto} doSavePhoto * @param {!DoSavePhoto} doSavePhoto
* @param {?Resolution} captureResolution * @param {?Resolution} captureResolution
* @param {!PlayShutterEffect} playShutterEffect * @param {!PlayShutterEffect} playShutterEffect
*/ */
constructor(stream, doSavePhoto, captureResolution, playShutterEffect) { constructor(
super(stream, captureResolution); stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
super(stream, facing, captureResolution);
/** /**
* Callback for saving picture. * Callback for saving picture.
...@@ -805,8 +832,11 @@ class Photo extends ModeBase { ...@@ -805,8 +832,11 @@ class Photo extends ModeBase {
}); });
} }
state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, true);
try { try {
const results = await this.crosImageCapture_.takePhoto(photoSettings); const results = await this.crosImageCapture_.takePhoto(photoSettings);
state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {facing: this.facing_});
this.playShutterEffect_(); this.playShutterEffect_();
state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, true); state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, true);
...@@ -814,8 +844,11 @@ class Photo extends ModeBase { ...@@ -814,8 +844,11 @@ class Photo extends ModeBase {
const image = await util.blobToImage(blob); const image = await util.blobToImage(blob);
const resolution = new Resolution(image.width, image.height); const resolution = new Resolution(image.width, image.height);
await this.doSavePhoto_({resolution, blob}, imageName); await this.doSavePhoto_({resolution, blob}, imageName);
state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {resolution}); state.set(
PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false,
{resolution, facing: this.facing_});
} catch (e) { } catch (e) {
state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {hasError: true});
state.set( state.set(
PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true}); PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true});
toast.show('error_msg_take_photo_failed'); toast.show('error_msg_take_photo_failed');
...@@ -901,12 +934,14 @@ class Photo extends ModeBase { ...@@ -901,12 +934,14 @@ class Photo extends ModeBase {
class Square extends Photo { class Square extends Photo {
/** /**
* @param {!MediaStream} stream * @param {!MediaStream} stream
* @param {!Facing} facing
* @param {!DoSavePhoto} doSavePhoto * @param {!DoSavePhoto} doSavePhoto
* @param {?Resolution} captureResolution * @param {?Resolution} captureResolution
* @param {!PlayShutterEffect} playShutterEffect * @param {!PlayShutterEffect} playShutterEffect
*/ */
constructor(stream, doSavePhoto, captureResolution, playShutterEffect) { constructor(
super(stream, doSavePhoto, captureResolution, playShutterEffect); stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
super(stream, facing, doSavePhoto, captureResolution, playShutterEffect);
this.doSavePhoto_ = async (result, ...args) => { this.doSavePhoto_ = async (result, ...args) => {
// Since the image blob after square cut will lose its EXIF including // Since the image blob after square cut will lose its EXIF including
...@@ -948,12 +983,14 @@ class Square extends Photo { ...@@ -948,12 +983,14 @@ class Square extends Photo {
class Portrait extends Photo { class Portrait extends Photo {
/** /**
* @param {!MediaStream} stream * @param {!MediaStream} stream
* @param {!Facing} facing
* @param {!DoSavePhoto} doSavePhoto * @param {!DoSavePhoto} doSavePhoto
* @param {?Resolution} captureResolution * @param {?Resolution} captureResolution
* @param {!PlayShutterEffect} playShutterEffect * @param {!PlayShutterEffect} playShutterEffect
*/ */
constructor(stream, doSavePhoto, captureResolution, playShutterEffect) { constructor(
super(stream, doSavePhoto, captureResolution, playShutterEffect); stream, facing, doSavePhoto, captureResolution, playShutterEffect) {
super(stream, facing, doSavePhoto, captureResolution, playShutterEffect);
} }
/** /**
...@@ -1036,6 +1073,7 @@ class Portrait extends Photo { ...@@ -1036,6 +1073,7 @@ class Portrait extends Photo {
} }
await refSave; await refSave;
state.set( state.set(
PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false, {hasError}); PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false,
{hasError, facing: this.facing_});
} }
} }
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