浏览代码

Adds device selection support.

release-8443
hristoterezov 10 年前
父节点
当前提交
bc1bc1d024
共有 9 个文件被更改,包括 473 次插入142 次删除
  1. 1
    0
      JitsiConference.js
  2. 7
    0
      JitsiMeetJS.js
  3. 19
    12
      doc/API.md
  4. 25
    6
      doc/example/example.js
  5. 5
    0
      doc/example/index.html
  6. 297
    64
      lib-jitsi-meet.js
  7. 4
    3
      modules/RTC/JitsiLocalTrack.js
  8. 11
    9
      modules/RTC/RTC.js
  9. 104
    48
      modules/RTC/RTCUtils.js

+ 1
- 0
JitsiConference.js 查看文件

@@ -257,6 +257,7 @@ function setupListeners(conference) {
257 257
     });
258 258
     conference.rtc.addListener(StreamEventTypes.EVENT_TYPE_LOCAL_ENDED, function (stream) {
259 259
         conference.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, stream);
260
+        conference.removeTrack(stream);
260 261
     });
261 262
     conference.rtc.addListener(StreamEventTypes.TRACK_MUTE_CHANGED, function (track) {
262 263
         conference.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track);

+ 7
- 0
JitsiMeetJS.js 查看文件

@@ -31,6 +31,13 @@ var LibJitsiMeet = {
31 31
      * Creates the media tracks and returns them trough the callback.
32 32
      * @param options Object with properties / settings specifying the tracks which should be created.
33 33
      * should be created or some additional configurations about resolution for example.
34
+     * @param {Array} options.devices the devices that will be requested
35
+     * @param {string} options.resolution resolution constraints
36
+     * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
37
+     * type: "audio" or "video", videoType: "camera" or "desktop"}
38
+     * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
39
+     * @param {string} options.cameraDeviceId
40
+     * @param {string} options.micDeviceId
34 41
      * @returns {Promise.<{Array.<JitsiTrack>}, JitsiConferenceError>} A promise that returns an array of created JitsiTracks if resolved,
35 42
      *     or a JitsiConferenceError if rejected.
36 43
      */

+ 19
- 12
doc/API.md 查看文件

@@ -46,6 +46,20 @@ The ```options``` parameter is JS object with the following properties:
46 46
 JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
47 47
 ```
48 48
 
49
+* ```JitsiMeetJS.createLocalTracks(options)``` - Creates the media tracks and returns them trough ```Promise``` object.
50
+    - options - JS object with configuration options for the local media tracks. You can change the following properties there:
51
+        1. devices - array with the devices - "video" and "audio" that will be passed to GUM. If that property is not set GUM will try to get all available devices.
52
+        2. resolution - the prefered resolution for the local video.
53
+        3. cameraDeviceId - the deviceID for the video device that is going to be used
54
+        4. micDeviceId - the deviceID for the audio device that is going to be used
55
+
56
+* ```JitsiMeetJS.enumerateDevices(callback)``` - returns list of the available devices as a parameter to the callback function. Every device is a object with the following format:
57
+    - label - the name of the device
58
+    - kind - "audioinput" or "videoinput"
59
+    - deviceId - the id of the device.
60
+
61
+* ```JitsiMeetJS.isDeviceListAvailable()```- returns true if retrieving the device list is support and false - otherwise.
62
+
49 63
 * ```JitsiMeetJS.events``` - JS object that contains all events used by the API. You will need that JS object when you try to subscribe for connection or conference events.
50 64
     We have two event types - connection and conference. You can access the events with the following code ```JitsiMeetJS.events.<event_type>.<event_name>```.
51 65
     For example if you want to use the conference event that is fired when somebody leave conference you can use the following code - ```JitsiMeetJS.events.conference.USER_LEFT```.
@@ -139,11 +153,6 @@ The object represents a conference. We have the following methods to control the
139 153
 
140 154
 2. leave() - leaves the conference
141 155
 
142
-3. createLocalTracks(options) - Creates the media tracks and returns them trough ```Promise``` object.
143
-    - options - JS object with configuration options for the local media tracks. You can change the following properties there:
144
-        1. devices - array with the devices - "video" and "audio" that will be passed to GUM. If that property is not set GUM will try to get all available devices.
145
-        2. resolution - the prefered resolution for the local video.
146
-
147 156
 4. getLocalTracks() - Returns array with JitsiTrack objects for the local streams.
148 157
 
149 158
 5. addEventListener(event, listener) - Subscribes the passed listener to the event.
@@ -202,7 +211,10 @@ The object represents a conference. We have the following methods to control the
202 211
 16. removeCommandListener(command) - removes the listeners for the specified command
203 212
     - command - the name of the command
204 213
 
205
-
214
+17. addTrack(track) - Adds JitsiLocalTrack object to the conference.
215
+    - track - the JitsiLocalTrack
216
+17. removeTrack(track) - Removes JitsiLocalTrack object to the conference.
217
+    - track - the JitsiLocalTrack
206 218
 JitsiTrack
207 219
 ======
208 220
 The object represents single track - video or audio. They can be remote tracks ( from the other participants in the call) or local tracks (from the devices of the local participant).
@@ -229,12 +241,7 @@ Note: This method is implemented only for the local tracks.
229 241
 
230 242
 Note: This method is implemented only for the local tracks.
231 243
 
232
-7. start() - start sending the track to the other participants in the conference.
233
-
234
-Note: This method is implemented only for the local tracks.
235
-
236
-8. getId() - returns unique string for the track.
237
-
244
+7. getId() - returns unique string for the track.
238 245
 
239 246
 
240 247
 Getting Started

+ 25
- 6
doc/example/example.js 查看文件

@@ -141,15 +141,34 @@ $(window).bind('unload', unload);
141 141
 // JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
142 142
 
143 143
 JitsiMeetJS.init();
144
-JitsiMeetJS.createLocalTracks({resolution: "720"}).then(onLocalTracks);
145
-var connection = new JitsiMeetJS.JitsiConnection(null, null, options);
146 144
 
145
+JitsiMeetJS.enumerateDevices(function (devices) {
146
+    for(var i = 0; i < devices.length; i++)
147
+    {
148
+        var device = devices[i];
149
+        if(device.kind === "videoinput")
150
+            $("#videoDevices").append("<option value=\"" + device.deviceId +
151
+                "\">" + device.label + "</option>");
152
+    }
153
+})
154
+
155
+var connection = null;
147 156
 var room = null;
148 157
 var localTracks = [];
149 158
 var remoteTracks = {};
150 159
 
151
-connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
152
-connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
153
-connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);
154 160
 
155
-connection.connect();
161
+/**
162
+ * Starts the conference with the selected device
163
+ */
164
+function selectDevice() {
165
+    var videoID = $("#videoDevices").val();
166
+    JitsiMeetJS.createLocalTracks({resolution: "720", cameraDeviceId: videoID}).then(onLocalTracks);
167
+    connection = new JitsiMeetJS.JitsiConnection(null, null, options);
168
+
169
+    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
170
+    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
171
+    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);
172
+
173
+    connection.connect();
174
+}

+ 5
- 0
doc/example/index.html 查看文件

@@ -12,6 +12,11 @@
12 12
 </head>
13 13
 <body>
14 14
     <a onclick="room.setDisplayName(Math.random())">Change Display Name</a>
15
+    <div>Select Video device:
16
+        <select id="videoDevices">
17
+        </select>
18
+        <a onclick = "selectDevice();">Select</a>
19
+    </div>
15 20
     <video id="localVideo" autoplay="true"></video>
16 21
     <!--<audio id="localAudio" autoplay="true" muted="true"></audio>-->
17 22
 </body>

+ 297
- 64
lib-jitsi-meet.js 查看文件

@@ -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
 

+ 4
- 3
modules/RTC/JitsiLocalTrack.js 查看文件

@@ -66,9 +66,10 @@ JitsiLocalTrack.prototype._setMute = function (mute) {
66 66
         } else {
67 67
             var self = this;
68 68
             var RTC = require("./RTCUtils");
69
-            RTC.obtainAudioAndVideoPermissions(
70
-                (isAudio ? ["audio"] : ["video"]),
71
-                  self.resolution, true)
69
+            RTC.obtainAudioAndVideoPermissions({
70
+                devices: (isAudio ? ["audio"] : ["video"]),
71
+                resolution: self.resolution,
72
+                dontCreateJitsiTrack: true})
72 73
                 .then(function (streams) {
73 74
                     var stream = null;
74 75
                     for(var i = 0; i < streams.length; i++) {

+ 11
- 9
modules/RTC/RTC.js 查看文件

@@ -38,15 +38,18 @@ function RTC(room, options) {
38 38
 
39 39
 /**
40 40
  * Creates the local MediaStreams.
41
- * @param options object for options (NOTE: currently only list of devices and resolution are supported)
42
- * @param dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
43
-  * type: "audio" or "video", videoType: "camera" or "desktop"}
41
+ * @param {Object} [options] optional parameters
42
+ * @param {Array} options.devices the devices that will be requested
43
+ * @param {string} options.resolution resolution constraints
44
+ * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
45
+ * type: "audio" or "video", videoType: "camera" or "desktop"}
44 46
  * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
47
+ * @param {string} options.cameraDeviceId
48
+ * @param {string} options.micDeviceId
45 49
  * @returns {*} Promise object that will receive the new JitsiTracks
46 50
  */
47
-RTC.obtainAudioAndVideoPermissions = function (options, dontCreateJitsiTrack) {
48
-    return RTCUtils.obtainAudioAndVideoPermissions(
49
-        options.devices, options.resolution, dontCreateJitsiTrack);
51
+RTC.obtainAudioAndVideoPermissions = function (options) {
52
+    return RTCUtils.obtainAudioAndVideoPermissions(options);
50 53
 }
51 54
 
52 55
 RTC.prototype.onIncommingCall = function(event) {
@@ -137,11 +140,10 @@ RTC.getPCConstraints = function () {
137 140
 };
138 141
 
139 142
 RTC.getUserMediaWithConstraints = function(um, success_callback,
140
-                                     failure_callback, resolution,
141
-                                     bandwidth, fps, desktopStream)
143
+                                     failure_callback, options)
142 144
 {
143 145
     return RTCUtils.getUserMediaWithConstraints(this, um, success_callback,
144
-        failure_callback, resolution, bandwidth, fps, desktopStream);
146
+        failure_callback, options);
145 147
 };
146 148
 
147 149
 RTC.attachMediaStream =  function (elSelector, stream) {

+ 104
- 48
modules/RTC/RTCUtils.js 查看文件

@@ -15,7 +15,7 @@ var eventEmitter = new EventEmitter();
15 15
 var devices = {
16 16
     audio: true,
17 17
     video: true
18
-};
18
+}
19 19
 
20 20
 var rtcReady = false;
21 21
 
@@ -65,22 +65,44 @@ function setResolutionConstraints(constraints, resolution) {
65 65
         constraints.video.mandatory.maxHeight =
66 66
             constraints.video.mandatory.minHeight;
67 67
 }
68
-
69
-function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
68
+/**
69
+ * @param {string[]} um required user media types
70
+ *
71
+ * @param {Object} [options={}] optional parameters
72
+ * @param {string} options.resolution
73
+ * @param {number} options.bandwidth
74
+ * @param {number} options.fps
75
+ * @param {string} options.desktopStream
76
+ * @param {string} options.cameraDeviceId
77
+ * @param {string} options.micDeviceId
78
+ * @param {bool} firefox_fake_device
79
+ */
80
+function getConstraints(um, options) {
70 81
     var constraints = {audio: false, video: false};
71 82
 
72 83
     if (um.indexOf('video') >= 0) {
73 84
         // same behaviour as true
74 85
         constraints.video = { mandatory: {}, optional: [] };
75 86
 
87
+        if (options.cameraDeviceId) {
88
+            constraints.video.optional.push({
89
+                sourceId: options.cameraDeviceId
90
+            });
91
+        }
92
+
76 93
         constraints.video.optional.push({ googLeakyBucket: true });
77 94
 
78
-        setResolutionConstraints(constraints, resolution);
95
+        setResolutionConstraints(constraints, options.resolution);
79 96
     }
80 97
     if (um.indexOf('audio') >= 0) {
81 98
         if (!RTCBrowserType.isFirefox()) {
82 99
             // same behaviour as true
83 100
             constraints.audio = { mandatory: {}, optional: []};
101
+            if (options.micDeviceId) {
102
+                constraints.audio.optional.push({
103
+                    sourceId: options.micDeviceId
104
+                });
105
+            }
84 106
             // if it is good enough for hangouts...
85 107
             constraints.audio.optional.push(
86 108
                 {googEchoCancellation: true},
@@ -92,7 +114,15 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
92 114
                 {googAutoGainControl2: true}
93 115
             );
94 116
         } else {
95
-            constraints.audio = true;
117
+            if (options.micDeviceId) {
118
+                constraints.audio = {
119
+                    mandatory: {},
120
+                    optional: [{
121
+                        sourceId: options.micDeviceId
122
+                    }]};
123
+            } else {
124
+                constraints.audio = true;
125
+            }
96 126
         }
97 127
     }
98 128
     if (um.indexOf('screen') >= 0) {
@@ -125,7 +155,7 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
125 155
         constraints.video = {
126 156
             mandatory: {
127 157
                 chromeMediaSource: "desktop",
128
-                chromeMediaSourceId: desktopStream,
158
+                chromeMediaSourceId: options.desktopStream,
129 159
                 googLeakyBucket: true,
130 160
                 maxWidth: window.screen.width,
131 161
                 maxHeight: window.screen.height,
@@ -135,28 +165,36 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
135 165
         };
136 166
     }
137 167
 
138
-    if (bandwidth) {
168
+    if (options.bandwidth) {
139 169
         if (!constraints.video) {
140 170
             //same behaviour as true
141 171
             constraints.video = {mandatory: {}, optional: []};
142 172
         }
143
-        constraints.video.optional.push({bandwidth: bandwidth});
173
+        constraints.video.optional.push({bandwidth: options.bandwidth});
144 174
     }
145
-    if (fps) {
175
+    if (options.fps) {
146 176
         // for some cameras it might be necessary to request 30fps
147 177
         // so they choose 30fps mjpg over 10fps yuy2
148 178
         if (!constraints.video) {
149 179
             // same behaviour as true;
150 180
             constraints.video = {mandatory: {}, optional: []};
151 181
         }
152
-        constraints.video.mandatory.minFrameRate = fps;
182
+        constraints.video.mandatory.minFrameRate = options.fps;
183
+    }
184
+
185
+    // we turn audio for both audio and video tracks, the fake audio & video seems to work
186
+    // only when enabled in one getUserMedia call, we cannot get fake audio separate by fake video
187
+    // this later can be a problem with some of the tests
188
+    if(RTCBrowserType.isFirefox() && options.firefox_fake_device)
189
+    {
190
+        constraints.audio = true;
191
+        constraints.fake = true;
153 192
     }
154 193
 
155 194
     return constraints;
156 195
 }
157 196
 
158 197
 function setAvailableDevices(um, available) {
159
-    var devices = {};
160 198
     if (um.indexOf("video") != -1) {
161 199
         devices.video = available;
162 200
     }
@@ -471,9 +509,23 @@ var RTCUtils = {
471 509
         }
472 510
 
473 511
     },
474
-    getUserMediaWithConstraints: function ( um, success_callback, failure_callback, resolution, bandwidth, fps, desktopStream) {
512
+    /**
513
+    * @param {string[]} um required user media types
514
+    * @param {function} success_callback
515
+    * @param {Function} failure_callback
516
+    * @param {Object} [options] optional parameters
517
+    * @param {string} options.resolution
518
+    * @param {number} options.bandwidth
519
+    * @param {number} options.fps
520
+    * @param {string} options.desktopStream
521
+    * @param {string} options.cameraDeviceId
522
+    * @param {string} options.micDeviceId
523
+    **/
524
+    getUserMediaWithConstraints: function ( um, success_callback, failure_callback, options) {
525
+        options = options || {};
526
+        resolution = options.resolution;
475 527
         var constraints = getConstraints(
476
-            um, resolution, bandwidth, fps, desktopStream);
528
+            um, options);
477 529
 
478 530
         logger.info("Get media constraints", constraints);
479 531
 
@@ -502,30 +554,28 @@ var RTCUtils = {
502 554
 
503 555
     /**
504 556
      * Creates the local MediaStreams.
505
-     * @param devices the devices that will be requested
506
-     * @param resolution resolution constraints
507
-     * @param dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
557
+     * @param {Object} [options] optional parameters
558
+     * @param {Array} options.devices the devices that will be requested
559
+     * @param {string} options.resolution resolution constraints
560
+     * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with the following structure {stream: the Media Stream,
508 561
      * type: "audio" or "video", videoType: "camera" or "desktop"}
509 562
      * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
563
+     * @param {string} options.cameraDeviceId
564
+     * @param {string} options.micDeviceId
510 565
      * @returns {*} Promise object that will receive the new JitsiTracks
511 566
      */
512
-    obtainAudioAndVideoPermissions: function (devices, resolution, dontCreateJitsiTracks) {
567
+    obtainAudioAndVideoPermissions: function (options) {
513 568
         var self = this;
514
-        // Get AV
515 569
 
570
+        options = options || {};
516 571
         return new Promise(function (resolve, reject) {
517 572
             var successCallback = function (stream) {
518
-                var streams = self.successCallback(stream, resolution);
519
-                resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
573
+                var streams = self.successCallback(stream, options.resolution);
574
+                resolve(options.dontCreateJitsiTracks?
575
+                    streams: self.createLocalTracks(streams));
520 576
             };
521 577
 
522
-            if (!devices)
523
-                devices = ['audio', 'video'];
524
-
525
-            if (devices.length === 0) {
526
-                successCallback();
527
-                return;
528
-            }
578
+            options.devices = options.devices || ['audio', 'video'];
529 579
 
530 580
             if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) {
531 581
 
@@ -549,39 +599,40 @@ var RTCUtils = {
549 599
                         function (error, resolution) {
550 600
                             logger.error(
551 601
                                 'failed to obtain video stream - stop', error);
552
-                            self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
602
+                            self.errorCallback(error, resolve, options);
553 603
                         },
554
-                            resolution || '360');
604
+                        {resolution: options.resolution || '360',
605
+                        cameraDeviceId: options.cameraDeviceId});
555 606
                 };
556 607
                 var obtainAudio = function () {
557 608
                     self.getUserMediaWithConstraints(
558 609
                         ['audio'],
559 610
                         function (audioStream) {
560
-                            if (devices.indexOf('video') !== -1)
611
+                            (options.devices.indexOf('video') === -1) ||
561 612
                                 obtainVideo(audioStream);
562 613
                         },
563 614
                         function (error) {
564 615
                             logger.error(
565 616
                                 'failed to obtain audio stream - stop', error);
566
-                            self.errorCallback(error, resolve, null, dontCreateJitsiTracks);
567
-                        }
568
-                    );
617
+                            self.errorCallback(error, resolve, options);
618
+                        },{micDeviceId: options.micDeviceId});
569 619
                 };
570
-                if (devices.indexOf('audio') !== -1) {
620
+                if((devices.indexOf('audio') === -1))
621
+                    obtainVideo(null)
622
+                else
571 623
                     obtainAudio();
572
-                } else {
573
-                    obtainVideo(null);
574
-                }
575 624
             } else {
576 625
                 this.getUserMediaWithConstraints(
577
-                    devices,
626
+                    options.devices,
578 627
                     function (stream) {
579 628
                         successCallback(stream);
580 629
                     },
581 630
                     function (error, resolution) {
582
-                        self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
631
+                        self.errorCallback(error, resolve, options);
583 632
                     },
584
-                    resolution || '360');
633
+                    {resolution: options.resolution || '360',
634
+                    cameraDeviceId: options.cameraDeviceId,
635
+                    micDeviceId: options.micDeviceId});
585 636
             }
586 637
         }.bind(this));
587 638
     },
@@ -605,15 +656,17 @@ var RTCUtils = {
605 656
      * Error callback called from GUM. Retries the GUM call with different resolutions.
606 657
      * @param error the error
607 658
      * @param resolve the resolve funtion that will be called on success.
608
-     * @param currentResolution the last resolution used for GUM.
659
+     * @param {Object} options with the following properties:
660
+     * @param resolution the last resolution used for GUM.
609 661
      * @param dontCreateJitsiTracks if <tt>true</tt> objects with the following structure {stream: the Media Stream,
610 662
      * type: "audio" or "video", videoType: "camera" or "desktop"}
611 663
      * will be returned trough the Promise, otherwise JitsiTrack objects will be returned.
612 664
      */
613
-    errorCallback: function (error, resolve, currentResolution, dontCreateJitsiTracks) {
665
+    errorCallback: function (error, resolve, options) {
614 666
         var self = this;
667
+        options = options || {};
615 668
         logger.error('failed to obtain audio/video stream - trying audio only', error);
616
-        var resolution = getPreviousResolution(currentResolution);
669
+        var resolution = getPreviousResolution(options.resolution);
617 670
         if (typeof error == "object" && error.constraintName && error.name
618 671
             && (error.name == "ConstraintNotSatisfiedError" ||
619 672
                 error.name == "OverconstrainedError") &&
@@ -623,23 +676,26 @@ var RTCUtils = {
623 676
             self.getUserMediaWithConstraints(['audio', 'video'],
624 677
                 function (stream) {
625 678
                     var streams = self.successCallback(stream, resolution);
626
-                    resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
679
+                    resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
627 680
                 }, function (error, resolution) {
628
-                    return self.errorCallback(error, resolve, resolution, dontCreateJitsiTracks);
629
-                }, resolution);
681
+                    return self.errorCallback(error, resolve,
682
+                        {resolution: resolution,
683
+                        dontCreateJitsiTracks: options.dontCreateJitsiTracks});
684
+                },
685
+                {resolution: options.resolution});
630 686
         }
631 687
         else {
632 688
             self.getUserMediaWithConstraints(
633 689
                 ['audio'],
634 690
                 function (stream) {
635 691
                     var streams = self.successCallback(stream, resolution);
636
-                    resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
692
+                    resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
637 693
                 },
638 694
                 function (error) {
639 695
                     logger.error('failed to obtain audio/video stream - stop',
640 696
                         error);
641 697
                     var streams = self.successCallback(null);
642
-                    resolve(dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
698
+                    resolve(options.dontCreateJitsiTracks? streams: self.createLocalTracks(streams));
643 699
                 }
644 700
             );
645 701
         }

正在加载...
取消
保存