通过 WebSocket 接收和播放 WSS 协议视频流_wss播放器
1.创建wss协议视频
1.1必备包
npm install ws @ffmpeg-installer/ffmpeg fluent-ffmpeg
说明:安装以下三个包。
1.2代码实现
说明:创建WebSocket服务器,端口为8080
import { WebSocket, WebSocketServer } from \'ws\'; // 导入 WebSocket 和 WebSocketServer 模块import ffmpeg from \'fluent-ffmpeg\'; // 导入 fluent-ffmpeg 模块,用于处理视频流import ffmpegInstaller from \'@ffmpeg-installer/ffmpeg\'; // 导入 ffmpeg-installer 模块,用于获取 FFmpeg 的路径// 设置 FFmpeg 路径ffmpeg.setFfmpegPath(ffmpegInstaller.path); // 将 FFmpeg 的路径设置为安装路径// 创建一个 WebSocket 服务器,监听端口 8080const wss = new WebSocketServer({ port: 8080 });wss.on(\'connection\', (ws) => { // 当有新的客户端连接时触发 console.log(\'新客户端连接\'); // 输出连接信息 // 使用 FFmpeg 转换视频为 MPEG-TS 格式 const command = ffmpeg(\'./ElephantsDream.mp4\') // 指定要转换的视频文件 .format(\'mpegts\') // 设置输出格式为 MPEG-TS .videoCodec(\'mpeg1video\') // 设置视频编码为 MPEG-1 .videoBitrate(\'1000k\') // 设置视频比特率为 1000k .size(\'640x480\') // 设置视频分辨率为 640x480 .audioCodec(\'mp2\') // 添加音频编码为 MP2 .audioBitrate(\'128k\') // 设置音频比特率为 128k .on(\'error\', (err) => { // 处理 FFmpeg 错误 console.error(\'FFmpeg 错误:\', err); // 输出错误信息 ws.close(); // 关闭 WebSocket 连接 }); // 将转换后的数据流通过 WebSocket 发送 const stream = command.pipe(); // 获取转换后的数据流 stream.on(\'data\', (chunk) => { // 当有数据块时触发 if (ws.readyState === WebSocket.OPEN) { // 检查 WebSocket 是否处于打开状态 ws.send(chunk); // 发送数据块给客户端 } }); // 处理断开连接 ws.on(\'close\', () => { // 当客户端断开连接时触发 console.log(\'客户端断开连接\'); // 输出断开连接信息 stream.destroy(); // 销毁数据流以释放资源 });});console.log(\'WebSocket 服务器启动在端口 8080\'); // 输出服务器启动信息
1.3文件启动
node server.js
说明:文件启动,打印日志。
2. 接收播放wss协议视频
2.1api测试
说明:服务器不停推送二进制的数据。
2.2代码实现
说明:安装jsmpeg-players播放器库,对通过websocket接收到的二进制数据进行播放。
时间: {{ formatTime(errorInfo.time) }} 错误次数: {{ errorInfo.count }} {{ errorMessage }} {{ status }} import { ref, reactive, onMounted, onBeforeUnmount } from \'vue\'; // 导入 Vue 的组合式 APIimport JSMpeg from \'jsmpeg-player\'; // 导入 JSMpeg 播放器库const videoCanvas = ref(null); // 用于引用画布元素const status = ref(\'\'); // 用于存储当前状态信息const errorMessage = ref(\'WebSocket连接错误\'); // 用于存储错误信息const errorInfo = reactive({ show: false, // 是否显示错误信息 time: \'\', // 错误发生时间 count: 0 // 错误次数});let player = null; // JSMpeg 播放器实例let ws = null; // WebSocket 实例let connectionTimeout = null; // 连接超时计时器// 格式化时间const formatTime = (timeStr) => { if (!timeStr || timeStr === \'不适用\') return \'--\'; // 如果时间不可用,返回占位符 return timeStr.split(\'.\')[0]; // 去掉毫秒部分};// 记录错误信息const recordError = (message = \'WebSocket连接错误\') => { errorInfo.show = true; // 显示错误信息 errorInfo.time = new Date().toLocaleTimeString(); // 记录当前时间 errorInfo.count++; // 错误次数加一 errorMessage.value = message; // 更新错误信息};// 重试连接const retryConnection = () => { if (connectionTimeout) { clearTimeout(connectionTimeout); // 清除连接超时计时器 } if (ws) { ws.close(); // 关闭现有 WebSocket 连接 } if (player) { player.destroy(); // 销毁播放器实例 player = null; // 重置播放器实例 } initVideo(); // 重新初始化视频连接};// 初始化视频连接const initVideo = () => { try { const streamUrl = \'ws://localhost:8080\'; // WebSocket 服务器地址 status.value = \'正在连接...\'; // 更新状态信息 // 设置连接超时 connectionTimeout = setTimeout(() => { if (ws && ws.readyState !== WebSocket.OPEN) { ws.close(); // 如果连接未打开,关闭连接 recordError(\'连接超时\'); // 记录连接超时错误 } }, 5000); // 创建WebSocket连接 ws = new WebSocket(streamUrl); ws.binaryType = \'arraybuffer\'; // 设置数据类型为二进制数组 let dataReceived = false; // 标记是否收到数据 let dataTimer = null; // 数据接收超时计时器 ws.onopen = () => { clearTimeout(connectionTimeout); // 清除连接超时计时器 status.value = \'连接成功,等待数据...\'; // 更新状态信息 // 设置数据接收超时 dataTimer = setTimeout(() => { if (!dataReceived) { recordError(\'连接成功但未收到有效数据\'); // 记录未收到数据错误 ws.close(); // 关闭连接 } }, 8000); }; ws.onmessage = (event) => { // 检查数据包是否有效 if (event.data instanceof ArrayBuffer) { dataReceived = true; // 标记已收到数据 clearTimeout(dataTimer); // 清除数据接收超时计时器 if (event.data.byteLength > 0) { console.log(\'收到数据包:\', event.data.byteLength, new Date().toLocaleTimeString()); // 如果播放器未初始化,初始化播放器 if (!player) { status.value = \'收到数据,初始化播放器...\'; // 更新状态信息 startPlayer(streamUrl); // 初始化播放器 } // 尝试手动处理数据 try { if (player && player.source) { const data = new Uint8Array(event.data); // 检查数据是否为MPEG-TS格式 if (data[0] === 0x47) { // MPEG-TS同步字节 player.source.write(data); // 写入数据到播放器 } else { console.warn(\'收到非MPEG-TS格式数据\'); // 警告非预期格式数据 } } } catch (e) { console.error(\'数据处理错误:\', e); // 输出数据处理错误信息 } } } }; // 其他事件处理保持不变... } catch (error) { // 错误处理保持不变... }};// 初始化播放器const startPlayer = (streamUrl) => { try { player = new JSMpeg.Player(streamUrl, { canvas: videoCanvas.value, // 指定画布元素 autoplay: true, // 自动播放 audio: true, // 启用音频 audioBufferSize: 512 * 1024, // 音频缓冲区大小 loop: true, // 循环播放 videoBufferSize: 1024 * 1024 * 2, // 视频缓冲区大小 onSourceEstablished: () => { console.log(\'视频源已建立\'); // 输出视频源建立信息 status.value = \'视频播放中\'; // 更新状态信息 }, onSourceCompleted: () => { console.log(\'视频源已完成\'); // 输出视频源完成信息 }, onStalled: () => { console.log(\'播放停滞\'); // 输出播放停滞信息 } }); } catch (error) { recordError(`播放器初始化失败: ${error.message}`); // 记录播放器初始化错误 console.error(\'播放器错误:\', error); // 输出播放器错误信息 }};// 组件挂载时初始化视频连接onMounted(() => { initVideo();});// 组件卸载前清理资源onBeforeUnmount(() => { if (connectionTimeout) { clearTimeout(connectionTimeout); // 清除连接超时计时器 } ws?.close(); // 关闭 WebSocket 连接 player?.destroy(); // 销毁播放器实例});.video-container { width: 100%; height: 100%; position: relative; /* 设置容器为相对定位 */}canvas { width: 100%; height: 100%; background: #000; /* 设置画布背景为黑色 */}.status { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* 居中显示状态信息 */ background: rgba(0,0,0,0.7); /* 半透明背景 */ color: #fff; /* 白色文字 */ padding: 12px 20px; /* 内边距 */ border-radius: 4px; /* 圆角边框 */ text-align: center; /* 文本居中 */}.error-status { color: #ff6b6b; /* 错误状态文字颜色 */}.retry-btn { margin-top: 10px; /* 按钮顶部外边距 */ padding: 5px 10px; /* 按钮内边距 */ background: #3498db; /* 按钮背景颜色 */ border: none; /* 无边框 */ border-radius: 4px; /* 圆角边框 */ color: white; /* 按钮文字颜色 */ cursor: pointer; /* 鼠标指针样式 */}.retry-btn:hover { background: #2980b9; /* 按钮悬停背景颜色 */}
2.3播放
说明:打开对应文件的路由,播放视频。
3.附件
3.1视频链接
说明:https://live.csdn.net/v/474456