4

我正在尝试在 nodeJS 服务器上创建照片捕获 Web 应用程序,并且我正在使用下面的javascript代码。

const btn = document.querySelector('#btn');

btn.addEventListener('click', (event) => {
 navigator.mediaDevices.getUserMedia({video: true})
  .then(gotMedia)
  .catch(error => console.error('getUserMedia() error:', error));

  event.preventDefault()
})

gotMedia 函数是这样的:

function gotMedia(mediaStream) {
    const mediaStreamTrack = mediaStream.getVideoTracks()[0];
    const imageCapture = new ImageCapture(mediaStreamTrack);
    console.log(imageCapture);

    const canvas = document.querySelector('canvas');
    // ...
    imageCapture.grabFrame()
    .then(imageBitmap => {
        canvas.width = imageBitmap.width;
        canvas.height = imageBitmap.height;
        canvas.getContext('2d').drawImage(imageBitmap, 0, 0);
    })
    .catch(error => console.error('grabFrame() error:', error));
}

一切正常,图像捕获还可以,但是当我一张一张快速拍摄照片时出现错误,上面写着:

grabFrame() 错误:DOMException:关联的 Track 处于无效状态。

这通常发生在我拍摄太多照片时(例如快速单击大约 20 秒以上),但前五张快照也发生了这种情况。有谁知道发生了什么,我应该改变什么,以解决这个问题?感谢您的时间。

4

4 回答 4

5

根据规范,这样的错误可能是不可接受的状态的结果。在铬源中,我发现了这种方法

我已经使用这样的代码克服了错误:

  const promise = getPrevImagePromise();

  if (!promise && !(imageCapture.track.readyState != 'live' || !imageCapture.track.enabled || imageCapture.track.muted)) {
    const imagePromise = imageCapture.grabFrame()
      .then((image) => {
        // do work with image
      })
      .then(() => {
        deletePrevImagePromise()
      })
      .catch((error) => {
        // 
      });

    setPrevImagePromise(imagePromise);
  }
于 2020-04-16T09:34:42.953 回答
3

我遇到了同样的问题,无法让前沿grabFrame功能可靠地工作。

相反,您可以从中间视频源中进行绘制。在您的函数中,这看起来像这样(未经测试):

function gotMedia(mediaStream) {
    const mediaStreamTrack = mediaStream.getVideoTracks()[0];
    const canvas = document.querySelector('canvas');
    const video = document.createElement('video');

    video.autoplay = true;
    video.srcObject = mediaStream;
    video.onplay = () => {
         canvas.width = video.videoWidth;
         canvas.height = video.videoHeight;
         canvas.getContext('2d').drawImage(video, 0, 0);
    };
}

当然,如果您要捕获大量帧,最好在流旁边创建一个视频并每次都从该视频中捕获。

于 2020-03-27T15:49:03.587 回答
0

我想在一个循环中抓取Frame()(在没有stop()的情况下重用),感谢fomasha,我的代码如下所示

async function aSleep(ms) {
    return new Promise(R => setTimeout(R, ms));
}
var prevP=null;
function getPrevImagePromise() {return prevP;}
function setPrevImagePromise(p) {prevP=p;}
function deletePrevImagePromise() {prevP=null;}
async function aRun() {
     imageCapture = new ImageCapture(gImageTrack);
  while(true) {
    const promise = getPrevImagePromise();
    if (!promise && !(imageCapture.track.readyState != 'live' || !imageCapture.track.enabled || imageCapture.track.muted)) {
    const imagePromise = imageCapture.grabFrame()
      .then((image) => {
        context.drawImage(image, 0, 0, 640, 480);
      })
      .then(() => {
        deletePrevImagePromise()
      })
      .catch((error) => {
        // 
      });
      setPrevImagePromise(imagePromise);
    }
    await aSleep(500);
  }
}

` 重构

`

function zCamera() {
    var T=this;
    T.aInit=async function() {
        T.mStm=await navigator.mediaDevices.getUserMedia({ video: { width: 4096, height: 3200, facingMode: 'environment' } });
        T.mTrk=T.mStm.getTracks()[0];
        T.mCap=new ImageCapture(T.mTrk);
    }
    T.getSetting=function() {
        return T.mTrk.getSettings();
    }
    T.aGrabFrame=async function() {
      var P=new Promise((R)=>{
        T.mCap.grabFrame().then((vImg) => {
          R(vImg);
        }).catch((error) => {
          R(null);
        });
      });
      return P;    
    }
}
var gContext;
var oCamera;
async function aInitCamera()
{
    var canvas=document.getElementById('canvas');
    gContext=canvas.getContext('2d');
    oCamera=new zCamera();
    await oCamera.aInit();
}
async function aRun() {
    while(true) {
        var wImg=await oCamera.aGrabFrame();
        if (wImg!=null)
            gContext.drawImage(wImg, 0, 0, 640, 480);
        await aSleep(100);
    }
}
async function jMain() {
    await aInitCamera();
    aRun();
}

`

于 2022-01-19T10:02:54.270 回答
0

我的两分钱:

let preview = document.getElementById('preview')
let recording = document.getElementById('recording')
let photo = document.getElementById('photo')
let photoButton = document.getElementById('photoButton')
let imageHolder = document.getElementById('image-holder')

async function startWebcam() {
  return navigator.mediaDevices.getUserMedia({
    video: true
  })
};

photoButton.addEventListener('click', async() => {
  let webcamStream = await startWebcam()
  preview.srcObject = webcamStream
  const track = webcamStream.getVideoTracks()[0]
  const imageCapture = new ImageCapture(track)
  const bitmap = await imageCapture.grabFrame()
  const canvas = document.getElementById('canvas')
  canvas.width = bitmap.width
  canvas.height = bitmap.height
  const context = canvas.getContext('2d')
  context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height)
  let image = canvas.toDataURL()
  imageHolder.innerHTML += `<img src="${image}"></img>`
  track.stop()

})
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Desktoprecord</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
  <script src="node_modules/axios/dist/axios.min.js"></script>
  <script src="app4.js" defer></script>
</head>

<div class="container">
  <video id="preview" width="25%" height="auto" autoplay muted poster="https://placekitten.com/g/150"></video>
  <canvas id="canvas"></canvas>
  <img src="http://placekitten.com/g/320/261" id="photo" alt="photo">
  <div id="image-holder">
    To be replaced with pretty pictures
  </div>
  <video src="" id="preview"></video>
  </jdiv>
  <div class="buttons are-large">
    <button id="photoButton" class="button is-success"><i class="fas fa-camera"></i></button>
  </div>
</div>

每次你要拍照时,你都会启动相机,在你拿到照片后,你必须停止轨道

于 2020-08-05T20:44:42.210 回答