Ver código fonte

Merge branch 'tsareg-master'

dev1
hristoterezov 9 anos atrás
pai
commit
3eeac2eb19

+ 24
- 4
JitsiMeetJS.js Ver arquivo

151
         return RTC.isDeviceListAvailable();
151
         return RTC.isDeviceListAvailable();
152
     },
152
     },
153
     /**
153
     /**
154
-     * Returns true if changing the camera / microphone device is supported and
155
-     * false if not.
154
+     * Returns true if changing the input (camera / microphone) or output
155
+     * (audio) device is supported and false if not.
156
+     * @params {string} [deviceType] - type of device to change. Default is
157
+     *      undefined or 'input', 'output' - for audio output device change.
156
      * @returns {boolean} true if available, false otherwise.
158
      * @returns {boolean} true if available, false otherwise.
157
      */
159
      */
158
-    isDeviceChangeAvailable: function () {
159
-        return RTC.isDeviceChangeAvailable();
160
+    isDeviceChangeAvailable: function (deviceType) {
161
+        return RTC.isDeviceChangeAvailable(deviceType);
162
+    },
163
+    /**
164
+     * Returns currently used audio output device id, '' stands for default
165
+     * device
166
+     * @returns {string}
167
+     */
168
+    getAudioOutputDevice: function () {
169
+        return RTC.getAudioOutputDevice();
170
+    },
171
+    /**
172
+     * Sets current audio output device.
173
+     * @param {string} deviceId - id of 'audiooutput' device from
174
+     *      navigator.mediaDevices.enumerateDevices(), '' is for default device
175
+     * @returns {Promise} - resolves when audio output is changed, is rejected
176
+     *      otherwise
177
+     */
178
+    setAudioOutputDevice: function (deviceId) {
179
+        return RTC.setAudioOutputDevice(deviceId);
160
     },
180
     },
161
     enumerateDevices: function (callback) {
181
     enumerateDevices: function (callback) {
162
         RTC.enumerateDevices(callback);
182
         RTC.enumerateDevices(callback);

+ 5
- 1
JitsiTrackEvents.js Ver arquivo

14
     /**
14
     /**
15
      * The video type("camera" or "desktop") of the track was changed.
15
      * The video type("camera" or "desktop") of the track was changed.
16
      */
16
      */
17
-     TRACK_VIDEOTYPE_CHANGED: "track.videoTypeChanged"
17
+    TRACK_VIDEOTYPE_CHANGED: "track.videoTypeChanged",
18
+    /**
19
+     * The audio output of the track was changed.
20
+     */
21
+    TRACK_AUDIO_OUTPUT_CHANGED: "track.audioOutputChanged"
18
 };
22
 };
19
 
23
 
20
 module.exports = JitsiTrackEvents;
24
 module.exports = JitsiTrackEvents;

+ 10
- 2
doc/API.md Ver arquivo

70
     - kind - "audioinput" or "videoinput"
70
     - kind - "audioinput" or "videoinput"
71
     - deviceId - the id of the device.
71
     - deviceId - the id of the device.
72
 
72
 
73
-* ```JitsiMeetJS.isDeviceListAvailable()```- returns true if retrieving the device list is support and false - otherwise.
74
-
73
+* ```JitsiMeetJS.isDeviceListAvailable()``` - returns true if retrieving the device list is support and false - otherwise.
74
+* ```JitsiMeetJS.isDeviceChangeAvailable(deviceType)``` - returns true if changing the input (camera / microphone) or output (audio) device is supported and false if not. ```deviceType``` is a type of device to change. Undefined or 'input' stands for input devices, 'output' - for audio output devices.
75
+* ```JitsiMeetJS.setAudioOutputDevice(deviceId)``` - sets current audio output device. ```deviceId``` - id of 'audiooutput' device from ```JitsiMeetJS.enumerateDevices()```, '' is for default device.
76
+* ```JitsiMeetJS.getAudioOutputDevice()``` - returns currently used audio output device id, '' stands for default device.
75
 * ```JitsiMeetJS.isDesktopSharingEnabled()``` - returns true if desktop sharing is supported and false otherwise. NOTE: that method can be used after ```JitsiMeetJS.init(options)``` is completed otherwise the result will be always null.
77
 * ```JitsiMeetJS.isDesktopSharingEnabled()``` - returns true if desktop sharing is supported and false otherwise. NOTE: that method can be used after ```JitsiMeetJS.init(options)``` is completed otherwise the result will be always null.
76
 * ```JitsiMeetJS.getGlobalOnErrorHandler()``` - returns function that can be used to be attached to window.onerror and if options.enableWindowOnErrorHandler is enabled returns the function used by the lib. (function(message, source, lineno, colno, error)).
78
 * ```JitsiMeetJS.getGlobalOnErrorHandler()``` - returns function that can be used to be attached to window.onerror and if options.enableWindowOnErrorHandler is enabled returns the function used by the lib. (function(message, source, lineno, colno, error)).
77
 
79
 
80
+
81
+
78
 * ```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.
82
 * ```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.
79
     We have two event types - connection and conference. You can access the events with the following code ```JitsiMeetJS.events.<event_type>.<event_name>```.
83
     We have two event types - connection and conference. You can access the events with the following code ```JitsiMeetJS.events.<event_type>.<event_name>```.
80
     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```.
84
     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```.
114
     3. tracks
118
     3. tracks
115
         - LOCAL_TRACK_STOPPED - indicates that a local track was stopped. This
119
         - LOCAL_TRACK_STOPPED - indicates that a local track was stopped. This
116
         event can be fired when ```dispose()``` method is called or for other reasons.
120
         event can be fired when ```dispose()``` method is called or for other reasons.
121
+        - TRACK_AUDIO_OUTPUT_CHANGED - indicates that audio output device for track was changed (parameters - deviceId (string) - new audio output device ID).
122
+        
117
 
123
 
118
 * ```JitsiMeetJS.errors``` - JS object that contains all errors used by the API. You can use that object to check the reported errors from the API
124
 * ```JitsiMeetJS.errors``` - JS object that contains all errors used by the API. You can use that object to check the reported errors from the API
119
     We have two error types - connection and conference. You can access the events with the following code ```JitsiMeetJS.errors.<error_type>.<error_name>```.
125
     We have two error types - connection and conference. You can access the events with the following code ```JitsiMeetJS.errors.<error_type>.<error_name>```.
337
 9. getParticipantId() - returns id(string) of the track owner
343
 9. getParticipantId() - returns id(string) of the track owner
338
 
344
 
339
    Note: This method is implemented only for the remote tracks.
345
    Note: This method is implemented only for the remote tracks.
346
+   
347
+10. setAudioOutput(audioOutputDeviceId) - sets new audio output device for track's DOM elements. Video tracks are ignored.
340
 
348
 
341
 
349
 
342
 Getting Started
350
 Getting Started

+ 29
- 0
doc/example/example.js Ver arquivo

36
             function () {
36
             function () {
37
                 console.log("local track stoped");
37
                 console.log("local track stoped");
38
             });
38
             });
39
+        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
40
+            function (deviceId) {
41
+                console.log("track audio output device was changed to " + deviceId);
42
+            });
39
         if(localTracks[i].getType() == "video") {
43
         if(localTracks[i].getType() == "video") {
40
             $("body").append("<video autoplay='1' id='localVideo" + i + "' />");
44
             $("body").append("<video autoplay='1' id='localVideo" + i + "' />");
41
             localTracks[i].attach($("#localVideo" + i)[0]);
45
             localTracks[i].attach($("#localVideo" + i)[0]);
71
         function () {
75
         function () {
72
             console.log("remote track stoped");
76
             console.log("remote track stoped");
73
         });
77
         });
78
+    track.addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
79
+        function (deviceId) {
80
+            console.log("track audio output device was changed to " + deviceId);
81
+        });
74
     var id = participant + track.getType() + idx;
82
     var id = participant + track.getType() + idx;
75
     if(track.getType() == "video") {
83
     if(track.getType() == "video") {
76
         $("body").append("<video autoplay='1' id='" + participant + "video" + idx + "' />");
84
         $("body").append("<video autoplay='1' id='" + participant + "video" + idx + "' />");
180
         });
188
         });
181
 }
189
 }
182
 
190
 
191
+function changeAudioOutput(selected) {
192
+    JitsiMeetJS.setAudioOutputDevice(selected.value);
193
+}
194
+
183
 $(window).bind('beforeunload', unload);
195
 $(window).bind('beforeunload', unload);
184
 $(window).bind('unload', unload);
196
 $(window).bind('unload', unload);
185
 
197
 
228
     console.log(error);
240
     console.log(error);
229
 });
241
 });
230
 
242
 
243
+
244
+if (JitsiMeetJS.isDeviceChangeAvailable('output')) {
245
+    JitsiMeetJS.enumerateDevices(function(devices) {
246
+        var audioOutputDevices = devices.filter(function(d) { return d.kind === 'audiooutput'; });
247
+
248
+        if (audioOutputDevices.length > 1) {
249
+            $('#audioOutputSelect').html(
250
+                audioOutputDevices.map(function (d) {
251
+                    return '<option value="' + d.deviceId + '">' + d.label + '</option>';
252
+                }).join('\n')
253
+            );
254
+
255
+            $('#audioOutputSelectWrapper').show();
256
+        }
257
+    })
258
+}
259
+
231
 var connection = null;
260
 var connection = null;
232
 var room = null;
261
 var room = null;
233
 var localTracks = [];
262
 var localTracks = [];

+ 4
- 0
doc/example/index.html Ver arquivo

13
 <body>
13
 <body>
14
     <a href="#" onclick="unload()">Unload</a>
14
     <a href="#" onclick="unload()">Unload</a>
15
     <a href="#" onclick="switchVideo()">switchVideo</a>
15
     <a href="#" onclick="switchVideo()">switchVideo</a>
16
+    <div id="audioOutputSelectWrapper" style="display: none;">
17
+        Change audio output device
18
+        <select id="audioOutputSelect" onchange="changeAudioOutput(this)"></select>
19
+    </div>
16
     <!-- <video id="localVideo" autoplay="true"></video> -->
20
     <!-- <video id="localVideo" autoplay="true"></video> -->
17
     <!--<audio id="localAudio" autoplay="true" muted="true"></audio>-->
21
     <!--<audio id="localAudio" autoplay="true" muted="true"></audio>-->
18
 </body>
22
 </body>

+ 39
- 0
modules/RTC/JitsiTrack.js Ver arquivo

76
         }
76
         }
77
         addMediaStreamInactiveHandler(stream, streamInactiveHandler);
77
         addMediaStreamInactiveHandler(stream, streamInactiveHandler);
78
     }
78
     }
79
+
80
+    RTCUtils.addListener(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED, this.setAudioOutput.bind(this));
79
 }
81
 }
80
 
82
 
81
 /**
83
 /**
310
     return (streamId && trackId) ? (streamId + " " + trackId) : null;
312
     return (streamId && trackId) ? (streamId + " " + trackId) : null;
311
 };
313
 };
312
 
314
 
315
+/**
316
+ * Sets new audio output device for track's DOM elements. Video tracks are
317
+ * ignored.
318
+ * @param {string} audioOutputDeviceId - id of 'audiooutput' device from
319
+ *      navigator.mediaDevices.enumerateDevices(), '' for default device
320
+ * @emits JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED
321
+ * @returns {Promise}
322
+ */
323
+JitsiTrack.prototype.setAudioOutput = function (audioOutputDeviceId) {
324
+    var self = this;
325
+
326
+    if (!RTCUtils.isDeviceChangeAvailable('output')) {
327
+        return Promise.reject(
328
+            new Error('Audio output device change is not supported'));
329
+    }
330
+
331
+    // All audio communication is done through audio tracks, so ignore changing
332
+    // audio output for video tracks at all.
333
+    if (this.isVideoTrack()) {
334
+        return Promise.resolve();
335
+    }
336
+
337
+    return Promise.all(this.containers.map(function(element) {
338
+        return element.setSinkId(audioOutputDeviceId)
339
+            .catch(function (error) {
340
+                logger.error('Failed to change audio output device on element',
341
+                    element, error);
342
+
343
+                    throw error;
344
+            });
345
+    }))
346
+    .then(function () {
347
+        self.eventEmitter.emit(JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED,
348
+            audioOutputDeviceId);
349
+    });
350
+};
351
+
313
 module.exports = JitsiTrack;
352
 module.exports = JitsiTrack;

+ 29
- 6
modules/RTC/RTC.js Ver arquivo

307
 };
307
 };
308
 
308
 
309
 /**
309
 /**
310
- * Returns true if changing the camera / microphone device is supported and
311
- * false if not.
310
+ * Returns true if changing the input (camera / microphone) or output
311
+ * (audio) device is supported and false if not.
312
+ * @params {string} [deviceType] - type of device to change. Default is
313
+ *      undefined or 'input', 'output' - for audio output device change.
314
+ * @returns {boolean} true if available, false otherwise.
315
+ */
316
+RTC.isDeviceChangeAvailable = function (deviceType) {
317
+    return RTCUtils.isDeviceChangeAvailable(deviceType);
318
+};
319
+
320
+/**
321
+ * Returns currently used audio output device id, '' stands for default
322
+ * device
323
+ * @returns {string}
324
+ */
325
+RTC.getAudioOutputDevice = function () {
326
+    return RTCUtils.getAudioOutputDevice();
327
+};
328
+
329
+/**
330
+ * Sets current audio output device.
331
+ * @param {string} deviceId - id of 'audiooutput' device from
332
+ *      navigator.mediaDevices.enumerateDevices()
333
+ * @returns {Promise} - resolves when audio output is changed, is rejected
334
+ *      otherwise
312
  */
335
  */
313
-RTC.isDeviceChangeAvailable = function () {
314
-    return RTCUtils.isDeviceChangeAvailable();
336
+RTC.setAudioOutputDevice = function (deviceId) {
337
+    return RTCUtils.setAudioOutputDevice(deviceId);
315
 };
338
 };
316
 
339
 
317
 /**
340
 /**
318
  * Returns <tt>true<tt/> if given WebRTC MediaStream is considered a valid
341
  * Returns <tt>true<tt/> if given WebRTC MediaStream is considered a valid
319
  * "user" stream which means that it's not a "receive only" stream nor a "mixed"
342
  * "user" stream which means that it's not a "receive only" stream nor a "mixed"
320
  * JVB stream.
343
  * JVB stream.
321
- * 
344
+ *
322
  * Clients that implement Unified Plan, such as Firefox use recvonly
345
  * Clients that implement Unified Plan, such as Firefox use recvonly
323
  * "streams/channels/tracks" for receiving remote stream/tracks, as opposed to
346
  * "streams/channels/tracks" for receiving remote stream/tracks, as opposed to
324
  * Plan B where there are only 3 channels: audio, video and data.
347
  * Plan B where there are only 3 channels: audio, video and data.
325
- * 
348
+ *
326
  * @param stream WebRTC MediaStream instance
349
  * @param stream WebRTC MediaStream instance
327
  * @returns {boolean}
350
  * @returns {boolean}
328
  */
351
  */

+ 94
- 20
modules/RTC/RTCUtils.js Ver arquivo

24
     video: true
24
     video: true
25
 };
25
 };
26
 
26
 
27
+var audioOuputDeviceId = ''; // default device
28
+
29
+var featureDetectionAudioEl = document.createElement('audio');
30
+var isAudioOutputDeviceChangeAvailable =
31
+    typeof featureDetectionAudioEl.setSinkId !== 'undefined';
32
+
27
 var rtcReady = false;
33
 var rtcReady = false;
28
 
34
 
29
 function setResolutionConstraints(constraints, resolution) {
35
 function setResolutionConstraints(constraints, resolution) {
303
                 //add auto devices
309
                 //add auto devices
304
                 devices.unshift(
310
                 devices.unshift(
305
                     createAutoDeviceInfo('audioinput'),
311
                     createAutoDeviceInfo('audioinput'),
306
-                    createAutoDeviceInfo('videoinput')
312
+                    createAutoDeviceInfo('videoinput'),
313
+                    createAutoDeviceInfo('audiooutput')
307
                 );
314
                 );
308
 
315
 
309
                 callback(devices);
316
                 callback(devices);
311
                 console.error('cannot enumerate devices: ', err);
318
                 console.error('cannot enumerate devices: ', err);
312
 
319
 
313
                 // return only auto devices
320
                 // return only auto devices
314
-                callback([createAutoDeviceInfo('audioinput'),
315
-                          createAutoDeviceInfo('videoinput')]);
321
+                callback([
322
+                    createAutoDeviceInfo('audioinput'),
323
+                    createAutoDeviceInfo('videoinput'),
324
+                    createAutoDeviceInfo('audiooutput')
325
+                ]);
316
             });
326
             });
317
         });
327
         });
318
     };
328
     };
330
             return {
340
             return {
331
                 facing: source.facing || null,
341
                 facing: source.facing || null,
332
                 label: source.label,
342
                 label: source.label,
333
-                kind: kind ? kind + 'input': null,
343
+                // theoretically deprecated MediaStreamTrack.getSources should
344
+                // not return 'audiooutput' devices but let's handle it in any
345
+                // case
346
+                kind: kind
347
+                    ? (kind === 'audiooutput' ? kind : kind + 'input')
348
+                    : null,
334
                 deviceId: source.id,
349
                 deviceId: source.id,
335
                 groupId: source.groupId || null
350
                 groupId: source.groupId || null
336
             };
351
             };
339
         //add auto devices
354
         //add auto devices
340
         devices.unshift(
355
         devices.unshift(
341
             createAutoDeviceInfo('audioinput'),
356
             createAutoDeviceInfo('audioinput'),
342
-            createAutoDeviceInfo('videoinput')
357
+            createAutoDeviceInfo('videoinput'),
358
+            createAutoDeviceInfo('audiooutput')
343
         );
359
         );
344
         callback(devices);
360
         callback(devices);
345
     });
361
     });
441
     return res;
457
     return res;
442
 }
458
 }
443
 
459
 
460
+/**
461
+ * Wraps original attachMediaStream function to set current audio output device
462
+ * if this is supported.
463
+ * @param {Function} origAttachMediaStream
464
+ * @returns {Function}
465
+ */
466
+function wrapAttachMediaStream(origAttachMediaStream) {
467
+    return function(element, stream) {
468
+        var res = origAttachMediaStream.apply(RTCUtils, arguments);
469
+
470
+        if (RTCUtils.isDeviceChangeAvailable('output') &&
471
+            stream.getAudioTracks && stream.getAudioTracks().length) {
472
+            element.setSinkId(RTCUtils.getAudioOutputDevice())
473
+                .catch(function (ex) {
474
+                    logger.error('Failed to set audio output on element',
475
+                        element, ex);
476
+                });
477
+        }
478
+
479
+        return res;
480
+    }
481
+}
482
+
444
 //Options parameter is to pass config options. Currently uses only "useIPv6".
483
 //Options parameter is to pass config options. Currently uses only "useIPv6".
445
 var RTCUtils = {
484
 var RTCUtils = {
446
     init: function (options) {
485
     init: function (options) {
461
                     navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
500
                     navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
462
                 );
501
                 );
463
                 this.pc_constraints = {};
502
                 this.pc_constraints = {};
464
-                this.attachMediaStream = function (element, stream) {
503
+                this.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
465
                     //  srcObject is being standardized and FF will eventually
504
                     //  srcObject is being standardized and FF will eventually
466
                     //  support that unprefixed. FF also supports the
505
                     //  support that unprefixed. FF also supports the
467
                     //  "element.src = URL.createObjectURL(...)" combo, but that
506
                     //  "element.src = URL.createObjectURL(...)" combo, but that
475
                     element.play();
514
                     element.play();
476
 
515
 
477
                     return element;
516
                     return element;
478
-                };
517
+                });
479
                 this.getStreamID = function (stream) {
518
                 this.getStreamID = function (stream) {
480
                     var id = stream.id;
519
                     var id = stream.id;
481
                     if (!id) {
520
                     if (!id) {
510
                     this.getUserMedia = getUserMedia;
549
                     this.getUserMedia = getUserMedia;
511
                     this.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
550
                     this.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
512
                 }
551
                 }
513
-                this.attachMediaStream = function (element, stream) {
552
+                this.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
514
 
553
 
515
                     // saves the created url for the stream, so we can reuse it
554
                     // saves the created url for the stream, so we can reuse it
516
                     // and not keep creating urls
555
                     // and not keep creating urls
522
                     element.src = stream.jitsiObjectURL;
561
                     element.src = stream.jitsiObjectURL;
523
 
562
 
524
                     return element;
563
                     return element;
525
-                };
564
+                });
526
                 this.getStreamID = function (stream) {
565
                 this.getStreamID = function (stream) {
527
                     // streams from FF endpoints have the characters '{' and '}'
566
                     // streams from FF endpoints have the characters '{' and '}'
528
                     // that make jQuery choke.
567
                     // that make jQuery choke.
573
                     self.peerconnection = RTCPeerConnection;
612
                     self.peerconnection = RTCPeerConnection;
574
                     self.getUserMedia = window.getUserMedia;
613
                     self.getUserMedia = window.getUserMedia;
575
                     self.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
614
                     self.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
576
-                    self.attachMediaStream = function (element, stream) {
615
+                    self.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
577
 
616
 
578
                         if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
617
                         if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
579
                             return;
618
                             return;
585
                         }
624
                         }
586
 
625
 
587
                         return attachMediaStream(element, stream);
626
                         return attachMediaStream(element, stream);
588
-                    };
627
+                    });
589
                     self.getStreamID = function (stream) {
628
                     self.getStreamID = function (stream) {
590
                         return SDPUtil.filter_special_chars(stream.label);
629
                         return SDPUtil.filter_special_chars(stream.label);
591
                     };
630
                     };
814
         return (MediaStreamTrack && MediaStreamTrack.getSources)? true : false;
853
         return (MediaStreamTrack && MediaStreamTrack.getSources)? true : false;
815
     },
854
     },
816
     /**
855
     /**
817
-     * Returns true if changing the camera / microphone device is supported and
818
-     * false if not.
856
+     * Returns true if changing the input (camera / microphone) or output
857
+     * (audio) device is supported and false if not.
858
+     * @params {string} [deviceType] - type of device to change. Default is
859
+     *      undefined or 'input', 'output' - for audio output device change.
860
+     * @returns {boolean} true if available, false otherwise.
819
      */
861
      */
820
-    isDeviceChangeAvailable: function () {
821
-        return RTCBrowserType.isChrome() ||
822
-            RTCBrowserType.isFirefox() ||
823
-            RTCBrowserType.isOpera() ||
824
-            RTCBrowserType.isTemasysPluginUsed() ||
825
-            RTCBrowserType.isNWJS();
862
+    isDeviceChangeAvailable: function (deviceType) {
863
+        return deviceType === 'output' || deviceType === 'audiooutput'
864
+            ? isAudioOutputDeviceChangeAvailable
865
+            : RTCBrowserType.isChrome() ||
866
+                RTCBrowserType.isFirefox() ||
867
+                RTCBrowserType.isOpera() ||
868
+                RTCBrowserType.isTemasysPluginUsed()||
869
+                RTCBrowserType.isNWJS();
826
     },
870
     },
827
     /**
871
     /**
828
      * A method to handle stopping of the stream.
872
      * A method to handle stopping of the stream.
853
      */
897
      */
854
     isDesktopSharingEnabled: function () {
898
     isDesktopSharingEnabled: function () {
855
         return screenObtainer.isSupported();
899
         return screenObtainer.isSupported();
856
-    }
900
+    },
901
+    /**
902
+     * Sets current audio output device.
903
+     * @param {string} deviceId - id of 'audiooutput' device from
904
+     *      navigator.mediaDevices.enumerateDevices(), '' for default device
905
+     * @returns {Promise} - resolves when audio output is changed, is rejected
906
+     *      otherwise
907
+     */
908
+    setAudioOutputDevice: function (deviceId) {
909
+        if (!this.isDeviceChangeAvailable('output')) {
910
+            Promise.reject(
911
+                new Error('Audio output device change is not supported'));
912
+        }
857
 
913
 
914
+        return featureDetectionAudioEl.setSinkId(deviceId)
915
+            .then(function() {
916
+                audioOuputDeviceId = deviceId;
917
+
918
+                logger.log('Audio output device set to ' + deviceId);
919
+
920
+                eventEmitter.emit(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
921
+                    deviceId);
922
+            });
923
+    },
924
+    /**
925
+     * Returns currently used audio output device id, '' stands for default
926
+     * device
927
+     * @returns {string}
928
+     */
929
+    getAudioOutputDevice: function () {
930
+        return audioOuputDeviceId;
931
+    }
858
 };
932
 };
859
 
933
 
860
 module.exports = RTCUtils;
934
 module.exports = RTCUtils;

+ 2
- 1
service/RTC/RTCEvents.js Ver arquivo

6
     LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
6
     LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
7
     AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed",
7
     AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed",
8
     FAKE_VIDEO_TRACK_CREATED: "rtc.fake_video_track_created",
8
     FAKE_VIDEO_TRACK_CREATED: "rtc.fake_video_track_created",
9
-    TRACK_ATTACHED: "rtc.track_attached"
9
+    TRACK_ATTACHED: "rtc.track_attached",
10
+    AUDIO_OUTPUT_DEVICE_CHANGED: "rtc.audio_output_device_changed"
10
 };
11
 };
11
 
12
 
12
 module.exports = RTCEvents;
13
 module.exports = RTCEvents;

Carregando…
Cancelar
Salvar