Maison  >  Article  >  interface Web  >  WebCodec - Envoi et réception

WebCodec - Envoi et réception

Patricia Arquette
Patricia Arquetteoriginal
2024-10-09 16:35:02565parcourir

Introduction

Bonjour ! ?

Dans ce tutoriel, je vais vous montrer comment utiliser l'API WebCodec pour envoyer et recevoir des vidéos.

Commençons par coder le serveur.


Configuration du serveur

Afin d'envoyer et de recevoir des paquets entre pairs, nous aurons besoin d'un serveur websocket.

Pour cela nous allons créer un serveur très basique en utilisant nodejs. Initialisez d'abord le projet :

npm init -y

Installez ensuite les modules requis :

npm i ws express

Créez ensuite un nouveau fichier appelé "index.js" et remplissez-le avec le code suivant :

// 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);
  });
});

Rien de trop compliqué, le code ci-dessus sert le répertoire public et gère la connexion Websocket en envoyant des paquets à tous les pairs connectés. ?

Ensuite, nous allons gérer la partie expéditeur, mais créons d'abord un nouveau répertoire appelé "public"

mkdir public

Création de l'expéditeur

Le premier fichier front-end que nous allons créer est celui qui diffuse, créez un nouveau fichier appelé "sender.html" sous public et remplissez-le avec le HTML suivant :

<!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>

Pour décomposer et expliquer ce que fait le code.

  1. Structure HTML
    • L'élément vidéo affiche la vidéo en direct de la caméra de l'utilisateur
    • L'élément canvas est utilisé pour afficher des images individuelles du flux vidéo. Cela fournit un aperçu visuel de chaque image encodée
  2. Code JavaScript
    • La fonction initWebSocket se connecte à un serveur WebSocket. Cette connexion est indispensable pour diffuser les images encodées vers un récepteur
    • La fonction initEncoder crée un objet VideoEncoder. Il définit un rappel de sortie qui s'exécute chaque fois que l'encodeur produit un nouveau morceau
    • Le videoEncoder.configure() définit le codec sur VP8 avec un débit binaire de 1 Mbps et 30 FPS, garantissant un encodage fluide et de haute qualité
    • L'appel getUserMedia est utilisé pour demander l'accès à la caméra. Le flux vidéo est attribué à l'élément vidéo, et le VideoTrackProcessor permet de traiter chaque image en temps réel
    • La fonction processFrames lit les images de la vidéo, les affiche sur l'élément canvas et encode chaque image à l'aide de videoEncoder.encode(). Chaque trame est ensuite envoyée au serveur sous forme de morceau codé.

Ouf ! J'espère que cela vous a été compréhensible. Ensuite, nous allons créer le fichier qui recevra le flux. ?


Création du récepteur

Ce fichier reçoit les morceaux vidéo encodés via WebSocket, les décode et les affiche sur un élément de canevas.

Créez un nouveau fichier dans le répertoire public appelé "receiver.html" et remplissez-le avec ce qui suit :

<!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>

Pour décomposer le fichier ci-dessus :

  1. HTML
    • L'élément canevas est la zone d'affichage principale des images vidéo décodées. Elle a une largeur, une hauteur et une bordure fixes, identiques à celles de la page de l'expéditeur.
  2. JavaScript
    • La fonction initWebSocket crée une nouvelle connexion WebSocket, recevant les trames codées de l'expéditeur et les transmettant à decodeFrame() pour le décodage.
    • initDecoder initialise un objet VideoDecoder configuré pour le codec VP8. Le décodeur envoie chaque image sur la toile.
    • decodeFrame prend les données codées, les enveloppe dans un EncodedVideoChunk (en tant qu'image clé avec l'horodatage actuel) et les décode via videoDecoder.decode(). Chaque image est affichée sur la toile en temps réel

Ouf ! Maintenant que nous avons toutes les pièces nécessaires, exécutons-le ! ?


Exécuter le code

Pour exécuter le code, exécutez simplement la commande suivante :

node index.js

Pointez ensuite votre navigateur sur http://localhost:3000/sender.html
Autorisez l'accès à votre caméra, puis ouvrez un autre onglet pour
http://localhost:3000/receiver.html

Comme ci-dessous, vous devriez voir le flux envoyé par l'expéditeur.

WebCodec - Sending and Receiving


Conclusion

Dans ce tutoriel, j'ai montré comment accéder à la caméra, l'encoder, envoyer les morceaux via WebSocket, les décoder et les afficher du côté du récepteur. J'espère que ce tutoriel vous a été utile. ?

Comme toujours, vous pouvez obtenir le code via mon github :
https://github.com/ethand91/webcodec-stream

Bon codage ! ?


Vous aimez mon travail ? Je publie sur une variété de sujets, si vous souhaitez en voir plus, aimez-moi et suivez-moi.
J'aime aussi le café.

WebCodec - Sending and Receiving

Si vous cherchez à apprendre des modèles d'algorithmes pour réussir l'entretien de codage, je vous recommande le [cours suivant](https://algolab.so/p/algorithms-and-data-structure-video-course?affcode=1413380_bzrepgch

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn