WebRTC의 동작 및 구현 (by. socket.io)

이윤우·2022년 1월 23일
1

WebRTC

목록 보기
3/4
post-thumbnail

Introduction

Peer A: 원래 있던 사용자 (새로운 사용자가 방에 참여하면 알림을 받게 되는 사용자)

Peer B: 새로운 사용자

공식문서: https://developer.mozilla.org/ko/docs/Web/API/WebRTC_API/Signaling_and_video_calling



1. Offers (Peer A)

1) 화상 화면 가져오기 - getUserMedia()

const myFace = document.getElementById("myFace")

// 방 입장하기 클릭 시 startMedia 함수 호출
function handleWelcomeSubmit(event) {
  event.preventDefault();
  const input = welcomeForm.querySelector("input");
  socket.emit("join_room", input.value, startMedia);
  input.value = "";
}

// 방 입자 화면 hidden, 화상화면 display
await function initCall() {
  welcome.hidden = true;
  call.hidden = false;
  await getMedia();
};

// 화상화면 가져오기
let constraints = {
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}

let myStream;

async function getMedia() {
  let constraints = {
    audio: true,
    video: {
      width: { min: 1024, ideal: 1280, max: 1920 },
      height: { min: 776, ideal: 720, max: 1080 }
    }
  }
  try {
    myStream = await navigator.mediaDevices.getUserMedia(constraints);
    myFace.srcObject = stream
  } catch(err) {
    console.log(err);
  }
}

2) peerConnection 만들기 + stream 추가 - addStream()

새로 방에 들어오는 모든 사용자 (첫 사용자 + 이후 사용자 모두 포함)

  • PeerConnection 을 만들고 그 안에 자신의 영상과 오디오를 담는다
// 어디서든 PeerConnection을 가져오기 위해 선언
let myPeerConnection;

// 서버에 연결되었을 때 RTC 연결을 만들어 주기 위해 startMedia 함수 안에서 호출
async function initCall() {
  welcome.hidden = true;
  call.hidden = false;
  await getMedia();
  // 연결 설정 함수 호출
  makeConnection();
}

// 새로 방에 연결되는 모든 사용자
function makeConnection() {
  myPeerConnection = new RTCPeerConnection();
  myStream
    .getTracks()
    .forEach((track) => myPeerConnection.addTrack(track, myStream));
}

3) Offer 만들기 - createOffer()

기존 사용자(Peer A) 에서 자신의 stream을 담은 PeerConnection 을 통해 offer을 만들다.

만들어진 offer를 description 형태로 바꿔준다.

바꿔준 offer를 서버로 보낸다

// Only Peer A
socket.on("welcome", async () => {
  const offer = await myPeerConnection.createOffer();
  myPeerConnection.setLocalDescription(offer);
  socket.emit("offer", offer, roomName);
})

offer 메세지를 받은 서버는 offer 요청을 사용자(Peer B - 새로운 사용자)에게 보낸다

// To Peer B
socket.on("offer", (offer, roomName) => {
  socket.to(roomName).emit("offer", offer);
})


2. Answers (Peer B)

1) setRemoteDescription()

Peer B에서 자신의 PeerConnection에 Peer A의 description을 세팅

// Only Peer B
socket.on("offer", (offer) => {
  myPeerConnection.setRemoteDescription(offer);
})

위의 코드는 논리적으로는 맞지만 에러가 생김.

==Web Socket의 속도가 media를 가져오고 PeerConnection을 만드는 것보다 빠르기 때문==

따라서 실행 순서를 media를 가져오고 Connection을 만들고 그 다음 서버에 참가를 알리는 것으로 바꿈

  • 기존 코드

    function handleWelcomeSubmit(event) {
      event.preventDefault();
      const input = welcomeForm.querySelector("input");
      // 서버에 startMedia 함수 보내고 서버에서 실행
      socket.emit("join_room", input.value, startMedia);
      input.value = "";
    }
  • 바뀐 코드

    async function handleWelcomeSubmit(event) {
      event.preventDefault();
      const input = welcomeForm.querySelector("input");
      // 1. Media를 가져온다 (Conncetion 함수 포함되어 있음)
      await startMedia();
      // 2. 서버에 참가 알림
      socket.emit("join_room", input.value);
      input.value = "";
    }

2) createAnswer()

Peer B 에서 answer를 만들고 서버로 보내준다.

// Only Peer B
socket.on("offer", async (offer) => {
  myPeerConnection.setRemoteDescription(offer);
  const answer = await myPeerConnection.createAnswer();
  myPeerConnection.setLocalDescription(answer);
  socket.emit("answer", answer, roomName);
});
socket.on("answer", (answer, roomName) => {  socket.to(roomName).emit("answer", answer);});

공식 문서: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Build_a_phone_with_peerjs/Connect_peers/Answer_a_call



3. IceCandidate

ICE Candidate는 RTCPeerConnection을 만들때 사용할 수 있는 ICE(Interactive Connectivity Establishment) 후보이다.

ICE 후보자는 WebRTC가 원격 장치와 통신할 수 있는 데 필요한 프로토콜과 라우팅을 설명합니다. WebRTC 피어 연결을 시작할 때, 일반적으로 수많은 후보들이 연결의 각 끝에 의해 제안되며, 그들이 결정한 연결에 대해 상호 합의할 때까지 제안된다. 그런 다음 WebRTC는 후보자의 세부 정보를 사용하여 연결을 시작합니다.

1) Ice Candidate event를 listen

// 연결을 만들 때 event listenfunction makeConnection() {  myPeerConnection = new RTCPeerConnection();  // Ice candidate event listen  myPeerConnection.addEventListener("icecandidate", handleIce);  myStream    .getTracks()    .forEach((track) => myPeerConnection.addTrack(track, myStream));}function handleIce(data) {  socket.emit("ice", data.candidate, roomName);}
socket.on("ice", (ice, roomName) => {  socket.to(roomName).emit("ice", ice);});

2) addICECandidate()

socket.on("ice", (ice) => {  console.log("received candidate");  myPeerConnection.addIceCandidate(ice);});

3) addStream event를 listen

// 연결을 만들 때 event listenfunction makeConnection() {  myPeerConnection = new RTCPeerConnection();  myPeerConnection.addEventListener("icecandidate", handleIce);  // addStream event event listen  myPeerConnection.addEventListener("addstream", handleAddStream);  myStream    .getTracks()    .forEach((track) => myPeerConnection.addTrack(track, myStream));}function handleAddStream(data) {  const peerFace = document.getElementById("peerFace");  peerFace.srcObject = data.stream;}

공식 문서: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate

0개의 댓글