<|start_filename|>test/share.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

test('share', expect({
  share: 'window',
  microphone: false,
  res: {
    max: { w: 1920, h: 1080 }
  },
  aspectRatio: {
    min: 1.77
  }
}));

test('share:window', expect({
  share: 'window',
  microphone: false,
  res: {
    max: { w: 1920, h: 1080 }
  },
  aspectRatio: {
    min: 1.77
  }
}));


<|start_filename|>test/camera-combo-constraints-mandatory.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints-mandatory');
var format = require('./helpers/format');

test('camera min:1280x720 15fps', expect({
  audio: true,
  video: {
    mandatory: {
      minFrameRate: 15,
      maxFrameRate: 15,
      frameRate: 15,
      minWidth: 1280,
      minHeight: 720,
    },
    optional: []
  }
}, format.LEGACY));


test('camera min:1280x720 15fps', expect({
  audio: true,
  video: {
    frameRate: 15,
    width: { min: 1280 },
    height: { min: 720 }
  }
}, format.STANDARD));

test('camera min:1280x720 max:1280x720 min:15fps max:25fps', expect({
  audio: true,
  video: {
    mandatory: {
      minFrameRate: 15,
      maxFrameRate: 25,
      minWidth: 1280,
      maxWidth: 1280,
      width: 1280,
      minHeight: 720,
      maxHeight: 720,
      height: 720
    },
    optional: []
  }
}, format.LEGACY));

test('camera min:1280x720 max:1280x720 min:15fps max:25fps', expect({
  audio: true,
  video: {
    frameRate: { min: 15, max: 25 },
    width: 1280,
    height: 720
  }
}, format.STANDARD));

<|start_filename|>test/camera-noaudio.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

test('camera microphone:off', expect({ camera: true, microphone: false }));
test('camera microphone:none', expect({ camera: true, microphone: false }));
test('camera microphone:false', expect({ camera: true, microphone: false }));

<|start_filename|>test/camera-resolution-constraints.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

test('camera min:1280x720', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 1280 },
      { minHeight: 720 }
    ]
  }
}, format.LEGACY));

test('camera min:1280x720', expect({
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}, format.STANDARD));

test('camera max:1280x720', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { maxWidth: 1280 },
      { maxHeight: 720 }
    ]
  }
}, format.LEGACY));

test('camera max:1280x720', expect({
  audio: true,
  video: {
    width: { max: 1280 },
    height: { max: 720 }
  }
}, format.STANDARD));

test('camera min:640x480 max:1280x720', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 640 },
      { maxWidth: 1280 },

      { minHeight: 480 },
      { maxHeight: 720 }
    ]
  }
}, format.LEGACY));

test('camera min:640x480 max:1280x720', expect({
  audio: true,
  video: {
    width: { min: 640, max: 1280 },
    height: { min: 480, max: 720 }
  }
}, format.STANDARD));

test('camera ideal:640x480 ', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { maxWidth: 640 },
      { maxHeight: 480 }
    ]
  }
}, format.LEGACY));

test('camera ideal:640x480', expect({
  audio: true,
  video: {
    width: { ideal: 640 },
    height: { ideal: 480 }
  }
}, format.STANDARD));

test('camera ideal:640x480 min:320x240', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 320 },
      { maxWidth: 640 },
      { minHeight: 240 },
      { maxHeight: 480 }
    ]
  }
}, format.LEGACY));

test('camera ideal:640x480 min:320x240', expect({
  audio: true,
  video: {
    width: { ideal: 640, min: 320 },
    height: { ideal: 480, min: 240 }
  }
}, format.STANDARD));

test('camera exact:640x480 ', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 640 },
      { maxWidth: 640 },
      { width: 640 },
      { minHeight: 480 },
      { maxHeight: 480 },
      { height: 480 }
    ]
  }
}, format.LEGACY));

test('camera exact:640x480', expect({
  audio: true,
  video: {
    width: { exact: 640 },
    height: { exact: 480 }
  }
}, format.STANDARD));

<|start_filename|>test/camera-targets-constraints-sourced.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints-sourced');
var format = require('./helpers/format');

test('camera', expect({ video: true, audio: true }, format.LEGACY));
test('camera', expect({ video: true, audio: true }, format.STANDARD));
test('camera:1', expect({
  video: {
    mandatory: {},
    optional: [{ sourceId: 91 }]
  },
  audio: true
}, format.LEGACY));
test('camera:1', expect({
  video: {
    deviceId: { exact: 91 }
  },
  audio: true
}, format.STANDARD));

// Test with resolution conditions
test('camera:1 min:320x240', expect({
  video: {
    mandatory: {},
    optional: [
      { minWidth: 320 },
      { minHeight: 240 },
      { sourceId: 91 }
    ]
  },
  audio: true
}, format.LEGACY));
test('camera:1 min:320x240', expect({
  video: {
    deviceId: { exact: 91 },
    width: { min: 320 },
    height: { min: 240 }
  },
  audio: true
}, format.STANDARD));

// Test with audio conditions
test('camera:1 min:320x240 microphone:1', expect({
  video: {
    mandatory: {},
    optional: [
      { minWidth: 320 },
      { minHeight: 240 },
      { sourceId: 91 }
    ]
  },
  audio: {
    mandatory: {},
    optional: [
      { sourceId: 81 }
    ]
  }
}, format.LEGACY));
test('camera:1 min:320x240 microphone:1', expect({
  video: {
    deviceId: { exact: 91 },
    width: { min: 320 },
    height: { min: 240 }
  },
  audio: {
    deviceId: { exact: 81 }
  }
}, format.STANDARD));

// Test with ideal and audio conditions
test('camera:1 ideal:800x600 microphone:1', expect({
  video: {
    mandatory: {},
    optional: [
      { maxWidth: 800 },
      { maxHeight: 600 },
      { sourceId: 91 }
    ]
  },
  audio: {
    mandatory: {},
    optional: [
      { sourceId: 81 }
    ]
  }
}, format.LEGACY));
test('camera:1 ideal:800x600 microphone:1', expect({
  video: {
    deviceId: { exact: 91 },
    width: { ideal: 800 },
    height: { ideal: 600 }
  },
  audio: {
    deviceId: { exact: 81 }
  }
}, format.STANDARD));

test('microphone', expect({ video: false, audio: true }, format.LEGACY));
test('microphone', expect({ video: false, audio: true }, format.STANDARD));

test('microphone:1', expect({
  video: false,
  audio: {
    mandatory: {},
    optional: [{ sourceId: 81 }]
  }
}, format.LEGACY));
test('microphone:1', expect({
  video: false,
  audio: {
    deviceId: { exact: 81 }
  }
}, format.STANDARD));


test('camera microphone:1', expect({
  video:true,
  audio: {
    mandatory: {},
    optional: [{ sourceId: 81 }]
  }
}, format.LEGACY));
test('camera microphone:1', expect({
  video:true,
  audio: {
    deviceId: { exact: 81 }
  }
}, format.STANDARD));

test('camera:1 microphone:2', expect({
  video: {
    mandatory: {},
    optional: [{ sourceId: 91 }]
  },
  audio: {
    mandatory: {},
    optional: [{ sourceId: 82 }]
  }
}, format.LEGACY));

test('camera:1 microphone:2', expect({
  video: {
    deviceId: { exact: 91 }
  },
  audio: {
    deviceId: { exact: 82 }
  }
}, format.STANDARD));

<|start_filename|>test/all.js<|end_filename|>
// internal config tests
require('./camera-resolution');
require('./camera-fps');
require('./camera-combo');
require('./camera-noaudio');
require('./camera-targets');

// constraints generation tests
require('./camera-resolution-constraints');
require('./camera-fps-constraints');
require('./camera-combo-constraints');
require('./camera-combo-constraints-mandatory');
require('./camera-noaudio-constraints');
require('./camera-targets-constraints');

// constraints with source information tests
require('./camera-targets-constraints-sourced');

// hd, full-hd tests
require('./hd');

// screenshare tests
require('./share');
require('./share-constraints');


<|start_filename|>test/share-constraints.js<|end_filename|>
var detect = require('rtc-core/detect');
var extend = require('cog/extend');
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

function mozMediaSource(type) {
  return {
    mozMediaSource: type,
    mediaSource: type
  };
}

test('share', expect({
  audio: false,
  video: extend(detect.moz ? mozMediaSource('window') : {}, {
    mandatory: detect.moz ? {} : {
      chromeMediaSource: 'screen'
    },
    optional: [
      { maxWidth: 1920 },
      { maxHeight: 1080 }
    ]
  })
}, format.LEGACY));

test('share', expect({
  audio: false,
  video: extend(detect.moz ? mozMediaSource('window') : {},
    detect.moz ? {
      width: { max: 1920 },
      height: { max: 1080 }
    } : {
      chromeMediaSource: 'screen',
      width: { max: 1920 },
      height: { max: 1080 }
    }
  )
}, format.STANDARD));

test('share:window', expect({
  audio: false,
  video: extend(detect.moz ? mozMediaSource('window') : {}, {
    mandatory: detect.moz ? {} : {
      chromeMediaSource: 'screen'
    },
    optional: [
      { maxWidth: 1920 },
      { maxHeight: 1080 }
    ]
  })
}, format.LEGACY));

test('share:window', expect({
  audio: false,
  video: extend(detect.moz ? mozMediaSource('window') : {},
    detect.moz ? {
      width: { max: 1920 },
      height: { max: 1080 }
    } : {
      chromeMediaSource: 'screen',
      width: { max: 1920 },
      height: { max: 1080 }
    }
  )
}, format.STANDARD));

<|start_filename|>test/camera-combo.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

test('camera min:1280x720 min:15fps', expect({
  camera: true,
  microphone: true,
  res: {
    min: { w: 1280, h: 720 }
  },

  fps: {
    min: 15
  }
}));

test('camera min:1280x720 max:1280x720 min:15fps max:25fps', expect({
  camera: true,
  microphone: true,
  res: {
    min: { w: 1280, h: 720 },
    max: { w: 1280, h: 720 }
  },

  fps: {
    min: 15,
    max: 25
  }
}));

<|start_filename|>examples/capture-targets.js<|end_filename|>
var media = require('rtc-media');
var capture = require('..');

// get the sources
MediaStreamTrack.getSources(function(sources) {
  // get the cameras
  var cameras = sources.filter(function(info) {
    return info && info.kind === 'video';
  });

  // create videos
  var videos = cameras.map(function(info, idx) {
    return media(capture('camera:' + idx).toConstraints({ sources: sources }));
  });

  // render the videos
  videos.forEach(function(vid) {
    vid.render(document.body);
  });
});

<|start_filename|>test/camera-fps-constraints.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

test('camera 15fps', expect({
  audio: true,
  video: {
    mandatory: {},

    optional: [
      { minFrameRate: 15 },
      { maxFrameRate: 15 },
      { frameRate: 15 }
    ]
  }
}, format.LEGACY));

test('camera 15fps', expect({
  audio: true,
  video: {
    frameRate: 15
  }
}, format.STANDARD));

test('camera max:15fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { maxFrameRate: 15 },
    ]
  }
}, format.LEGACY));

test('camera max:15fps', expect({
  audio: true,
  video: {
    frameRate: { max: 15 }
  }
}, format.STANDARD));

test('camera min:25fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minFrameRate: 25 }
    ]
  }
}, format.LEGACY));

test('camera min:25fps', expect({
  audio: true,
  video: {
    frameRate: { min: 25 }
  }
}, format.STANDARD));

test('camera min:15fps max:25fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minFrameRate: 15 },
      { maxFrameRate: 25 }
    ]
  }
}, format.LEGACY));

test('camera min:15fps max:25fps', expect({
  audio: true,
  video: {
    frameRate: { min: 15, max: 25 }
  }
}, format.STANDARD));

test('camera min:15fps ideal:25fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minFrameRate: 15 },
      { maxFrameRate: 25 }
    ]
  }
}, format.LEGACY));

test('camera min:15fps ideal:25fps', expect({
  audio: true,
  video: {
    frameRate: { min: 15, ideal: 25 }
  }
}, format.STANDARD));

<|start_filename|>test/hd.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

test('hd', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 1280 },
      { minHeight: 720 }
    ]
  }
}, format.LEGACY));

test('hd', expect({
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}, format.STANDARD));

test('720p', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 1280 },
      { minHeight: 720 }
    ]
  }
}, format.LEGACY));

test('720p', expect({
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}, format.STANDARD));

test('fullhd', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 1920 },
      { minHeight: 1080 }
    ]
  }
}, format.LEGACY));

test('fullhd', expect({
  audio: true,
  video: {
    width: { min: 1920 },
    height: { min: 1080 }
  }
}, format.STANDARD));

test('1080p', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minWidth: 1920 },
      { minHeight: 1080 }
    ]
  }
}, format.LEGACY));

test('1080p', expect({
  audio: true,
  video: {
    width: { min: 1920 },
    height: { min: 1080 }
  }
}, format.STANDARD));

<|start_filename|>test/camera-targets.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

// single attribute tests
test('camera', expect({ camera: true, microphone: true }));
test('camera:1', expect({ camera: 1, microphone: true }));
test('microphone', expect({ microphone: true }));
test('microphone:1', expect({ microphone: 1 }));

// camera + microphone
test('camera microphone:1', expect({ camera: true, microphone: 1 }));
test('camera:1 microphone:2', expect({ camera: 1, microphone: 2 }));

test('camera min:320x240 microphone:1', expect({
	camera: true,
	microphone: 1,
	res: {
		min: { w: 320, h: 240 }
	}
}));
test('camera:1 ideal:640x480 microphone:2', expect({
	camera: 1,
	microphone: 2,
	res: {
		ideal: { w: 640, h: 480 }
	}
}));

<|start_filename|>examples/capture-screen.js<|end_filename|>
// load in capture config
var capture = require('..');
var constraints = capture('screen').toConstraints();

// pull in the getusermedia helper module
// see: https://github.com/HenrikJoreteg/getUserMedia
var getUserMedia = require('getusermedia');

getUserMedia(constraints, function(err, stream) {
  if (err) {
    return console.log('Could not capture stream: ', err);
  }

  console.log('captured stream: ', stream);
});

<|start_filename|>constraints/legacy.js<|end_filename|>
'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),
  Internet Explorer (Temasys) and iOS.

  It constructs constraints in the form of min{attrName}: {value}, so a fully
  constructed constraint may look like the following:

  ```
  {
    audio: true,
    video: {
      optional: [
        { minFrameRate: 15 },
        { maxFrameRate: 30 },
        { minWidth: 720 },
        { maxWidth: 1080 }
      ]
    }
  }
  ```

  It will also NOT produce any combined syntax which would produce failures
  such as
  ```
  {
    frameRate: { min: 15, max: 30 }
  }
  ```
 **/
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,
      ideal: cfg.res.ideal && cfg.res.ideal.w,
      exact: cfg.res.exact && cfg.res.exact.w,
    }, opts));

    addConstraints('video', buildConstraints('height', {
      min: cfg.res.min && cfg.res.min.h,
      max: cfg.res.max && cfg.res.max.h,
      ideal: cfg.res.ideal && cfg.res.ideal.h,
      exact: cfg.res.exact && cfg.res.exact.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.ideal && !data.max) {
    data.max = data.ideal;
  }

  if (data.exact) {
    data.min = data.max = data.exact;
  }

  if (data.min) {
    output.push(createAttr('min', attrName, data.min));
  }

  if (data.max) {
    output.push(createAttr('max', attrName, data.max));
  }

  if (data.min && data.max && data.min === data.max) {
    output.push(createAttr(null, attrName, data.min));
  }
  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;
}

<|start_filename|>test/helpers/format.js<|end_filename|>
exports.LEGACY = { constraintsType: 'legacy' };
exports.STANDARD = { constraintsType: 'standard' };

<|start_filename|>capabilities.js<|end_filename|>
'use strict';

var browser = require('detect-browser').detect();
var compareVersions = require('compare-versions')

var capabilities = module.exports = {
	moz: typeof navigator != 'undefined' && !!navigator.mozGetUserMedia,
	browser: browser.name,
	browserVersion: browser.version
};

// 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');
}
// Safari constraints handling
else if (browser.name === 'safari') {
	capabilities.constraintsType = (compareVersions(browser.version, '12.0.0') >= 0 ? 'standard' : 'legacy');
}
// iOS Safari constraints handling
else if (browser.name === 'ios') {
	capabilities.constraintsType = (compareVersions(browser.version, '11.0.0') >= 0 ? 'standard' : 'legacy');
}
// Default constraints handling
else {
	capabilities.constraintsType = 'legacy';
}

<|start_filename|>test/helpers/expect-constraints-sourced.js<|end_filename|>
var extend = require('cog/extend');
var captureconfig = require('../../');
var sources = [
  { kind: 'audio', id: 80, label: '' },
  { kind: 'video', id: 90, label: '' },
  { kind: 'audio', id: 81, label: '' },
  { kind: 'video', id: 91, label: '' },
  { kind: 'video', id: 92, label: '' },
  { kind: 'audio', id: 82, label: '' },
  { kind: 'audio', id: 83, label: '' }
];

module.exports = function(expected, opts) {
  return function(t) {
    t.plan(1);
    t.deepEqual(
      captureconfig(t.name).toConstraints(extend({ sources: sources }, opts || {})),
      expected,
      JSON.stringify(expected)
    );
  };
};

<|start_filename|>test/helpers/expect.js<|end_filename|>
var captureconfig = require('../../');

module.exports = function(expected) {
  return function(t) {
    t.plan(1);
    t.deepEqual(
      captureconfig(t.name).cfg,
      expected,
      JSON.stringify(expected)
    );
  };
};

<|start_filename|>test/camera-fps.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

test('camera max:15fps', expect({
  camera: true,
  microphone: true,
  fps: {
    max: 15
  }
}));

test('camera min:25fps', expect({
  camera: true,
  microphone: true,
  fps: {
    min: 25
  }
}));

test('camera min:15fps max:25fps', expect({
  camera: true,
  microphone: true,
  fps: {
    min: 15,
    max: 25
  }
}));

test('camera min:10fps ideal:15fps', expect({
  camera: true,
  microphone: true,
  fps: {
    min: 10,
    ideal: 15
  }
}));

<|start_filename|>test/camera-resolution.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect');

test('camera min:1280x720', expect({
  camera: true,
  microphone: true,
  res: {
    min: { w: 1280, h: 720 }
  }
}));

test('camera max:1280x720', expect({
  camera: true,
  microphone: true,
  res: {
    max: { w: 1280, h: 720 }
  }
}));

test('camera min:640x480 max:1280x720', expect({
  camera: true,
  microphone: true,
  res: {
    min: { w: 640, h: 480 },
    max: { w: 1280, h: 720 }
  }
}));

test('camera ideal:640x480', expect({
camera: true,
  microphone: true,
  res: {
    ideal: { w: 640, h: 480 },
  }
}));

test('camera ideal:640x480 min:320x240', expect({
camera: true,
  microphone: true,
  res: {
    ideal: { w: 640, h: 480 },
    min: { w: 320, h: 240 }
  }
}));

test('camera exact:640x480', expect({
camera: true,
  microphone: true,
  res: {
    exact: { w: 640, h: 480 },
  }
}));

<|start_filename|>buildConstraints.js<|end_filename|>
'use strict';
// Detect the capabilities
var capabilities = require('./capabilities');

// This is the extensible list of constraint builders
var builders = {
  // This is the current (August 25, 2015) specification for MediaTrackConstraints
  standard: require('./constraints/standard'),
  // This is the legacy specification that is still used by Chrome
  legacy: require('./constraints/legacy')
};

/**
  Returns a constraints builder for the appropriate version of the getUserMedia constraints
  that are required
 **/
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(cfg, opts);
};

<|start_filename|>test/camera-noaudio-constraints.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

test('camera microphone:off', expect({ video: true, audio: false }, format.LEGACY));
test('camera microphone:off', expect({ video: true, audio: false }, format.STANDARD));
test('camera microphone:none', expect({ video: true, audio: false }, format.LEGACY));
test('camera microphone:none', expect({ video: true, audio: false }, format.STANDARD));
test('camera microphone:false', expect({ video: true, audio: false }, format.LEGACY));
test('camera microphone:false', expect({ video: true, audio: false }, format.STANDARD));

<|start_filename|>test/device-sources.js<|end_filename|>
var test = require('tape');
var expect = require('./helpers/expect-constraints');
var format = require('./helpers/format');

var sources = [
	{deviceId: "default", kind: "audioinput", label: "Default", groupId: "2004946474"},
	{deviceId: "62b678fb9a24ad6a6996550510cf3d31c8de040cefa64306928ba24678d29de7", kind: "audioinput", label: "Built-in Microphone", groupId: "3235599890"},
	{deviceId: "<KEY>",kind: "audioinput", label:"Display Audio", groupId: "1631477159"},
	{deviceId: "<KEY>",kind: "videoinput", label:"FaceTime HD Camera", groupId: ""},
	{deviceId: "<KEY>",kind: "videoinput", label:"FaceTime HD Camera (Display) (05ac:1112)", groupId: ""},
	{deviceId: "default", kind: "audiooutput", label: "Default", groupId: "2004946474"},
	{deviceId: "<KEY>",kind: "audiooutput", label:"AirPlay", groupId: "1000399423"},
	{deviceId: "8268ccad673144d9b6ab46065d21251be8287fe31e88f2e348f0d1dd5ca489d1",kind: "audiooutput", label:"Built-in Output", groupId: "4189744161"},
	{deviceId: "<KEY>",kind: "audiooutput", label:"Display Audio", groupId: "1631477159"}
];

// single attribute tests
test('camera', expect({ video: true, audio: true }, format.LEGACY));
test('camera', expect({ video: true, audio: true }, format.STANDARD));
test('camera:1', expect({ video: true, audio: true }, format.LEGACY));
test('camera:1', expect({ video: true, audio: true }, format.STANDARD));

test('camera:<KEY>', expect({
	video: { deviceId: '<KEY>' },
	audio: true
}), format.LEGACY);

test('camera:<KEY>', expect({
	video: { deviceId: { exact: '<KEY>' } },
	audio: true
}), format.STANDARD);


test('microphone', expect({ video: false, audio: true }, format.LEGACY));
test('microphone', expect({ video: false, audio: true }, format.STANDARD));
test('microphone:1', expect({ video: false, audio: true }, format.LEGACY));
test('microphone:1', expect({ video: false, audio: true }, format.STANDARD));

// camera + microphone
test('camera microphone:1', expect({ video:true, audio: true }, format.LEGACY));
test('microphone:1', expect({ video: false, audio: true }, format.STANDARD));
test('camera:1 microphone:2', expect({ video: true, audio: true }, format.LEGACY));
test('microphone:1', expect({ video: false, audio: true }, format.STANDARD));


<|start_filename|>index.js<|end_filename|>
/* jshint node: true */
'use strict';

var reSeparator = /[\,\s]\s*/;
var offFlags = ['false', 'none', 'off'];
var reFPS = /(\d+)fps/i;
var buildConstraints = require('./buildConstraints');


/**
  # rtc-captureconfig

  This is a simple parser that takes a string of text and determines what
  that means in the context of WebRTC.

  ## Why?

  It provides a simple, textual way of describing your requirements for
  media capture.  Trying to remember the structure of the constraints object
  is painful.

  ## How

  A simple text string is converted to an intermediate JS object
  representation, which can then be converted to a getUserMedia constraints
  data structure using a `toConstraints()` call.

  For example, the following text input:

  ```
  camera min:1280x720 max:1280x720 min:15fps max:25fps
  ```

  Is converted into an intermedia representation (via the `CaptureConfig`
  utility class) that looks like the following:

  ```js
  {
    camera: 0,
    microphone: 0,
    res: {
      min: { w: 1280, h: 720 },
      max: { w: 1280, h: 720 }
    },

    fps: {
      min: 15,
      max: 25
    }
  }
  ```

  Which in turn is converted into the following media constraints for
  a getUserMedia call:

  ```js
  {
    audio: true,
    video: {
      mandatory: {},
      optional: [
        { minFrameRate: 15 },
        { maxFrameRate: 25 },
        { frameRate: { min: 15, max: 25 } },

        { minWidth: 1280 },
        { maxWidth: 1280 },
        { width: 1280 },

        { minHeight: 720 },
        { maxHeight: 720 },
        { height: 720 }
      ]
    }
  }
  ```

  ### Experimental: Targeted Device Capture

  While the `rtc-captureconfig` module itself doesn't contain any media
  identification logic, it is able to the sources information from a
  `MediaStreamTrack.getSources` call to generate device targeted constraints.

  For instance, the following example demonstrates how we can request
  `camera:1` (the 2nd video device on our local machine) when we are making
  a getUserMedia call:

  <<< examples/camera-two.js

  It's worth noting that if the requested device does not exist on the
  machine (in the case above, if your machine only has a single webcam - as
  is common) then no device selection constraints will be generated (i.e.
  the standard `{ video: true, audio: true }` constraints will be returned
  from the `toConstraints` call).

  ### Experimental: Screen Capture

  If you are working with chrome and serving content of a HTTPS connection,
  then you will be able to experiment with experimental getUserMedia screen
  capture.

  In the simplest case, screen capture can be invoked by using the capture
  string of:

  ```
  screen
  ```

  Which generates the following contraints:

  ```js
  {
    audio: false,
    video: {
      mandatory: {
        chromeMediaSource: 'screen'
      },

      optional: []
    }
  }
  ```

  ## Reference

**/

module.exports = function(input) {
  // create a new configuration object using defaults
  var config = new CaptureConfig();

  // process each of the directives
  (input || '').split(reSeparator).forEach(function(directive) {
    // now further split the directive on the : character
    var parts = directive.split(':');
    var method = config[(parts[0] || '').toLowerCase()];
    var fpsMatch = (! method) && reFPS.exec(parts[0]);

    // if we have an 'fps' directive then process
    if (fpsMatch) {
      method = config.fps;
      parts = [ 'fps', 15 ];
    }

    // if we have the method apply
    if (typeof method == 'function') {
      method.apply(config, parts.slice(1));
    }
  });

  return config;
};

/**
  ### CaptureConfig

  This is a utility class that is used to update capture configuration
  details and is able to generate suitable getUserMedia constraints based
  on the configuration.

**/
function CaptureConfig() {
  if (! (this instanceof CaptureConfig)) {
    return new CaptureConfig();
  }

  // initialise the base config
  this.cfg = {
    microphone: true
  };
}

var prot = CaptureConfig.prototype;

/**
  #### camera(index)

  Update the camera configuration to the specified index
**/
prot.camera = function(index) {
  this.cfg.camera = trueOrValue(index);
};

/**
  #### microphone(index)

  Update the microphone configuration to the specified index
**/
prot.microphone = function(index) {
  this.cfg.microphone = trueOrValue(index);
};

/**
  #### screen(target)

  Specify that we would like to capture the screen
**/
prot.screen = function() {
  // unset the microphone config
  delete this.cfg.microphone;

  // set the screen configuration
  this.cfg.screen = true;
};

/**
 #### _apply(prop, data)

 Parses and applies the resolution or fps from data to prop
**/
prot._apply = function(prop, data) {
  var res;

  // if this is an fps specification parse
  if (data.slice(-3).toLowerCase() == 'fps') {
    return this[prop + 'fps'](data);
  }

  // parse the resolution
  res = this._parseRes(data);

  // initialise the fps config stuff
  this.cfg.res = this.cfg.res || {};
  this.cfg.res[prop] = res;
}

/**
  #### max(data)

  Update a maximum constraint.  If an fps constraint this will be directed
  to the `maxfps` modifier.

**/
prot.max = function(data) {
  this._apply('max', data);
};

/**
  #### maxfps(data)

  Update the maximum fps
**/
prot.maxfps = function(data) {
  // ensure we have an fps component
  this.cfg.fps = this.cfg.fps || {};

  // set the max fps
  this.cfg.fps.max = parseFloat(data.slice(0, -3));
};

/**
  #### min(data)

  Update a minimum constraint.  This can be either related to resolution
  or FPS.
**/
prot.min = function(data) {
  this._apply('min', data);
};

/**
  #### minfps(data)

  Update the minimum fps
**/
prot.minfps = function(data) {
  // ensure we have an fps component
  this.cfg.fps = this.cfg.fps || {};

  // set the max fps
  this.cfg.fps.min = parseFloat(data.slice(0, -3));
};

/**
  #### ideal(data)

  Updates an ideal constraint.  This can be either related to resolution
  or FPS.
**/
prot.ideal = function(data) {
  this._apply('ideal', data);
};

/**
  #### idealfps(data)

  Sets the ideal fps
**/
prot.idealfps = function(data) {
  // ensure we have an fps component
  this.cfg.fps = this.cfg.fps || {};

  // set the max fps
  this.cfg.fps.ideal = parseFloat(data.slice(0, -3));
};

/**
  #### exact(data)

  Updates an exact constraint.  This can be either related to resolution
  or FPS.
**/
prot.exact = function(data) {
  this._apply('exact', data);
};

/**
  #### exactfps(data)

  Sets the exact fps
**/
prot.exactfps = function(data) {
  // ensure we have an fps component
  this.cfg.fps = this.cfg.fps || {};

  // set the max fps
  this.cfg.fps.exact = parseFloat(data.slice(0, -3));
};

prot.hd = prot['720p'] = function() {
  this.cfg.camera = true;
  this.min('1280x720');
};

prot.fullhd = prot['1080p'] = function() {
  this.cfg.camera = true;
  this.min('1920x1080');
};

prot.fps = function(fps) {
  this.cfg.fps = { min: fps, max: fps };
};

prot.hd = prot['720p'] = function() {
  this.cfg.camera = true;
  this.min('1280x720');
};

prot.fullhd = prot['1080p'] = function() {
  this.cfg.camera = true;
  this.min('1920x1080');
};

prot.fps = function(fps) {
  this.cfg.fps = { min: fps, max: fps };
};

/**
  #### share(target)

  Whether we want to capture a window rather than webcam source.

  ```
  share:window
  ```

  __NOTE:__ This feature must be supported by your browser (and you
  may need to have a suitable extension installed).
**/
prot.share = function(target) {
  this.cfg.share = target || 'window';
  this.cfg.microphone = false;

  // initialise a default max resolution
  this.max('1920x1080');

  // set a default min aspect ratio
  this.cfg.aspectRatio = {
    min: 1.77
  };
};

/**
  #### toConstraints(opts?)

  Convert the internal configuration object to a valid media constraints
  representation.  In compatible browsers a list of media sources can
  be passed through in the `opts.sources` to create contraints that will
  target a specific device when captured.

  <<< examples/capture-targets.js

**/
prot.toConstraints = function(opts) {
  // Create the constraints builder
  return buildConstraints(this.cfg, opts);
};

/**
  ### "Internal" methods
**/

/**
  #### _parseRes(data)

  Parse a resolution specifier (e.g. 1280x720) into a simple JS object
  (e.g. { w: 1280, h: 720 })
**/
prot._parseRes = function(data) {
  // split the data on the 'x' character
  var parts = data.split('x');

  // if we don't have two parts, then complain
  if (parts.length < 2) {
    throw new Error('Invalid resolution specification: ' + data);
  }

  // return the width and height object
  return {
    w: parseInt(parts[0], 10),
    h: parseInt(parts[1], 10)
  };
};

/* internal helper */

function trueOrValue(val) {
  if (typeof val == 'string' && offFlags.indexOf(val.toLowerCase()) >= 0) {
    return false;
  }

  return val === undefined || val === '' || parseInt(val || 0, 10);
}


<|start_filename|>test/helpers/expect-constraints.js<|end_filename|>
var captureconfig = require('../../');

module.exports = function(expected, opts) {
  return function(t) {
    t.plan(1);
    t.deepEqual(
      captureconfig(t.name).toConstraints(opts),
      expected,
      JSON.stringify(expected)
    );
  };
};

<|start_filename|>examples/camera-two.js<|end_filename|>
// load in capture config
var capture = require('..');

// pull in the getusermedia helper module
// see: https://github.com/HenrikJoreteg/getUserMedia
var getUserMedia = require('getusermedia');

// get the sources
MediaStreamTrack.getSources(function(sources) {
  var constraints = capture('camera:1').toConstraints({ sources: sources });

  /* here is an example of what the generated constraints actually look like
  var constraints = {
    audio:true,
    video: {
      mandatory: {},
      optional: [
        { sourceId: '30a3f6408175c22df739bcbf9573d841d9f99289' }
      ]
    }
  };
  */

  // get user media
  getUserMedia(constraints, function(err, stream) {
    if (err) {
      return console.log('Could not capture stream: ', err);
    }

    console.log('captured stream: ', stream);
  });
});

<|start_filename|>constraints/standard.js<|end_filename|>
'use strict';

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

/**
  This constraints builder constructs MediaStreamConstraints in the current
  specification (http://w3c.github.io/mediacapture-main/getusermedia.html#mediastreamconstraints).

  This is currently used by Firefox 38+, and will presumably be implemented in other agents
  soon.

  It constructs constraints according to http://w3c.github.io/mediacapture-main/getusermedia.html#idl-def-MediaTrackConstraintSet
  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: {
      // 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
    }
  }
  ```

  Important Note: This syntax, when passed to some non-implementing agents (ie. iOS, Temasys)
  will cause critical failures.

  ```
 **/
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,
      ideal: cfg.res.ideal && cfg.res.ideal.w,
      exact: cfg.res.exact && cfg.res.exact.w,
    }, opts));

    addConstraints('video', buildConstraints('height', {
      min: cfg.res.min && cfg.res.min.h,
      max: cfg.res.max && cfg.res.max.h,
      ideal: cfg.res.ideal && cfg.res.ideal.h,
      exact: cfg.res.exact && cfg.res.exact.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 (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;
};

<|start_filename|>test/helpers/expect-constraints-mandatory.js<|end_filename|>
var extend = require('cog/extend');
var captureconfig = require('../../');

module.exports = function(expected, opts) {
  return function(t) {
    t.plan(1);
    t.deepEqual(
      captureconfig(t.name).toConstraints(extend({ useMandatory: true }, opts || {})),
      expected,
      JSON.stringify(expected)
    );
  };
};

<|start_filename|>examples/capture-thumbnail.js<|end_filename|>
var async = require('async');
var crel = require('crel');
var media = require('rtc-media');
var capture = require('..');
var resolutions = [
  '640x360',
  '320x240',
  '320x180'
];

function captureFrame(res, callback) {
  var constraints = capture('camera:0 min:' + res + ' max:' + res).toConstraints();

  media({ constraints: constraints })
    .on('error', callback)
    .on('capture', function(stream) {
    })
    .render(createContainer());
}

function createContainer() {
  var container = crel('div', {
    class: 'videocontainer'
  });

  document.body.appendChild(container);

  return container;
}

document.body.appendChild(crel('style', [
  '.videocontainer { width: 320px; overflow: hidden; }',
  '.videocontainer > * { object-fit: contain; width: 100%; height: 100%; }'
].join('\n')));

async.forEachSeries(resolutions, captureFrame, function(err) {
  if (err) {
    console.error(err);
  }
});
