Skip to content

Commit

Permalink
Update legalegacy and standard constraint handling to handle new Chro…
Browse files Browse the repository at this point in the history
…me 53+ requirements
  • Loading branch information
nathanoehlman committed Sep 19, 2016
1 parent 9331554 commit e26b6d9
Show file tree
Hide file tree
Showing 13 changed files with 342 additions and 230 deletions.
9 changes: 4 additions & 5 deletions buildConstraints.js
Expand Up @@ -11,13 +11,12 @@ var builders = {
};

/**
Constructs the appropriate constraints object, depending on the type of required
constraints (ie. whether it adheres to the new constraints spec (Firefox 38+), the legacy
constraints (Chrome, Firefox 37 and lower), or something else (ie. Opera, IE, iOS)
Returns a constraints builder for the appropriate version of the getUserMedia constraints
that are required
**/
module.exports = function(attrName, data, opts) {
module.exports = function(cfg, opts) {
var constraintsType = (opts || {}).constraintsType || capabilities.constraintsType || 'legacy';
var builder = builders[constraintsType];
if (!builder) throw new Error('Unsupported constraints builder');
return builder(attrName, data);
return builder(cfg, opts);
};
13 changes: 12 additions & 1 deletion capabilities.js
Expand Up @@ -9,4 +9,15 @@ var capabilities = module.exports = {
browserVersion: browser.version
};

capabilities.constraintsType = (capabilities.moz && compareVersions(browser.version, '38.0.0') >= 0 ? 'standard' : 'legacy');
// Mozilla constraings handling
if (capabilities.moz) {
capabilities.constraintsType = (compareVersions(browser.version, '38.0.0') >= 0 ? 'standard' : 'legacy');
}
// Chrome constraints handling
else if (browser.name === 'chrome') {
capabilities.constraintsType = (compareVersions(browser.version, '53.0.0') >= 0 ? 'standard' : 'legacy');
}
// Default constraints handling
else {
capabilities.constraintsType = 'legacy';
}
126 changes: 124 additions & 2 deletions constraints/legacy.js
@@ -1,5 +1,8 @@
'use strict';

var detect = require('rtc-core/detect');
var extend = require('cog/extend');

/**
This constraints builder constructs MediaStreamConstraints in the form
that was originally proposed, and used in Chrome (currently), Firefox (<=37),
Expand Down Expand Up @@ -30,7 +33,123 @@
}
```
**/
module.exports = function(attrName, data) {
module.exports = function(cfg, opts) {
// Setup the config
cfg = cfg || {};

// Setup the default constraints
var constraints = {
audio: cfg.microphone === true ||
(typeof cfg.microphone == 'number' && cfg.microphone >= 0),

video: cfg.camera === true || cfg.share ||
(typeof cfg.camera == 'number' && cfg.camera >= 0)
};

// mandatory constraints
var m = {
video: {},
audio: {}
};

// optional constraints
var o = {
video: [],
audio: []
};

var sources = (opts || {}).sources || [];
var cameras = sources.filter(function(info) {
return info && info.kind === 'video';
});
var microphones = sources.filter(function(info) {
return info && info.kind === 'audio';
});
var selectedSource;
var useMandatory = !!(opts || {}).useMandatory;

function addConstraints(section, constraints) {
if (useMandatory) {
return extend.apply(null, [m[section]].concat(constraints));
}

o[section] = o[section].concat(constraints);
}

function complexConstraints(target) {
if (constraints[target] && typeof constraints[target] != 'object') {
constraints[target] = {
mandatory: m[target],
optional: o[target]
};
}
}

// if we have screen constraints, make magic happen
if (typeof cfg.share != 'undefined') {
complexConstraints('video');
if (detect.moz) {
constraints.video.mozMediaSource = constraints.video.mediaSource = 'window';
}
else {
m.video.chromeMediaSource = 'screen';
}
}

// fps
if (cfg.fps) {
complexConstraints('video');
addConstraints('video', buildConstraints('frameRate', cfg.fps, opts));
}

// min res specified
if (cfg.res) {
complexConstraints('video');

addConstraints('video', buildConstraints('width', {
min: cfg.res.min && cfg.res.min.w,
max: cfg.res.max && cfg.res.max.w
}, opts));

addConstraints('video', buildConstraints('height', {
min: cfg.res.min && cfg.res.min.h,
max: cfg.res.max && cfg.res.max.h
}, opts));
}

// input camera selection
if (typeof cfg.camera == 'number' && cameras.length) {
selectedSource = cameras[cfg.camera];

if (selectedSource) {
complexConstraints('video');
addConstraints('video', { sourceId: selectedSource.id });
}
}

// input microphone selection
if (typeof cfg.microphone == 'number' && microphones.length) {
selectedSource = microphones[cfg.microphone];

if (selectedSource) {
complexConstraints('audio');
addConstraints('audio', { sourceId: selectedSource.id });
}
}

['video', 'audio'].forEach(function(target) {
if (constraints[target] && constraints[target].optional) {
constraints[target].optional = o[target];
}
});

return constraints;
};

/**
Builds an attribute constraint in the legacy format
**/
function buildConstraints(attrName, data) {
var output = [];

if (data.min) {
Expand All @@ -47,10 +166,13 @@ module.exports = function(attrName, data) {
return output;
};

/**
Creates the attribute with the given value
**/
function createAttr(prefix, attrName, value) {
var attr = {};
var key = prefix && (prefix + attrName.slice(0, 1).toUpperCase() + attrName.slice(1));

attr[key || attrName] = value;
return attr;
}
}
111 changes: 102 additions & 9 deletions constraints/standard.js
@@ -1,5 +1,6 @@
'use strict';

var detect = require('rtc-core/detect');
var rangeValues = ['min', 'max', 'ideal', 'exact'];

/**
Expand All @@ -13,17 +14,20 @@ var rangeValues = ['min', 'max', 'ideal', 'exact'];
Primarily, this involves numeric values being either expressed as a singular value (ie. frameRate: 15),
or as a range (ie. frameRate: { min: 10, max: 30, ideal: 20 } ).
It should also be noted that optional and mandatory constraints do not exist, rather have been replaced
by the use of `exact`, `min`, `max`, and `ideal`, or the provision of a default preferred value.
A fully constructed constraint may look like the following:
```
{
audio: true,
video: {
optional: [
{ frameRate: { min: 15, max: 30 },
{ width: { min: 720, max: 1080 },
{ height: 480 }
]
// Require a min of 15, max of 30
frameRate: { min: 15, max: 30 },
width: { min: 720, max: 1080 },
// Prefer a height of 480
height: 480
}
}
```
Expand All @@ -33,17 +37,106 @@ var rangeValues = ['min', 'max', 'ideal', 'exact'];
```
**/
module.exports = function(attrName, data) {
module.exports = function(cfg, opts) {
// Setup the config
cfg = cfg || {};

// Setup the default constraints
var constraints = {
audio: cfg.microphone === true ||
(typeof cfg.microphone == 'number' && cfg.microphone >= 0),

video: cfg.camera === true || cfg.share ||
(typeof cfg.camera == 'number' && cfg.camera >= 0)
};

var media = {
video: {},
audio: {}
};

var sources = (opts || {}).sources || [];
var cameras = sources.filter(function(info) {
return info && info.kind === 'video';
});
var microphones = sources.filter(function(info) {
return info && info.kind === 'audio';
});
var selectedSource;

function addConstraints(mediaType, updatedConstraints) {
if (!updatedConstraints) return;
Object.keys(updatedConstraints).forEach(function(constraint) {
if (!updatedConstraints[constraint]) return;
media[mediaType][constraint] = updatedConstraints[constraint];
});
}

// if we have screen constraints, make magic happen
if (typeof cfg.share != 'undefined') {
if (detect.moz) {
media.video.mozMediaSource = media.video.mediaSource = 'window';
} else {
media.video.chromeMediaSource = 'screen';
}
}

// fps
if (cfg.fps) {
addConstraints('video', buildConstraints('frameRate', cfg.fps, opts));
}

// min res specified
if (cfg.res) {
addConstraints('video', buildConstraints('width', {
min: cfg.res.min && cfg.res.min.w,
max: cfg.res.max && cfg.res.max.w
}, opts));

addConstraints('video', buildConstraints('height', {
min: cfg.res.min && cfg.res.min.h,
max: cfg.res.max && cfg.res.max.h
}, opts));
}

// input camera selection
if (typeof cfg.camera == 'number' && cameras.length) {
selectedSource = cameras[cfg.camera];

if (selectedSource) {
addConstraints('video', { deviceId: { exact: selectedSource.id } });
}
}

// input microphone selection
if (typeof cfg.microphone == 'number' && microphones.length) {
selectedSource = microphones[cfg.microphone];

if (selectedSource) {
addConstraints('audio', { deviceId: { exact: selectedSource.id } });
}
}

['video', 'audio'].forEach(function(target) {
if (constraints[target] && Object.keys(media[target]).length > 0) {
constraints[target] = media[target];
}
});

return constraints;
};

function buildConstraints(attrName, data) {
var value = {};
var constraints = {};

if (data.min && data.max && data.min === data.max) {
if (typeof data.min === 'number' && typeof data.max === 'number' && data.min === data.max) {
if (data.min === 0) return;
value = data.min;
} else {
rangeValues.forEach(function(prop) {
if (data[prop]) value[prop] = data[prop];
});
}
constraints[attrName] = value;
return [constraints];
return constraints;
};

0 comments on commit e26b6d9

Please sign in to comment.