Rumah  >  Artikel  >  hujung hadapan web  >  WebCodec - Menghantar dan Menerima

WebCodec - Menghantar dan Menerima

Patricia Arquette
Patricia Arquetteasal
2024-10-09 16:35:02565semak imbas

pengenalan

Hello! ?

Dalam tutorial ini saya akan menunjukkan kepada anda cara menggunakan API WebCodec untuk menghantar dan menerima video.

Mula-mula mari dapatkan pengekodan pelayan.


Menyediakan Pelayan

Untuk menghantar dan menerima paket antara rakan sebaya, kami memerlukan pelayan soket web.

Untuk ini kami akan mencipta pelayan yang sangat asas menggunakan nodejs. Mula-mula mulakan projek:

npm init -y

Kemudian pasang modul yang diperlukan:

npm i ws express

Seterusnya buat fail baharu yang dipanggil "index.js" dan isikannya dengan kod berikut:

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

Tiada yang terlalu rumit kod di atas menyediakan direktori awam dan mengendalikan sambungan soket web yang menghantar paket kepada semua rakan sebaya yang disambungkan. ?

Seterusnya kami akan mengendalikan bahagian penghantar, tetapi mula-mula buat direktori baharu yang dipanggil "awam"

mkdir public

Mencipta Pengirim

Fail hujung hadapan pertama yang kami akan buat ialah yang sedang menyiarkan, buat fail baharu yang dipanggil "sender.html" di bawah awam dan isikannya dengan HTML berikut:

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

Untuk memecahkan dan menerangkan perkara yang dilakukan oleh kod.

  1. Struktur HTML
    • Elemen video memaparkan video langsung daripada kamera pengguna
    • Elemen kanvas digunakan untuk memaparkan bingkai individu suapan video. Ini menyediakan pratonton visual setiap bingkai yang dikodkan
  2. Kod JavaScript
    • Fungsi initWebSocket bersambung ke pelayan WebSocket. Sambungan ini penting untuk menstrim bingkai yang dikodkan ke penerima
    • Fungsi initEncoder mencipta objek VideoEncoder. Ia mentakrifkan panggilan balik output yang dijalankan setiap kali pengekod menghasilkan bongkah baharu
    • VideoEncoder.configure() menetapkan codec kepada VP8 dengan kadar bit 1Mbps dan 30 FPS, memastikan pengekodan yang lancar dan berkualiti tinggi
    • panggilan getUserMedia digunakan untuk meminta akses kepada kamera. Suapan video diperuntukkan kepada elemen video dan VideoTrackProcessor membenarkan memproses setiap bingkai dalam masa nyata
    • Fungsi processFrames membaca bingkai daripada video, memaparkannya pada elemen kanvas dan mengekod setiap bingkai menggunakan videoEncoder.encode(). Setiap bingkai kemudiannya dihantar ke pelayan sebagai ketulan yang dikodkan.

Fuh! Mudah-mudahan itu dapat difahami oleh anda. Seterusnya kami akan mencipta fail yang akan menerima aliran. ?


Mencipta Penerima

Fail ini menerima ketulan video yang dikodkan melalui WebSocket, menyahkodnya dan memaparkannya pada elemen kanvas.

Buat fail baharu di bawah direktori awam yang dipanggil "receiver.html" dan isikannya dengan yang berikut:

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

Untuk memecahkan fail di atas:

  1. HTML
    • Elemen kanvas ialah kawasan paparan utama untuk bingkai video yang dinyahkod. Ia mempunyai lebar, ketinggian dan jidar tetap, sama seperti halaman penghantar.
  2. JavaScript
    • Fungsi initWebSocket mencipta sambungan WebSocket baharu, menerima bingkai yang dikodkan daripada penghantar dan menghantarnya kepada decodeFrame() untuk penyahkodan.
    • initDecoder memulakan objek VideoDecoder yang dikonfigurasikan untuk codec VP8. Penyahkod mengeluarkan setiap bingkai ke kanvas.
    • decodeFrame mengambil data yang dikodkan, membungkusnya dalam EncodedVideoChunk (sebagai bingkai kunci dengan cap masa semasa), dan menyahkodnya melalui videoDecoder.decode(). Setiap bingkai dipaparkan pada kanvas dalam masa nyata

Fuh! Sekarang setelah kita mempunyai semua bahagian yang diperlukan, mari kita jalankan! ?


Menjalankan Kod

Untuk menjalankan kod hanya jalankan arahan berikut:

node index.js

Kemudian arahkan penyemak imbas anda ke http://localhost:3000/sender.html
Benarkan akses kepada kamera anda dan kemudian buka tab lain untuk
http://localhost:3000/receiver.html

Seperti di bawah, anda sepatutnya melihat strim dihantar daripada pengirim.

WebCodec - Sending and Receiving


Kesimpulan

Dalam tutorial ini saya telah menunjukkan cara untuk mendapatkan akses kepada kamera, mengekodnya, menghantar ketulan melalui WebSocket dan menyahkod serta memaparkannya pada bahagian penerima. Saya harap tutorial ini berguna kepada anda. ?

Seperti biasa anda boleh mendapatkan kod melalui github saya:
https://github.com/ethand91/webcodec-stream

Selamat Pengekodan! ?


Suka kerja saya? Saya menyiarkan tentang pelbagai topik, jika anda ingin melihat lebih banyak sila like dan ikuti saya.
Saya juga suka kopi.

WebCodec - Sending and Receiving

Jika anda ingin mempelajari Corak Algoritma untuk mengikuti temu duga pengekodan, saya mengesyorkan [kursus berikut](https://algolab.so/p/algorithms-and-data-structure-video-course?affcode=1413380_bzrepgch

Atas ialah kandungan terperinci WebCodec - Menghantar dan Menerima. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn