Yusril Maulidan Raji
Yusril Maulidan Raji

Reputation: 1932

Assign the value of MediaDevices.enumerateDevices() into global variable in JavaScript

I have a question about the sequence of JavaScript. Let me show you my code first:

Here is my HTML:

<video id="video" width="320" height="320" autoplay></video><br>
<button id="snap">Snap Photo</button><br>
<canvas id="canvas" width="320" height="320"></canvas>
<p id="pngHolder"></p>

And here is my JavaScript:

<script>
var Id;
//List cameras and microphones.
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
    console.log("enumerateDevices() not supported.");
}
navigator.mediaDevices.enumerateDevices()
.then(function (devices) {
    devices.forEach(function (device) {
        if (device.kind == "videoinput" && device.label.indexOf('back') >= 0) {
            Id = device.deviceId;
            alert("ID 1 : " + Id);
        }
    });
})
.catch(function (err) {
    console.log(err.name + ": " + err.message);
});

// Put event listeners into place
window.addEventListener("DOMContentLoaded", function () {
    // Grab elements, create settings, etc.
    alert("ID 2 : "+ Id);
    var canvas = document.getElementById("canvas"),
        videoObj = {
            video: {
                optional: [{ deviceId: Id }]
            }
        },
        context = canvas.getContext("2d"),
        video = document.getElementById("video"),
        errBack = function (error) {
            console.log("Video capture error: ", error.code);
        };

    // Trigger photo take
    document.getElementById("snap").addEventListener("click", function () {
        context.drawImage(video, 0, 0, 640, 480);
        // Get the image
        var image = convertCanvasToImage(canvas);
        // Actions
        document.getElementById("pngHolder").appendChild(image);
        // Converts canvas to an image
        function convertCanvasToImage(canvas) {
            var image = new Image();
            image.src = canvas.toDataURL("image/png");
            return image;
        }
    });

    //alert("ID 2 : " + Id);
    // Put video listeners into place
    if (navigator.getUserMedia) { // Standard
        navigator.getUserMedia(videoObj, function (stream) {
            video.src = stream;
            video.play();
        }, errBack);
    } else if (navigator.webkitGetUserMedia) { // WebKit-prefixed
        navigator.webkitGetUserMedia(videoObj, function (stream) {
            video.src = window.webkitURL.createObjectURL(stream);
            video.play();
        }, errBack);
    } else if (navigator.mozGetUserMedia) { // Firefox-prefixed
        navigator.mozGetUserMedia(videoObj, function (stream) {
            video.src = window.URL.createObjectURL(stream);
            video.play();
        }, errBack);
    }
}, false);

I want to insert the value of device.deviceId into variable Id that I define on the first of my JavaScript row. It is still a success (it is shown by the alert("ID 1 : " + Id);). But when I try to put it into optional: [{ deviceId: Id }], the Id doesn't have any value.

And also, when I try to run it with browser, I have found that the alert("ID 2 : " + Id); is shown first instead of alert("ID 1 : " + Id);. In fact, I have already put the alert("ID 1 : " + Id); first. I think that's why the variable is still empty.

My question is how can I insert the device.deviceId value to optional: [{ deviceId: Id }] ?

Upvotes: 3

Views: 7379

Answers (2)

jib
jib

Reputation: 42430

navigator.mediaDevices.enumerateDevices and DOMContentLoaded are racing, and the latter is winning, so you're using Id before it's set.

To solve this use a temporary haveId promise:

var haveId = navigator.mediaDevices.enumerateDevices()
  .then(devices => devices.find(d => d.kind == "videoinput" &&
                                d.label.indexOf("back") >= 0));

// Put event listeners into place
window.addEventListener("DOMContentLoaded", function () {
  haveId.then(id => {
    // Grab elements, create settings, etc.
    alert("ID 2 : "+ id);
    var canvas = document.getElementById("canvas"),
    videoObj = { video: { deviceId: id } }, // <-- adapter.js constraints

Promise-chains create dependencies, and this way the getUserMedia code wont proceed until both things have happened.

The second problem is you're mixing new and outdated Chrome-specific constraints. Either use adapter.js until Chrome catches up, or in a pinch, use the Chrome-only sourceId (but that wont work in any other browser).

Upvotes: 5

Philipp Hancke
Philipp Hancke

Reputation: 17275

navigator.mediaDevices.enumerateDevices() is asynchronous. It returns a promise (think callback, but fancier).

You should trigger your call to getUserMedia from there or wait for both DOMContentLoaded and enumerateDevices and then execute getUserMedia.

Upvotes: 1

Related Questions