首页 >web前端 >js教程 >WebCodec - 发送和接收

WebCodec - 发送和接收

Patricia Arquette
Patricia Arquette原创
2024-10-09 16:35:02727浏览

介绍

您好! ?

在本教程中,我将向您展示如何使用 WebCodec API 发送和接收视频。

首先让我们对服务器进行编码。


设置服务器

为了在对等点之间发送和接收数据包,我们需要一个 websocket 服务器。

为此,我们将使用 Nodejs 创建一个非常基本的服务器。首先初始化项目:

npm init -y

然后安装所需的模块:

npm i ws express

接下来创建一个名为“index.js”的新文件并使用以下代码填充它:

// server.js
const WebSocket = require('ws');
const express = require('express');

const app = express();
const port = 3000;
const connectedClients = new Set();

app.use(express.static(__dirname + '/public'));

const wss = new WebSocket.Server({ noServer: true });

wss.on('connection', ws => {
  console.log('new connection');
  connectedClients.add(ws);

  ws.on('message', message => {
    connectedClients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });

  ws.once('close', () => {
    connectedClients.delete(ws);
    console.log('connection closed');
  });
});

const server = app.listen(port, () => {
  console.log(`server running on port ${port}`);
});

server.on('upgrade', (request, socket, head) => {
  wss.handleUpgrade(request, socket, head, (ws) => {
    wss.emit('connection', ws, request);
  });
});

上面的代码没有太复杂,它服务于公共目录并处理 websocket 连接,将数据包发送到所有连接的对等点。 ?

接下来我们将处理发件人部分,但首先创建一个名为“public”的新目录

mkdir public

创建发件人

我们将创建的第一个前端文件是广播的,在 public 下创建一个名为“sender.html”的新文件,并使用以下 HTML 填充它:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Sender</title>
    <style>
      video, canvas {
        width: 640px;
        height: 480px;
        border: 2px solid black;
        margin: 10px;
      }
    </style>
  </head>
  <body>
    <video id="video" autoplay playsinline></video>
    <canvas id="canvas" width="640" height="480"></canvas>

    <script>
      const videoElement = document.getElementById('video');
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      let videoEncoder;
      let socket;

      const initWebSocket = () => {
        socket = new WebSocket('ws://localhost:3000');

        socket.onopen = () => console.log('WebSocket connected');
        socket.onerror = error => console.error('WebSocket error:', error);
      };

      const initEncoder = () => {
        videoEncoder = new VideoEncoder({
          output: (encodedChunk) => {
            const chunkData = new Uint8Array(encodedChunk.byteLength);
            encodedChunk.copyTo(chunkData);

            if (socket.readyState === WebSocket.OPEN) {
              socket.send(chunkData.buffer);
            }
          },
          error: (error) => console.error('Encoding error:', error)
        });

        videoEncoder.configure({
          codec: 'vp8',
          width: 640,
          height: 480,
          bitrate: 1_000_000,
          framerate: 30
        });
      };

      navigator.mediaDevices.getUserMedia({ video: true })
        .then((stream) => {
          videoElement.srcObject = stream;
          const videoTrack = stream.getVideoTracks()[0];
          const processor = new MediaStreamTrackProcessor(videoTrack);
          const reader = processor.readable.getReader();

          const processFrames = async () => {
            while (true) {
              const { value: videoFrame, done } = await reader.read();
              if (done) break;

              ctx.drawImage(videoFrame, 0, 0, canvas.width, canvas.height);
              videoEncoder.encode(videoFrame, { keyFrame: true });
              videoFrame.close();
            }
          };

          processFrames();
        })
        .catch((error) => console.error('Failed to get camera', error));

        initEncoder();
        initWebSocket();
    </script>
  </body>
</html>

分解并解释代码的作用。

  1. HTML 结构
    • 视频元素显示来自用户相机的实时视频
    • canvas 元素用于显示视频源的各个帧。这提供了每个编码帧的视觉预览
  2. JavaScript 代码
    • initWebSocket 函数连接到 WebSocket 服务器。此连接对于将编码帧流式传输到接收器至关重要
    • initEncoder 函数创建一个 VideoEncoder 对象。它定义了一个输出回调,该回调在编码器每次生成新块时运行
    • videoEncoder.configure() 将编解码器设置为 VP8,码率 1Mbps,FPS 30,确保流畅、高质量的编码
    • getUserMedia 调用用于请求访问摄像头。视频源被分配给视频元素,VideoTrackProcessor 允许实时处理每一帧
    • processFrames 函数从视频中读取帧,将它们显示在 canvas 元素上,并使用 videoEncoder.encode() 对每个帧进行编码。然后,每个帧都会作为编码块发送到服务器。

唷!希望您能理解。接下来我们将创建将接收流的文件。 ?


创建接收器

此文件通过 WebSocket 接收编码的视频块,对其进行解码,并将它们显示在画布元素上。

在公共目录下创建一个名为“receiver.html”的新文件,并使用以下内容填充它:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Receiver</title>
    <style>
      canvas {
        width: 640px;
        height: 480px;
        border: 2px solid black;
        margin: 10px;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="640" height="480"></canvas>

    <script>
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      let videoDecoder;

      const initWebSocket = () => {
        const socket = new WebSocket('ws://localhost:3000');
        socket.binaryType = 'arraybuffer';

        socket.onmessage = event => {
          decodeFrame(event.data);
        };
        socket.onerror = error => console.error('WebSocket error:', error);
      };

      const initDecoder = () => {
        videoDecoder = new VideoDecoder({
          output: (videoFrame) => {
            ctx.drawImage(videoFrame, 0, 0, canvas.width, canvas.height);
            videoFrame.close();
          },
          error: (error) => console.error('Decoding error:', error)
        });

        videoDecoder.configure({
          codec: 'vp8',
          width: 640,
          height: 480
        });
      };

      const decodeFrame = (encodedData) => {
        const chunk = new EncodedVideoChunk({
          type: 'key',
          timestamp: performance.now(),
          data: new Uint8Array(encodedData)
        });

        videoDecoder.decode(chunk);
      };

      initDecoder();
      initWebSocket();
    </script>
  </body>
</html>

分解上述文件:

  1. HTML
    • canvas 元素是解码视频帧的主要显示区域。它有固定的宽度、高度和边框,与发件人页面相同。
  2. JavaScript
    • initWebSocket 函数创建一个新的 WebSocket 连接,接收来自发送者的编码帧并将其传递给decodeFrame() 进行解码。
    • initDecoder 初始化为 VP8 编解码器配置的 VideoDecoder 对象。解码器将每一帧输出到画布。
    • decodeFrame 获取编码数据,将其包装在 EncodedVideoChunk 中(作为具有当前时间戳的关键帧),并通过 videoDecoder.decode() 对其进行解码。每一帧都实时显示在画布上

唷!现在我们已经拥有了所需的所有部分,让我们实际运行它吧! ?


运行代码

要运行代码,只需运行以下命令:

node index.js

然后将浏览器指向http://localhost:3000/sender.html
允许访问您的相机,然后打开另一个选项卡
http://localhost:3000/receiver.html

如下所示,您应该看到从发送者发送的流。

WebCodec - Sending and Receiving


结论

在本教程中,我展示了如何访问摄像头、对其进行编码、通过 WebSocket 发送数据块以及如何解码并在接收器端显示它们。我希望本教程对您有用。 ?

一如既往,您可以通过我的 github 获取代码:
https://github.com/ehand91/webcodec-stream

快乐编码! ?


喜欢我的作品吗?我的帖子内容很丰富,如果你想看更多,请点赞并关注我。
我也喜欢咖啡。

WebCodec - Sending and Receiving

如果您想学习算法模式以在编码面试中取得好成绩,我推荐[以下课程](https://algolab.so/p/algorithms-and-data-struct-video-course?affcode=1413380_bzrepgch

以上是WebCodec - 发送和接收的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn