> 技术文档 > uniapp微信小程序视频实时流+pc端预览方案_uniapp预览视频

uniapp微信小程序视频实时流+pc端预览方案_uniapp预览视频

方案类型 技术实现 是否免费 优点 缺点 适用场景 延迟范围 开发复杂度 ​WebSocket+图片​ 定时拍照+Base64传输 ✅ 完全免费 无需服务器
纯前端实现 高延迟高流量
帧率极低 个人demo测试
超低频监控 500ms-2s ⭐⭐ ​RTMP推流​ TRTC/即构SDK推流 ❌ 付费方案
(部分有免费额度) 专业直播方案
支持高并发 需流媒体服务器
SDK可能收费 中小型直播场景 1-3s ⭐⭐⭐⭐ ​开源WebRTC​ 自建coturn+mediasoup ✅ 开源免费 超低延迟
完全可控 需自建信令服务器
维护成本高 技术团队内网项目 200-500ms ⭐⭐⭐⭐⭐ ​商业WebRTC​ 腾讯TRTC/声网Agora ❌ 付费方案
(免费试用) 企业级服务
全球节点 按流量/时长计费
绑定厂商 商业视频通话应用 200-800ms ⭐⭐⭐⭐ ​HLS切片方案​ FFmpeg切片+nginx ✅ 服务器可自建免费 兼容所有浏览器
支持CDN分发 延迟10秒以上 非实时录播场景 10s+ ⭐⭐⭐ ​UDP自定义协议​ 开发原生插件 ✅ 协议层免费
❌ 人力成本高 完全自定义优化 需原生开发能力
过审风险 军工/工业特殊场景 200-500ms ⭐⭐⭐⭐⭐⭐

免费方案选择建议:

  1. 完全零成本​:

    • WebSocket图片帧(仅适合原型验证)
    • 开源WebRTC(需技术储备)
  2. 轻度付费​:

    • 腾讯云RTMP(免费10GB/月流量)
    • 阿里云直播(免费20GB/月流量)
  3. 企业级推荐​:

    • 声网Agora(首月赠送1万分钟)
    • 即构科技(首月免费)

下面我将介绍WebSocket+图片帧的实现方法:

 

WebSocket + 图片帧传输方案详解

该方案是 ​Uniapp微信小程序 + PC端视频实时预览​ 的一种 ​低成本、纯前端实现​ 的技术方案,适用于 ​低帧率、非严格实时​ 的场景。


🔹 方案原理

  1. 小程序端​:

    • 使用  组件获取实时画面。
    • 通过 uni.createCameraContext().takePhoto() ​定时拍照​(如300ms/次)。
    • 将图片转为 ​Base64​ 格式,通过 ​WebSocket​ 发送到服务器。
  2. PC端​:

    • 建立 WebSocket 连接,接收 Base64 图片数据。
    • 使用  或  ​连续渲染图片,模拟视频流效果。

uniapp微信小程序端:

        export default { data() { return { pushState: \"未连接\", devicePosition: \'front\', flash: \'off\', timer: null, ws: null } }, methods: { flipCamera() { this.devicePosition = this.devicePosition === \'back\' ? \'front\' : \'back\'; }, switchFlash() { this.flash = this.flash === \'off\' ? \'torch\' : \'off\'; }, startPushing() { // 如果已连接,则不再重复连接 if (this.pushState === \'连接成功\') return; const randomToken = new Date().getTime(); const url = \'ws://192.168.1.34:7097/liveWebSocket?linkInfo=a-\' + randomToken; this.ws = uni.connectSocket({ url, success: () => { console.log(\'正在尝试连接WebSocket\', url); } }); this.ws.onOpen(() => { uni.showToast({ title: \'连接成功\' }); this.pushState = \'连接成功\'; this.startCapture(); }); this.ws.onError((err) => { uni.showToast({ title: \'连接异常\', icon: \'none\' }); this.pushState = \'连接异常\'; this.stopPushing(); }); this.ws.onClose(() => { this.pushState = \'已关闭\'; this.stopPushing(); }); }, stopPushing() { if (this.timer) { clearInterval(this.timer); this.timer = null; } if (this.ws) { this.ws.close(); this.ws = null; this.pushState = \"未连接\"; } }, startCapture() { const context = uni.createCameraContext(this); // 调整为300ms间隔,减轻设备压力 this.timer = setInterval(() => { context.takePhoto({ quality: \'low\', success: (res) => { this.processAndSendImage(res.tempImagePath); }, fail: (err) => { console.error(\'拍照失败:\', err); } }); }, 300); }, processAndSendImage(tempImagePath) { uni.getFileSystemManager().readFile({ filePath: tempImagePath, encoding: \'base64\', success: (res) => { const base64Image = `data:image/jpeg;base64,${res.data}`; if (this.ws) { this.ws.send({  data: base64Image,  success: () => { console.log(\'图片发送成功\'); this.cleanTempFile(tempImagePath);  },  fail: (err) => { console.warn(\'图片发送失败:\', err);  } }); } }, fail: (err) => { console.warn(\'读取图片失败:\', err); } }); }, cleanTempFile(filePath) { setTimeout(() => { uni.getFileSystemManager().removeSavedFile({ filePath, success: () => { console.log(\'临时文件已删除\'); }, fail: (err) => { console.warn(\'删除临时文件失败:\', err); } }); }, 2000); }, error(e) { console.error(\'摄像头错误:\', e); } }, onUnload() { this.stopPushing(); }}

pc端预览:

   管理员监控页面  
0\" style=\"display: flex;\">
状态:{{item.status}}
接口数据:
视频列表: 0\">

{{item2}}

new Vue({ el: \'#app\', data: { datas: \"\", videos: [ // { // sessionId: \'1\', // status: \'未连接\', // videoSrc: \'\' //图片帧 // } ] }, mounted() { }, methods: { // 开始请求 toSend() { //断开所有webscoket连接 if (this.videos && this.videos.length > 0) { this.videos.forEach(item => { if (item.ws) { item.ws.close(); } }); } this.datas = \"\"; this.videos = []; // 请求直播人员列表 fetch(\'http://192.168.1.34:7097/liveWebStock/getAcceptList\') .then(response => response.json()) .then(data => { if (data.code == 200) { // console.log(6666, data.data); this.datas = data.data; // 初始化每个视频流对象并建立 WebSocket this.videos = data.data.map(item => ({ ...item, status: \'未连接\', videoSrc: \'\', ws: null })); // 建立 WebSocket 连接 this.videos.forEach(item => { this.initWebSocket(item.sessionId); }); } }) .catch(error => { console.error(\'请求直播人员列表失败:\', error); }); }, initWebSocket(sessionId) { if (!sessionId) return; const wsUrl = `ws://192.168.1.34:7097/liveWebSocket?linkInfo=b-${sessionId}`; const index = this.videos.findIndex(v => v.sessionId === sessionId); if (index === -1) return; const ws = new WebSocket(wsUrl); ws.onopen = () => { this.$set(this.videos, index, { ...this.videos[index], status: \'已连接到服务器\', ws }); }; // 处理接收到的数据 ws.onmessage = (event) => { console.log(\"接收到base64图片\", event); // 假设是 base64 数据 const base64Data = event.data; const url = base64Data; this.$set(this.videos, index, { ...this.videos[index], videoSrc: url }); }; ws.onerror = (error) => { this.$set(this.videos, index, { ...this.videos[index], status: `WebSocket 错误: ${error.message}` }); console.error(`WebSocket 错误 (${sessionId}):`, error); }; ws.onclose = () => { this.$set(this.videos, index, { ...this.videos[index], status: \'WebSocket 连接已关闭\' }); }; } } });