Extracted CameraSource; Added applyConfig to change config during runtime

feature/image-source
Christoph Oberhofer 8 years ago
parent b2ac66eb72
commit eae4904e7a

@ -100,10 +100,14 @@ $(function() {
this._accessByPath(this.state, path, value); this._accessByPath(this.state, path, value);
console.log(JSON.stringify(this.state)); console.log(JSON.stringify(this.state));
this.detachListeners();
this.scanner.stop(); this.scanner.applyConfig({
this.scanner.removeEventListener(); constraints: this.state.inputStream.constraints,
App.init(); locator: this.state.locator,
decoder: this.state.decoder,
numOfWorkers: this.state.numOfWorkers,
})
.catch(e => console.error(e));
}, },
inputMapper: { inputMapper: {
inputStream: { inputStream: {
@ -111,6 +115,17 @@ $(function() {
if (/^(\d+)x(\d+)$/.test(value)) { if (/^(\d+)x(\d+)$/.test(value)) {
var values = value.split('x'); var values = value.split('x');
return { return {
landscape: {
width: {ideal: parseInt(values[0])},
height: {ideal: parseInt(values[1])},
zoom: 1.5,
},
portrait: {
width: {ideal: parseInt(values[0])},
height: {ideal: parseInt(values[0])},
zoom: 1.5,
aspectRatio: 1,
},
width: {ideal: parseInt(values[0])}, width: {ideal: parseInt(values[0])},
height: {ideal: parseInt(values[1])} height: {ideal: parseInt(values[1])}
}; };

@ -0,0 +1,208 @@
import {clone} from 'lodash';
import {determineOrientation, PORTRAIT} from '../common/device';
import CameraAccess from './camera_access';
import {getViewport} from '../common/utils';
const ConstraintPresets = [
{
width: 720,
height: 1280,
}, {
width: 540,
height: 960,
}, {
width: 600,
height: 800,
}, {
width: 480,
height: 640,
}, {
width: 1280,
height: 720,
}, {
width: 960,
height: 540,
}, {
width: 800,
height: 600,
}, {
width: 640,
height: 480,
}, {
width: 1280,
height: 1280,
}, {
width: 1080,
height: 1080,
}, {
width: 960,
height: 960,
}, {
width: 800,
height: 800,
}, {
width: 640,
height: 640,
},
].map((preset) => Object.assign({}, preset, {aspectRatio: preset.width / preset.height}));
function getFilter(aspectRatio) {
if (aspectRatio === 1) {
return pre => pre.aspectRatio === aspectRatio;
} else if (aspectRatio > 1) {
return pre => pre.aspectRatio > 1;
}
return pre => pre.aspectRatio < 1;
}
function resolveMinWidthToAdvanced({aspectRatio, minPixels}) {
return [...ConstraintPresets]
.filter(getFilter(aspectRatio))
.map((pre) => {
return {
error: Math.abs((pre.width * pre.height) - minPixels),
pre,
};
})
.sort(({error: errorA}, {error: errorB}) => {
if (errorB > errorA) {
return -1;
}
if (errorB < errorA) {
return 1;
}
return 0;
})
.map(({pre}) => pre);
}
function getOrCreateVideo(target) {
const $viewport = getViewport(target);
if ($viewport) {
let $video = $viewport.querySelector("video");
if (!$video) {
$video = document.createElement("video");
$viewport.appendChild($video);
}
return $video;
}
return document.createElement("video");
}
function constraintToNumber(constraint) {
if (!constraint) {
return null;
}
if (typeof constraint === 'number') {
return constraint;
}
const {ideal, exact, min, max} = constraint;
if (typeof exact !== 'undefined') {
return exact;
}
if (typeof ideal !== 'undefined') {
return ideal;
}
if (typeof min !== 'undefined') {
return min;
}
if (typeof max !== 'undefined') {
return max;
}
return null;
}
function adjustWithZoom(videoConstraints) {
const constraints = clone(videoConstraints);
const orientation = determineOrientation();
let zoom = constraintToNumber(constraints.zoom) || 1,
width = constraintToNumber(constraints.width),
height = constraintToNumber(constraints.height),
aspectRatio = constraintToNumber(constraints.aspectRatio) || (width / height);
if (constraints[orientation]) {
zoom = constraintToNumber(constraints[orientation].zoom) || zoom;
width = constraintToNumber(constraints[orientation].width) || width;
height = constraintToNumber(constraints[orientation].height) || height;
aspectRatio = constraintToNumber(constraints[orientation].aspectRatio) || (width / height);
}
if (zoom > 1) {
width = Math.floor(width * zoom);
height = Math.floor(height * zoom);
}
delete constraints.zoom;
delete constraints.orientation;
delete constraints.landscape;
delete constraints.portrait;
const advanced = resolveMinWidthToAdvanced({minPixels: (width * height), aspectRatio});
return {
zoom,
video: Object.assign({}, constraints, {
width: {ideal: advanced[0].width},
height: {ideal: advanced[0].height},
aspectRatio: {exact: advanced[0].aspectRatio || aspectRatio},
advanced,
}),
};
}
export function fromCamera(constraints, {target} = {}) {
let {video: videoConstraints, zoom} = adjustWithZoom(constraints);
const video = getOrCreateVideo(target);
return CameraAccess.request(video, videoConstraints)
.then((mediastream) => {
let track = mediastream.getVideoTracks()[0];
return {
type: "CAMERA",
getDimensions() {
const viewport = {
x: 0,
y: 0,
width: video.videoWidth,
height: video.videoHeight,
};
if (zoom > 1) {
viewport.width = Math.floor(video.videoWidth / zoom);
viewport.height = Math.floor(video.videoHeight / zoom);
viewport.x = Math.floor((video.videoWidth - viewport.width) / 2);
viewport.y = Math.floor((video.videoHeight - viewport.height) / 2);
}
return {
viewport,
canvas: {
width: viewport.width, // AR
height: viewport.height, // AR
},
};
},
getConstraints: function() {
return videoConstraints;
},
getDrawable: function() {
return video;
},
applyConstraints: function(newConstraints) {
track.stop();
constraints = newConstraints;
const adjustment = adjustWithZoom(constraints);
videoConstraints = adjustment.video;
zoom = adjustment.zoom;
return CameraAccess.request(video, videoConstraints)
.then((stream) => {
mediastream = stream;
track = mediastream.getVideoTracks()[0];
});
},
getLabel: function() {
return track.label;
}
};
});
}

@ -1,218 +1,5 @@
import {clone} from 'lodash';
import {determineOrientation, PORTRAIT, LANDSCAPE, SQUARE} from '../common/device';
import CameraAccess from './camera_access';
import {getViewport} from '../common/utils';
export * from './ImageSource'; export * from './ImageSource';
export * from './CameraSource';
const ConstraintPresets = [
{
width: 720,
height: 1280,
}, {
width: 540,
height: 960,
}, {
width: 600,
height: 800,
}, {
width: 480,
height: 640,
}, {
width: 1280,
height: 720,
}, {
width: 960,
height: 540,
}, {
width: 800,
height: 600,
}, {
width: 640,
height: 480,
}, {
width: 1280,
height: 1280,
}, {
width: 1080,
height: 1080,
}, {
width: 960,
height: 960,
}, {
width: 800,
height: 800,
}, {
width: 640,
height: 640,
},
].map((preset) => Object.assign({}, preset, {aspectRatio: preset.width / preset.height}));
function getFilter(aspectRatio) {
if (aspectRatio === 1) {
return pre => pre.aspectRatio === aspectRatio;
} else if (aspectRatio > 1) {
return pre => pre.aspectRatio > 1;
}
return pre => pre.aspectRatio < 1;
}
function resolveMinWidthToAdvanced({aspectRatio, minPixels}) {
return [...ConstraintPresets]
.filter(getFilter(aspectRatio))
.map((pre) => {
return {
error: Math.abs((pre.width * pre.height) - minPixels),
pre,
};
})
.sort(({error: errorA}, {error: errorB}) => {
if (errorB > errorA) {
return -1;
}
if (errorB < errorA) {
return 1;
}
return 0;
})
.map(({pre}) => pre);
}
function getOrCreateVideo(target) {
const $viewport = getViewport(target);
if ($viewport) {
let $video = $viewport.querySelector("video");
if (!$video) {
$video = document.createElement("video");
$viewport.appendChild($video);
}
return $video;
}
return document.createElement("video");
}
function constraintToNumber(constraint) {
if (!constraint) {
return null;
}
if (typeof constraint === 'number') {
return constraint;
}
const {ideal, exact, min, max} = constraint;
if (typeof exact !== 'undefined') {
return exact;
}
if (typeof ideal !== 'undefined') {
return ideal;
}
if (typeof min !== 'undefined') {
return min;
}
if (typeof max !== 'undefined') {
return max;
}
return null;
}
function adjustWithZoom(videoConstraints) {
const constraints = clone(videoConstraints);
const orientation = determineOrientation();
let zoom = constraintToNumber(constraints.zoom) || 1,
width = constraintToNumber(constraints.width),
height = constraintToNumber(constraints.height),
aspectRatio = constraintToNumber(constraints.aspectRatio) || (width / height);
if (constraints[orientation]) {
zoom = constraintToNumber(constraints[orientation].zoom) || zoom;
width = constraintToNumber(constraints[orientation].width) || width;
height = constraintToNumber(constraints[orientation].height) || height;
aspectRatio = constraintToNumber(constraints[orientation].aspectRatio) || (width / height);
}
if (zoom > 1) {
width = Math.floor(width * zoom);
height = Math.floor(height * zoom);
}
delete constraints.zoom;
delete constraints.orientation;
delete constraints.landscape;
delete constraints.portrait;
const advanced = resolveMinWidthToAdvanced({minPixels: (width * height), aspectRatio});
return {
zoom,
video: Object.assign({}, constraints, {
width: {ideal: advanced[0].width},
height: {ideal: advanced[0].height},
aspectRatio: {exact: advanced[0].aspectRatio || aspectRatio},
advanced,
}),
};
}
export function fromCamera(constraints, {target} = {}) {
var {video: videoConstraints, zoom} = adjustWithZoom(constraints);
const video = getOrCreateVideo(target);
return CameraAccess.request(video, videoConstraints)
.then(function(mediastream) {
const track = mediastream.getVideoTracks()[0];
return {
type: "CAMERA",
getDimensions() {
const viewport = {
x: 0,
y: 0,
width: video.videoWidth,
height: video.videoHeight,
};
if (zoom > 1) {
viewport.width = Math.floor(video.videoWidth / zoom);
viewport.height = Math.floor(video.videoHeight / zoom);
viewport.x = Math.floor((video.videoWidth - viewport.width) / 2);
viewport.y = Math.floor((video.videoHeight - viewport.height) / 2);
}
return {
viewport,
canvas: {
width: viewport.width, // AR
height: viewport.height, // AR
},
};
},
getConstraints: function() {
return videoConstraints;
},
getDrawable: function() {
return video;
},
applyConstraints: function(constraints) {
track.stop();
videoConstraints = Object.assign({}, constraints);
if (determineOrientation() === PORTRAIT) {
constraints = Object.assign({}, constraints, {
width: constraints.height,
height: constraints.width,
});
}
console.log(videoConstraints, constraints);
if (constraints.zoom && constraints.zoom.exact > 1) {
constraints.width.ideal = Math.floor(constraints.width.ideal * constraints.zoom.exact);
constraints.height.ideal = Math.floor(constraints.height.ideal * constraints.zoom.exact);
delete constraints.zoom;
}
return CameraAccess.request(video, videoConstraints);
},
getLabel: function() {
return track.label;
}
};
});
}
export function fromCanvas(input) { export function fromCanvas(input) {
var $canvas = null; var $canvas = null;

@ -4,12 +4,19 @@ import ImageWrapper from './common/image_wrapper';
import ImageDebug from './common/image_debug'; import ImageDebug from './common/image_debug';
import ResultCollector from './analytics/result_collector'; import ResultCollector from './analytics/result_collector';
import Config from './config/config'; import Config from './config/config';
import {merge} from 'lodash'; import {merge, isEqual} from 'lodash';
import CameraAccess from './input/camera_access'; import CameraAccess from './input/camera_access';
import * as PixelCapture from './input/PixelCapture'; import * as PixelCapture from './input/PixelCapture';
import * as Source from './input/Source'; import * as Source from './input/Source';
import {PORTRAIT, LANDSCAPE, SQUARE} from './common/device'; import {PORTRAIT, LANDSCAPE, SQUARE} from './common/device';
function hasConfigChanged(currentConfig, newConfig, prop) {
if (!prop) {
return !isEqual(currentConfig, newConfig);
}
return newConfig[prop] && !isEqual(currentConfig[prop], newConfig[prop]);
}
function fromConfig(pixelCapturer, config) { function fromConfig(pixelCapturer, config) {
const scanner = createScanner(pixelCapturer); const scanner = createScanner(pixelCapturer);
const source = pixelCapturer.getSource(); const source = pixelCapturer.getSource();
@ -98,16 +105,21 @@ function fromConfig(pixelCapturer, config) {
return pixelCapturer.getCanvas(); return pixelCapturer.getCanvas();
}, },
applyConfig(newConfig) { applyConfig(newConfig) {
// during runtime? const normalizedConfig = merge({}, Config, currentConfig, newConfig);
// running scanners must know that! let promise = Promise.resolve();
// apply constraints to source, only if changed if (hasConfigChanged(currentConfig, normalizedConfig, "constraints")) {
console.log("constraints changed!", currentConfig.constraints, normalizedConfig.constraints);
const normalizedConfig = merge({}, Config, newConfig); promise = promise.then(() => {
this.stop(); scanner.pause();
return source.applyConstraints(normalizedConfig.constraints);
});
}
if (hasConfigChanged(currentConfig, normalizedConfig)) {
console.log("config changed!");
promise = promise.then(() => scanner.applyConfig(normalizedConfig));
}
currentConfig = normalizedConfig; currentConfig = normalizedConfig;
return promise;
return source.applyConstraints(currentConfig.constraints)
.then(() => this.start());
}, },
getSource() { getSource() {
return pixelCapturer.getSource(); return pixelCapturer.getSource();
@ -120,11 +132,6 @@ function fromSource(config, source) {
return fromConfig(pixelCapturer, config); return fromConfig(pixelCapturer, config);
} }
function setConfig(configuration = {}, key, config = {}) {
var mergedConfig = merge({}, configuration, {[key]: config});
return createApi(mergedConfig);
}
function createApi() { function createApi() {
return { return {
fromImage(image, options) { fromImage(image, options) {
@ -142,21 +149,6 @@ function createApi() {
fromSource(src, inputConfig) { fromSource(src, inputConfig) {
return fromSource(configuration, src, inputConfig); return fromSource(configuration, src, inputConfig);
}, },
fromConfig(conf) {
return fromConfig(merge({}, configuration, conf));
},
decoder(conf) {
return setConfig(configuration, "decoder", conf);
},
locator(conf) {
return setConfig(configuration, "locator", conf);
},
throttle(timeInMs) {
return setConfig(configuration, "frequency", 1000 / parseInt(timeInMs));
},
config(conf) {
return createApi(merge({}, configuration, conf));
},
CameraAccess, CameraAccess,
ImageWrapper, ImageWrapper,
ImageDebug, ImageDebug,

@ -442,6 +442,12 @@ function createScanner(pixelCapturer) {
CameraAccess.release(); CameraAccess.release();
} }
}, },
applyConfig(newConfig) {
_stopped = true;
adjustWorkerPool(0);
_config = merge({}, Config, _config, newConfig);
return setup(_config).then(start);
},
pause: function() { pause: function() {
_stopped = true; _stopped = true;
}, },

Loading…
Cancel
Save