流html5 canvas内容的有效方法吗?

时间:2018-09-10 11:20:38

标签: javascript html5 canvas

我正在尝试使用html5 canvaswebsockets实时传输nodejs的内容。

html5画布的内容只是一个视频。

到目前为止,我所做的是:

我将画布转换为blob,然后获取blob URL并使用websockets将URL发送到我的nodejs服务器。

我得到这样的Blob URL:

canvas.toBlob(function(blob) {
   url = window.URL.createObjectURL(blob);
});

每个视频帧都会生成blob URL(准确地说是每秒20帧),它们看起来像这样:

blob:null/e3e8888e-98da-41aa-a3c0-8fe3f44frt53

然后我通过websocket从服务器获取该blob URL,以便可以将其拖放到另一个画布上,以供其他用户查看。

我确实搜索了如何从Blob URL绘制到画布上,但是找不到与我想做的事情相似的事情。

所以我的问题是:

  1. 这是我想要实现的正确方法吗?任何 优点和缺点将不胜感激。

  2. 还有其他更有效的方法吗?还是我说的很对 路径?

先谢谢了。

编辑:

我应该提到我不能在该项目中使用WebRTC,而我必须用自己的所有东西来做。

为了让我现在所在的每个地方都更容易,这就是我尝试使用websocket在画布中显示上面提到的Blob URL的方式:

websocket.onopen = function(event) {

        websocket.onmessage = function(evt) {
            var val = evt.data;
            console.log("new data "+val);
            var canvas2 = document.querySelector('.canvMotion2');
            var ctx2 = canvas2.getContext('2d');
            var img = new Image();     

            img.onload = function(){
                ctx2.drawImage(img, 0, 0)
            }
            img.src = val;
        };

        // Listen for socket closes
        websocket.onclose = function(event) {

        };

        websocket.onerror = function(evt) {

        };

};

问题是,当我在FireFox中运行该代码时,画布始终为空/空白,但是我在控制台中看到了Blob URL,这使我觉得自己在做错了。

在Google chrome中,我收到Not allowed to load local resource: blob:错误。

第二编辑:

这是我现在的位置。

第一个选项

我尝试通过websocket发送整个blob,并成功地进行了管理。但是,由于某种奇怪的原因,我无法在客户端读回它!

当我查看Node.js服务器的控制台时,对于发送到服务器的每个Blob,我都能看到类似的内容:

<buffer fd67676 hdsjuhsd8 sjhjs....

第二个选项:

因此上述选项失败了,我想到了其他方法,即将每个画布框架转换为base64(jpeg),然后通过websocket将其发送到服务器,然后将这些base64图像显示/绘制到客户端的画布上。 / p>

我正在每秒发送24帧到服务器。

这有效。但是客户端画布在其中再次显示这些base64图像的速度非常慢,并且就像每秒绘制1帧一样。这就是我目前遇到的问题。

第三种选择:

我也尝试使用没有画布的视频。因此,使用WebRTC,我将video Stream作为单个Blob 。但是我不确定如何使用它并将其发送给客户端,以便人们可以看到。

重要提示:我正在使用的该系统不是对等连接。这只是我想要实现的一种流式传输。

1 个答案:

答案 0 :(得分:1)

流传输画布内容的最自然的方式:WebRTC

OP明确表示他们无法使用它,很多情况可能是这样,

  1. Browser support仍然不是很好。
  2. 这意味着要运行MediaServer(至少要运行ICE + STUN / TURN,如果要流式传输到多个对等端,则可能是网关)。

但是,如果您负担得起,那么从canvas元素获取MediaStream所需要的就是

const canvas_stream = canvas.captureStream(minimumFrameRate);

,然后只需将其添加到RTCPeerConnection:

pc.addTrack(stream.getVideoTracks()[0], stream);

下面的示例仅将MediaStream显示为<video>元素。

let x = 0;
const ctx = canvas.getContext('2d');
draw();
startStream();

function startStream() {
  // grab our MediaStream
  const stream = canvas.captureStream(30);
  // feed the <video>
  vid.srcObject = stream;
  vid.play();
}
function draw() {
  x = (x + 1) % (canvas.width + 50);
  ctx.fillStyle = 'white';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle = 'red';
  ctx.beginPath();
  ctx.arc(x - 25, 75, 25, 0, Math.PI*2);
  ctx.fill();
  requestAnimationFrame(draw);
}
video,canvas{border:1px solid}
<canvas id="canvas">75</canvas>
<video id="vid" controls></video>


流实时画布图形的最有效方法是:流图形操作。

OP再次表示,由于其设置不匹配,他们不希望使用此解决方案,但可能对许多读者有帮助:

与其发送画布结果,不如将绘画命令发送给您的同伴,它们将在他们的同侧执行这些命令。

但是这种方法有其自身的警告:

  • 您必须编写自己的编码器/解码器才能传递命令。
  • 某些情况下可能很难共享(例如,外部媒体必须在所有同位体上共享和预加载,更糟糕的情况是绘制另一幅画布,在该画布上您还必须共享自己的绘图处理)。
  • 您可能要避免对所有同级进行密集的图像处理(例如ImageData操作)。

因此,第三种绝对不那么有效的方法,就像OP尝试做的那样:

以固定的时间间隔上传帧。

在这里我不会详细介绍,但是请记住,您要发送的是独立的图像文件,因此比起编码为视频的数据要多得多。

相反,我将集中讨论 OP的代码为什么不起作用?

首先,最好提醒一下什么是Blob(canvas.toBlob(callback)的回调函数中提供的东西)。

Blob是一个特殊的JavaScript对象,代表二进制数据,通常存储在浏览器的内存中,或者至少存储在用户磁盘上,浏览器可以访问。
但是,此二进制数据不能直接用于JavaScript。为了能够访问它,我们需要(通过FileReader或Response对象)读取此Blob,或者创建一个BlobURI ,这是一个伪造的URI,允许大多数API指向二进制数据就像存储在真实服务器上一样,即使二进制数据仍只是在浏览器分配的内存中。

但是此BlobURI只是到浏览器内存的虚假,临时和受域限制的路径,无法与任何其他跨域文档,应用程序甚至更少的计算机共享。

所有这些说明应该发送到WebSocket的直接是Blob,而不是BlobURI。

您只能在消费者方面创建BlobURI,以便他们可以从Blob的二进制数据中加载这些图像,这些二进制数据现在位于分配的内存中。

发射器侧:

canvas.toBlob(blob=>ws.send(blob));

消费者方面:

ws.onmessage = function(evt) {
  const blob = evt.data;
  const url = URL.createObjectURL(blob);
  img.src = url;
};

但是实际上,为了更好地回答OP的问题,有一个最终的解决方案,在这种情况下可能是最好的,

共享在画布上绘制的视频流。