webrtc实现远程控制-前端部分

3,126 阅读3分钟

最近有一个功能需要在网页web端实现对另一个设备的控制,参考了网上的教程是用webrtc实现,梳理了整体的实现过程

最终效果

GIF 17-26 09 March 2023.gif

整体逻辑

主要分为三部分:web端(控制端)中继服务端被控端,各部分的工作内容如下

web端

  • 建立Websocket服务端通信
  • 建立Webrct 通道与被控端通信
  • 远程桌面视频流的渲染
  • 反向控制(键、鼠:按比例将数据下发)

被控端

  • 建立Websocket服务端通信
  • 建立Webrct 通道与控制端通信
  • 设备屏幕截图、截屏压缩处理、将截取的图片转成流推送【屏幕截图、截屏压缩处理耗时处理】
  • 解析和响应控制(键、鼠:按比例将数据解析后模拟鼠标事件)

中继服务端

它是在建立连接之前交换信息的任何一种通信渠道,

  • turn服务的搭建
  • 开发websocket服务、账号管理、消息转发

流程

整体功能流程

image.png

实现过程

主要梳理的是web前端部分的实现过程,分为4部分:

  • 建立Websocket服务端通信
  • 建立Webrct 通道与被控端通信
  • 远程桌面视频流的渲染
  • 反向控制(键、鼠:按比例将数据下发)

Websocket 与服务端通信

websocket的连接是为了后续建立p2p连接做的前置工作。websocket的通信比较简单,soket连接直接使用new WebSocket(地址)可以实例化sokcet对象sokcet对象的回调函数有onopen/onerror/onclose/onmessage等,可以在对应的回调里执行相关操作

this.socket = new WebSocket(this.socketUrl)

this.socket.onopen = () => {}

this.socket.onerror = (error) => {}

this.socket.onclose = (error) => {}

this.socket.onmessage = (Message) => {}

Webrct 通道建立

Webrct 通道建立也就是我们常提到的p2p连接,第一步控制端被控端都与中继服务建立了websoket连接后,可以借用中继服务交换OfferAnswer,成功建立p2p连接。对应的是流程图中的这部分

image.png

websocket连接成功后,中继服务器转发给控制端connected指令,控制端onmessage接收到指令connected后,控制端要做的事情有:

    1. 执行初始化peer的操作
    1. 创建数据通道
    1. 给中继服务发送ready消息
this.socket.onmessage = (Message) => {
  const obj = JSON.parse(Message.data)
  const command = obj.command
  let data = null
  switch (command) {
    case 'connected': // 链接成功
      // 1. 初始化 peer
      const PeerConnection =
          window.RTCPeerConnection ||
          window.mozRTCPeerConnection ||
          window.webkitRTCPeerConnection
      this.peer = new PeerConnection(SERVERS, PC_OPTIONS)

      // 2. 创建数据通道
      this.channel = this.peer.createDataChannel('msgdatachannel')
      
      // 3. 发送ready消息
      data = JSON.stringify({
        'command': 'ready'
      })
      this.send(data)
      break
    default: {
      console.log(Message.data)
    }
  }
}

中继服务收到ready转发给被控端,被控端也进行初始化peerB的操作,初始化完成同样发送ready中继服务,中继再转发给控制端,控制端就能在onmessage回调里接收到ready指令后,就能进行offer创建的操作,创建offer后,再把offer发送给中继服务

this.socket.onmessage = (Message) => {
  const obj = JSON.parse(Message.data)
  const command = obj.command
  switch (command) {
    case 'ready':
      // 创建offer
      this.peer.createOffer(
        (desc) => {
          this.peer.setLocalDescription(desc, () => {
            const obj = JSON.stringify({
              'command': 'offer',
              'desc': desc
            })
            // 发送offer给中继服务
            this.send(obj)
          }
        }
      )
      break
    default: {
      console.log(Message.data)
    }
  }
}

中继服务收到offer后转发给被控端,被控端创建answer,同样再通过中继服务转发给控制端,此时连接完成,会自动调用peerpeerBonopen事件,后续就可以进行指令的发送和视频流的接收了。

远程桌面视频流的渲染

peer.onaddstream可以监听到视频流,然后通过标签play即可

this.peer.onaddstream = (e) => {
  try {
    const vid2 = document.getElementById('remote-control')
    vid2.srcObject = e.stream
    vid2.onloadedmetadata = function() {
      vid2.play()
    }
  } catch (ex) {
    this.socket.close()
  }
}

鼠标/键盘指令的发送

已经建立了p2p连接,并且在前面已经创建了数据通道,指令可以通过channel.send发送,常用指令定义参考如下

image.png

完整代码

完整代码:github.com/Xmengling/r…

参考文章

www.jianshu.com/p/ad0a7c463…

developer.mozilla.org/en-US/docs/…

原创文章,记录成长,也希望对你有帮助!喜欢请点赞哦~

作者:前端小小梦

主页:了解更多,点击个人主页