p5.js - Getting an array of all available video devices (webcams) with id

858 Views Asked by At

I would like to use x to output a list of all connected webcams with ID. Unfortunately, I always get the following error message (see picture). enter image description here Does anyone have an idea what this could be? I am thankful for any help!

Here is my code:

const devices = [];
var list;
let video;

function setup() {
    list = navigator.mediaDevices.enumerateDevices().then(gotDevices);
    var constraints1 = {
    video: {
      deviceId: {
        exact: list[0].id
      },
    }
  };
    
    canvas = createCanvas(width,height);

    background(255);
    video = createCapture(constraints1);
}

function gotDevices(deviceInfos) {
  for (let i = 0; i !== deviceInfos.length; ++i) {
    const deviceInfo = deviceInfos[i];
    if (deviceInfo.kind == 'videoinput') {
      devices.push({
        label: deviceInfo.label,
        id: deviceInfo.deviceId
      });
    }
  }
  return devices;
}

-------------------------------EDIT (Current status)-----------------------

var deviceList = [];

function preload() {
  navigator.mediaDevices.enumerateDevices().then(getDevices);
}

function setup() {
  var constraints = {
  video: {
      deviceId: {
        exact: deviceList[1].id
      },
    }
  };

canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
//console.log(deviceList);
}

function getDevices(devices) {

  //arrayCopy(devices, deviceList);
  for (let i = 0; i < devices.length; ++i) {
    let deviceInfo = devices[i];
    
      //Only get videodevices and push them into deviceList
      if (deviceInfo.kind == 'videoinput') {
        deviceList.push({
        label: deviceInfo.label,
        id: deviceInfo.deviceId
      });
//      console.log("Device name :", devices[i].label);
//      console.log("DeviceID :", devices[i].deviceId);
    }
  }
}

Unfortunately, I get the following error here: enter image description here

2

There are 2 best solutions below

11
apodidae On BEST ANSWER

The information that you are looking for is down in the 'getDevices' function. The following runs on my system and will show the device name and id in the console window. It will also create a global array for audio and video devices that you may access in setup(); Note that the deviceList is obtained in preload() which is run only once before the rest of your code.

var deviceList = [];

function preload() {
  navigator.mediaDevices.enumerateDevices().then(getDevices);
}

function setup() {
  var constraints = {
  video:
  {
  }
};
canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
//console.log(deviceList);
for (let x = 0; x < deviceList.length; x++) {
  console.log(deviceList[x]);
}

}

function getDevices(devices) {
  // console.log(devices); // To see all devices
  arrayCopy(devices, deviceList);
  for (let i = 0; i < devices.length; ++i) {
    let deviceInfo = devices[i];
    if (deviceInfo.kind == 'videoinput') {
      console.log("Device name :", devices[i].label);
      console.log("DeviceID :", devices[i].deviceId);
    }
  }
}

DropDownList of Devices

// N.B. Will not run in Processing IDE with Safari - Requires p5.js web editor and Chrome browser
// Loads deviceList array into pullDown list
// Drop Down List parts => a.)display field, b.)arrow, c.)listItems
// Syntax: List(x, y, w, h, itemH, txtSize)

let list;
let selectedItem = -1;
let drop = false;
let itemY = [];
var deviceList = [];

function preload() {
  navigator.mediaDevices.enumerateDevices().then(getDevices);
}

class List {

  constructor(x, y, w, h, itemH, txtSize) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.itemH = itemH;
    this.txtSize = txtSize;
    this.arrwX = this.x + this.w;
    this.arrwY = this.y;
    this.arrwH = this.h;
  }

  openVideoInput (videoSelected) {
    var constraints = {
    video: {
    deviceId: {
      exact: deviceList[videoSelected].id
      },
    }
  };
  createCapture(constraints);
}

press(mx, my) {
  // arrow touches
  if ((mx >= this.arrwX) && (mx <= this.arrwX+this.arrwH) && (my >= this.arrwY) && (my <= this.arrwY+this.arrwH)) {
    if (drop == true) {
      drop = false;
    } else {
      drop = true;
    }
  } // list touches
  if (drop) {
    if (deviceList.length > 0) {
      for (let j = 0; j < deviceList.length; j++) {
        if ((mx >= this.x) && (mx <= this.x + this.w) && (my >= itemY[j] ) && (my <= itemY[j] + this.itemH)) {
          selectedItem = j;
          console.log("selectedItem :", selectedItem);
          list.openVideoInput(selectedItem);
          drop = false;
        }
      }
    }
  }
}

displayFieldString(title) {
  fill(255); // background color
  rect(this.x, this.y, this.w, this.h);
  fill(0); // text color
  textSize(this.txtSize);
  text(title, this.x + 10, this.y + this.txtSize);
}

display() {
  if (selectedItem == -1) {
    this.displayFieldString("Select video input:");
  } else {
    this.displayFieldString(deviceList[selectedItem].label);
  }
  // arrow
  fill(255); // arrow background color
  rect(this.arrwX, this.arrwY, this.arrwH, this.arrwH);
  fill(0, 255, 0); // arrow color
  triangle(this.arrwX+5, this.arrwY+5, this.arrwX+this.arrwH-5, this.arrwY+5, this.arrwX+this.arrwH/2, this.arrwY+this.arrwH-5);
  // listItems
  if ((deviceList.length > 0) && (drop)) {
    for (let j = 0; j < deviceList.length; j++) {
      itemY[j] = (this.y + this.h) + j*this.itemH;
      fill(255);
      rect(this.x, itemY[j], this.w, this.itemH);
      fill(0);
      textSize(this.txtSize);
      text(deviceList[j].label, this.x + 10, itemY[j] + this.txtSize);
    }
  }
  if (!drop) {
    rect(this.x, this.y + this.h, this.w, 0);
  }
}
}

function setup() {
  createCanvas(400, 200);
  list = new List(30, 30, 320, 24, 24, 16);
}

function draw() {
  background(220);
  list.display();
}

function getDevices(devices) {

  for (let i = 0; i < devices.length; ++i) {
    let deviceInfo = devices[i];
    //Only get videodevices and push them into deviceList
    if (deviceInfo.kind == 'videoinput') {
      deviceList.push( {
      label:deviceInfo.label,
        id:deviceInfo.deviceId
      }
      );
    }
  }
}

function mousePressed() {
  list.press(mouseX, mouseY);
}

0
MasterKneater On

Ok, now that I've looked further on the Internet for a solution and also found one, I thought I'd post it here too. Maybe it still helps someone else. The problem was that the array deviceList[] is not completely filled when the code reaches the following line:

deviceList[1].id within setup()

The easiest way to solve this problem was to postpone the automatically initialization of the p5js library until your callback getDevices() has finished its job. Thanks again to apodidae for his help.

var deviceList = [];

navigator.mediaDevices.enumerateDevices().then(getDevices);

const setup = function () {
    
    var constraints = {
        video: {
            deviceId: {
                exact: deviceList[0].id
      },
    }
  };
    var constraints1 = {
        video: {
            deviceId: {
                exact: deviceList[1].id
      },
    }
  };
canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
    video2 = createCapture(constraints1);
console.log(deviceList);
};

const draw = function () {
    
};


function startP5() {
    globalThis.setup = setup; // place callback setup() into global context
    globalThis.draw  = draw;
    new p5;
}


function getDevices(devices) {

  //arrayCopy(devices, deviceList);
  for (let i = 0; i < devices.length; ++i) {
    let deviceInfo = devices[i];
    
      //Only get videodevices and push them into deviceList
      if (deviceInfo.kind == 'videoinput') {
        deviceList.push({
        label: deviceInfo.label,
        id: deviceInfo.deviceId
      });
//      console.log("Device name :", devices[i].label);
//      console.log("DeviceID :", devices[i].deviceId);
    }
  }
    startP5();
}

Here again the link to the other discussion