Step-by-Step

[6] 스트림 데이터 전송 P2P 본문

프로젝트/Zoom (WebRTC)

[6] 스트림 데이터 전송 P2P

희주(KHJ) 2023. 4. 10. 22:14

우선 같은 Room 내 접속한 Peer간 비디오 및 오디오 전송을 구현해야 한다. (나중에 채팅도 작성할 것임)

 

https://webrtc.org/getting-started/peer-connections?hl=ko 

 

피어 연결 시작하기  |  WebRTC

Google은 흑인 공동체를 위한 인종적 평등을 추구하기 위해 노력하고 있습니다. 자세히 알아보기 이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 피어 연결 시작하기 피어 연

webrtc.org

 

※ P2P 통신 구성은 다음과 같다.

1. ICE Candidate 

- 정보 교환을 통해 상대 Peer의 네트워크 정보인 ICE(Interactive Connectivity Establishment) Candidate를 가져온다

- Candidate : STUN, TURN 등으로 찾아낸 연결 가능한 네트워크 주소들

STUN, TRUN 정보는 다음 확인 : https://developer.mozilla.org/ko/docs/Web/API/WebRTC_API/Protocols

단순히 타 네트워크에 있는 사용자 간 IP 접근 루트를 설정해주는 거라고 생각하면 된다 (local 사용해서 생략했음)

 

2. SDP

- SDP (Session Description Protocol) : 해상도나 형식, 코덱, 암호화등의 멀티미디어 컨텐츠의 연결을 설명하기 위한 표준

- 어떤 종류의 데이터를 주고 받을지 보내는 형식으로, createOffer()로 RTCSessionDescription 객체 생성 후 교환한다.

 

 

초기 설정

 

async function initCall(){
    welcome.hidden = true;
    call.style.display='inline-flex';
    chat.style.display='block';
    await getMedia();
    makeConnection();
}

저번에 룸 입장시 처음 실행했던 초기 함수이다. 마지막 makeConnection()은 ICE 과정을 담고 있다

 

 

ICE Candidate

 

let myPeerConnection;

function makeConnection(){
    // Server.js 에서 callback으로 실행하면 너무 빠르게 일어나서 offer의 setRemoteDescription때 에러가 발생
    myPeerConnection = new RTCPeerConnection(

    /*    STUN Server
    {
        // GOOGLE STUN Server Ref : https://gist.github.com/zziuni/3741933
        iceServers: [
            {
                urls: [
                    "stun:stun.l.google.com:19302",
                    "stun:stun1.l.google.com:19302",
                    "stun:stun2.l.google.com:19302",
                    "stun:stun3.l.google.com:19302",
                    "stun:stun4.l.google.com:19302"
                ]
            }
        ]
    } 
    */
    );
    
    myPeerConnection.addEventListener("icecandidate", handleIce);
    myPeerConnection.addEventListener("track", handleTrack);

    /* myStream 데이터 저장하기 - P2P 로 데이터 보내기 위함 */
    myStream.getTracks().forEach(track => myPeerConnection.addTrack(track, myStream));
}

인터넷에 나와있는 많은 STUN, TURN 서버 참조해서 구성해주면 된다.

 

 

function handleTrack(data) {
    console.log("got an event from my peer");
    //console.log("Peer's Stream", data.streams[0]);
    
    const peerFace = document.getElementById("peerFace");
    peerFace.srcObject = data.streams[0];
    //console.log("My Stream", myStream);
}

function handleIce(data) {
    console.log("Sent Candidate");
    socket.emit("ice", data.candidate, roomName);
}

handleTrack을 통해 상대 오디오와 비디오 정보를 등록하고, 

handleIce를 통해 동일 roomName 상대에게 candidate 정보를 전달해준다.

 

 

wsServer.on("connection", socket => {
	/* ICE 받음 - (App.js) ice 넣어줌 */
    socket.on("ice", (ice, roomName) => {
        socket.to(roomName).emit("ice", ice);
    });
})

서버에서 ICE 받아서 다른 Peer에게 전달

 

 

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

다른 Peer에서 ice 받으면 myPeerConnection에 등록해줌

 

 

 

룸 입장시 Offer 통한 객체 생성 및 전달 

 

/* 입장 후 동일 roomName 가진 사람에게 객체 전달 */
socket.on("welcome", async () => {

    /* 객체 생성 후  */
    const offer = await myPeerConnection.createOffer();
    myPeerConnection.setLocalDescription(offer);

    /* 입장한 사람에게 보내기 */
    console.log("sent the offer")
    socket.emit("offer", offer, roomName);
});

참조 : https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer

룸에 입장한 사용자는 Offer 생성 후 Local에 등록 후 동일 룸의 타 Peer에게 전달해준다.

 

 

wsServer.on("connection", socket => {
	/* offer 받음 - (App.js) offer remote 등록 후 answer 제공해줌 */
    socket.on("offer", (offer, roomName) => {
        socket.to(roomName).emit("offer", offer);
    });
}

사용자로부터 Offer과 roomName을 받아서 동일 룸 내의 다른 Peer에게 전송해준다.

 

 

socket.on("offer", async (offer) => {
    console.log("received the offer");

    myPeerConnection.setRemoteDescription(offer);
    const answer = await myPeerConnection.createAnswer();
    myPeerConnection.setLocalDescription(answer);

    console.log("sent the answer");
    socket.emit("answer", answer, roomName);
});

Offer를 받은 타 Peer들은 remote에 등록하고 자신의 Answer을 생성해서 Local에 저장 후 돌려준다.

 

 

wsServer.on("connection", socket => {

        /* answer 받음 - (App.js) answer remote 등록 */
        socket.on("answer", (answer, roomName) => {
            socket.to(roomName).emit("answer", answer);
        });
}

서버에서 Answer을 받은 후 Answer 생성자 제외 타 Peer에 전달한다.

(현재는 P2P 구현이니까 Offer 준 Peer에게만 가는 것임)

 

 

socket.on("answer", (answer)=>{
    console.log("Received the Answer");
    myPeerConnection.setRemoteDescription(answer);
});

Answer 받아서 Remote에 등록해준다.

 

 

엄청난 핑퐁이다...

 

 

비디오 바꿀 경우

 

async function handleCameraChange(){
    await getMedia(camerasSelect.value);

    /* 다른 Peer에 있는 my track 바꾸기 */
    if(myPeerConnection){
        const videoTrack = myStream.getVideoTracks()[0];
        const videoSender = myPeerConnection.getSenders().find((sender) => sender.track.kind === "video");
        videoSender.replaceTrack(videoTrack);
    }
}

myPeerConnection에 등록된 Sender의 비디오를 바꿔준다. Peer 사전 등록하니까 편리한 기능이 많네요ㅋㅋㅋ

문서 : https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack

 

Comments