Commit 8fe3241d authored by Kuo Jen Wei's avatar Kuo Jen Wei Committed by Commit Bot

[CCA] Extract ResultSaver interface.

Extract ResultSaver interface for saving captured photos and video.

Bug: 967611
Test: All CCA capture function work as expected.

Change-Id: Ifde8ee94d88357f4541e248819e0c3a1a98c0fd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1757720Reviewed-by: default avatarShik Chen <shik@chromium.org>
Commit-Queue: Kuo Jen Wei <inker@chromium.org>
Auto-Submit: Kuo Jen Wei <inker@chromium.org>
Cr-Commit-Position: refs/heads/master@{#689398}
parent 562c5482
......@@ -158,6 +158,8 @@ copy("chrome_camera_app_js_models") {
"src/js/models/filenamer.js",
"src/js/models/filesystem.js",
"src/js/models/gallery.js",
"src/js/models/result_saver.js",
"src/js/models/video_saver.js",
]
outputs = [
......
......@@ -128,6 +128,8 @@ RESOURCES = \
src/js/models/filenamer.js \
src/js/models/filesystem.js \
src/js/models/gallery.js \
src/js/models/result_saver.js \
src/js/models/video_saver.js \
src/js/mojo/imagecapture.js \
src/js/nav.js \
src/js/resolution_event_broker.js \
......
......@@ -38,6 +38,7 @@
<structure name="IDR_CAMERA_PREVIEW_JS" file="src/js/views/camera/preview.js" type="chrome_html" />
<structure name="IDR_CAMERA_RECORDTIME_JS" file="src/js/views/camera/recordtime.js" type="chrome_html" />
<structure name="IDR_CAMERA_RESOLUTION_EVENT_BROKER_JS" file="src/js/resolution_event_broker.js" type="chrome_html" />
<structure name="IDR_CAMERA_RESULT_SAVER_JS" file="src/js/models/result_saver.js" type="chrome_html" />
<structure name="IDR_CAMERA_SCROLLBAR_JS" file="src/js/scrollbar.js" type="chrome_html" />
<structure name="IDR_CAMERA_SETTINGS_JS" file="src/js/views/settings.js" type="chrome_html" />
<structure name="IDR_CAMERA_SOUND_JS" file="src/js/sound.js" type="chrome_html" />
......@@ -46,6 +47,7 @@
<structure name="IDR_CAMERA_TOAST_JS" file="src/js/toast.js" type="chrome_html" />
<structure name="IDR_CAMERA_TOOLTIP_JS" file="src/js/tooltip.js" type="chrome_html" />
<structure name="IDR_CAMERA_UTIL_JS" file="src/js/util.js" type="chrome_html" />
<structure name="IDR_CAMERA_VIDEO_SAVER_JS" file="src/js/models/video_saver.js" type="chrome_html" />
<structure name="IDR_CAMERA_VIEW_JS" file="src/js/views/view.js" type="chrome_html" />
<structure name="IDR_CAMERA_WARNING_JS" file="src/js/views/warning.js" type="chrome_html" />
<structure name="IDR_CAMERA_WEBUI_BROWSER_PROXY" file="src/js/browser_proxy/webui_browser_proxy.js" type="chrome_html" />
......
......@@ -21,6 +21,8 @@ js_library("camera3_device_info") {
js_library("constraints_preferrer") {
deps = [
"..:resolution_event_broker",
"..:state",
":camera3_device_info",
"../browser_proxy:browser_proxy",
]
}
......
......@@ -18,7 +18,7 @@ cca.App = function() {
* @type {cca.models.Gallery}
* @private
*/
this.model_ = new cca.models.Gallery();
this.gallery_ = new cca.models.Gallery();
/**
* @type {cca.ResolutionEventBroker}
......@@ -51,20 +51,20 @@ cca.App = function() {
* @type {cca.GalleryButton}
* @private
*/
this.galleryButton_ = new cca.GalleryButton(this.model_);
this.galleryButton_ = new cca.GalleryButton(this.gallery_);
/**
* @type {cca.views.Browser}
* @private
*/
this.browserView_ = new cca.views.Browser(this.model_);
this.browserView_ = new cca.views.Browser(this.gallery_);
/**
* @type {cca.views.Camera}
* @private
*/
this.cameraView_ = new cca.views.Camera(
this.model_, this.infoUpdater_, this.photoPreferrer_,
this.gallery_, this.infoUpdater_, this.photoPreferrer_,
this.videoPreferrer_);
// End of properties. Seal the object.
......@@ -163,11 +163,11 @@ cca.App.prototype.start = function() {
});
}).then((external) => {
cca.state.set('ext-fs', external);
this.model_.addObserver(this.galleryButton_);
this.gallery_.addObserver(this.galleryButton_);
if (!cca.App.useGalleryApp()) {
this.model_.addObserver(this.browserView_);
this.gallery_.addObserver(this.browserView_);
}
this.model_.load();
this.gallery_.load();
cca.nav.open('camera');
}).catch((error) => {
console.error(error);
......
......@@ -94,13 +94,13 @@ cca.metrics.launchType_ = function(ackMigrate) {
/**
* Returns event builder for the metrics type: capture.
* @param {?string} facingMode Camera facing-mode of the capture.
* @param {number=} length Length of 1 minute buckets for captured video.
* @param {number} length Length of 1 minute buckets for captured video.
* @param {number} width The width of the capture resolution.
* @param {number} height The height of the capture resolution.
* @return {analytics.EventBuilder}
* @private
*/
cca.metrics.captureType_ = function(facingMode, length, [width, height]) {
cca.metrics.captureType_ = function(facingMode, length, {width, height}) {
var condState = (states, cond, strict) => {
// Return the first existing state among the given states only if there is
// no gate condition or the condition is met.
......
......@@ -9,6 +9,8 @@ js_type_check("closure_compile") {
":filenamer",
":filesystem",
":gallery",
":result_saver",
":video_saver",
]
}
......@@ -26,3 +28,9 @@ js_library("gallery") {
":filesystem",
]
}
js_library("result_saver") {
}
js_library("video_saver") {
}
......@@ -288,13 +288,18 @@ cca.models.FileSystem.savePhoto = function(blob, filename) {
/**
* Creates a file for saving temporary video recording result.
* @return {Promise<?FileEntry>} Newly created temporary file.
* @return {!Promise<!FileEntry>} Newly created temporary file.
* @throws {Error} If failed to create video temp file.
*/
cca.models.FileSystem.createTempVideoFile = async function() {
const dir =
cca.models.FileSystem.externalDir || cca.models.FileSystem.internalDir;
const filename = new cca.models.Filenamer().newVideoName();
return await cca.models.FileSystem.getFile(dir, filename, true);
const file = await cca.models.FileSystem.getFile(dir, filename, true);
if (file === null) {
throw new Error('Failed to create video temp file.');
}
return file;
};
/**
......
......@@ -17,6 +17,7 @@ cca.models = cca.models || {};
/**
* Creates the gallery model controller.
* @constructor
* @implements {cca.models.ResultSaver}
*/
cca.models.Gallery = function() {
/**
......@@ -285,12 +286,9 @@ cca.models.Gallery.prototype.wrapPicture_ = function(
};
/**
* Saves photo capture result into persistent storage and adds it into gallery.
* @param {!Blob} blob Data of the photo to be added.
* @param {string} filename Filename of photo to be added.
* @return {!Promise} Promise for the operation.
* @override
*/
cca.models.Gallery.prototype.savePhoto = function(blob, filename) {
cca.models.Gallery.prototype.savePhoto = function(blob, name) {
// TODO(yuli): models.Gallery listens to models.FileSystem's file-added event
// and then add a new picture into the model.
var saved = new Promise((resolve) => {
......@@ -301,7 +299,7 @@ cca.models.Gallery.prototype.savePhoto = function(blob, filename) {
cca.util.orientPhoto(blob, resolve, () => resolve(blob));
})
.then((blob) => {
return cca.models.FileSystem.savePhoto(blob, filename);
return cca.models.FileSystem.savePhoto(blob, name);
})
.then((pictureEntry) => {
return this.wrapPicture_(pictureEntry);
......@@ -311,12 +309,19 @@ cca.models.Gallery.prototype.savePhoto = function(blob, filename) {
};
/**
* Saves video capture result into persistent storage and adds it into gallery.
* @param {FileEntry} tempfile File saving temporary video recording result.
* @param {string} filename Filename of picture to be added.
* @override
*/
cca.models.Gallery.prototype.startSaveVideo = async function() {
const tempFile = await cca.models.FileSystem.createTempVideoFile();
return cca.models.VideoSaver.create(tempFile);
};
/**
* @override
*/
cca.models.Gallery.prototype.saveVideo = async function(tempfile, filename) {
const savedFile = await cca.models.FileSystem.saveVideo(tempfile, filename);
cca.models.Gallery.prototype.finishSaveVideo = async function(video, name) {
const tempFile = await video.endWrite();
const savedFile = await cca.models.FileSystem.saveVideo(tempFile, name);
const picture = await this.wrapPicture_(savedFile);
await this.addPicture_(picture);
};
......
// 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 models.
*/
cca.models = cca.models || {};
/**
* Handles captured result photos and video.
* @interface
*/
cca.models.ResultSaver = class {
/**
* Saves photo capture result.
* @param {!Blob} blob Data of the photo to be added.
* @param {string} name Name of the photo to be saved.
* @return {!Promise} Promise for the operation.
*/
async savePhoto(blob, name) {}
/**
* Returns a video saver to save captured result video.
* @return {!Promise<!cca.models.VideoSaver>}
*/
async startSaveVideo() {}
/**
* Saves captured video result.
* @param {!cca.models.VideoSaver} video Contains the video result to be
* saved.
* @param {string} name Name of the video to be saved.
* @return {!Promise}
*/
async finishSaveVideo(video, name) {}
};
// 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 models.
*/
cca.models = cca.models || {};
/**
* Used to save captured video.
*/
cca.models.VideoSaver = class {
/**
* @param {!FileEntry} file
* @param {!FileWriter} writer
* @private
*/
constructor(file, writer) {
/**
* @const {!FileEntry}
*/
this.file_ = file;
/**
* @const {!FileWriter}
*/
this.writer_ = writer;
/**
* Promise of the ongoing write.
* @type {!Promise}
*/
this.curWrite_ = Promise.resolve();
}
/**
* Writes video data to result video.
* @param {!Blob} blob Video data to be written.
* @return {!Promise}
*/
async write(blob) {
this.curWrite_ = (async () => {
await this.curWrite_;
await new Promise((resolve) => {
this.writer_.onwriteend = resolve;
this.writer_.write(blob);
});
})();
await this.curWrite_;
}
/**
* Finishes the write of video data parts and returns result video file.
* @return {!Promise<!FileEntry>} Result video file.
*/
async endWrite() {
await this.curWrite_;
return this.file_;
}
/**
* Create VideoSaver.
* @param {!FileEntry} file The file which VideoSaver saves the result video
* into.
* @return {!Promise<!cca.models.VideoSaver>}
*/
static async create(file) {
const writer = await new Promise(
(resolve, reject) => file.createWriter(resolve, reject));
return new cca.models.VideoSaver(file, writer);
}
};
......@@ -222,7 +222,7 @@ cca.mojo.ImageCapture.prototype.getPhotoCapabilities = async function() {
* @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.
* @return {!Array<!Promise<!Blob>>} Array of promises for the result.
*/
cca.mojo.ImageCapture.prototype.takePhoto = function(
photoSettings, photoEffects) {
......
......@@ -892,7 +892,7 @@ cca.util.setupI18nElements = function(rootElement) {
/**
* Reads blob into Image.
* @param {!Blob} blob
* @return {Promise<HTMLImageElement>}
* @return {!Promise<!HTMLImageElement>}
* @throws {Error}
*/
cca.util.blobToImage = function(blob) {
......
......@@ -16,23 +16,16 @@ cca.views = cca.views || {};
/**
* Creates the camera-view controller.
* @param {cca.models.Gallery} model Model object.
* @param {cca.models.ResultSaver} resultSaver
* @param {cca.device.DeviceInfoUpdater} infoUpdater
* @param {cca.device.PhotoResolPreferrer} photoPreferrer
* @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
* @constructor
*/
cca.views.Camera = function(
model, infoUpdater, photoPreferrer, videoPreferrer) {
resultSaver, infoUpdater, photoPreferrer, videoPreferrer) {
cca.views.View.call(this, '#camera');
/**
* Gallery model used to save taken pictures.
* @type {cca.models.Gallery}
* @private
*/
this.model_ = model;
/**
* @type {cca.device.DeviceInfoUpdater}
* @private
......@@ -71,36 +64,37 @@ cca.views.Camera = function(
*/
this.bannerLearnMore_ = document.querySelector('#banner-learn-more');
const doSavePhoto = async (result, name) => {
cca.metrics.log(
cca.metrics.Type.CAPTURE, this.facingMode_, 0, result.resolution);
try {
await resultSaver.savePhoto(result.blob, name);
} catch (e) {
cca.toast.show('error_msg_save_file_failed');
throw e;
}
};
const createVideoSaver = async () => resultSaver.startSaveVideo();
const doSaveVideo = async (result, name) => {
cca.metrics.log(
cca.metrics.Type.CAPTURE, this.facingMode_, result.duration,
result.resolution);
try {
await resultSaver.finishSaveVideo(result.videoSaver, name);
} catch (e) {
cca.toast.show('error_msg_save_file_failed');
throw e;
}
};
/**
* Modes for the camera.
* @type {cca.views.camera.Modes}
* @private
*/
this.modes_ = new cca.views.camera.Modes(
photoPreferrer, videoPreferrer, this.restart.bind(this),
async (result, filename) => {
if (result.blob) {
cca.metrics.log(
cca.metrics.Type.CAPTURE, this.facingMode_, 0, result.resolution);
try {
await this.model_.savePhoto(result.blob, filename);
} catch (e) {
cca.toast.show('error_msg_save_file_failed');
throw e;
}
}
},
async (result, filename) => {
cca.metrics.log(
cca.metrics.Type.CAPTURE, this.facingMode_, result.duration,
result.resolution);
try {
await this.model_.saveVideo(result.chunkfile, filename);
} catch (e) {
cca.toast.show('error_msg_save_file_failed');
throw e;
}
});
photoPreferrer, videoPreferrer, this.restart.bind(this), doSavePhoto,
createVideoSaver, doSaveVideo);
/**
* @type {?string}
......
......@@ -19,84 +19,72 @@ cca.views = cca.views || {};
*/
cca.views.camera = cca.views.camera || {};
/**
* Callback for saving photo capture result. It's called with parameter of photo
* capture result and filename to be saved to.
* @typedef {function(cca.views.camera.PhotoResult, string): Promise}
* DoSavePhoto
*/
/* eslint-disable no-unused-vars */
/**
* Callback for saving video capture result. It's called with parameter of video
* capture result and filename to be saved to.
* @typedef {function(cca.views.camera.VideoResult, string): Promise}
* DoSaveVideo
* Contains video recording result.
* @typedef {{
* resolution: {width: number, height: number},
* duration: number,
* videoSaver: !cca.models.VideoSaver,
* }}
*/
cca.views.camera.VideoResult;
/**
* Object contains video recording result.
* @param {number} width Resolution width of the video.
* @param {number} height Resolution height of the video.
* @param {number} duration Recorded time in minutes.
* @param {FileEntry} chunkfile File saving recorded chunks.
* @constructor
* Contains photo taking result.
* @typedef {{
* resolution: {width: number, height: number},
* blob: !Blob,
* }}
*/
cca.views.camera.VideoResult = function(width, height, duration, chunkfile) {
/**
* @type {[number, number]} Resolution of the video.
*/
this.resolution = [width, height];
cca.views.camera.PhotoResult;
/**
* @type {number}
*/
this.duration = duration;
/**
* @type {FileEntry}
*/
this.chunkfile = chunkfile;
/* eslint-enable no-unused-vars */
// End of properties, seal the object.
Object.seal(this);
};
/**
* Callback to trigger mode switching.
* @callback DoSwitchMode
* @return {!Promise}
*/
/**
* Object contains photo taking result.
* @param {number} width Resolution width of the photo.
* @param {number} height Resolution height of the photo.
* @param {?Blob} blob Blob saving photo result.
* @constructor
* Callback for saving photo capture result.
* @callback DoSavePhoto
* @param {!cca.views.camera.PhotoResult} Captured photo result.
* @param {string} Name of the photo result to be saved as.
* @return {!Promise}
*/
cca.views.camera.PhotoResult = function(width, height, blob) {
/**
* @type {[number, number]} Resolution of the photo.
*/
this.resolution = [width, height];
/**
* @type {?Blob}
*/
this.blob = blob;
/**
* Callback for allocating VideoSaver to save video capture result.
* @callback CreateVideoSaver
* @return {!Promise<!cca.models.VideoSaver>}
*/
// End of properties, seal the object.
Object.seal(this);
};
/**
* Callback for saving video capture result.
* @callback DoSaveVideo
* @param {!cca.views.camera.VideoResult} Captured video result.
* @param {string} Name of the video result to be saved as.
* @return {!Promise}
*/
/**
* Mode controller managing capture sequence of different camera mode.
* @param {cca.device.PhotoResolPreferrer} photoResolPreferrer
* @param {cca.device.VideoConstraintsPreferrer} videoPreferrer
* @param {function()} doSwitchMode Callback to trigger mode switching.
* @param {DoSavePhoto} doSavePhoto
* @param {DoSaveVideo} doSaveVideo
* @param {!DoSwitchMode} doSwitchMode
* @param {!DoSavePhoto} doSavePhoto
* @param {!CreateVideoSaver} createVideoSaver
* @param {!DoSaveVideo} doSaveVideo
* @constructor
*/
cca.views.camera.Modes = function(
photoResolPreferrer, videoPreferrer, doSwitchMode, doSavePhoto,
doSaveVideo) {
createVideoSaver, doSaveVideo) {
/**
* @type {function()}
* @type {!DoSwitchMode}
* @private
*/
this.doSwitchMode_ = doSwitchMode;
......@@ -133,8 +121,8 @@ cca.views.camera.Modes = function(
*/
this.allModes_ = {
'video-mode': {
captureFactory: () =>
new cca.views.camera.Video(this.stream_, doSaveVideo),
captureFactory: () => new cca.views.camera.Video(
this.stream_, createVideoSaver, doSaveVideo),
isSupported: async () => true,
resolutionConfig: videoPreferrer,
v1Config: cca.views.camera.Modes.getV1Constraints.bind(this, true),
......@@ -429,16 +417,22 @@ cca.views.camera.Mode.prototype.stop_ = function() {};
/**
* Video mode capture controller.
* @param {MediaStream} stream
* @param {DoSaveVideo} doSaveVideo
* @param {!CreateVideoSaver} createVideoSaver
* @param {!DoSaveVideo} doSaveVideo
* @constructor
*/
cca.views.camera.Video = function(stream, doSaveVideo) {
cca.views.camera.Video = function(stream, createVideoSaver, doSaveVideo) {
cca.views.camera.Mode.call(this, stream, null);
/**
* Callback for saving video.
* @type {DoSaveVideo} doSaveVideo
* @protected
* @type {!CreateVideoSaver}
* @private
*/
this.createVideoSaver_ = createVideoSaver;
/**
* @type {!DoSaveVideo}
* @private
*/
this.doSaveVideo_ = doSaveVideo;
......@@ -458,7 +452,7 @@ cca.views.camera.Video = function(stream, doSaveVideo) {
/**
* Record-time for the elapsed recording time.
* @type {cca.views.camera.RecordTime}
* @type {!cca.views.camera.RecordTime}
* @private
*/
this.recordTime_ = new cca.views.camera.RecordTime();
......@@ -498,7 +492,7 @@ cca.views.camera.Video.prototype.start_ = async function() {
this.recordTime_.start();
try {
var chunkfile = await this.createChunkfile_();
var videoSaver = await this.captureVideo_();
} catch (e) {
cca.toast.show('error_msg_empty_recording');
throw e;
......@@ -509,7 +503,7 @@ cca.views.camera.Video.prototype.start_ = async function() {
const {width, height} = this.stream_.getVideoTracks()[0].getSettings();
await this.doSaveVideo_(
new cca.views.camera.VideoResult(width, height, duration, chunkfile),
{resolution: {width, height}, duration, videoSaver},
(new cca.models.Filenamer()).newVideoName());
};
......@@ -536,47 +530,31 @@ cca.views.camera.Video.VIDEO_MIMETYPE = 'video/x-matroska;codecs=avc1';
/**
* Starts recording and waits for stop recording event triggered by stop
* shutter.
* @return {FileEntry} File saving recorded chunks.
* @async
* @return {!Promise<!cca.models.VideoSaver>} Saves recorded video.
* @private
*/
cca.views.camera.Video.prototype.createChunkfile_ = async function() {
const chunkfile = await cca.models.FileSystem.createTempVideoFile();
const writer = await new Promise(
(resolve, reject) => chunkfile.createWriter(resolve, reject));
cca.views.camera.Video.prototype.captureVideo_ = async function() {
const saver = await this.createVideoSaver_();
return await new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
let noChunk = true;
let prevWrite = Promise.resolve();
var ondataavailable = (event) => {
if (event.data && event.data.size > 0) {
noChunk = false;
prevWrite = (async () => {
await prevWrite;
await new Promise((resolve) => {
writer.onwriteend = resolve;
writer.write(event.data);
});
})();
saver.write(event.data);
}
};
var onstop = (event) => {
this.mediaRecorder_.removeEventListener('dataavailable', ondataavailable);
this.mediaRecorder_.removeEventListener('stop', onstop);
prevWrite.then(() => {
if (noChunk) {
reject(new Error('Video blob error.'));
} else {
resolve(chunkfile);
}
});
prevWrite.catch(
(e) => {
// TODO(yuli): Handle insufficient storage.
});
if (noChunk) {
reject(new Error('Video blob error.'));
} else {
// TODO(yuli): Handle insufficient storage.
resolve(saver);
}
};
this.mediaRecorder_.addEventListener('dataavailable', ondataavailable);
this.mediaRecorder_.addEventListener('stop', onstop);
......@@ -587,7 +565,7 @@ cca.views.camera.Video.prototype.createChunkfile_ = async function() {
/**
* Photo mode capture controller.
* @param {MediaStream} stream
* @param {DoSavePhoto} doSavePhoto
* @param {!DoSavePhoto} doSavePhoto
* @param {?[number, number]} captureResolution
* @constructor
*/
......@@ -596,7 +574,7 @@ cca.views.camera.Photo = function(stream, doSavePhoto, captureResolution) {
/**
* Callback for saving picture.
* @type {DoSavePhoto}
* @type {!DoSavePhoto}
* @protected
*/
this.doSavePhoto_ = doSavePhoto;
......@@ -639,7 +617,7 @@ cca.views.camera.Photo.prototype.start_ = async function() {
/**
* Takes a photo and returns capture result.
* @async
* @return {cca.views.camera.PhotoResult} Image capture result.
* @return {!cca.views.camera.PhotoResult} Image capture result.
* @private
*/
cca.views.camera.Photo.prototype.createPhotoResult_ = async function() {
......@@ -656,14 +634,14 @@ cca.views.camera.Photo.prototype.createPhotoResult_ = async function() {
};
}
const blob = await this.imageCapture_.takePhoto(photoSettings);
const image = await cca.util.blobToImage(blob);
return new cca.views.camera.PhotoResult(image.width, image.height, blob);
const {width, height} = await cca.util.blobToImage(blob);
return {resolution: {width, height}, blob};
};
/**
* Square mode capture controller.
* @param {MediaStream} stream
* @param {DoSavePhoto} doSavePhoto
* @param {!DoSavePhoto} doSavePhoto
* @param {?[number, number]} captureResolution
* @constructor
*/
......@@ -672,7 +650,7 @@ cca.views.camera.Square = function(stream, doSavePhoto, captureResolution) {
/**
* Photo saving callback from parent.
* @type {DoSavePhoto}
* @type {!DoSavePhoto}
* @private
*/
this.doAscentSave_ = this.doSavePhoto_;
......@@ -681,9 +659,7 @@ cca.views.camera.Square = function(stream, doSavePhoto, captureResolution) {
Object.seal(this);
this.doSavePhoto_ = async (result, ...args) => {
if (result.blob) {
result.blob = await this.cropSquare(result.blob);
}
result.blob = await this.cropSquare(result.blob);
await this.doAscentSave_(result, ...args);
};
};
......@@ -694,8 +670,8 @@ cca.views.camera.Square.prototype = {
/**
* Crops out maximum possible centered square from the image blob.
* @param {Blob} blob
* @return {Blob} Promise with result cropped square image.
* @param {!Blob} blob
* @return {!Blob} Promise with result cropped square image.
* @async
*/
cca.views.camera.Square.prototype.cropSquare = async function(blob) {
......@@ -718,7 +694,7 @@ cca.views.camera.Square.prototype.cropSquare = async function(blob) {
/**
* Portrait mode capture controller.
* @param {MediaStream} stream
* @param {DoSavePhoto} doSavePhoto
* @param {!DoSavePhoto} doSavePhoto
* @param {?[number, number]} captureResolution
* @constructor
*/
......@@ -788,9 +764,9 @@ cca.views.camera.Portrait.prototype.start_ = async function() {
playSound = true;
cca.sound.play('#sound-shutter');
}
const image = await cca.util.blobToImage(blob);
const {width, height} = await cca.util.blobToImage(blob);
await this.doSavePhoto_(
new cca.views.camera.PhotoResult(image.width, image.height, blob),
{resolution: {width, height}, blob},
filenamer.newBurstName(!isPortrait));
});
try {
......
......@@ -26,6 +26,8 @@
<script src="../js/models/filenamer.js"></script>
<script src="../js/models/gallery.js"></script>
<script src="../js/models/filesystem.js"></script>
<script src="../js/models/result_saver.js"></script>
<script src="../js/models/video_saver.js"></script>
<script src="../js/mojo/mojo_bindings_lite.js"></script>
<script src="../js/mojo/camera_metadata_tags.mojom-lite.js"></script>
<script src="../js/mojo/camera_metadata.mojom-lite.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