Search
Duplicate
🌍

간단한 WebRTC 실습

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
React
Nest
WebRTC
Scrap
태그
9 more properties

1. WebRTC 를 이용한 P2P 연결 순서

1.
Fetching
a.
webRTC의 기본 API 인 MediaStream, getUserMedia 를 이용해 사용자의 영상 및 음성 정보를 가져옵니다.
b.
const mediaStreamConstraints: MediaStreamConstraints = { video: false, audio: true, }; navigator.mediaDevices .getUserMedia(mediaStreamConstraints) .then((mediaStream: MediaStream) => { localVideo.current!.srcObject = mediaStream; localStream.current = mediaStream; console.log("Received local Stream"); }) .catch((error) => { console.log(`getUserMedia Error : ${error.toString()}`); });
c.
getUserMedia 의 argument 에 대해서는 참조 : https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints
d.
이 함수의 callback 을 통해 획득한 mediaStream 을 RTCPeerConnection 을 생성한 후 addTrack(mediaStream)으로 더해줍니다. Signaling 을 통해 connection 이 이루어지기 전 미리 되어있어야 합니다. 또한 RTCPeerConnection.onTrack() 이벤트로, 스트림이 연결되었을 때 어떠한 행동을 취할지 코딩해줍니다. (보통 스트림을 Video.srcObject = stream; 이런식으로 HTMLVideoElement 에 srcObject 로 연결하여 보여줍니다.)
2.
Signaling
a.
Signaling 단계는 앞에 개요에서 설명했듯이, 두 Peer 간에 Network 정보(ICE)와, 브라우저의 해상도, 코덱 등의 정보를 담고있는 SDP(Session Description Protocol)를 교환합니다. ICE candidate 를 수집하는데 시간이 들기 때문에 우선 SDP 부터 생성하여 교환합니다. 교환과정은 다음과 같습니다.
b.
A, B 두 RTCPeerConnection (peerA, peerB) 가 연결한다고 하고, A가 먼저 B 에게 연결 요청을 보낸다고 가정한다면
i.
peerA.createOffer() 를 통해 SDP (descA) 를 생성하여, peerA.setLocalDescription(descA); 를 호출하여 로컬 SDP 를 세팅한 후, Signaling server 를 통해 B 에게 descA 를 전송합니다.
ii.
B 는 descA 를 받으면, peerB.setRemoteDescription(descB); 를 호출하여 리모트 SDP 를 세팅한 후, peerB. createAnswer() 를 통해 SDP (desdB) 를 생성하여 peerB.setLocalDescription(descB); 호출해 로컬 SDP 를 세팅한 후 (이때 B는 로컬, 리모트 description 둘다 세팅 완료) Signaling server 를 통해 A 에게 descB 를 전송합니다.
iii.
A 는 Signaling server 를 통해 descB 를 전송받고, peerA.setRemoteDescription(descB) 를 호출하면 A 또한 로컬, 리모트 description 을 모두 세팅 완료 하게 됩니다.
iv.
이렇게 두 피어간 SDP 교환이 완료됩니다.
v.
각각의 RTCPeerConnection 객체에서 .setLocalDescription() 이 호출되게 되면, 자동으로 ICE candidate 수집이 시작되는데, 모두 수집되면 RTCPeerConnection.onicecandidate 이벤트가 호출됩니다. 참조 : https://developer.mozilla.org/ko/docs/Web/API/RTCPeerConnection/onicecandidate
vi.
이때 onicecandidate 이벤트는 꼭 ICE 가 완전히 확보된 다음에만 호출되는게 아니기 때문에 보통 아래와 같이 작성합니다.
vii.
peerConnection.addEventListener("icecandidate", (event) => { if (event.candidate) { // 교환을 위하여 Signaling server 를 통해 연결을 원하는 Peer 에게 전송 } else { console.log("checking candidate..."); } });
viii.
이렇게 만들어진 event.candidate 를 서로 교환하고, 교환받은 evnet.candidate 를 이용해서 아래와 같이 RTCIceCandidate 객체를 만들어서, RTCPeerConnection.addIceCandidate() 함수로 서로의 정보를 더합니다.
ix.
const icecandidate = receivedCandidateFromServer; const newIceCandidate = new RTCIceCandidate(icecandidate); otherPeer.addIceCandidate(newIceCandidate)
x.
위 과정이 모두 완료되면 연결이 수립되고, 수립된 연결을 통해 mediaStream 과 data 를 주고받을 수 있게 됩니다.
3.
주의
a.
Peer 간 연결이 굉장히 자주 끊긴다(고 합니다.)
b.
아래의 코드로 현재의 연결상태를 알 수 있습니다. 해서 적절히 다시 연결하면 될듯 합니다
c.
localPeerConnection.addEventListener( "connectionstatechange", (event) => { const peerConnection = event.target as RTCPeerConnection; console.log(peerConnection.connectionState); } );
4.
코드
a.
Signaling server 를 사용하지 않고 간단히 로컬에서만 두 연결을 수립하는 react 예제 : https://github.com/pearan2/WebRTCLocalReactExample
b.
Signaling server 를 사용하여 연결을 수립하는 react, nest 예제 : https://github.com/pearan2/webRTCExampleWithSignaling.git