当前位置: 首页 > news >正文

基于WebTransport(底层QUIC)实现视频传输(HTML+JavaScript)

工作目录和基本操作见博客《基于HTTP3的WebTransport实践》,在此仅展示服务器端和客户端代码。

服务器端


import { readFile } from "node:fs/promises";
import { createServer } from "node:https";
import {Server} from "socket.io";
import { Http3Server } from "@fails-components/webtransport";
import { randomBytes } from 'crypto';
import path from "path";
import fs from "fs";// const key = await readFile("./certs/localhost.key");
// const cert = await readFile("./certs/localhost.crt");
const key = await readFile("./certs/localhost+2-key.pem");
const cert = await readFile("./certs/localhost+2.pem");const httpsServer = createServer({key,cert
}, async (req, res) => {if (req.method === "GET" && req.url === "/") {const content = await readFile("./longvideo.html");res.writeHead(200, {"content-type": "text/html"});res.write(content);res.end();}else if (req.method === "GET" && req.url === "/longvideo.js") {const content = await readFile("./longvideo.js");res.writeHead(200, {"content-type": "application/javascript"});res.write(content);res.end();}else {res.writeHead(404).end();}
});const port = process.env.PORT || 3001;httpsServer.listen(port, () => {console.log(`server listening at https://localhost:${port}`);
});// const io = new Server(httpsServer);
const io = new Server(httpsServer, {transports: ["polling", "websocket", "webtransport"]
});io.on("connection", (socket) => {console.log(`connected with transport ${socket.conn.transport.name}`);socket.conn.on("upgrade", (transport) => {console.log(`transport upgraded to ${transport.name}`);});socket.on("disconnect", (reason) => {console.log(`disconnected due to ${reason}`);});
});const h3Server = new Http3Server({port,host: "0.0.0.0",secret: 'ChangeIt', //不换也行cert,privKey: key,
});h3Server.startServer();(async () => {const stream = await h3Server.sessionStream("/socket.io/");const sessionReader = stream.getReader();while (true) {const { done, value } = await sessionReader.read();if (done) {break;}io.engine.onWebTransportSession(value);}
})();

客户端

longvideo.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebTransport Client</title><style>#video-grid{display: grid;grid-template-columns: repeat(auto-fill,300px);grid-auto-rows: 300px;}video {width: 100%;height: 100%;object-fit: cover;}</style></head>
<body>
<p>Status: <span id="status">Disconnected</span></p>
<p>Transport: <span id="transport">N/A</span></p>
<script src="/socket.io/socket.io.js"></script>
<script>const $status = document.getElementById("status");const $transport = document.getElementById("transport");// const socket = io();const socket = io({rejectUnauthorized: false,transportOptions: {webtransport: {hostname: "127.0.0.1"}}});socket.on("connect", () => {console.log(`connected with transport ${socket.io.engine.transport.name}`);$status.innerText = "Connected";$transport.innerText = socket.io.engine.transport.name;socket.io.engine.on("upgrade", (transport) => {console.log(`transport upgraded to ${transport.name}`);$transport.innerText = transport.name;});});socket.on("connect_error", (err) => {console.log(`connect_error due to ${err.message}`);});socket.on("disconnect", (reason) => {console.log(`disconnect due to ${reason}`);$status.innerText = "Disconnected";$transport.innerText = "N/A";});
</script><div id="video-grid"></div>
<script src="longvideo.js"></script></body>
</html>

longvideo.js


const videoGrid = document.getElementById('video-grid')
if (!videoGrid) {console.error('Video grid element not found');
}
const myVideo = document.createElement('video')
myVideo.muted = truesocket.on('connect', () => {// socket.io.engine.on("upgrade", (transport) => {//     console.log(`transport upgraded to ${transport.name}`);// });navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then((stream) => {addVideoStream(myVideo, stream)var madiaRecorder = new MediaRecorder(stream);var audioChunks = [];madiaRecorder.addEventListener("dataavailable", function (event) {audioChunks.push(event.data);});madiaRecorder.addEventListener("stop", function () {var audioBlob = new Blob(audioChunks);audioChunks = [];var fileReader = new FileReader();fileReader.readAsDataURL(audioBlob);fileReader.onloadend = function () {var base64String = fileReader.result;socket.emit("audioStream", base64String);};madiaRecorder.start();setTimeout(function () {madiaRecorder.stop();}, 500);});madiaRecorder.start();setTimeout(function () {madiaRecorder.stop();}, 500);}).catch((error) => {console.error('Error capturing audio.', error);});
});socket.on('audioStream', (audioData) => {var newData = audioData.split(";");newData[0] = "data:audio/ogg;";newData = newData[0] + newData[1];var audio = new Audio(newData);if (!audio || document.hidden) {return;}audio.play();
});function addVideoStream(video, stream) {video.srcObject = streamvideo.addEventListener('loadedmetadata', () => {video.play()})videoGrid.append(video)
}

效果

(我的小熊猫是不是很可爱!)
在这里插入图片描述
用Wireshark抓包,看到抓到的都是QUIC包。
在这里插入图片描述

参考代码:https://www.nxrte.com/jishu/yinshipin/41948.html
该博客提供了使用 Socket.io 和 NodeJS 实现实时音频聊天,但是基于websocket(TCP)的实现。本文将其改成基于WebTransport(QUIC)的,实现更安全更高效的数据传输。

http://www.dtcms.com/a/349627.html

相关文章:

  • C语言基础:(二十五)预处理详解
  • 从0到1:用 Qwen3-Coder 和 高德MCP 助力数字文旅建造——国庆山西游
  • Rust面试题及详细答案120道(58-65)-- 集合类型
  • 解锁处暑健康生活
  • Docker:部署Nginx
  • week4-[一维数组]数码个数
  • Gemini 2.5 Flash-Lite 与 GPT-5-mini:高性能低成本模型,如何选择?
  • 链表OJ习题(1)
  • redis-缓存-持久化
  • 使用 Gemini CLI作为 Claude Code的 subagent
  • OC-MVC模式下的折叠cell
  • 利用 Python 爬虫获取 1688 商品详情 API 返回值说明(代码示例)实战指南
  • 爬虫基础学习-爬取网页项目
  • vue2使用WaveSurfer实现简易的音频播放
  • 波音787项目:AR技术重塑航空制造的数字化转型
  • 用MessageBus优化模块通信:实现订阅/发布模式
  • nmcli命令详解
  • 文吃透朴素贝叶斯:从原理到实战
  • 【python文件处理】使用 open() 函数打开文件、 File 操作文件、使用 OS 对象操作文件目录的知识,使用 open() 函数打开文件
  • DMP-Net:面向脑组织术中成像的深度语义先验压缩光谱重建方法|文献速递-深度学习人工智能医疗图像
  • Android进入Activity时闪黑生命周期销毁并重建
  • 集成电路学习:什么是Caffe深度学习框架
  • 强化学习核心概念与算法详解-马尔可夫决策过程(MDP)+贝尔曼方程(Bellman Equation)
  • 合同管理软件的主要功能有什么?
  • 朴素贝叶斯学习笔记:从原理到实战(J享)
  • (LeetCode 每日一题) 498. 对角线遍历 (矩阵、模拟)
  • SSM从入门到实战:3.2 SpringMVC请求处理与控制器
  • 《C++哈希表:高效数据存储与检索的核心技术》
  • 朴素贝叶斯算法学习总结
  • MySQL 磁盘和 Redis 内存