Change video resource for an ongoing video, change the device id

818 Views Asked by At

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.

1

There are 1 best solutions below

0
Jivan On BEST ANSWER

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.