an html webcam playerยค
i like having control of my software and knowing what is going on. recently i tried tried compositing my stream using more native html features rather than configuring obs. i really liked how it felt and wanted to explore video capabilities more.
this approach, and it extended to a screen share, makes it possible to quickly remix video streams.
%%
our `video` is represented as a `form` to give easier access form data in the javascript.
{{one.data}}
one=\
```html
<form class="webcam" name="one">
<label for="camera">cameras</label>
<select name="camera"></select>
<select name="direction">
<option value="user">forward</option>
<option value="environment">back</option>
</select>
<br/>
<video autoplay=""></video>
</form>
```
we make two videos to demonstrate this works for multiple video streams, and we could add a screen share.
{{two.data}}
two=\
```html
<form class="webcam" name="two">
<label for="camera">cameras</label>
<select name="camera"></select>
<select name="direction">
<option value="user">forward</option>
<option value="environment">back</option>
</select>
<br/>
<video autoplay=""></video>
</form>
```
our javascript needs update the avaiable cameras dynamically.
user intervention is needed to give access to a camera.
updateVideoSources=\
```js
function updateVideoSources(device, camera){
if (device.kind == "videoinput") {
let option = document.createElement('option');
if (!camera.querySelector(`[value="${device.deviceId}"]`)){
option.setAttribute("value", device.deviceId);
option.insertAdjacentHTML( 'beforeend', device.label || "unknown" );
camera.appendChild(option);
}
};
}
```
we iterate through any elements on the page and update the media devices available while adding hooks for updating sources.
webcams=\
```js
Array.from(document.getElementsByClassName("webcam")).forEach(
(form) => {
let [video] = form.getElementsByTagName("video");
let camera = form.elements.camera;
let direction = form.elements.direction;
if (!navigator.mediaDevices?.enumerateDevices) {
console.log("enumerateDevices() not supported.");
} else {
navigator.mediaDevices
.enumerateDevices({video: true, audio: false})
.then((devices) => {
devices.forEach((device) => {updateVideoSources(device, camera)});
})
.catch((err) => {console.error(`${err.name}: ${err.message}`);});
}
function change(){
console.log("changing source");
navigator.mediaDevices.getUserMedia({
video: {
facingMode: form.elements.direction.value,
deviceId: {
exact: form.elements.camera.value
}
}, audio: false
})
.then(stream => video.srcObject = stream)
.catch(console.log);
}
change();
camera.addEventListener("change", change);
direction.addEventListener("change", change);
}
)
```
<script>{{updateVideoSources}}
{{webcams}}</script>