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