Appearance

通过 webrtc和socket 做远程桌面分享

coderzhouyujavascriptwebrtc socket

1. 简单的同文件内两个 peer 服务通信

// 1. 拿到要分享的桌面流
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
// 2. 创建两个端点
/**
     const options = {
                iceServers: [
                    {
                        urls: 'turn:' + 'webrtc-from-chat.glitch.me', // A TURN server
                        username: 'webrtc',
                        credential: 'turnserver'
                    }
                ]
            }
*/
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();

// 推流
for (const track of stream.getTracks()) {
  pc1.addTrack(track, stream);
}
pc1.onnegotiationneeded = async (e) => {
  const offer = await pc1.createOffer();
  await pc1.setLocalDescription(offer);
  await pc2.setRemoteDescription(offer);

  // 创建应答
  const answer = await pc2.createAnswer();
  await pc2.setLocalDescription(answer);
  await pc1.setRemoteDescription(answer);
};

// 设置通信方式
pc1.onicecandidate = (event) => {
  if (event.candidate) {
    pc2.addIceCandidate(event.candidate);
  }
};

pc2.onicecandidate = (event) => {
  if (event.candidate) {
    pc1.addIceCandidate(event.candidate);
  }
};

this.$refs["video"].srcObject = stream;

pc2.addEventListener(
  "track",
  (e) => {
    console.log("得到流", e);
    this.$refs["video1"].srcObject = e.streams[0];
  },
  false
);

2. 通过 socket 分享桌面

使用技术栈 spring-boot + websocket + socketJs 发起端


接受端

<template>
  <div class="applicant">
    <h2 class="title">人脸识别确认</h2>
    <div class="content">
      人脸识别确认
      <video ref="video" autoplay controls width="200"></video>
    </div>
    <div class="option">
      <button @click="handlerClick">确认</button>
      <button>重新选择</button>
    </div>
  </div>
</template>

<script>
import { mapState } from "vuex";

export default {
  name: "FaceConfirm",
  data() {
    return {
      peer: null,
      sendCandidate: false,
      getOfferState: false,
      sendCandidateState: false,
    };
  },
  computed: mapState({}),
  mounted() {
    setTimeout(() => {
      this.$socket.on("closeFaceConfirm", () => {
        if (this.peer) {
          this.peer.close();
        }
        debugger;
        this.peer = null;
        this.$router.push("/");
      });
    }, 1000);
    this.init();
  },
  methods: {
    init() {
      this.peer = new RTCPeerConnection();
      this.peer.onicecandidate = (event) => {
        if (event.candidate && !this.sendCandidate) {
          console.log("发送本地凭证", event.candidate);
          this.$socket.emit("addIceCandidate2", event.candidate);
          this.sendCandidate = true;
        }
      };

      this.$socket.on("receiveOffer", async (offer) => {
        if (this.getOfferState) return;
        console.log(offer, "我收到offer了");
        // 设置offer
        await this.peer.setRemoteDescription(offer);
        // 创建应答
        const answer = await this.peer.createAnswer();
        await this.peer.setLocalDescription(answer);
        console.log(answer);
        // 发送应答给发送端
        this.$socket.emit("receiveAnswer", answer);
        this.getOfferState = true;
      });

      this.$socket.on("addIceCandidate", async (candidate) => {
        if (this.sendCandidateState) return;
        candidate = new RTCIceCandidate(candidate);
        await this.peer.addIceCandidate(candidate);
        this.sendCandidateState = true;
      });

      // 等到页面挂载完成 给 video 推流
      this.peer.addEventListener(
        "track",
        (e) => {
          console.log("得到流", e);
          this.$refs["video"].srcObject = e.streams[0];
        },
        false
      );
    },
    handlerClick() {
      // 提交确定信息给桌面端
      // this.$socket.emit()
    },
  },
};
</script>
Last Updated 2024/3/23 14:35:18