Implemented new MediaDevices API; Fallback for facingMode on Chrome-Mobile

feature/#97
Christoph Oberhofer 9 years ago
parent 322413b3c5
commit 4019ea867f

@ -98,8 +98,7 @@ $(function() {
var values = value.split('x'); var values = value.split('x');
return { return {
width: parseInt(values[0]), width: parseInt(values[0]),
height: parseInt(values[1]), height: parseInt(values[1])
facing: "environment"
} }
} }
}, },
@ -118,7 +117,7 @@ $(function() {
constraints: { constraints: {
width: 640, width: 640,
height: 480, height: 480,
facing: "environment" // or user facingMode: "environment"
} }
}, },
locator: { locator: {

@ -77,6 +77,7 @@
"gl-matrix": "^2.3.1", "gl-matrix": "^2.3.1",
"lodash": "^3.10.1", "lodash": "^3.10.1",
"ndarray": "^1.0.18", "ndarray": "^1.0.18",
"ndarray-linear-interpolate": "^1.0.0" "ndarray-linear-interpolate": "^1.0.0",
"webrtc-adapter": "^1.0.6"
} }
} }

@ -14,10 +14,6 @@ if (typeof window !== 'undefined') {
window.setTimeout(callback, 1000 / 60); window.setTimeout(callback, 1000 / 60);
}; };
})(); })();
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
} }
Math.imul = Math.imul || function(a, b) { Math.imul = Math.imul || function(a, b) {
var ah = (a >>> 16) & 0xffff, var ah = (a >>> 16) & 0xffff,

@ -5,9 +5,9 @@ module.exports = {
constraints: { constraints: {
width: 640, width: 640,
height: 480, height: 480,
minAspectRatio: 0, // aspectRatio: 640/480, // optional
maxAspectRatio: 100, facingMode: "environment", // or user
facing: "environment" // or user // deviceId: "38745983457387598375983759834"
}, },
area: { area: {
top: "0%", top: "0%",

@ -1,28 +1,13 @@
const merge = require('lodash/object/merge'); const merge = require('lodash/object/merge');
const pick = require('lodash/object/pick');
const mapKeys = require('lodash/object/mapKeys');
var streamRef, var streamRef,
loadedDataHandler; loadedDataHandler;
/** function waitForVideo(video) {
* Wraps browser-specific getUserMedia return new Promise((resolve, reject) => {
* @param {Object} constraints let attempts = 10;
* @param {Object} success Callback
* @param {Object} failure Callback
*/
function getUserMedia(constraints, success, failure) {
if (typeof navigator.getUserMedia !== 'undefined') {
navigator.getUserMedia(constraints, function (stream) {
streamRef = stream;
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
success.apply(null, [videoSrc]);
}, failure);
} else {
failure(new TypeError("getUserMedia not available"));
}
}
function loadedData(video, callback) {
var attempts = 10;
function checkVideo() { function checkVideo() {
if (attempts > 0) { if (attempts > 0) {
@ -30,16 +15,17 @@ function loadedData(video, callback) {
if (ENV.development) { if (ENV.development) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px"); console.log(video.videoWidth + "px x " + video.videoHeight + "px");
} }
callback(); resolve();
} else { } else {
window.setTimeout(checkVideo, 500); window.setTimeout(checkVideo, 500);
} }
} else { } else {
callback('Unable to play video stream. Is webcam working?'); reject('Unable to play video stream. Is webcam working?');
} }
attempts--; attempts--;
} }
checkVideo(); checkVideo();
});
} }
/** /**
@ -47,89 +33,72 @@ function loadedData(video, callback) {
* and calls the callback function when the content is ready * and calls the callback function when the content is ready
* @param {Object} constraints * @param {Object} constraints
* @param {Object} video * @param {Object} video
* @param {Object} callback
*/ */
function initCamera(constraints, video, callback) { function initCamera(video, constraints) {
getUserMedia(constraints, function(src) { return navigator.mediaDevices.getUserMedia(constraints)
video.src = src; .then((stream) => {
if (loadedDataHandler) { return new Promise((resolve, reject) => {
video.removeEventListener("loadeddata", loadedDataHandler, false); streamRef = stream;
} video.src = window.URL.createObjectURL(stream);
loadedDataHandler = loadedData.bind(null, video, callback); video.onloadedmetadata = (e) => {
video.addEventListener('loadeddata', loadedDataHandler, false);
video.play(); video.play();
}, function(e) { resolve();
callback(e); };
}); });
})
.then(waitForVideo.bind(null, video));
} }
/** function deprecatedConstraints(videoConstraints) {
* Normalizes the incoming constraints to satisfy the current browser const normalized = pick(videoConstraints, ["width", "height", "facingMode",
* @param config "aspectRatio", "deviceId"]);
* @param cb Callback which is called whenever constraints are created
* @returns {*}
*/
function normalizeConstraints(config, cb) {
var constraints = {
audio: false,
video: true
},
videoConstraints = merge({
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment"
}, config);
if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { if (typeof videoConstraints["minAspectRatio"] !== 'undefined' &&
MediaStreamTrack.getSources(function(sourceInfos) { videoConstraints["minAspectRatio"] > 0) {
var videoSourceId; normalized["aspectRatio"] = videoConstraints["minAspectRatio"];
for (var i = 0; i < sourceInfos.length; ++i) { console.log("WARNING: Constraint 'minAspectRatio' is deprecated; Use 'aspectRatio' instead");
var sourceInfo = sourceInfos[i];
if (sourceInfo.kind === "video" && sourceInfo.facing === videoConstraints.facing) {
videoSourceId = sourceInfo.id;
} }
if (typeof videoConstraints["facing"] !== 'undefined') {
normalized["facingMode"] = videoConstraints["facing"];
console.log("WARNING: Constraint 'facing' is deprecated. Use 'facingMode' instead'");
} }
constraints.video = { return normalized;
mandatory: { }
minWidth: videoConstraints.width,
minHeight: videoConstraints.height, function applyCameraFacing(facing, constraints) {
minAspectRatio: videoConstraints.minAspectRatio, if (typeof constraints.video.deviceId !== 'undefined' || !facing){
maxAspectRatio: videoConstraints.maxAspectRatio return Promise.resolve(constraints);
}, }
optional: [{ if ( typeof MediaStreamTrack !== 'undefined' &&
sourceId: videoSourceId typeof MediaStreamTrack.getSources !== 'undefined') {
}] return new Promise((resolve, reject) => {
}; MediaStreamTrack.getSources((sourceInfos) => {
return cb(constraints); const videoSource = sourceInfos.filter((sourceInfo) => (
sourceInfo.kind === "video" && sourceInfo.facing === facing
))[0];
if (videoSource) {
return resolve(merge({}, constraints,
{video: {deviceId: videoSource.id}}));
}
return resolve(constraints);
});
}); });
} else {
constraints.video = {
mediaSource: "camera",
width: { min: videoConstraints.width, max: videoConstraints.width },
height: { min: videoConstraints.height, max: videoConstraints.height },
require: ["width", "height"]
};
return cb(constraints);
} }
return Promise.resolve(merge({}, videoConstraints, {facingMode: facing}));
} }
/** function pickConstraints(videoConstraints) {
* Requests the back-facing camera of the user. The callback is called const constraints = {
* whenever the stream is ready to be consumed, or if an error occures. audio: false,
* @param {Object} video video: deprecatedConstraints(videoConstraints)
* @param {Object} callback };
*/ return applyCameraFacing(constraints.video.facingMode, constraints);
function request(video, videoConstraints, callback) {
normalizeConstraints(videoConstraints, function(constraints) {
initCamera(constraints, video, callback);
});
} }
export default { export default {
request: function(video, constraints, callback) { request: function(video, videoConstraints) {
request(video, constraints, callback); return pickConstraints(videoConstraints)
.then(initCamera.bind(null, video));
}, },
release: function() { release: function() {
var tracks = streamRef && streamRef.getVideoTracks(); var tracks = streamRef && streamRef.getVideoTracks();

@ -1,4 +1,5 @@
import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars
import WebrtcAdapter from 'webrtc-adapter'; // eslint-disable-line no-unused-vars
import ImageWrapper from './common/image_wrapper'; import ImageWrapper from './common/image_wrapper';
import BarcodeLocator from './locator/barcode_locator'; import BarcodeLocator from './locator/barcode_locator';
import BarcodeDecoder from './decoder/barcode_decoder'; import BarcodeDecoder from './decoder/barcode_decoder';
@ -56,12 +57,11 @@ function initInputStream(cb) {
} }
} }
_inputStream = InputStream.createLiveStream(video); _inputStream = InputStream.createLiveStream(video);
CameraAccess.request(video, _config.inputStream.constraints, function(err) { CameraAccess.request(video, _config.inputStream.constraints)
if (!err) { .then(() => {
_inputStream.trigger("canrecord"); _inputStream.trigger("canrecord");
} else { }).catch((err) => {
return cb(err); return cb(err);
}
}); });
} }

Loading…
Cancel
Save