Javascript iOS Safari 13的getUserMedia和NotReadableError

Javascript iOS Safari 13的getUserMedia和NotReadableError,javascript,safari,mobile-safari,getusermedia,safari-web-inspector,Javascript,Safari,Mobile Safari,Getusermedia,Safari Web Inspector,编辑:当我在Github上使用https托管我的站点时,自动允许摄像头访问。然而,流媒体仍然不能正常工作,我将对此提出另一个问题 我遵循了一个关于getUserMedia API的规则。它在我的桌面Chrome和Safari上运行良好。我现在已经用ngrok(https://....ngrok.io)我可以通过Safari 13在手机上看到我的应用程序 但是,当我按下打开桌面版网络摄像头的“播放”按钮时,它不会做出反应。 当我从myiPhone检查Safari控制台时,它给出了这个错误 Unha

编辑:当我在Github上使用https托管我的站点时,自动允许摄像头访问。然而,流媒体仍然不能正常工作,我将对此提出另一个问题

我遵循了一个关于getUserMedia API的规则。它在我的桌面Chrome和Safari上运行良好。我现在已经用ngrok(https://....ngrok.io)我可以通过Safari 13在手机上看到我的应用程序

但是,当我按下打开桌面版网络摄像头的“播放”按钮时,它不会做出反应。 当我从myiPhone检查Safari控制台时,它给出了这个错误

Unhandled Promise Rejection: NotReadableError: The I/O read operation failed.
来自index.js:76,即
const stream=await navigator.mediaDevices.getUserMedia(约束)

我想知道是否有人能帮上忙?我将在下面附上我的index.js代码。我想知道我是否需要添加一些要求用户允许使用相机的内容

另外:我知道getUserMedia API可能会变得很难使用。我最初的计划包括一个应用程序,它可以访问用户的后置摄像头,并在我的浏览器中使用Tensorflow.js对内容进行分类(因为我希望有一个服务器连接)。如果有人有做类似事情的其他建议,我真的很高兴听到


const controls = document.querySelector('.controls');
const cameraOptions = document.querySelector('.video-options>select');
const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const screenshotImage = document.querySelector('img');
const buttons = [...controls.querySelectorAll('button')];
let streamStarted = false; // was false

const [play, pause, screenshot] = buttons;

const constraints = {
  video: {
    width: {
      min: 224,
      ideal: 224,
      max: 224,
    },
    height: {
      min: 224,
      ideal: 224,
      max: 224
    },
  }
};

cameraOptions.onchange = () => {
  const updatedConstraints = {
    ...constraints,
    deviceId: {
      exact: cameraOptions.value
    }
  };

  startStream(updatedConstraints);
};

play.onclick = () => {
  if (streamStarted) {
    video.play();
    play.classList.add('d-none');
    pause.classList.remove('d-none');
    return;
  }
  if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
    const updatedConstraints = {
      ...constraints,
      deviceId: {
        exact: cameraOptions.value
      }
    };
    startStream(updatedConstraints);
    console.log("Camera available")
  }
};

const pauseStream = () => {
  video.pause();
  play.classList.remove('d-none');
  pause.classList.add('d-none');
};

const doScreenshot = () => {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);
  screenshotImage.src = canvas.toDataURL('image/webp');
  screenshotImage.classList.remove('d-none');
};

pause.onclick = pauseStream;
screenshot.onclick = doScreenshot;

const startStream = async (constraints) => {
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  handleStream(stream);
};


const handleStream = (stream) => {
  video.srcObject = stream;
  play.classList.add('d-none');
  pause.classList.remove('d-none');
  screenshot.classList.remove('d-none');

};


const getCameraSelection = async () => {
  const devices = await navigator.mediaDevices.enumerateDevices();
  const videoDevices = devices.filter(device => device.kind === 'videoinput');
  const options = videoDevices.map(videoDevice => {
    return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
  });
  cameraOptions.innerHTML = options.join('');
};

getCameraSelection();

const controls=document.querySelector('.controls');
const camerapoptions=document.querySelector('.video options>select');
const video=document.querySelector('video');
const canvas=document.querySelector('canvas');
const screenshotImage=document.querySelector('img');
const buttons=[…controls.querySelectorAll('button')];
让streamStarted=false;//是假的
const[播放、暂停、截图]=按钮;
常量约束={
视频:{
宽度:{
min:224,
理想值:224,
最高:224,
},
高度:{
min:224,
理想值:224,
最高:224
},
}
};
cameraOptions.onchange=()=>{
常量更新约束={
…限制条件,
设备ID:{
精确:cameraOptions.value
}
};
startStream(更新的约束);
};
play.onclick=()=>{
如果(流启动){
video.play();
play.classList.add('d-none');
pause.classList.remove('d-none');
返回;
}
if('mediaDevices'位于navigator&&navigator.mediaDevices.getUserMedia中){
常量更新约束={
…限制条件,
设备ID:{
精确:cameraOptions.value
}
};
startStream(更新的约束);
控制台日志(“摄像机可用”)
}
};
const pauseStream=()=>{
video.pause();
play.classList.remove('d-none');
pause.classList.add('d-none');
};
常数doScreenshot=()=>{
canvas.width=video.videoWidth;
canvas.height=video.videoHeight;
canvas.getContext('2d').drawImage(视频,0,0);
screenshotImage.src=canvas.toDataURL('image/webp');
screenshotImage.classList.remove('d-none');
};
pause.onclick=pauseStream;
screenshot.onclick=doScreenshot;
const startStream=async(约束)=>{
const stream=await navigator.mediaDevices.getUserMedia(约束);
手流(溪流);
};
常量handleStream=(流)=>{
video.srcObject=流;
play.classList.add('d-none');
pause.classList.remove('d-none');
屏幕截图.classList.remove('d-none');
};
const getCameraSelection=async()=>{
const devices=wait navigator.mediaDevices.enumerateDevices();
const videoDevices=devices.filter(device=>device.kind==='videoinput');
const options=videoDevices.map(videoDevice=>{
返回“${videoDevice.label}”;
});
cameraOptions.innerHTML=options.join(“”);
};
getCameraSelection();

几个可能的问题

  • Safari不支持将
    image/webp
    作为MIME类型。看见如果需要无损编码,请使用
    screenshotImage.src=canvas.toDataURL('image/jpeg')
    screenshotImage.src=canvas.toDataURL('image/png')
  • 试着放松约束,只给出理想的值
  • Safari中的
    getUserMedia()
    有点像谚语中的会说话的驴子。这并不令人惊讶,它工作得很糟糕,令人惊讶的是它工作得很好。在
    getUserMedia()
    类似的东西上,您肯定需要一个catch处理程序

  • 因为口香糖很可能会出错。当然,要在mobile safari上查看控制台,您必须使用桌面safari进行调试。很多教程都解释了这一点。这里只有一个

    几个可能的问题

  • Safari不支持将
    image/webp
    作为MIME类型。看见如果需要无损编码,请使用
    screenshotImage.src=canvas.toDataURL('image/jpeg')
    screenshotImage.src=canvas.toDataURL('image/png')
  • 试着放松约束,只给出理想的值
  • Safari中的
    getUserMedia()
    有点像谚语中的会说话的驴子。这并不令人惊讶,它工作得很糟糕,令人惊讶的是它工作得很好。在
    getUserMedia()
    类似的东西上,您肯定需要一个catch处理程序
  • 因为口香糖很可能会出错。当然,要在mobile safari上查看控制台,您必须使用桌面safari进行调试。很多教程都解释了这一点。这里只有一个

    let devices
    try {
      devices = await navigator.mediaDevices.enumerateDevices();
    } catch (error) {
      console.error(error)
    }