WebRTC 使用之 —— 使用 RTCPeerConnection 建立视频连接 | 掘金技术征文

2,226

接下来的例子,将展示如何使用 WebRTC 的 RTCPeerConnection API 建立视频连接。

创建工程

创建如下的工程:

一个 index.html + 一个main.js。

在 html 中添加 video

在 index.html 中,添加两个 video,分别是 localVideo 和 remoteVideo,用于模拟建立视频连接的两端。

在创建三个按钮:Start、Call、Hang Up,用来模拟视频建立过程中的操作:

Start:localVideo 打开摄像头并显示视频

Call:localVideo 向 remoteVideo 发出请求,并显示 remoteVideo 的视频

Hang Up:remoteVideo 挂断

因此 index.html 的代码为:

<!DOCTYPE html>

<html>

<body>

<div id="container">

    <video id="localVideo" playsinline autoplay muted></video>
    <video id="remoteVideo" playsinline autoplay></video>

    <div class="box">
        <button id="startButton">Start</button>
        <button id="callButton">Call</button>
        <button id="hangupButton">Hang Up</button>
    </div>

</div>

<script src="js/main.js" async></script>

</body>
</html>

显示的效果如下:

Index.html 里的布局实现后,开始在 main.js 里实现具体的功能。

Start 功能实现

Start 功能要实现 localVideo 打开摄像头并显示视频的功能。

这时候需要用到 getUserMedia 这个 API。

代码如下:

let localStream;
const startButton = document.getElementById('startButton');
startButton.addEventListener('click', start);

async function start() {
  console.log('Requesting local stream');
  startButton.disabled = true;
  try {
    const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
    console.log('Received local stream');
    localVideo.srcObject = stream;
    localStream = stream;
    callButton.disabled = false;
  } catch (e) {
    alert(`getUserMedia() error: ${e.name}`);
  }
}

只要点击 Start,就会在网页上展示当前的视频,如下:

![image-20190603030244840](/Users/koude/Library/Application Support/typora-user-images/image-20190603030244840.png)

Call 功能实现

Call 功能要实现 localVideo 向 remoteVideo 发出请求,并显示 remoteVideo 的视频。

Call 功能是一个核心功能,会为两端的视频建立连接,需要用到 RTCPeerConnection 这个 API。

首先获取 localVideo 和 remoteVideo 这两个 Video,并创建两个变量来分别代表这两个 Video 的 PeerConnection,localVideo 为 pc1,remoteVideo 为 pc2:

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');

let pc1;
let pc2;

然后获取 Call 按钮,并为其分配点击事件:

const callButton = document.getElementById('callButton');
callButton.addEventListener('click', call);
async function call() {
}

在 call() 里面就要使用到 RTCPeerConnection 来建立连接,一个是 pc1,一个是 pc1,然后 pc1 向 pc2 建立连接:

async function call() {
  callButton.disabled = true;
  hangupButton.disabled = false;
  console.log('Starting call');
  caches
  pc1 = new RTCPeerConnection();
  console.log('Created local peer connection object pc1');
  pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
  pc2 = new RTCPeerConnection({});
  console.log('Created remote peer connection object pc2');
  pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
  pc2.addEventListener('track', gotRemoteStream);

  localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
  console.log('Added local stream to pc1');

  try {
    console.log('pc1 createOffer start');
    const offer = await pc1.createOffer(offerOptions);
    await onCreateOfferSuccess(offer);
  } catch (e) {
  }
}

async function onCreateOfferSuccess(desc) {
  console.log(`Offer from pc1\n${desc.sdp}`);
  console.log('pc1 setLocalDescription start');
  try {
    await pc1.setLocalDescription(desc);
  } catch (e) {
  }

  console.log('pc2 setRemoteDescription start');
  try {
    await pc2.setRemoteDescription(desc);
  } catch (e) {
  }

  console.log('pc2 createAnswer start');
  // Since the 'remote' side has no media stream we need
  // to pass in the right constraints in order for it to
  // accept the incoming offer of audio and video.
  try {
    const answer = await pc2.createAnswer();
    await onCreateAnswerSuccess(answer);
  } catch (e) {
  }
}


function gotRemoteStream(e) {
  if (remoteVideo.srcObject !== e.streams[0]) {
    remoteVideo.srcObject = e.streams[0];
    console.log('pc2 received remote stream');
  }
}

async function onCreateAnswerSuccess(desc) {
  console.log(`Answer from pc2:\n${desc.sdp}`);
  console.log('pc2 setLocalDescription start');
  try {
    await pc2.setLocalDescription(desc);
  } catch (e) {
  }
  console.log('pc1 setRemoteDescription start');
  try {
    await pc1.setRemoteDescription(desc);
  } catch (e) {
  }
}

async function onIceCandidate(pc, event) {
  try {
    await (getOtherPc(pc).addIceCandidate(event.candidate));
  } catch (e) {
  }
}

const offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1
};

function getName(pc) {
  return (pc === pc1) ? 'pc1' : 'pc2';
}

function getOtherPc(pc) {
  return (pc === pc1) ? pc2 : pc1;
}

至此,只要点击 Call,就会建立连接,因为这里用到的视频是同一台电脑的,所以画面如下:

Hang Up 功能实现

Hang Up 功能要实现挂断 remoteVideo 的视频连接的功能。很好实现,只要将 pc1和pc2的连接关闭就行,代码如下:

const hangupButton = document.getElementById('hangupButton');
hangupButton.addEventListener('click', hangup);


function hangup() {
  console.log('Ending call');
  pc1.close();
  pc2.close();
  pc1 = null;
  pc2 = null;
  hangupButton.disabled = true;
  callButton.disabled = false;
}

点击 Hang Up 之后的画面如下:

总结

至此一个使用 WebRTC 的 RTCPeerConnection API 实现的简单的视频连接功能就实现完成了。

Agora SDK 使用体验征文大赛 | 掘金技术征文,征文活动正在进行中