文章目录

  • 1. 写在前面
  • 2. 项目需求 (安全帽视频对接)
    • 2.1 完成效果
  • 3. 开始搞,uni-app 开发H5视频对接
    • 3.1 html代码
    • 3.2 js 代码(核心步骤)
      • 3.2.1 根据接口获取安全帽在线的房间号,点击在线的安全帽列表,进入视频页面观看
      • 3.2.2 进入房间后,首先 `uni.connectSocket` 创建初始化websocket连接
      • 3.2.3 `uni.onSocketOpen` 打开连接,向服务端发送进入房间信息;并且创建心跳,每隔10s发送心跳信息。用于判断连接状态,如果断开,需要重新连接。
      • 3.2.4 `uni.onSocketMessage` 进行服务端响应消息监听,
      • 3.2.5 `connSignalServer` 进行连接音视频
      • 3.2.6 `connFun` 进行监听服务端返回的值,然后进行一些逻辑操作。
      • 3.2.7 `createPeerConnection` 创建本地流媒体链接
      • 3.2.8 `call` 创建`createOffer` ,设置sdp,发送 message消息,发送sdp
      • 3.2.10 设置拉流到video中
  • 4. webrtc 媒体协商过程解释
    • 4.1 媒体协商流程
    • 4.2 媒体协商方法

1. 写在前面

之前本人一直没有做过webrtc相关的开发(进行实时语音对话或视频对话的),我上家公司的老板突然找到我,让我帮他做一个webrtc的模块功能。通过uni-app 去开发,然后打包到H5网页上进行音视频沟通。我主要是没有接触过,也不知道怎么去做,只是会uni-app,但是去对接webrtc 拿到手一脸雾水。不知道从何开始。后面各种百度,各种查资料,算是把这个功能搞出来了,现在想起来还是挺心酸的。

  1. uni-app websocket 开发参考 https://uniapp.dcloud.io/api/request/websocket.html
  2. webrtc 开发参考 https://webrtc.github.io/samples/

2. 项目需求 (安全帽视频对接)

安全帽是一个特制的帽子,不同与普通的安全帽,而是一个有电源开关,有摄像头,有开灯光的帽子。

通俗点就是,安全帽那边是一个工地的工作人员(A端),带上帽子进行作业。遇到困难,需要办公室高级技术人员(B端)去指挥工人作业操作,安全帽A端是无法看到B端,但是B端可以通过在H5网页上,然后进行观A端看那边的作业情况,进行指挥。

2.1 完成效果

3. 开始搞,uni-app 开发H5视频对接

3.1 html代码

就是声明一个video标签,进行视频播放使用,(关键的,那几个按钮的不重要这里不写了)

<template><view class="container"><div class="video-cont"><video id="remoteVideo" :muted="muted" autoplay></video></div></view>
</template>

3.2 js 代码(核心步骤)

3.2.1 根据接口获取安全帽在线的房间号,点击在线的安全帽列表,进入视频页面观看


data 数据

return {hatid: '', //房间号ws: null,//wssocket: null,//socketstate: 'init',//状态pc: null,localStream: null,socketOpen: false,muted: false, //是否静音
};

进入页面获取列表传的 hatid ,调用 initWebSocket 方法

onLoad(option) {if (option.hat_id) {this.hatid = option.hat_id}
},
onReady() {this.initWebSocket(this.hatid); //连接WebSocket
},

3.2.2 进入房间后,首先 uni.connectSocket 创建初始化websocket连接

this.ws = uni.connectSocket({url: "wss://rtc.xxxxxxx.cn/ws",//你自己的地址success: (res) => {console.log("WebSocket服务连接成功!");},fail: (err) => {uni.showToast({title: JSON.stringify(err),icon: 'error'});}
});

3.2.3 uni.onSocketOpen 打开连接,向服务端发送进入房间信息;并且创建心跳,每隔10s发送心跳信息。用于判断连接状态,如果断开,需要重新连接。

// 2. 连接打开
uni.onSocketOpen((res) => {this.socketOpen = true// 打开后发送一条消息uni.sendSocketMessage({data: `{"isHat":"N" ,"type":"on_line" ,"hatId":${this.hatid} }`});// 10s 发送一次心跳this.heartbeatInterval = setInterval(() => {// console.log("轮询监听WebSocket状态:" + this.ws.readyState)// CONNECTING 0  OPEN 1 打卡状态  CLOSING 2  CLOSED 3 断开状态if (this.ws.readyState === this.ws.OPEN) {// 打开状态uni.sendSocketMessage({data: "keep alive admin:" + 'xiehao' + " connect:" + this.hatid,});} else if (this.ws.readyState === this.ws.CLOSED) {// 判断如果断开,需要重新链接this.initWebSocket(this.hatid)} else if (this.ws.readyState === this.ws.CLOSING || this.ws.readyState === this.ws.CONNECTING) {//不用管}}, 10000)
});

可以看到,我们已经创建了连接,并且在发送心跳信息,服务的响应消息为的 allow

3.2.4 uni.onSocketMessage 进行服务端响应消息监听,

这里判断如果返回消息为full 则是房间已满,无法进行查询通话
如果返回 allow 则没有人进入房间,允许进入房间进行通话,然后进入方法 connSignalServer 连接音视频

uni.onSocketMessage((res) => {var msg = res.data;if (msg.indexOf("full") !== -1) {uni.showToast({title: '当前安全帽有人在查看,您暂时无法查看!',icon: 'error'});this.state = 'full';} else if (msg.indexOf("allow") !== -1) {console.log("准备连接音视频。。。。。。")this.connSignalServer(); //连接音视频}
});

3.2.5 connSignalServer 进行连接音视频

navigator.mediaDevices 进行媒体兼容判断,如果浏览器支持播放,则进入connFun 方法
这里涉及到一个开发问题,则是在本地开发环境,浏览器访问需要使用https或者localhost进行访问,不能使用http进行访问,否则会走不下去,报错进入handleError 方法。

connSignalServer() {// 开启本地视频 if (!navigator.mediaDevices ||!navigator.mediaDevices.getUserMedia) {alert("getUserMedia is not supported!")return;} else {//1 ===============配置音视频参数===============let constraints = {video: false, //先设置为false进行调试audio: true}navigator.mediaDevices.getUserMedia(constraints).then(this.getMediaStream).catch(this.handleError)}
},
getMediaStream(stream) {this.localStream = stream;//这个函数的调用时机特别重要 一定要在getMediaStream之后再调用,否则会出现绑定失败的情况this.connFun();
},
handleError(err) {if (err) console.error("getUserMedia  error:", err);
}

3.2.6 connFun 进行监听服务端返回的值,然后进行一些逻辑操作。

1 创建 socket 连接,emit 发送 join 进入房间 ,服务的正常会返回 joinedotherjoin (这个是根据前端和后端协商的,并不是固定的,只是我这里是这个。)
2 监听返回 joined 进入 createPeerConnection 方法

3 监听返回 otherjoin 进入 call 进行媒体协商

import io from './js/socket.io.js'
connFun() {this.socket = io('https://rtc.xxxxxxx.cn/');this.socket.on('joined', (roomid, id) => {this.state = 'joined';this.createPeerConnection()});this.socket.on('otherjoin', (roomid, id) => {this.state = 'joined_conn';//媒体协商this.call();});this.socket.on('message', (roomid, id, data) => {//媒体协商if (data) {if (data.type === 'offer') {this.pc.setRemoteDescription(new RTCSessionDescription(data));this.pc.createAnswer().then(this.getAnswer).catch(this.handleAnswerError);} else if (data.type === 'answer') {this.pc.setRemoteDescription(new RTCSessionDescription(data));} else if (data.type === 'candidate') {var candidate = new RTCIceCandidate({sdpMLineIndex: data.label,candidate: data.candidate});} else {console.error('the message is invalid!', data)}}});if (this.socket.emit()) {this.socket.emit('join', this.hatid);}return;
},
getAnswer(desc) {this.pc.setLocalDescription(desc);this.socket.emit('message', this.hatid, desc);
},
handleAnswerError(err) {console.error('Failed to get Answer!', JSON.stringify(err));
},

3.2.7 createPeerConnection 创建本地流媒体链接

createPeerConnection() {if (!this.pc) {this.pc = new RTCPeerConnection({'iceServers': [{'urls': 'turn:175.178.21.191:xxxx','credential': 'xxxxxxxx','username': 'xxxx'}],});this.pc.onicecandidate = (e) => {if (e.candidate) {this.socket.emit('message', this.hatid, {type: 'candidate',label: e.candidate.sdpMLineIndex,id: e.candidate.sdpMid,candidate: e.candidate.candidate});}}this.pc.ontrack = (e) => {if (e.streams.length > 0) {let videoElement = document.getElementsByTagName('video')[0]videoElement.srcObject = e.streams[0];}}}if (this.pc === null || this.pc === undefined) {console.log('pc is null or undefined!');return;}if (this.localStream === null || this.localStream === undefined) {console.log('localStream is null or undefined!');// return;}if (this.localStream) {this.localStream.getTracks().forEach((track) => {this.pc.addTrack(track, this.localStream);});}
},

3.2.8 call 创建createOffer ,设置sdp,发送 message消息,发送sdp

在3.2.6 中 的方法,已经监听了服务的返回 message 消息

call() {if (this.state === 'joined_conn') {if (this.pc) {var options = {offerToReceiveAudio: 1,offerToReceiveVideo: 1}this.pc.createOffer(options).then(this.getOffer).catch(this.handleOfferError);}}
},
getOffer(desc) {this.pc.setLocalDescription(desc);if (this.socket) {this.socket.emit('message', this.hatid, desc);}
},
handleOfferError(err) {console.error('Failed to get Offer!', JSON.stringify(err));
},

3.2.10 设置拉流到video中

  1. 这里使用原生的 document.getElementsByTagName('video')[0] 去获取,不使用refs,使用refs会报错
  2. 这里使用srcObject 不能使用src去设置
this.pc.ontrack = (e) => {if(e.streams.length > 0) {let videoElement = document.getElementsByTagName('video')[0]videoElement.srcObject = e.streams[0];}
}

4. webrtc 媒体协商过程解释

媒体协商是为了保证交互双方通过交换信息来保证交互的正常进行,比如A用的是H264编码,通过协商告知B,B来判断自己是否可以进行相应的数据解析来确定是否可以进行交互通信。WebRTC默认情况下使用的V8引擎。

4.1 媒体协商流程

  1. 首先发起端要创建一个offer,并调用setLocalDescription设置本地的SDP
  2. 然后通过信令服务器将含有SDP的offer设置给对端
  3. 对端拿到此offer以后调用setRemoteDescription将此SDP信息保存
  4. 对端创建一个answer,并调用setLocalDescription设置本地的SDP
  5. 通过信令服务器将含有SDP的answer发送给发起端
  6. 发起端调用setRemoteDescription将此SDP信息保存

4.2 媒体协商方法

  1. createOffer
  2. createAnswer
  3. setLocalDescription
  4. setRemoteDescription

uni-app webrtc 实现H5音视频通讯相关推荐

  1. 音视频通讯QoS技术及其演进

    利用多种算法和策略进行网络传输控制,最大限度满足弱网场景下的音视频用户体验. 良逸|技术作者 01 什么是QoS?音视频通讯QoS是哪一类? QoS(Quality of Service)是服务质量的 ...

  2. 基于webrtc的一对多音视频通讯

    基于webrtc的一对多音视频通讯 本次实验使用windows平台,其他平台如html5.android.ios.linux.mac等思路大同小异,上一篇文章已经提及,在此不再赘述. 在此唯一对初学者 ...

  3. 如何使用Intel CS for WebRTC 快速搭建实时音视频通讯系统

    如何使用Intel CS for WebRTC 快速搭建实时音视频通讯系统 Intel CS for WebRTC是一套完整的WebRTC的通讯架构套件,包括了服务端软件和客户端SDK,其中客户端SD ...

  4. uniapp实现音视频通讯

    uniapp实现音视频通讯 还在为uniapp的音视频通讯发愁吗?anyRTC为解决广大开发者需求,特别研发了uniapp版的音视频通讯. anyRTC 实时通信包括两个模块: 实时音视频模块- 音视 ...

  5. anyRTC聊聊【子弹短信】的音视频通讯

    在即时通讯(或者说IM)领域,可能一提起这个词,微信和QQ肯定处于这一领域的霸主地位,因为小米的"米聊"悄无声息的走出了大众的视野,阿里的"来往"也在风生水起的 ...

  6. WebRTC / Jitsi / 多人视频通讯常用架构 Mesh / MCU / SFU

    问题:为什么要搞这么多架构? WebRTC 虽然是一项主要使用 P2P 的实时通讯技术,本应该是无中心化节点的,但是在一些大型多人通讯场景,如果都使用端对端直连,端上会遇到很带宽和性能的问题,所以就有 ...

  7. iOS WebRTC多人音视频建立的流程

    前言 本文主要以"代码是最好的注释"为基点,介绍在处理iOS端多人音视频的建立流程. 在看本篇前建议先了解一下多人音视频通讯现在的常用架构,参考<WebRTC多人音视频聊天架 ...

  8. 基于WebRTC实现1v1音视频聊天室

    一. 前言 WebRTC(Web Real-Time Communication)旨在将实时通信功能引入到浏览器,用户无需安装其他任何软件或插件即可在浏览器间进行实时通信功能.本文介绍基于 WebRT ...

  9. 从无到有实现音视频通讯

    由于近年国内外疫情肆虐,全国中小学都开始实行网上授课,很多公司也纷纷推出一些音视频通讯类的产品,由于从无到有研发音视频成本较高,所以大部分公司选择使用由 anyRTC 提供的 RTC SDK 进行开发 ...

最新文章

  1. pinctrl框架【转】
  2. 程序员眼中的UML(2)--克服用例图的恐惧
  3. python list超出范围_使用lxmldjango/python-list索引超出范围
  4. 避免App沦为“僵尸”的12个秘诀
  5. 【开发管理类软件必备知识视频教程之二】登录窗体后台注意事项
  6. java jaspersoft,Jaspersoft Studio
  7. classpath*: 和classpath:有什么区别_我们可以从Java“HelloWorld”中学到什么?
  8. LeetCode MySQL 1398. 购买了产品A和产品B却没有购买产品C的顾客
  9. javascript读取用户名和计算机名
  10. @Html.ActionLink方法
  11. tensorflow画损失函数的代码_使用TensorFlow编写您的第一个神经网络
  12. double类型的数值转为小数点2位
  13. 大数据十道经典海量数据处理面试题与十个方法大总结
  14. Qt实现全局键盘事件监听器-Windows
  15. 进击的海姆达尔Heimdallr,2021年链游最后一趟财富专列
  16. linux常见服务解释
  17. easyexcel使用问题处理
  18. 线上会议竞品调研报告
  19. 21个经典的哲理故事
  20. springboot-No7 加入异常拦截机制ExceptionHandler

热门文章

  1. android:关于字体问题
  2. c语言编辑mapgis花纹库,自定义编辑section角度花纹库
  3. FinePrint.PdfFactory.Pro.v3.51.Incl.Keymaker-ZWT
  4. android 4.2 关闭 strict mode,Strictmode的使用
  5. eclipse右下角一直提示“jpa project change event handler” 用着很卡
  6. python中until的用法_python之selenium随记(几种等待的用法)
  7. 研究生面试英语口语常见话题
  8. 用python编写程序 ---简单的海龟作图
  9. 华硕rog魔霸新锐2022和联想拯救者Y9000P哪个好
  10. 作为数据分析师,如何评估活动效果