|
|
@@ -619,15 +619,27 @@ var LibJitsiMeet = {
|
|
619
|
619
|
* Creates the media tracks and returns them trough the callback.
|
|
620
|
620
|
* @param options Object with properties / settings specifying the tracks which should be created.
|
|
621
|
621
|
* should be created or some additional configurations about resolution for example.
|
|
|
622
|
+ * @param {Array} options.devices the devices that will be requested
|
|
|
623
|
+ * @param {string} options.resolution resolution constraints
|
|
|
624
|
+ * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
|
625
|
+ * type: "audio" or "video", videoType: "camera" or "desktop"}
|
|
|
626
|
+ * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
|
|
|
627
|
+ * @param {string} options.cameraDeviceId
|
|
|
628
|
+ * @param {string} options.micDeviceId
|
|
622
|
629
|
* @returns {Promise.<{Array.<JitsiTrack>}, JitsiConferenceError>} A promise that returns an array of created JitsiTracks if resolved,
|
|
623
|
630
|
* or a JitsiConferenceError if rejected.
|
|
624
|
631
|
*/
|
|
625
|
632
|
createLocalTracks: function (options) {
|
|
626
|
633
|
return RTC.obtainAudioAndVideoPermissions(options || {});
|
|
|
634
|
+ },
|
|
|
635
|
+ isDeviceListAvailable: function () {
|
|
|
636
|
+ return RTC.isDeviceListAvailable();
|
|
|
637
|
+ },
|
|
|
638
|
+ enumerateDevices: function (callback) {
|
|
|
639
|
+ RTC.enumerateDevices(callback);
|
|
627
|
640
|
}
|
|
628
|
641
|
};
|
|
629
|
642
|
|
|
630
|
|
-
|
|
631
|
643
|
//Setups the promise object.
|
|
632
|
644
|
window.Promise = window.Promise || require("es6-promise").polyfill();
|
|
633
|
645
|
|
|
|
@@ -1031,9 +1043,10 @@ JitsiLocalTrack.prototype._setMute = function (mute) {
|
|
1031
|
1043
|
} else {
|
|
1032
|
1044
|
var self = this;
|
|
1033
|
1045
|
var RTC = require("./RTCUtils");
|
|
1034
|
|
- RTC.obtainAudioAndVideoPermissions(
|
|
1035
|
|
- (isAudio ? ["audio"] : ["video"]),
|
|
1036
|
|
- self.resolution, true)
|
|
|
1046
|
+ RTC.obtainAudioAndVideoPermissions({
|
|
|
1047
|
+ devices: (isAudio ? ["audio"] : ["video"]),
|
|
|
1048
|
+ resolution: self.resolution,
|
|
|
1049
|
+ dontCreateJitsiTrack: true})
|
|
1037
|
1050
|
.then(function (streams) {
|
|
1038
|
1051
|
var stream = null;
|
|
1039
|
1052
|
for(var i = 0; i < streams.length; i++) {
|
|
|
@@ -1373,15 +1386,18 @@ function RTC(room, options) {
|
|
1373
|
1386
|
|
|
1374
|
1387
|
/**
|
|
1375
|
1388
|
* Creates the local MediaStreams.
|
|
1376
|
|
- * @param options object for options (NOTE: currently only list of devices and resolution are supported)
|
|
1377
|
|
- * @param dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
1378
|
|
- * type: "audio" or "video", videoType: "camera" or "desktop"}
|
|
|
1389
|
+ * @param {Object} [options] optional parameters
|
|
|
1390
|
+ * @param {Array} options.devices the devices that will be requested
|
|
|
1391
|
+ * @param {string} options.resolution resolution constraints
|
|
|
1392
|
+ * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
|
1393
|
+ * type: "audio" or "video", videoType: "camera" or "desktop"}
|
|
1379
|
1394
|
* will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
|
|
|
1395
|
+ * @param {string} options.cameraDeviceId
|
|
|
1396
|
+ * @param {string} options.micDeviceId
|
|
1380
|
1397
|
* @returns {*} Promise object that will receive the new JitsiTracks
|
|
1381
|
1398
|
*/
|
|
1382
|
|
-RTC.obtainAudioAndVideoPermissions = function (options, dontCreateJitsiTrack) {
|
|
1383
|
|
- return RTCUtils.obtainAudioAndVideoPermissions(
|
|
1384
|
|
- options.devices, options.resolution, dontCreateJitsiTrack);
|
|
|
1399
|
+RTC.obtainAudioAndVideoPermissions = function (options) {
|
|
|
1400
|
+ return RTCUtils.obtainAudioAndVideoPermissions(options);
|
|
1385
|
1401
|
}
|
|
1386
|
1402
|
|
|
1387
|
1403
|
RTC.prototype.onIncommingCall = function(event) {
|
|
|
@@ -1472,11 +1488,10 @@ RTC.getPCConstraints = function () {
|
|
1472
|
1488
|
};
|
|
1473
|
1489
|
|
|
1474
|
1490
|
RTC.getUserMediaWithConstraints = function(um, success_callback,
|
|
1475
|
|
- failure_callback, resolution,
|
|
1476
|
|
- bandwidth, fps, desktopStream)
|
|
|
1491
|
+ failure_callback, options)
|
|
1477
|
1492
|
{
|
|
1478
|
1493
|
return RTCUtils.getUserMediaWithConstraints(this, um, success_callback,
|
|
1479
|
|
- failure_callback, resolution, bandwidth, fps, desktopStream);
|
|
|
1494
|
+ failure_callback, options);
|
|
1480
|
1495
|
};
|
|
1481
|
1496
|
|
|
1482
|
1497
|
RTC.attachMediaStream = function (elSelector, stream) {
|
|
|
@@ -1491,6 +1506,18 @@ RTC.getVideoSrc = function (element) {
|
|
1491
|
1506
|
return RTCUtils.getVideoSrc(element);
|
|
1492
|
1507
|
};
|
|
1493
|
1508
|
|
|
|
1509
|
+RTC.isDeviceListAvailable = function () {
|
|
|
1510
|
+ return RTCUtils.isDeviceListAvailable();
|
|
|
1511
|
+};
|
|
|
1512
|
+
|
|
|
1513
|
+/**
|
|
|
1514
|
+ * Allows to receive list of available cameras/microphones.
|
|
|
1515
|
+ * @param {function} callback would receive array of devices as an argument
|
|
|
1516
|
+ */
|
|
|
1517
|
+RTC.enumerateDevices = function (callback) {
|
|
|
1518
|
+ RTCUtils.enumerateDevices(callback);
|
|
|
1519
|
+};
|
|
|
1520
|
+
|
|
1494
|
1521
|
RTC.setVideoSrc = function (element, src) {
|
|
1495
|
1522
|
RTCUtils.setVideoSrc(element, src);
|
|
1496
|
1523
|
};
|
|
|
@@ -1808,7 +1835,7 @@ var eventEmitter = new EventEmitter();
|
|
1808
|
1835
|
var devices = {
|
|
1809
|
1836
|
audio: true,
|
|
1810
|
1837
|
video: true
|
|
1811
|
|
-};
|
|
|
1838
|
+}
|
|
1812
|
1839
|
|
|
1813
|
1840
|
var rtcReady = false;
|
|
1814
|
1841
|
|
|
|
@@ -1858,22 +1885,44 @@ function setResolutionConstraints(constraints, resolution) {
|
|
1858
|
1885
|
constraints.video.mandatory.maxHeight =
|
|
1859
|
1886
|
constraints.video.mandatory.minHeight;
|
|
1860
|
1887
|
}
|
|
1861
|
|
-
|
|
1862
|
|
-function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
|
|
|
1888
|
+/**
|
|
|
1889
|
+ * @param {string[]} um required user media types
|
|
|
1890
|
+ *
|
|
|
1891
|
+ * @param {Object} [options={}] optional parameters
|
|
|
1892
|
+ * @param {string} options.resolution
|
|
|
1893
|
+ * @param {number} options.bandwidth
|
|
|
1894
|
+ * @param {number} options.fps
|
|
|
1895
|
+ * @param {string} options.desktopStream
|
|
|
1896
|
+ * @param {string} options.cameraDeviceId
|
|
|
1897
|
+ * @param {string} options.micDeviceId
|
|
|
1898
|
+ * @param {bool} firefox_fake_device
|
|
|
1899
|
+ */
|
|
|
1900
|
+function getConstraints(um, options) {
|
|
1863
|
1901
|
var constraints = {audio: false, video: false};
|
|
1864
|
1902
|
|
|
1865
|
1903
|
if (um.indexOf('video') >= 0) {
|
|
1866
|
1904
|
// same behaviour as true
|
|
1867
|
1905
|
constraints.video = { mandatory: {}, optional: [] };
|
|
1868
|
1906
|
|
|
|
1907
|
+ if (options.cameraDeviceId) {
|
|
|
1908
|
+ constraints.video.optional.push({
|
|
|
1909
|
+ sourceId: options.cameraDeviceId
|
|
|
1910
|
+ });
|
|
|
1911
|
+ }
|
|
|
1912
|
+
|
|
1869
|
1913
|
constraints.video.optional.push({ googLeakyBucket: true });
|
|
1870
|
1914
|
|
|
1871
|
|
- setResolutionConstraints(constraints, resolution);
|
|
|
1915
|
+ setResolutionConstraints(constraints, options.resolution);
|
|
1872
|
1916
|
}
|
|
1873
|
1917
|
if (um.indexOf('audio') >= 0) {
|
|
1874
|
1918
|
if (!RTCBrowserType.isFirefox()) {
|
|
1875
|
1919
|
// same behaviour as true
|
|
1876
|
1920
|
constraints.audio = { mandatory: {}, optional: []};
|
|
|
1921
|
+ if (options.micDeviceId) {
|
|
|
1922
|
+ constraints.audio.optional.push({
|
|
|
1923
|
+ sourceId: options.micDeviceId
|
|
|
1924
|
+ });
|
|
|
1925
|
+ }
|
|
1877
|
1926
|
// if it is good enough for hangouts...
|
|
1878
|
1927
|
constraints.audio.optional.push(
|
|
1879
|
1928
|
{googEchoCancellation: true},
|
|
|
@@ -1885,7 +1934,15 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
|
|
1885
|
1934
|
{googAutoGainControl2: true}
|
|
1886
|
1935
|
);
|
|
1887
|
1936
|
} else {
|
|
1888
|
|
- constraints.audio = true;
|
|
|
1937
|
+ if (options.micDeviceId) {
|
|
|
1938
|
+ constraints.audio = {
|
|
|
1939
|
+ mandatory: {},
|
|
|
1940
|
+ optional: [{
|
|
|
1941
|
+ sourceId: options.micDeviceId
|
|
|
1942
|
+ }]};
|
|
|
1943
|
+ } else {
|
|
|
1944
|
+ constraints.audio = true;
|
|
|
1945
|
+ }
|
|
1889
|
1946
|
}
|
|
1890
|
1947
|
}
|
|
1891
|
1948
|
if (um.indexOf('screen') >= 0) {
|
|
|
@@ -1918,7 +1975,7 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
|
|
1918
|
1975
|
constraints.video = {
|
|
1919
|
1976
|
mandatory: {
|
|
1920
|
1977
|
chromeMediaSource: "desktop",
|
|
1921
|
|
- chromeMediaSourceId: desktopStream,
|
|
|
1978
|
+ chromeMediaSourceId: options.desktopStream,
|
|
1922
|
1979
|
googLeakyBucket: true,
|
|
1923
|
1980
|
maxWidth: window.screen.width,
|
|
1924
|
1981
|
maxHeight: window.screen.height,
|
|
|
@@ -1928,28 +1985,36 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
|
|
1928
|
1985
|
};
|
|
1929
|
1986
|
}
|
|
1930
|
1987
|
|
|
1931
|
|
- if (bandwidth) {
|
|
|
1988
|
+ if (options.bandwidth) {
|
|
1932
|
1989
|
if (!constraints.video) {
|
|
1933
|
1990
|
//same behaviour as true
|
|
1934
|
1991
|
constraints.video = {mandatory: {}, optional: []};
|
|
1935
|
1992
|
}
|
|
1936
|
|
- constraints.video.optional.push({bandwidth: bandwidth});
|
|
|
1993
|
+ constraints.video.optional.push({bandwidth: options.bandwidth});
|
|
1937
|
1994
|
}
|
|
1938
|
|
- if (fps) {
|
|
|
1995
|
+ if (options.fps) {
|
|
1939
|
1996
|
// for some cameras it might be necessary to request 30fps
|
|
1940
|
1997
|
// so they choose 30fps mjpg over 10fps yuy2
|
|
1941
|
1998
|
if (!constraints.video) {
|
|
1942
|
1999
|
// same behaviour as true;
|
|
1943
|
2000
|
constraints.video = {mandatory: {}, optional: []};
|
|
1944
|
2001
|
}
|
|
1945
|
|
- constraints.video.mandatory.minFrameRate = fps;
|
|
|
2002
|
+ constraints.video.mandatory.minFrameRate = options.fps;
|
|
|
2003
|
+ }
|
|
|
2004
|
+
|
|
|
2005
|
+ // we turn audio for both audio and video tracks, the fake audio & video seems to work
|
|
|
2006
|
+ // only when enabled in one getUserMedia call, we cannot get fake audio separate by fake video
|
|
|
2007
|
+ // this later can be a problem with some of the tests
|
|
|
2008
|
+ if(RTCBrowserType.isFirefox() && options.firefox_fake_device)
|
|
|
2009
|
+ {
|
|
|
2010
|
+ constraints.audio = true;
|
|
|
2011
|
+ constraints.fake = true;
|
|
1946
|
2012
|
}
|
|
1947
|
2013
|
|
|
1948
|
2014
|
return constraints;
|
|
1949
|
2015
|
}
|
|
1950
|
2016
|
|
|
1951
|
2017
|
function setAvailableDevices(um, available) {
|
|
1952
|
|
- var devices = {};
|
|
1953
|
2018
|
if (um.indexOf("video") != -1) {
|
|
1954
|
2019
|
devices.video = available;
|
|
1955
|
2020
|
}
|
|
|
@@ -1968,6 +2033,131 @@ function onReady () {
|
|
1968
|
2033
|
eventEmitter.emit(RTCEvents.RTC_READY, true);
|
|
1969
|
2034
|
};
|
|
1970
|
2035
|
|
|
|
2036
|
+/**
|
|
|
2037
|
+ * Apply function with arguments if function exists.
|
|
|
2038
|
+ * Do nothing if function not provided.
|
|
|
2039
|
+ * @param {function} [fn] function to apply
|
|
|
2040
|
+ * @param {Array} [args=[]] arguments for function
|
|
|
2041
|
+ */
|
|
|
2042
|
+function maybeApply(fn, args) {
|
|
|
2043
|
+ if (fn) {
|
|
|
2044
|
+ fn.apply(null, args || []);
|
|
|
2045
|
+ }
|
|
|
2046
|
+}
|
|
|
2047
|
+
|
|
|
2048
|
+var getUserMediaStatus = {
|
|
|
2049
|
+ initialized: false,
|
|
|
2050
|
+ callbacks: []
|
|
|
2051
|
+};
|
|
|
2052
|
+
|
|
|
2053
|
+/**
|
|
|
2054
|
+ * Wrap `getUserMedia` to allow others to know if it was executed at least
|
|
|
2055
|
+ * once or not. Wrapper function uses `getUserMediaStatus` object.
|
|
|
2056
|
+ * @param {Function} getUserMedia native function
|
|
|
2057
|
+ * @returns {Function} wrapped function
|
|
|
2058
|
+ */
|
|
|
2059
|
+function wrapGetUserMedia(getUserMedia) {
|
|
|
2060
|
+ return function (constraints, successCallback, errorCallback) {
|
|
|
2061
|
+ getUserMedia(constraints, function (stream) {
|
|
|
2062
|
+ maybeApply(successCallback, [stream]);
|
|
|
2063
|
+ if (!getUserMediaStatus.initialized) {
|
|
|
2064
|
+ getUserMediaStatus.initialized = true;
|
|
|
2065
|
+ getUserMediaStatus.callbacks.forEach(function (callback) {
|
|
|
2066
|
+ callback();
|
|
|
2067
|
+ });
|
|
|
2068
|
+ getUserMediaStatus.callbacks.length = 0;
|
|
|
2069
|
+ }
|
|
|
2070
|
+ }, function (error) {
|
|
|
2071
|
+ maybeApply(errorCallback, [error]);
|
|
|
2072
|
+ });
|
|
|
2073
|
+ };
|
|
|
2074
|
+}
|
|
|
2075
|
+
|
|
|
2076
|
+/**
|
|
|
2077
|
+ * Create stub device which equals to auto selected device.
|
|
|
2078
|
+ * @param {string} kind if that should be `audio` or `video` device
|
|
|
2079
|
+ * @returns {Object} stub device description in `enumerateDevices` format
|
|
|
2080
|
+ */
|
|
|
2081
|
+function createAutoDeviceInfo(kind) {
|
|
|
2082
|
+ return {
|
|
|
2083
|
+ facing: null,
|
|
|
2084
|
+ label: 'Auto',
|
|
|
2085
|
+ kind: kind,
|
|
|
2086
|
+ deviceId: '',
|
|
|
2087
|
+ groupId: null
|
|
|
2088
|
+ };
|
|
|
2089
|
+}
|
|
|
2090
|
+
|
|
|
2091
|
+
|
|
|
2092
|
+/**
|
|
|
2093
|
+ * Execute function after getUserMedia was executed at least once.
|
|
|
2094
|
+ * @param {Function} callback function to execute after getUserMedia
|
|
|
2095
|
+ */
|
|
|
2096
|
+function afterUserMediaInitialized(callback) {
|
|
|
2097
|
+ if (getUserMediaStatus.initialized) {
|
|
|
2098
|
+ callback();
|
|
|
2099
|
+ } else {
|
|
|
2100
|
+ getUserMediaStatus.callbacks.push(callback);
|
|
|
2101
|
+ }
|
|
|
2102
|
+}
|
|
|
2103
|
+
|
|
|
2104
|
+/**
|
|
|
2105
|
+ * Wrapper function which makes enumerateDevices to wait
|
|
|
2106
|
+ * until someone executes getUserMedia first time.
|
|
|
2107
|
+ * @param {Function} enumerateDevices native function
|
|
|
2108
|
+ * @returns {Funtion} wrapped function
|
|
|
2109
|
+ */
|
|
|
2110
|
+function wrapEnumerateDevices(enumerateDevices) {
|
|
|
2111
|
+ return function (callback) {
|
|
|
2112
|
+ // enumerate devices only after initial getUserMedia
|
|
|
2113
|
+ afterUserMediaInitialized(function () {
|
|
|
2114
|
+
|
|
|
2115
|
+ enumerateDevices().then(function (devices) {
|
|
|
2116
|
+ //add auto devices
|
|
|
2117
|
+ devices.unshift(
|
|
|
2118
|
+ createAutoDeviceInfo('audioinput'),
|
|
|
2119
|
+ createAutoDeviceInfo('videoinput')
|
|
|
2120
|
+ );
|
|
|
2121
|
+
|
|
|
2122
|
+ callback(devices);
|
|
|
2123
|
+ }, function (err) {
|
|
|
2124
|
+ console.error('cannot enumerate devices: ', err);
|
|
|
2125
|
+
|
|
|
2126
|
+ // return only auto devices
|
|
|
2127
|
+ callback([createAutoDeviceInfo('audioInput'),
|
|
|
2128
|
+ createAutoDeviceInfo('videoinput')]);
|
|
|
2129
|
+ });
|
|
|
2130
|
+ });
|
|
|
2131
|
+ };
|
|
|
2132
|
+}
|
|
|
2133
|
+
|
|
|
2134
|
+/**
|
|
|
2135
|
+ * Use old MediaStreamTrack to get devices list and
|
|
|
2136
|
+ * convert it to enumerateDevices format.
|
|
|
2137
|
+ * @param {Function} callback function to call when received devices list.
|
|
|
2138
|
+ */
|
|
|
2139
|
+function enumerateDevicesThroughMediaStreamTrack (callback) {
|
|
|
2140
|
+ MediaStreamTrack.getSources(function (sources) {
|
|
|
2141
|
+ var devices = sources.map(function (source) {
|
|
|
2142
|
+ var kind = (source.kind || '').toLowerCase();
|
|
|
2143
|
+ return {
|
|
|
2144
|
+ facing: source.facing || null,
|
|
|
2145
|
+ label: source.label,
|
|
|
2146
|
+ kind: kind ? kind + 'input': null,
|
|
|
2147
|
+ deviceId: source.id,
|
|
|
2148
|
+ groupId: source.groupId || null
|
|
|
2149
|
+ };
|
|
|
2150
|
+ });
|
|
|
2151
|
+
|
|
|
2152
|
+ //add auto devices
|
|
|
2153
|
+ devices.unshift(
|
|
|
2154
|
+ createAutoDeviceInfo('audioinput'),
|
|
|
2155
|
+ createAutoDeviceInfo('videoinput')
|
|
|
2156
|
+ );
|
|
|
2157
|
+ callback(devices);
|
|
|
2158
|
+ });
|
|
|
2159
|
+}
|
|
|
2160
|
+
|
|
1971
|
2161
|
//Options parameter is to pass config options. Currently uses only "useIPv6".
|
|
1972
|
2162
|
var RTCUtils = {
|
|
1973
|
2163
|
init: function (options) {
|
|
|
@@ -1976,7 +2166,10 @@ var RTCUtils = {
|
|
1976
|
2166
|
var FFversion = RTCBrowserType.getFirefoxVersion();
|
|
1977
|
2167
|
if (FFversion >= 40) {
|
|
1978
|
2168
|
this.peerconnection = mozRTCPeerConnection;
|
|
1979
|
|
- this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
|
|
|
2169
|
+ this.getUserMedia = wrapGetUserMedia(navigator.mozGetUserMedia.bind(navigator));
|
|
|
2170
|
+ this.enumerateDevices = wrapEnumerateDevices(
|
|
|
2171
|
+ navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
|
|
|
2172
|
+ );
|
|
1980
|
2173
|
this.pc_constraints = {};
|
|
1981
|
2174
|
this.attachMediaStream = function (element, stream) {
|
|
1982
|
2175
|
// srcObject is being standardized and FF will eventually
|
|
|
@@ -2022,7 +2215,16 @@ var RTCUtils = {
|
|
2022
|
2215
|
|
|
2023
|
2216
|
} else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) {
|
|
2024
|
2217
|
this.peerconnection = webkitRTCPeerConnection;
|
|
2025
|
|
- this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
|
|
|
2218
|
+ var getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
|
|
|
2219
|
+ if (navigator.mediaDevices) {
|
|
|
2220
|
+ this.getUserMedia = wrapGetUserMedia(getUserMedia);
|
|
|
2221
|
+ this.enumerateDevices = wrapEnumerateDevices(
|
|
|
2222
|
+ navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
|
|
|
2223
|
+ );
|
|
|
2224
|
+ } else {
|
|
|
2225
|
+ this.getUserMedia = getUserMedia;
|
|
|
2226
|
+ this.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
|
|
|
2227
|
+ }
|
|
2026
|
2228
|
this.attachMediaStream = function (element, stream) {
|
|
2027
|
2229
|
element.attr('src', webkitURL.createObjectURL(stream));
|
|
2028
|
2230
|
};
|
|
|
@@ -2072,6 +2274,7 @@ var RTCUtils = {
|
|
2072
|
2274
|
|
|
2073
|
2275
|
self.peerconnection = RTCPeerConnection;
|
|
2074
|
2276
|
self.getUserMedia = getUserMedia;
|
|
|
2277
|
+ self.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
|
|
2075
|
2278
|
self.attachMediaStream = function (elSel, stream) {
|
|
2076
|
2279
|
|
|
2077
|
2280
|
if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
|
|
|
@@ -2126,9 +2329,23 @@ var RTCUtils = {
|
|
2126
|
2329
|
}
|
|
2127
|
2330
|
|
|
2128
|
2331
|
},
|
|
2129
|
|
- getUserMediaWithConstraints: function ( um, success_callback, failure_callback, resolution, bandwidth, fps, desktopStream) {
|
|
|
2332
|
+ /**
|
|
|
2333
|
+ * @param {string[]} um required user media types
|
|
|
2334
|
+ * @param {function} success_callback
|
|
|
2335
|
+ * @param {Function} failure_callback
|
|
|
2336
|
+ * @param {Object} [options] optional parameters
|
|
|
2337
|
+ * @param {string} options.resolution
|
|
|
2338
|
+ * @param {number} options.bandwidth
|
|
|
2339
|
+ * @param {number} options.fps
|
|
|
2340
|
+ * @param {string} options.desktopStream
|
|
|
2341
|
+ * @param {string} options.cameraDeviceId
|
|
|
2342
|
+ * @param {string} options.micDeviceId
|
|
|
2343
|
+ **/
|
|
|
2344
|
+ getUserMediaWithConstraints: function ( um, success_callback, failure_callback, options) {
|
|
|
2345
|
+ options = options || {};
|
|
|
2346
|
+ resolution = options.resolution;
|
|
2130
|
2347
|
var constraints = getConstraints(
|
|
2131
|
|
- um, resolution, bandwidth, fps, desktopStream);
|
|
|
2348
|
+ um, options);
|
|
2132
|
2349
|
|
|
2133
|
2350
|
logger.info("Get media constraints", constraints);
|
|
2134
|
2351
|
|
|
|
@@ -2157,30 +2374,28 @@ var RTCUtils = {
|
|
2157
|
2374
|
|
|
2158
|
2375
|
/**
|
|
2159
|
2376
|
* Creates the local MediaStreams.
|
|
2160
|
|
- * @param devices the devices that will be requested
|
|
2161
|
|
- * @param resolution resolution constraints
|
|
2162
|
|
- * @param dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
|
2377
|
+ * @param {Object} [options] optional parameters
|
|
|
2378
|
+ * @param {Array} options.devices the devices that will be requested
|
|
|
2379
|
+ * @param {string} options.resolution resolution constraints
|
|
|
2380
|
+ * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
2163
|
2381
|
* type: "audio" or "video", videoType: "camera" or "desktop"}
|
|
2164
|
2382
|
* will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
|
|
|
2383
|
+ * @param {string} options.cameraDeviceId
|
|
|
2384
|
+ * @param {string} options.micDeviceId
|
|
2165
|
2385
|
* @returns {*} Promise object that will receive the new JitsiTracks
|
|
2166
|
2386
|
*/
|
|
2167
|
|
- obtainAudioAndVideoPermissions: function (devices, resolution, dontCreateJitsiTracks) {
|
|
|
2387
|
+ obtainAudioAndVideoPermissions: function (options) {
|
|
2168
|
2388
|
var self = this;
|
|
2169
|
|
- // Get AV
|
|
2170
|
2389
|
|
|
|
2390
|
+ options = options || {};
|
|
2171
|
2391
|
return new Promise(function (resolve, reject) {
|
|
2172
|
2392
|
var successCallback = function (stream) {
|
|
2173
|
|
- var streams = self.successCallback(stream, resolution);
|
|
2174
|
|
- resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
|
2393
|
+ var streams = self.successCallback(stream, options.resolution);
|
|
|
2394
|
+ resolve(options.dontCreateJitsiTracks?
|
|
|
2395
|
+ streams: self.createLocalTracks(streams));
|
|
2175
|
2396
|
};
|
|
2176
|
2397
|
|
|
2177
|
|
- if (!devices)
|
|
2178
|
|
- devices = ['audio', 'video'];
|
|
2179
|
|
-
|
|
2180
|
|
- if (devices.length === 0) {
|
|
2181
|
|
- successCallback();
|
|
2182
|
|
- return;
|
|
2183
|
|
- }
|
|
|
2398
|
+ options.devices = options.devices || ['audio', 'video'];
|
|
2184
|
2399
|
|
|
2185
|
2400
|
if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) {
|
|
2186
|
2401
|
|
|
|
@@ -2204,39 +2419,40 @@ var RTCUtils = {
|
|
2204
|
2419
|
function (error, resolution) {
|
|
2205
|
2420
|
logger.error(
|
|
2206
|
2421
|
'failed to obtain video stream - stop', error);
|
|
2207
|
|
- self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
|
|
|
2422
|
+ self.errorCallback(error, resolve, options);
|
|
2208
|
2423
|
},
|
|
2209
|
|
- resolution || '360');
|
|
|
2424
|
+ {resolution: options.resolution || '360',
|
|
|
2425
|
+ cameraDeviceId: options.cameraDeviceId});
|
|
2210
|
2426
|
};
|
|
2211
|
2427
|
var obtainAudio = function () {
|
|
2212
|
2428
|
self.getUserMediaWithConstraints(
|
|
2213
|
2429
|
['audio'],
|
|
2214
|
2430
|
function (audioStream) {
|
|
2215
|
|
- if (devices.indexOf('video') !== -1)
|
|
|
2431
|
+ (options.devices.indexOf('video') === -1) ||
|
|
2216
|
2432
|
obtainVideo(audioStream);
|
|
2217
|
2433
|
},
|
|
2218
|
2434
|
function (error) {
|
|
2219
|
2435
|
logger.error(
|
|
2220
|
2436
|
'failed to obtain audio stream - stop', error);
|
|
2221
|
|
- self.errorCallback(error, resolve, null, dontCreateJitsiTracks);
|
|
2222
|
|
- }
|
|
2223
|
|
- );
|
|
|
2437
|
+ self.errorCallback(error, resolve, options);
|
|
|
2438
|
+ },{micDeviceId: options.micDeviceId});
|
|
2224
|
2439
|
};
|
|
2225
|
|
- if (devices.indexOf('audio') !== -1) {
|
|
|
2440
|
+ if((devices.indexOf('audio') === -1))
|
|
|
2441
|
+ obtainVideo(null)
|
|
|
2442
|
+ else
|
|
2226
|
2443
|
obtainAudio();
|
|
2227
|
|
- } else {
|
|
2228
|
|
- obtainVideo(null);
|
|
2229
|
|
- }
|
|
2230
|
2444
|
} else {
|
|
2231
|
2445
|
this.getUserMediaWithConstraints(
|
|
2232
|
|
- devices,
|
|
|
2446
|
+ options.devices,
|
|
2233
|
2447
|
function (stream) {
|
|
2234
|
2448
|
successCallback(stream);
|
|
2235
|
2449
|
},
|
|
2236
|
2450
|
function (error, resolution) {
|
|
2237
|
|
- self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
|
|
|
2451
|
+ self.errorCallback(error, resolve, options);
|
|
2238
|
2452
|
},
|
|
2239
|
|
- resolution || '360');
|
|
|
2453
|
+ {resolution: options.resolution || '360',
|
|
|
2454
|
+ cameraDeviceId: options.cameraDeviceId,
|
|
|
2455
|
+ micDeviceId: options.micDeviceId});
|
|
2240
|
2456
|
}
|
|
2241
|
2457
|
}.bind(this));
|
|
2242
|
2458
|
},
|
|
|
@@ -2260,15 +2476,17 @@ var RTCUtils = {
|
|
2260
|
2476
|
* Error callback called from GUM. Retries the GUM call with different resolutions.
|
|
2261
|
2477
|
* @param error the error
|
|
2262
|
2478
|
* @param resolve the resolve funtion that will be called on success.
|
|
2263
|
|
- * @param currentResolution the last resolution used for GUM.
|
|
|
2479
|
+ * @param {Object} options with the following properties:
|
|
|
2480
|
+ * @param resolution the last resolution used for GUM.
|
|
2264
|
2481
|
* @param dontCreateJitsiTracks if <tt>true</tt> objects with the following structure {stream: the Media Stream,
|
|
2265
|
2482
|
* type: "audio" or "video", videoType: "camera" or "desktop"}
|
|
2266
|
2483
|
* will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
|
|
2267
|
2484
|
*/
|
|
2268
|
|
- errorCallback: function (error, resolve, currentResolution, dontCreateJitsiTracks) {
|
|
|
2485
|
+ errorCallback: function (error, resolve, options) {
|
|
2269
|
2486
|
var self = this;
|
|
|
2487
|
+ options = options || {};
|
|
2270
|
2488
|
logger.error('failed to obtain audio/video stream - trying audio only', error);
|
|
2271
|
|
- var resolution = getPreviousResolution(currentResolution);
|
|
|
2489
|
+ var resolution = getPreviousResolution(options.resolution);
|
|
2272
|
2490
|
if (typeof error == "object" && error.constraintName && error.name
|
|
2273
|
2491
|
&& (error.name == "ConstraintNotSatisfiedError" ||
|
|
2274
|
2492
|
error.name == "OverconstrainedError") &&
|
|
|
@@ -2278,23 +2496,26 @@ var RTCUtils = {
|
|
2278
|
2496
|
self.getUserMediaWithConstraints(['audio', 'video'],
|
|
2279
|
2497
|
function (stream) {
|
|
2280
|
2498
|
var streams = self.successCallback(stream, resolution);
|
|
2281
|
|
- resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
|
2499
|
+ resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
2282
|
2500
|
}, function (error, resolution) {
|
|
2283
|
|
- return self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
|
|
2284
|
|
- }, resolution);
|
|
|
2501
|
+ return self.errorCallback(error, resolve,
|
|
|
2502
|
+ {resolution: resolution,
|
|
|
2503
|
+ dontCreateJitsiTracks: options.dontCreateJitsiTracks});
|
|
|
2504
|
+ },
|
|
|
2505
|
+ {resolution: options.resolution});
|
|
2285
|
2506
|
}
|
|
2286
|
2507
|
else {
|
|
2287
|
2508
|
self.getUserMediaWithConstraints(
|
|
2288
|
2509
|
['audio'],
|
|
2289
|
2510
|
function (stream) {
|
|
2290
|
2511
|
var streams = self.successCallback(stream, resolution);
|
|
2291
|
|
- resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
|
2512
|
+ resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
2292
|
2513
|
},
|
|
2293
|
2514
|
function (error) {
|
|
2294
|
2515
|
logger.error('failed to obtain audio/video stream - stop',
|
|
2295
|
2516
|
error);
|
|
2296
|
2517
|
var streams = self.successCallback(null);
|
|
2297
|
|
- resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
|
2518
|
+ resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
|
|
2298
|
2519
|
}
|
|
2299
|
2520
|
);
|
|
2300
|
2521
|
}
|
|
|
@@ -2397,8 +2618,20 @@ var RTCUtils = {
|
|
2397
|
2618
|
eventEmitter.emit(eventType, localStream);
|
|
2398
|
2619
|
}
|
|
2399
|
2620
|
return newStreams;
|
|
|
2621
|
+ },
|
|
|
2622
|
+
|
|
|
2623
|
+ /**
|
|
|
2624
|
+ * Checks if its possible to enumerate available cameras/micropones.
|
|
|
2625
|
+ * @returns {boolean} true if available, false otherwise.
|
|
|
2626
|
+ */
|
|
|
2627
|
+ isDeviceListAvailable: function () {
|
|
|
2628
|
+ var isEnumerateDevicesAvailable = navigator.mediaDevices && navigator.mediaDevices.enumerateDevices;
|
|
|
2629
|
+ if (isEnumerateDevicesAvailable) {
|
|
|
2630
|
+ return true;
|
|
|
2631
|
+ }
|
|
|
2632
|
+ return (MediaStreamTrack && MediaStreamTrack.getSources)? true : false;
|
|
2400
|
2633
|
}
|
|
2401
|
|
-}
|
|
|
2634
|
+};
|
|
2402
|
2635
|
|
|
2403
|
2636
|
module.exports = RTCUtils;
|
|
2404
|
2637
|
|