You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas drawImage方法阻止ondataavailable事件触发问题求助

问题解答

问题1:视频不显示动态摄像头画面

核心原因集中在跨域限制和资源加载/绘制逻辑上:

  1. Canvas污染(CORS问题):如果DroidCam的IP流未配置CORS响应头,且你的视频/图片元素未设置跨域属性,绘制跨域资源后Canvas会被标记为「污染状态」,此时捕获的流无法获取有效帧,最终录制的视频为空或仅显示非跨域绘制的内容(比如你调试时的色块)。
  2. 视频流加载/播放异常:浏览器有自动播放政策,未通过用户交互触发的视频可能无法正常播放;若直接用img标签加载视频流,无法持续获取更新帧,应该用video元素处理流媒体。
  3. 绘制同步问题:requestAnimationFrame的循环可能在视频帧未更新时就执行,导致重复绘制同一帧,或者Canvas尺寸与视频流不一致,造成画面裁剪/空白。

问题2:drawImage阻止ondataavailable触发

本质还是Canvas污染导致的流无效:
当你绘制了无CORS许可的跨域资源后,Canvas会被污染,此时captureStream()返回的视频流是无效的——MediaRecorder无法从这个流中获取到可编码的帧数据,因此不会定期触发ondataavailable(只有在调用stop()时才会生成最后一块空数据)。移除drawImage后,Canvas未被污染,流恢复有效,事件自然能正常触发。


解决方案

1. 解决CORS问题

  • 给视频/图片元素添加跨域属性:
    <video id="videoEl" crossOrigin="anonymous" autoplay playsinline></video>
    
    或在JS中设置:
    videoEl.crossOrigin = "anonymous";
    
  • 配置DroidCam服务器添加CORS响应头(如Access-Control-Allow-Origin: *),若无法修改DroidCam设置,可通过本地代理服务器转发请求并添加CORS头。

2. 修正录制逻辑代码

<video id="videoEl" crossOrigin="anonymous" autoplay playsinline></video>
<canvas id="canV"></canvas>
<button id="startBtn">开始录制</button>
<button id="stopBtn">停止录制</button>

<script>
const videoEl = document.getElementById('videoEl');
const canvas2 = document.getElementById("canV");
const ctx = canvas2.getContext("2d");
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');

// 加载摄像头流
videoEl.src = 'http://192.168.223.101:8080/video?640x480';

// 视频元数据加载完成后初始化Canvas
videoEl.addEventListener('loadedmetadata', () => {
  canvas2.width = videoEl.videoWidth;
  canvas2.height = videoEl.videoHeight;
  update();
});

// 同步绘制视频帧
function update(){
  ctx.drawImage(videoEl, 0, 0, canvas2.width, canvas2.height);
  // 强制捕获当前Canvas帧到流中
  if (videoStream) {
    videoStream.getVideoTracks()[0].requestFrame();
  }
  requestAnimationFrame(update);
}

let videoStream;
let mediaRecorder;
let chunks = [];

startBtn.addEventListener('click', () => {
  videoStream = canvas2.captureStream(10); // 设置10fps捕获帧率
  mediaRecorder = new MediaRecorder(videoStream);
  chunks = [];

  mediaRecorder.ondataavailable = function(e) {
    // 仅添加非空数据块
    if (e.data.size > 0) chunks.push(e.data);
  };

  mediaRecorder.onstop = function(e) {
    if (chunks.length === 0) {
      console.error('无录制数据,请检查CORS或流有效性');
      return;
    }
    // 优先用webm格式(浏览器兼容性更好)
    const blob = new Blob(chunks, { 'type': 'video/webm' });
    const videoURL = URL.createObjectURL(blob);
    const video = document.createElement("video");
    video.src = videoURL;
    video.controls = true;
    document.body.appendChild(video);
  };

  mediaRecorder.start(1000); // 每1秒生成一个数据块
});

stopBtn.addEventListener('click', () => {
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
  }
});
</script>

关键优化点

  • 用video元素替代img加载流,确保能持续获取更新帧;
  • 监听loadedmetadata事件,确保视频加载完成后再初始化Canvas;
  • 绘制后调用requestFrame()强制捕获当前帧,保证MediaRecorder能获取到新数据;
  • 用video/webm作为录制格式(MediaRecorder对mp4的编码器支持有限,容易出现无效Blob);
  • 过滤空数据块,避免生成无效的Blob文件。

内容的提问来源于stack exchange,提问作者Merlin the Immortal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:07:11