Peer A: 원래 있던 사용자 (새로운 사용자가 방에 참여하면 알림을 받게 되는 사용자)
Peer B: 새로운 사용자
공식문서: https://developer.mozilla.org/ko/docs/Web/API/WebRTC_API/Signaling_and_video_calling
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);
}
}
새로 방에 들어오는 모든 사용자 (첫 사용자 + 이후 사용자 모두 포함)
// 어디서든 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));
}
기존 사용자(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);
})
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 = "";
}
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);});
ICE Candidate는 RTCPeerConnection을 만들때 사용할 수 있는 ICE(Interactive Connectivity Establishment) 후보이다.
ICE 후보자는 WebRTC가 원격 장치와 통신할 수 있는 데 필요한 프로토콜과 라우팅을 설명합니다. WebRTC 피어 연결을 시작할 때, 일반적으로 수많은 후보들이 연결의 각 끝에 의해 제안되며, 그들이 결정한 연결에 대해 상호 합의할 때까지 제안된다. 그런 다음 WebRTC는 후보자의 세부 정보를 사용하여 연결을 시작합니다.
// 연결을 만들 때 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);});
socket.on("ice", (ice) => { console.log("received candidate"); myPeerConnection.addIceCandidate(ice);});
// 연결을 만들 때 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