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.
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.