mirror of
https://github.com/serratus/quaggaJS.git
synced 2026-08-13 05:31:37 +08:00
Added Configuration-Factory for various modes of input
This commit is contained in:
@@ -0,0 +1,138 @@
|
|||||||
|
import {merge, pick, omitBy, isEmpty} from 'lodash';
|
||||||
|
|
||||||
|
const isDataURL = {regex: /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i}, // eslint-disable-line max-len
|
||||||
|
isBlobURL = {regex: /^\s*blob:(.*)$/i},
|
||||||
|
isMediaURL = {regex: /^(?:(?:http[s]?|ftp):\/)?\/?(?:(?:[^:\/\s]+)(?:(?:\/\w+)*\/))?([\w\-]+\.([^#?\s]+))(?:.*)?(?:#[\w\-]+)?$/i}, // eslint-disable-line max-len
|
||||||
|
isImageExt = {regex: /(jpe?g|png|gif|tiff)(?:\s+|$)/i},
|
||||||
|
isVideoExt = {regex: /(webm|ogg|mp4|m4v)/i};
|
||||||
|
|
||||||
|
const MediaStream = "MediaStream" in window ? window.MediaStream : function(){};
|
||||||
|
|
||||||
|
export function createConfigFromSource(config, sourceConfig, source) {
|
||||||
|
if (source instanceof MediaStream) {
|
||||||
|
return createConfigForStream(config, sourceConfig, {srcObject: source});
|
||||||
|
} else if (source instanceof HTMLImageElement) {
|
||||||
|
throw new Error('Source "HTMLImageElement": not yet supported');
|
||||||
|
// return createConfigForImage(config, inputConfig, {image: source});
|
||||||
|
} else if (source instanceof HTMLVideoElement) {
|
||||||
|
throw new Error('Source "HTMLVideoElement": not yet supported');
|
||||||
|
// return createConfigForVideo(config, inputConfig, {video: source});
|
||||||
|
} else if (source instanceof HTMLCanvasElement) {
|
||||||
|
return createConfigForCanvas(config, sourceConfig, {canvas: source});
|
||||||
|
} else if (source instanceof FileList) {
|
||||||
|
if (source.length > 0) {
|
||||||
|
return createConfigForFile(config, sourceConfig, source[0]);
|
||||||
|
}
|
||||||
|
} else if (source instanceof File) {
|
||||||
|
return createConfigForFile(config, sourceConfig, source);
|
||||||
|
} else if (typeof source === 'string') {
|
||||||
|
return createConfigForString(config, sourceConfig, source);
|
||||||
|
} else if (typeof source === 'object'
|
||||||
|
&& (typeof source.constraints !== 'undefined'
|
||||||
|
|| typeof source.area !== 'undefined')) {
|
||||||
|
return createConfigForLiveStream(config, source);
|
||||||
|
} else {
|
||||||
|
throw new Error("No source given!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForImage(config, source, inputConfig = {}) {
|
||||||
|
const staticImageConfig = {
|
||||||
|
inputStream: merge({
|
||||||
|
type: "ImageStream",
|
||||||
|
sequence: false,
|
||||||
|
size: 800
|
||||||
|
}, source),
|
||||||
|
numOfWorkers: (ENV.development && config.debug) ? 0 : 1
|
||||||
|
};
|
||||||
|
return merge(
|
||||||
|
config,
|
||||||
|
staticImageConfig,
|
||||||
|
{numOfWorkers: typeof config.numOfWorkers === 'number' && config.numOfWorkers > 0 ? 1 : 0},
|
||||||
|
{inputStream: omitBy(pick(config.inputStream, ['size']), isEmpty)},
|
||||||
|
{inputStream: inputConfig});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForMimeType(config, inputConfig, {src, mime}) {
|
||||||
|
const [, type] = mime.match(/^(video|image)\/(.*)$/i) || [];
|
||||||
|
if (type === 'video') {
|
||||||
|
return createConfigForVideo(config, {src}, inputConfig);
|
||||||
|
} else if (type === 'image') {
|
||||||
|
return createConfigForImage(config, {src}, inputConfig);
|
||||||
|
}
|
||||||
|
throw new Error(`Source with mimetype: "${type}" not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForFile(config, inputConfig, file) {
|
||||||
|
const src = window.URL.createObjectURL(file);
|
||||||
|
return createConfigForMimeType(config, inputConfig, {
|
||||||
|
src,
|
||||||
|
mime: file.type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForString(config, inputConfig = {}, source) {
|
||||||
|
const [, mime] = source.match(isDataURL.regex) || [];
|
||||||
|
if (mime) {
|
||||||
|
return createConfigForMimeType(config, inputConfig, {src: source, mime});
|
||||||
|
}
|
||||||
|
const blobURL = source.match(isBlobURL.regex);
|
||||||
|
if (blobURL) {
|
||||||
|
throw new Error(`Source "objectURL": not supported`);
|
||||||
|
}
|
||||||
|
const [, , ext] = source.match(isMediaURL.regex) || [];
|
||||||
|
if (ext) {
|
||||||
|
return createConfigForMediaExtension(config, inputConfig, {src: source, ext});
|
||||||
|
}
|
||||||
|
throw new Error(`Source "${source}": not recognized`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForMediaExtension(config, inputConfig, {src, ext}) {
|
||||||
|
if (ext.match(isImageExt.regex)) {
|
||||||
|
return createConfigForImage(config, {src}, inputConfig);
|
||||||
|
} else if (ext.match(isVideoExt.regex)) {
|
||||||
|
return createConfigForVideo(config, {src}, inputConfig);
|
||||||
|
}
|
||||||
|
throw new Error(`Source "MediaString": not recognized`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForCanvas (config, {canvas}, inputConfig = {}) {
|
||||||
|
// TODO: adjust stream & frame-grabber
|
||||||
|
// once/continous
|
||||||
|
throw new Error('Source "Canvas": not implemented!');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForVideo (config, source, inputConfig = {}) {
|
||||||
|
return merge({},
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
inputStream: merge({
|
||||||
|
type: "VideoStream"
|
||||||
|
}, source)
|
||||||
|
}, {
|
||||||
|
inputStream: inputConfig
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForStream(config, {srcObject}, inputConfig = {}) {
|
||||||
|
// TODO: attach to <video> element
|
||||||
|
// wait for the video to be ready (dimensions known)
|
||||||
|
throw new Error('Source "MediaStream": not implemented!');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConfigForLiveStream(config, inputConfig = {}) {
|
||||||
|
return merge({},
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
inputStream: {
|
||||||
|
type: "LiveStream",
|
||||||
|
constraints: {
|
||||||
|
width: 640,
|
||||||
|
height: 480,
|
||||||
|
facingMode: "environment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
inputStream: inputConfig
|
||||||
|
});
|
||||||
|
}
|
||||||
+16
-83
@@ -5,85 +5,11 @@ 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, pick, omitBy, isEmpty} from 'lodash';
|
import {merge} from 'lodash';
|
||||||
|
import {createConfigFromSource} from './input/config_factory';
|
||||||
|
|
||||||
function fromImage(config, imageSrc, inputConfig = {}) {
|
function fromSource(config, source, inputConfig = {}) {
|
||||||
const staticImageConfig = {
|
config = createConfigFromSource(config, inputConfig, source);
|
||||||
inputStream: {
|
|
||||||
type: "ImageStream",
|
|
||||||
sequence: false,
|
|
||||||
size: 800,
|
|
||||||
src: imageSrc
|
|
||||||
},
|
|
||||||
numOfWorkers: (ENV.development && config.debug) ? 0 : 1
|
|
||||||
};
|
|
||||||
config = merge(
|
|
||||||
config,
|
|
||||||
staticImageConfig,
|
|
||||||
{numOfWorkers: typeof config.numOfWorkers === 'number' && config.numOfWorkers > 0 ? 1 : 0},
|
|
||||||
{inputStream: omitBy(pick(config.inputStream, ['size', 'src']), isEmpty)},
|
|
||||||
{inputStream: inputConfig});
|
|
||||||
|
|
||||||
const scanner = createScanner();
|
|
||||||
return {
|
|
||||||
addEventListener(eventType, cb) {
|
|
||||||
scanner.subscribe(eventType, cb);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
removeEventListener(eventType, cb) {
|
|
||||||
scanner.unsubscribe(eventType, cb);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
start() {
|
|
||||||
scanner.init(config, () => {
|
|
||||||
scanner.start();
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
stop() {
|
|
||||||
scanner.stop();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
toPromise() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
scanner.decodeSingle(config, (result) => {
|
|
||||||
if (result && result.codeResult && result.codeResult.code) {
|
|
||||||
return resolve(result);
|
|
||||||
}
|
|
||||||
return reject(result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromVideo(config, source, inputConfig = {}) {
|
|
||||||
// remember last instance
|
|
||||||
// check if anything but the imagesrc has changed
|
|
||||||
//
|
|
||||||
let sourceConfig = {
|
|
||||||
type: "LiveStream",
|
|
||||||
constraints: {
|
|
||||||
width: 640,
|
|
||||||
height: 480,
|
|
||||||
facingMode: "environment"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*if (source instanceof MediaStream) {
|
|
||||||
// stream
|
|
||||||
} else*/ if (source instanceof Element) {
|
|
||||||
// video element
|
|
||||||
} else if (typeof source === 'string') {
|
|
||||||
// video source
|
|
||||||
} else if (typeof source === 'object'
|
|
||||||
&& (typeof source.constraints !== 'undefined'
|
|
||||||
|| typeof source.area !== 'undefined')) {
|
|
||||||
inputConfig = source;
|
|
||||||
} else if (!source) {
|
|
||||||
// LiveStream
|
|
||||||
}
|
|
||||||
config = merge({}, config, {inputStream: sourceConfig}, {inputStream: inputConfig});
|
|
||||||
console.log(config);
|
console.log(config);
|
||||||
const scanner = createScanner();
|
const scanner = createScanner();
|
||||||
return {
|
return {
|
||||||
@@ -108,6 +34,16 @@ function fromVideo(config, source, inputConfig = {}) {
|
|||||||
stop() {
|
stop() {
|
||||||
scanner.stop();
|
scanner.stop();
|
||||||
return this;
|
return this;
|
||||||
|
},
|
||||||
|
toPromise() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
scanner.decodeSingle(config, (result) => {
|
||||||
|
if (result && result.codeResult && result.codeResult.code) {
|
||||||
|
return resolve(result);
|
||||||
|
}
|
||||||
|
return reject(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -121,11 +57,8 @@ function setConfig(configuration = {}, key, config = {}) {
|
|||||||
|
|
||||||
function createApi(configuration = Config) {
|
function createApi(configuration = Config) {
|
||||||
return {
|
return {
|
||||||
fromImage(src, conf) {
|
fromSource(src, inputConfig) {
|
||||||
return fromImage(configuration, src, conf);
|
return fromSource(configuration, src, inputConfig);
|
||||||
},
|
|
||||||
fromVideo(src, inputConfig) {
|
|
||||||
return fromVideo(configuration, src, inputConfig);
|
|
||||||
},
|
},
|
||||||
decoder(conf) {
|
decoder(conf) {
|
||||||
return setConfig(configuration, "decoder", conf);
|
return setConfig(configuration, "decoder", conf);
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import {createConfigFromSource} from '../../src/input/config_factory';
|
||||||
|
|
||||||
|
function MyFileList(file) {
|
||||||
|
Array.call(this);
|
||||||
|
this.push(file);
|
||||||
|
};
|
||||||
|
MyFileList.prototype = Object.create(Array.prototype);
|
||||||
|
MyFileList.prototype.constructor = MyFileList;
|
||||||
|
|
||||||
|
const OriginalFileList = window.FileList;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
window.FileList = MyFileList;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
window.FileList = OriginalFileList;
|
||||||
|
});
|
||||||
|
|
||||||
|
function expectImageConfig(config) {
|
||||||
|
expect(config.inputStream.type).to.equal("ImageStream");
|
||||||
|
expect(config.inputStream.sequence).to.equal(false);
|
||||||
|
expect(config.inputStream.size).to.be.above(0);
|
||||||
|
expect(config.numOfWorkers).to.equal(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectVideoConfig(config) {
|
||||||
|
expect(config.inputStream.type).to.equal("VideoStream");
|
||||||
|
expect(config.inputStream.src).to.be.a('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectLiveConfig(config) {
|
||||||
|
expect(config.inputStream.type).to.equal("LiveStream");
|
||||||
|
expect(config.inputStream.src).to.not.exist;
|
||||||
|
expect(config.inputStream.constraints.width).to.be.above(0);
|
||||||
|
expect(config.inputStream.constraints.height).to.be.above(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("createConfigFromSource", () => {
|
||||||
|
it("should create an image config for an image-file", () => {
|
||||||
|
const file = new File([], "image.jpg", {type: 'image/jpg'});
|
||||||
|
const config = createConfigFromSource({}, {}, file);
|
||||||
|
expectImageConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("blob:");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create an image config for a data-url", () => {
|
||||||
|
const config = createConfigFromSource({}, {}, "data:image/png;base64," +
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/d" +
|
||||||
|
"AAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A" +
|
||||||
|
"6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");
|
||||||
|
|
||||||
|
expectImageConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("data:image/png");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create an image config for a regular image url", () => {
|
||||||
|
const config = createConfigFromSource({}, {}, "/image-001.jpg");
|
||||||
|
expectImageConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("/image-001.jpg");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create an image config for an absolute image url", () => {
|
||||||
|
const config = createConfigFromSource({}, {}, "http://dja.com/ige.png");
|
||||||
|
expectImageConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("http://dja.com/ige.png");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case of an blob-url", () => {
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, "blob:das"))
|
||||||
|
.to.throw(Error, /objectURL/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case an arbitrary string is given", () => {
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, "dhfskjdfhsdfsdf"))
|
||||||
|
.to.throw(Error, /dhfskjdfhsdfsdf/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case of an unsupported mime type", () => {
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, "data:audio/mp3;base64,379"))
|
||||||
|
.to.throw(Error, /mimetype/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case of an unsupported extension", () => {
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, "sdflsdkf.mp3"))
|
||||||
|
.to.throw(Error, /MediaString/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case of an HTMLImageElement", () => {
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, new Image()))
|
||||||
|
.to.throw(Error, /HTMLImageElement/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error in case of an HTMLVideoElement", () => {
|
||||||
|
const video = document.createElement("video");
|
||||||
|
console.log(typeof video);
|
||||||
|
expect(createConfigFromSource.bind(null, {}, {}, video))
|
||||||
|
.to.throw(Error, /HTMLVideoElement/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with a fileList", () => {
|
||||||
|
const file = new File([], "image.jpg", {type: 'image/jpg'});
|
||||||
|
const fileList = new MyFileList(file);
|
||||||
|
const config = createConfigFromSource({}, {}, fileList);
|
||||||
|
expectImageConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("blob:");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a video config for a given url", () => {
|
||||||
|
const config = createConfigFromSource({}, {}, "/video-001.ogg");
|
||||||
|
expectVideoConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("/video-001.ogg");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a video config for a given file", () => {
|
||||||
|
const file = new File([], "video-001.ogg", {type: 'video/ogg'});
|
||||||
|
const config = createConfigFromSource({}, {}, file);
|
||||||
|
expectVideoConfig(config);
|
||||||
|
expect(config.inputStream.src).to.have.string("blob:");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a live config", () => {
|
||||||
|
const config = createConfigFromSource({}, {}, {
|
||||||
|
constraints: {
|
||||||
|
width: 1280,
|
||||||
|
height: 480,
|
||||||
|
facingMode: "user"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expectLiveConfig(config);
|
||||||
|
expect(config.inputStream.constraints.facingMode).to.equal("user");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user