0

Usecase: Change the available video src by selecting from a dropdown. Also the video is on.

I want to change the video source for an ongoing video stream started by navigator.mediaDevices.getUserMedia. I tried, closing the existing tracks, making the srcObject null and starting it again by passing new deviceId to the constraints.

JS

$(document).ready(function() {
  var videoDeviceList = [];
  var selectedDeviceId = "";
  if (!navigator.mediaDevices.enumerateDevices) {
    console.log("enumerateDevices() not supported.");
  } else {
    // List cameras and microphones.
    navigator.mediaDevices.enumerateDevices()
      .then((devices) => {
        devices.forEach((device) => {
          if (device.kind === 'videoinput') {
            selectedDeviceId = selectedDeviceId === "" ? device.deviceId : selectedDeviceId;
            videoDeviceList.push({
              deviceId: device.deviceId,
              deviceLabel: device.label
            });
          }
        });
        videoDeviceList.forEach((item) => {
          $("#videoList").append("<option id='" + item.deviceId + "'>" + item.deviceLabel + "</option>");
        });

      })
      .catch((err) => {
        console.error(`${err.name}: ${err.message}`);
      });

  }
  $("#btn").click(function() {
    $(this).attr("disabled", true);
    startOrUpdateCameraFeed();
  });
  $('#videoList').change(function() {
    selectedDeviceId = $(this).find('option:selected').attr('id');
    console.log("selectedDeviceId: ", selectedDeviceId);
    startOrUpdateCameraFeed();
  });

  function startOrUpdateCameraFeed() {
    const video = document.getElementById('video');
    // to shut all the open or running video streams
    if (window.videoStream !== null && window.videoStream !== undefined && window.videoStream.length > 0 &&
      video) {
      window.videoStream.forEach((stream) => {
        if (stream) {
          if ('getTracks' in stream && stream.getTracks().length > 0) {
            stream.getTracks().forEach((track) => stopTrack(track));
          }
          if ('getVideoTracks' in stream && stream.getVideoTracks().length > 0) {
            stream.getVideoTracks().forEach((track) => stopTrack(track));
          }
          if ('getAudioTracks' in stream && stream.getAudioTracks().length > 0) {
            stream.getAudioTracks().forEach((track) => stopTrack(track));
          }
        }
      });
      window.videoStream = [];
      video.srcObject = null;
    }
    // start a new feed with changed camera device id
    var constraints = {
      deviceId: {
        exact: selectedDeviceId
      }
    };
    console.log("constraints: ", constraints);
    navigator.mediaDevices
      .getUserMedia({
        video: true,
        video: {
          width: {
            ideal: 4096,
            max: 4096
          },
          height: {
            ideal: 2160,
            max: 2160
          },
          deviceId: constraints,
        }
      }).then(function(selfStream) {
        if (window.videoStream === null || window.videoStream === undefined) {
          window.videoStream = [];
        }
        if (video) {
          console.log('called video: ', window.videoStream);
          video.srcObject = selfStream;
          window.videoStream.push(selfStream);
        }
      })
      .catch(function(err0r) {
        console.log('Something went wrong regular before video!', err0r);
      });
  }
  function stopTrack (track){
    if (track && 'stop' in track) {
      track.stop();
    }
  };
});

HTML && CSS

<b>Please give permissions for the site to view the camera list. </b><br/><br/>
Change the camera source:
<select id="videoList">
  <option disabled>Select</option>
</select>
<div id="videoContainer">
  <video id="video" height="100%" width="100%" autoplay></video>
</div>

<button id="btn">
  Start initial feed
</button>

video {
  height: 100%;
  width:100%;
  border: 1px solid red;
}
#videoContainer {
  height: 150px;
  width: 250px;
}

Please check the fiddle link for the question. https://jsfiddle.net/jivansupe/576qjcn9/1/

The video element shows new device id but the source does not change. thanks in advance.

Jivan
  • 520
  • 1
  • 5
  • 14
  • [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) -> Add a [mcve] – Andreas Oct 21 '22 at 11:55
  • Help me understand @Andreas how should I attach 2 camera devices to the question so you can show the way to switch between resources. I think the question is self explanatory. – Jivan Oct 21 '22 at 12:14
  • No it is not, because those two "snippets" on their own don't tell the whole story (when/how are they executed, ...). We need a [mcve] so we can reproduce(*) the problem. If that [mcve] would need external resources like a webcam than just assume we have those at hand. Add the relevant markup and all scripts that are directly related to the problem. – Andreas Oct 21 '22 at 12:29
  • @Andreas I have edited the question and added the fiddle for the reference. It has the exact scenario which I am trying to achieve. thanks. – Jivan Oct 27 '22 at 08:37
  • The [mcve] has to be _in the question itself_ and not only on an external resource (that might break in the future or is not accessible for whatever reason). That said... Your fiddle works. – Andreas Oct 27 '22 at 08:45
  • What do you need more? @Andreas – Jivan Oct 27 '22 at 10:07

1 Answers1

0

I had made a mistake with passing the new deviceId.

Fix:

var constraints = {
  exact: selectedDeviceId
};

Updated code:

$(document).ready(function() {
  var videoDeviceList = [];
  var selectedDeviceId = "";
  if (!navigator.mediaDevices.enumerateDevices) {
    console.log("enumerateDevices() not supported.");
  } else {
    // List cameras and microphones.
    navigator.mediaDevices.enumerateDevices()
      .then((devices) => {
        devices.forEach((device) => {
          if (device.kind === 'videoinput') {
            selectedDeviceId = selectedDeviceId === "" ? device.deviceId : selectedDeviceId;
            videoDeviceList.push({
              deviceId: device.deviceId,
              deviceLabel: device.label
            });
          }
        });
        videoDeviceList.forEach((item) => {
          $("#videoList").append("<option id='" + item.deviceId + "'>" + item.deviceLabel + "</option>");
        });

      })
      .catch((err) => {
        console.error(`${err.name}: ${err.message}`);
      });

  }
  $("#btn").click(function() {
    $(this).attr("disabled", true);
    startOrUpdateCameraFeed();
  });
  $('#videoList').change(function() {
    selectedDeviceId = $(this).find('option:selected').attr('id');
    console.log("selectedDeviceId: ", selectedDeviceId);
    startOrUpdateCameraFeed();
  });

  function startOrUpdateCameraFeed() {
    const video = document.getElementById('video');
    // to shut all the open or running video streams
    if (window.videoStream !== null && window.videoStream !== undefined && window.videoStream.length > 0 &&
      video) {
      window.videoStream.forEach((stream) => {
        if (stream) {
          if ('getTracks' in stream && stream.getTracks().length > 0) {
            stream.getTracks().forEach((track) => stopTrack(track));
          }
          if ('getVideoTracks' in stream && stream.getVideoTracks().length > 0) {
            stream.getVideoTracks().forEach((track) => stopTrack(track));
          }
          if ('getAudioTracks' in stream && stream.getAudioTracks().length > 0) {
            stream.getAudioTracks().forEach((track) => stopTrack(track));
          }
        }
      });
      window.videoStream = [];
      video.srcObject = null;
    }
    // start a new feed with changed camera device id
    // BELOW IS THE CODE CHANGE
    var constraints = {
      exact: selectedDeviceId
    };
    console.log("constraints: ", constraints);
    navigator.mediaDevices
      .getUserMedia({
        video: true,
        video: {
          width: {
            ideal: 4096,
            max: 4096
          },
          height: {
            ideal: 2160,
            max: 2160
          },
          deviceId: constraints,
        }
      }).then(function(selfStream) {
        if (window.videoStream === null || window.videoStream === undefined) {
          window.videoStream = [];
        }
        if (video) {
          console.log('called video: ', window.videoStream);
          video.srcObject = selfStream;
          window.videoStream.push(selfStream);
        }
      })
      .catch(function(err0r) {
        console.log('Something went wrong regular before video!', err0r);
      });
  }
  function stopTrack (track){
    if (track && 'stop' in track) {
      track.stop();
    }
  };
});

thanks.

Jivan
  • 520
  • 1
  • 5
  • 14