|
@@ -24,6 +24,10 @@ var devices = {
|
24
|
24
|
video: true
|
25
|
25
|
};
|
26
|
26
|
|
|
27
|
+var audioOuputDeviceId = ''; // default device
|
|
28
|
+
|
|
29
|
+var featureDetectionVideoEl = document.createElement('video');
|
|
30
|
+
|
27
|
31
|
var rtcReady = false;
|
28
|
32
|
|
29
|
33
|
function setResolutionConstraints(constraints, resolution) {
|
|
@@ -303,7 +307,8 @@ function wrapEnumerateDevices(enumerateDevices) {
|
303
|
307
|
//add auto devices
|
304
|
308
|
devices.unshift(
|
305
|
309
|
createAutoDeviceInfo('audioinput'),
|
306
|
|
- createAutoDeviceInfo('videoinput')
|
|
310
|
+ createAutoDeviceInfo('videoinput'),
|
|
311
|
+ createAutoDeviceInfo('audiooutput')
|
307
|
312
|
);
|
308
|
313
|
|
309
|
314
|
callback(devices);
|
|
@@ -311,8 +316,11 @@ function wrapEnumerateDevices(enumerateDevices) {
|
311
|
316
|
console.error('cannot enumerate devices: ', err);
|
312
|
317
|
|
313
|
318
|
// return only auto devices
|
314
|
|
- callback([createAutoDeviceInfo('audioinput'),
|
315
|
|
- createAutoDeviceInfo('videoinput')]);
|
|
319
|
+ callback([
|
|
320
|
+ createAutoDeviceInfo('audioinput'),
|
|
321
|
+ createAutoDeviceInfo('videoinput'),
|
|
322
|
+ createAutoDeviceInfo('audiooutput')
|
|
323
|
+ ]);
|
316
|
324
|
});
|
317
|
325
|
});
|
318
|
326
|
};
|
|
@@ -341,7 +349,8 @@ function enumerateDevicesThroughMediaStreamTrack (callback) {
|
341
|
349
|
//add auto devices
|
342
|
350
|
devices.unshift(
|
343
|
351
|
createAutoDeviceInfo('audioinput'),
|
344
|
|
- createAutoDeviceInfo('videoinput')
|
|
352
|
+ createAutoDeviceInfo('videoinput'),
|
|
353
|
+ createAutoDeviceInfo('audiooutput')
|
345
|
354
|
);
|
346
|
355
|
callback(devices);
|
347
|
356
|
});
|
|
@@ -443,6 +452,28 @@ function handleLocalStream(streams, resolution) {
|
443
|
452
|
return res;
|
444
|
453
|
}
|
445
|
454
|
|
|
455
|
+/**
|
|
456
|
+ * Wraps original attachMediaStream function to set current audio output device
|
|
457
|
+ * if this is supported.
|
|
458
|
+ * @param {Function} origAttachMediaStream
|
|
459
|
+ * @returns {Function}
|
|
460
|
+ */
|
|
461
|
+function wrapAttachMediaStream(origAttachMediaStream) {
|
|
462
|
+ return function(element, stream) {
|
|
463
|
+ var res = origAttachMediaStream.apply(RTCUtils, arguments);
|
|
464
|
+
|
|
465
|
+ if (RTCUtils.isAudioOutputDeviceChangeAvailable()) {
|
|
466
|
+ element.setSinkId(RTCUtils.getAudioOutputDevice())
|
|
467
|
+ .catch(function (ex) {
|
|
468
|
+ console.error('Failed to set audio output on element',
|
|
469
|
+ element, ex);
|
|
470
|
+ });
|
|
471
|
+ }
|
|
472
|
+
|
|
473
|
+ return res;
|
|
474
|
+ }
|
|
475
|
+}
|
|
476
|
+
|
446
|
477
|
//Options parameter is to pass config options. Currently uses only "useIPv6".
|
447
|
478
|
var RTCUtils = {
|
448
|
479
|
init: function (options) {
|
|
@@ -463,7 +494,7 @@ var RTCUtils = {
|
463
|
494
|
navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
|
464
|
495
|
);
|
465
|
496
|
this.pc_constraints = {};
|
466
|
|
- this.attachMediaStream = function (element, stream) {
|
|
497
|
+ this.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
|
467
|
498
|
// srcObject is being standardized and FF will eventually
|
468
|
499
|
// support that unprefixed. FF also supports the
|
469
|
500
|
// "element.src = URL.createObjectURL(...)" combo, but that
|
|
@@ -477,7 +508,7 @@ var RTCUtils = {
|
477
|
508
|
element.play();
|
478
|
509
|
|
479
|
510
|
return element;
|
480
|
|
- };
|
|
511
|
+ });
|
481
|
512
|
this.getStreamID = function (stream) {
|
482
|
513
|
var id = stream.id;
|
483
|
514
|
if (!id) {
|
|
@@ -512,7 +543,7 @@ var RTCUtils = {
|
512
|
543
|
this.getUserMedia = getUserMedia;
|
513
|
544
|
this.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
|
514
|
545
|
}
|
515
|
|
- this.attachMediaStream = function (element, stream) {
|
|
546
|
+ this.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
|
516
|
547
|
|
517
|
548
|
// saves the created url for the stream, so we can reuse it
|
518
|
549
|
// and not keep creating urls
|
|
@@ -524,7 +555,7 @@ var RTCUtils = {
|
524
|
555
|
element.src = stream.jitsiObjectURL;
|
525
|
556
|
|
526
|
557
|
return element;
|
527
|
|
- };
|
|
558
|
+ });
|
528
|
559
|
this.getStreamID = function (stream) {
|
529
|
560
|
// streams from FF endpoints have the characters '{' and '}'
|
530
|
561
|
// that make jQuery choke.
|
|
@@ -575,7 +606,7 @@ var RTCUtils = {
|
575
|
606
|
self.peerconnection = RTCPeerConnection;
|
576
|
607
|
self.getUserMedia = window.getUserMedia;
|
577
|
608
|
self.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
|
578
|
|
- self.attachMediaStream = function (element, stream) {
|
|
609
|
+ self.attachMediaStream = wrapAttachMediaStream(function (element, stream) {
|
579
|
610
|
|
580
|
611
|
if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
|
581
|
612
|
return;
|
|
@@ -587,7 +618,7 @@ var RTCUtils = {
|
587
|
618
|
}
|
588
|
619
|
|
589
|
620
|
return attachMediaStream(element, stream);
|
590
|
|
- };
|
|
621
|
+ });
|
591
|
622
|
self.getStreamID = function (stream) {
|
592
|
623
|
return SDPUtil.filter_special_chars(stream.label);
|
593
|
624
|
};
|
|
@@ -825,6 +856,13 @@ var RTCUtils = {
|
825
|
856
|
RTCBrowserType.isOpera() ||
|
826
|
857
|
RTCBrowserType.isTemasysPluginUsed();
|
827
|
858
|
},
|
|
859
|
+ /**
|
|
860
|
+ * Returns true if changing the audio output of media elements is supported
|
|
861
|
+ * and false if not.
|
|
862
|
+ */
|
|
863
|
+ isAudioOutputDeviceChangeAvailable: function () {
|
|
864
|
+ return typeof featureDetectionVideoEl.setSinkId !== 'undefined';
|
|
865
|
+ },
|
828
|
866
|
/**
|
829
|
867
|
* A method to handle stopping of the stream.
|
830
|
868
|
* One point to handle the differences in various implementations.
|
|
@@ -854,8 +892,36 @@ var RTCUtils = {
|
854
|
892
|
*/
|
855
|
893
|
isDesktopSharingEnabled: function () {
|
856
|
894
|
return screenObtainer.isSupported();
|
857
|
|
- }
|
|
895
|
+ },
|
|
896
|
+ /**
|
|
897
|
+ * Sets current audio output device.
|
|
898
|
+ * @param {string} deviceId - id of 'audiooutput' device from
|
|
899
|
+ * navigator.mediaDevices.enumerateDevices()
|
|
900
|
+ * @returns {Promise} - resolves when audio output is changed, is rejected
|
|
901
|
+ * otherwise
|
|
902
|
+ */
|
|
903
|
+ setAudioOutputDevice: function (deviceId) {
|
|
904
|
+ if (!this.isAudioOutputDeviceChangeAvailable()) {
|
|
905
|
+ Promise.reject(
|
|
906
|
+ new Error('Audio output device change is not supported'));
|
|
907
|
+ }
|
|
908
|
+
|
|
909
|
+ return featureDetectionVideoEl.setSinkId(deviceId)
|
|
910
|
+ .then(function() {
|
|
911
|
+ audioOuputDeviceId = deviceId;
|
858
|
912
|
|
|
913
|
+ eventEmitter.emit(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
|
|
914
|
+ deviceId);
|
|
915
|
+ });
|
|
916
|
+ },
|
|
917
|
+ /**
|
|
918
|
+ * Returns currently used audio output device id, '' stands for default
|
|
919
|
+ * device
|
|
920
|
+ * @returns {string}
|
|
921
|
+ */
|
|
922
|
+ getAudioOutputDevice: function () {
|
|
923
|
+ return audioOuputDeviceId;
|
|
924
|
+ }
|
859
|
925
|
};
|
860
|
926
|
|
861
|
927
|
module.exports = RTCUtils;
|