Temasys / SkylinkJS

SkylinkJS Javascript WebRTC SDK
http://skylink.io/web
Other
275 stars 57 forks source link

Change video/audio source #243

Closed xvs32x closed 7 years ago

xvs32x commented 8 years ago

Hello. How i can change camera and microphone with API. Documentation is empty. Thanks

oooookk7 commented 8 years ago

Hi @xvs32x, you can change the camera and microphone source by passing the sourceId in the optional parameter for both audio and video objects, which is available in joinRoom(), getUserMedia() and sendStream() methods.

1. Obtaining the list of Media sources

To obtain the list, you may use navigator.mediaDevices.enumerateDevices API:

navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
  devices.forEach(function(device) {
    console.log(device.kind + ": " + device.label + " id = " + device.deviceId);
  });
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});

(Demo code from Mozilla MDN).

For browsers like Safari 9.1 < and IE that do not support Promise API yet, it is recommended that you use MediaStreamTrack.getSources API only for these browsers to use this:

MediaStreamTrack.getSources(function (mt) {
   for (var i = 0; i < mt.length; i++) {
     var device = mt[i];
     console.log(device.kind + ": " + device.label + " id = " + device.id);
   }
});

2. Passing the selected Media source to Skylink

After you have obtained the the values, you may select the source with this:

var skylink = new Skylink();
skylink.joinRoom({
  audio: { optional: [{ sourceId: audioDevice.deviceId || audioDevice.id }] },
  video: { optional: [{ sourceId: videoDevice.deviceId || videoDevice.id }] }
});

Note that that for this is only supported for Firefox 39 (and above) and Chrome 45 (and above). But you shouldn't have any issues if you are following our supported browsers list which starts with Firefox version 40 and Chrome 45.

(See supported list here)

xvs32x commented 8 years ago

@letchoo, many thanks for answer! :) But I have a trouble. Your solution work in Chrome only, firefox(47) display standart system dialog with input devices list and ignore my sourceId :(

oooookk7 commented 8 years ago

Hi @xvs32x, this is likely a Firefox issue since they have a prompt to always display the dialog with the input devices list regardless of providing the sourceId in the optional constraints.

There seems to be a related bugzilla ticket raised on their end which you may check it out. I suggest raising this issue with the Firefox team to resolve this.

xvs32x commented 8 years ago

Hi @letchoo. Thanks for your answer. :) Display dialog box is not terrible for me. The problem is that the device I chose is not selected in this dialog box. For example, if I send the sourceID with the name of "Camera 2", in the dialog box I still see the "camera 1" (as the first in the list). I found working example: https://webrtc.github.io/samples/src/content/devices/input-output/ In this example standart dialog displayed "Camera 2" if I selected "Camera 2". If I understand correctly is not worked in Skylink because "navigator.mediaDevices.getUserMedia" overwritten... https://github.com/Temasys/SkylinkJS/blob/master/publish/skylink.complete.js#L8278 But i'm not sure... :)

oooookk7 commented 8 years ago

Hi @xvs32x, yes you are right. We should be able to set the source ID regardless.

Meanwhile, if you require a quick fix before we implement the proper fixes in the future releases, here's what you can do:

1. Separately referencing the dependencies and SkylinkJS code

Instead of using skylink.complete.js directly:

<script src="https://cdn.temasys.com.sg/skylink/skylinkjs/0.6.13/skylink.complete.js"></script>

Reference the SkylinkJS codes and it's dependencies separately:

<script src="https://cdn.socket.io/socket.io-1.4.4.js"></script>
<script src="adapter.screenshare.js"></script>
<script src="https://cdn.temasys.com.sg/skylink/skylinkjs/0.6.13/skylink.debug.js"></script>

The adapter.screenshare.js source code can be obtained from here. Copy and paste the source code and save it to a file named adapter.screenshare.js.

2. Modify the adapter.screenshare.js file

There will be two changes that you will have to make.

For the first modification (which is in this line):

  if (window.navigator.mozGetUserMedia) {
    baseGetUserMedia = window.navigator.getUserMedia;
    . . .

Replace the baseGetUserMedia = window.navigator.getUserMedia; line with the following changes:

  if (window.navigator.mozGetUserMedia) {
   // Modifications start here
    if (window.navigator.mediaDevices &&
        typeof window.navigator.mediaDevices.getUserMedia === 'function') {
      var filterSourceId = function (constraintItem) {
        if (constraintItem && typeof constraintItem === 'object') {
          if (Array.isArray(constraintItem.optional)) {
            var updatedConstItem = clone(constraintItem);
            for (var i = 0; i < updatedConstItem.optional.length; i++) {
              if (updatedConstItem.optional[i] && typeof updatedConstItem.optional[i] === 'object'
                && updatedConstItem.optional[i].hasOwnProperty('sourceId')
                && updatedConstItem.optional[i].sourceId) {
                updatedConstItem.deviceId = { exact: updatedConstItem.optional[i].sourceId };
                updatedConstItem.optional.splice(i, 1);
                i--;
              }
            }
            return updatedConstItem;
          }
          return constraintItem;
        }
        return !!constraintItem;
      };
      baseGetUserMedia = function (constraints, successCb, failureCb) {
        var updatedConstraints = {
          audio: filterSourceId(constraints.audio),
          video: filterSourceId(constraints.video)
        };
       window.navigator.mediaDevices.getUserMedia(updatedConstraints).
         then(successCb).catch(failureCb);
      };
    } else {
      baseGetUserMedia = window.navigator.getUserMedia;
    }

For the second modification (which is in this line):

Comment out the modifications to navigator.mediaDevices.getUserMedia:

/*navigator.mediaDevices.getUserMedia = function(constraints) {
      return new Promise(function(resolve, reject) {
        window.getUserMedia(constraints, resolve, reject);
      });
   };*/

Save the adapter.screenshare.js file. After that, the changes should work with the examples given in the comment above.

oooookk7 commented 7 years ago

Hi @xvs32x, your issue should be resolved with the AdapterJS 0.14.0 release.

One thing to note though, when using the navigator.mediaDevices.getUserMedia method, it doesn't prompt the user to install the Firefox add-on when it's not available, but you can use the navigator.getUserMedia API instead since internally it's been polyfilled with the webrtc/adapter (AdapterJS uses 2.0.3 version) so your sourceId: { exact: xxx } should work.

oooookk7 commented 7 years ago

I'm closing this ticket since the issue has been resolved on AdapterJS end. If the issue persist, please open the ticket on AdapterJS github.