-
Notifications
You must be signed in to change notification settings - Fork 3
๐ฆบ ์ด๋ฒคํธ ํ๋ฆ๋
https://excalidraw.com/#room=b1539017e58ba9e9a170,euAtvFzfmxnB-uHCAVwF7g
-
์์ ํ ์๊ทธ๋๋ง ์์ฑ ํ ์คํธ๋ฆผ์ ์ถ๊ฐํ๋ ค๋ฉด Peer ์ฐ๊ฒฐ์ด ์คํธ๋ฆผ๊ณผย ์ฌํ์ย ํด์ผ ํฉ๋๋ค.
https://stackoverflow.com/questions/16015022/webrtc-how-to-add-stream-after-offer-and-answer
-
๋น๋์ค/์ค๋์ค๋ฅผ ํ์ฑํ/๋นํ์ฑํ ํ ๋ offer/answer๋ฅผ ์๋ก ๋ณด๋ด์ง ์์๋ ๋ ๊ฑฐ ๊ฐ์ต๋๋ค.
- J290 : ์ด ๋ถ๋ถ์
track
์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ ๊ฐ๋จ ์๊ฐ๋ ๋๋ค์..?
- J290 : ์ด ๋ถ๋ถ์
-
์ด๊ธฐ ์ ์ฅ์๋ง offer/answer๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ
publishButton.onclick = async () => { if (!localStream) { localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }); pubVideo.srcObject = localStream; } // ๋น๋์ค ๋๊ธฐ ๋๋ ์ผ๊ธฐ ์ ํ const videoTrack = localStream.getVideoTracks()[0]; videoTrack.enabled = !videoTrack.enabled; if (videoTrack.enabled) { pubVideo.srcObject = localStream; // ๋น๋์ค ์ก์ถ ํ์ฑํ } else { pubVideo.srcObject = null; // ๋น๋์ค ์ก์ถ ์ค์ง } // ์๋ฒ์ ํผ๋ธ๋ฆฌ์ ๋ฑ๋ก ์์ฒญ socket.emit('publish'); }; // peerConnection = new RTCPeerConnection(); peerConnection.ontrack = event => { const [remoteStream] = event.streams; subVideo.srcObject = remoteStream; // ๋น๋์ค ํธ๋์ ๊ฐ์ ธ์ค๊ธฐ const videoTrack = remoteStream.getVideoTracks()[0]; // ์๋๋ฐฉ์ ๋น๋์ค๊ฐ ๊บผ์ง ๋(mute)์ ์ผ์ง ๋(unmute) ์ด๋ฒคํธ ๊ฐ์ง videoTrack.onmute = () => { console.log('์๋๋ฐฉ ๋น๋์ค๊ฐ ๊บผ์ก์ต๋๋ค.'); // ์ฌ๊ธฐ์ ๋น๋์ค๊ฐ ๊บผ์ง ์ํ๋ฅผ UI์ ๋ฐ์ํ ์ ์์ }; videoTrack.onunmute = () => { console.log('์๋๋ฐฉ ๋น๋์ค๊ฐ ๋ค์ ์ผ์ก์ต๋๋ค.'); // ๋น๋์ค๊ฐ ๋ค์ ์ผ์ก์ ๋ UI๋ฅผ ์ ๋ฐ์ดํธ }; };
- connection โ offerRequest โ sendOffer โ answerRequest โ sendAnswer
room = [์ฐธ๊ฐ์ ์์ผ ์์ด๋ โฆ];
newAttendee = ์์ผ ์์ด๋;
server.on('sendOffer', () โ {})
//
server.on('connection', (socket) โ {
socket.on('sendOffer', () โ {});
});
-
sendOffer(์๋ฒ) (์ ๊ท ์ฐธ๊ฐ์์ ์์ผ, ๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ, offer) โ void
- ์ ์ ์ offer๋ฅผ ๊ธฐ์กด ์ฐธ๊ฐ์์๊ฒ ์ ๋ฌ (socket.to(๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ).emit answerRequest(์ ๊ท ์ฐธ๊ฐ์์ ์์ผ, offer))
-
sendIcecandidate(์๋ฒ) (์๋ ์ฐธ๊ฐ์์ ์์ผ, iceCandidate) โ void
- ์ ์ ์ icecandidate๋ฅผ ์๋ ์ฐธ๊ฐ์์๊ฒ ์ ๋ฌ(socket.to(์๋ ์ฐธ๊ฐ์์ ์์ผ).emit setIcecandidate(์์ ์ ์์ผ, iceCandidate))
-
connection(์๋ฒ) (socket ์ ๊ท ์ฐธ๊ฐ์์ ์์ผ) โ void
- ์๋ฒ์์ ํด์ผ ํ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฑ๋ก
- ํด๋ผ์ด์ธํธ ์์ผ ์ ๋ณด ์ ์ฅ: [socket.id](http://socket.id)
- ๊ธฐ์กด ์ฐธ๊ฐ์๋ค์ socket.id๋ฅผ ํด๋ผ์ด์ธํธ์ ์ ์ก([socket.to](http://socket.to/)([socket.id](http://socket.id/) ์ ๊ท ์ฐธ๊ฐ์์ ์์ผ).emit offerRequest(๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ))
-
sendAnswer(์๋ฒ) (answer) โ void
- ์ ์ ์ answer๋ฅผ ์ ๊ท ์ฐธ๊ฐ์์๊ฒ ์ ๋ฌ (socket.to(์ ๊ท ์ฐธ๊ฐ์ ์์ผ).emit completeConnection(๊ธฐ์กด ์ฐธ๊ฐ์์ socket, answer))
-
โ ย offerRequest(ํด๋ผ์ด์ธํธ) (socketId ๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ) โ void
-
์์ ์ offer๋ก setLocalDescription ๋ฑ๋ก
-
๊ธฐ์กด ์ฐธ๊ฐ์๋ค์๊ฒ offer (socket.emit sendOffer(๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ, offer))
-
Offer์ Answer๋
setLocalDescription
์ ํ๋ ์๊ฐ๋ถํฐ ICE Candidate ๊ตํ์ ์งํํ๋ฏ๋ก, ์์ฑ ์งํ ์ค์ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค. -
ํด๋ผ์ด์ธํธ์์๋ socket.to ๋ฅผ ์ฌ์ฉํ ์ ์์..!
// ํด๋ผ์ด์ธํธ socket.emit('sendMessage', { to: targetSocketId, // ์์ ์ socket.id message: 'hello' // ์ค์ ๋ฐ์ดํฐ }); // ์๋ฒ socket.on('sendMessage', (data) => { // ์ฌ๊ธฐ์ socket.to๋ฅผ ์ฌ์ฉํด ํน์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌ socket.to(data.to).emit('receiveMessage', { from: socket.id, message: data.message }); });
-
-
โ ย answerRequest(ํด๋ผ์ด์ธํธ) (socketId ์ ๊ท ์ฐธ๊ฐ์์ ์์ผ, offer) โ void
- ๋ฐ์ offer๋ก setRemoteDescription ๋ฑ๋ก
- ์์ ์ offer๋ก setLocalDescription ๋ฑ๋ก
- Offer์ Answer๋
setLocalDescription
์ ํ๋ ์๊ฐ๋ถํฐ ICE Candidate ๊ตํ์ ์งํํ๋ฏ๋ก, ์์ฑ ์งํ ์ค์ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค. - ์ ๊ท ์ฐธ๊ฐ์์ offer์ ๋ํ answer (socket.emit sendAnswer(answer))
-
โ ย completeConnection(ํด๋ผ์ด์ธํธ) (socketId ๊ธฐ์กด ์ฐธ๊ฐ์์ ์์ผ, answer) โ void
- ๋ฐ์ answer๋ก setRemoteDescription ๋ฑ๋ก
-
โ ย peerConnection.onicecandidate(ํด๋ผ์ด์ธํธ) = (event) โ { emit sendIceCandidate(iceCandidate)};
-
โ ย setIceCandidate(ํด๋ผ์ด์ธํธ) (์ ์ก์์ socketId, iceCandidate) โ void
- ๋ฐ์ iceCandidate๋ฅผ ์ถ๊ฐ
sequenceDiagram
participant User1 as ์ฌ์ฉ์ 1
participant Server as ์๋ฒ
participant User2 as ์ฌ์ฉ์ 2
participant User3 as ์ฌ์ฉ์ 3
Note over User1: ์ฌ์ฉ์1 ์
์ฅ
User1->>Server: ์น์์ผ ์ฐ๊ฒฐ
Server-->>User1: ํ์ฌ ๋ฐฉ ์ ์
Note over User2: ์ฌ์ฉ์2 ์
์ฅ
User2->>Server: ์น์์ผ ์ฐ๊ฒฐ
Server-->>User1: ํ์ฌ ๋ฐฉ ์ ์
Server-->>User2: ํ์ฌ ๋ฐฉ ์ ์
User2->>User1: offer
User1->>User2: answer
Note over User2: ์ฌ์ฉ์2 ์ก์ถ ์์
Note over User3: ์ฌ์ฉ์3 ์
์ฅ
User3->>Server: ์น์์ผ ์ฐ๊ฒฐ
Server-->>User1: ํ์ฌ ๋ฐฉ ์ ์
Server-->>User2: ํ์ฌ ๋ฐฉ ์ ์
Server-->>User3: ํ์ฌ ๋ฐฉ ์ ์
User3->>User2: offer
User2->>User3: answer
User3->>User1: offer
User1->>User3: answer
sequenceDiagram
participant Client1 as ์ฌ์ฉ์ 1
participant Server as ์๊ทธ๋๋ง ์๋ฒ
participant Client2 as ์ฌ์ฉ์ 2
Note over Client1,Client2: ์๊ทธ๋๋ง ์๋ฒ ์ฐ๊ฒฐ ๋จ๊ณ
Client1->>Server: ์น์์ผ ์ฐ๊ฒฐ
Client2->>Server: ์น์์ผ ์ฐ๊ฒฐ
Note over Client1,Client2: ICE ๊ณผ์ ์์
Client1->>Client1: RTCPeerConnection ์์ฑ
Client1->>Client1: getUserMedia() ํธ์ถ<br/>(์นด๋ฉ๋ผ/๋ง์ดํฌ ๊ถํ ์์ฒญ)
Client1->>Server: ๋ฐฉ ์์ฑ/์
์ฅ ์์ฒญ
Server->>Client2: ์๋ก์ด ์ฐธ๊ฐ์ ์๋ฆผ
Note over Client1,Client2: Offer/Answer ๊ตํ
Client1->>Client1: createOffer() ํธ์ถ
Client1->>Client1: setLocalDescription(offer)
Client1->>Server: Offer SDP ์ ์ก
Server->>Client2: Offer SDP ์ ๋ฌ
Client2->>Client2: RTCPeerConnection ์์ฑ
Client2->>Client2: setRemoteDescription(offer)
Client2->>Client2: createAnswer() ํธ์ถ
Client2->>Client2: setLocalDescription(answer)
Client2->>Server: Answer SDP ์ ์ก
Server->>Client1: Answer SDP ์ ๋ฌ
Client1->>Client1: setRemoteDescription(answer)
Note over Client1,Client2: ICE Candidate ๊ตํ
Client1->>Server: ICE candidate ์ ์ก
Server->>Client2: ICE candidate ์ ๋ฌ
Client2->>Server: ICE candidate ์ ์ก
Server->>Client1: ICE candidate ์ ๋ฌ
Note over Client1,Client2: P2P ์ฐ๊ฒฐ ์๋ฃ
Client1-->>Client2: ๋ฏธ๋์ด ์คํธ๋ฆผ ์ ์ก
Client2-->>Client1: ๋ฏธ๋์ด ์คํธ๋ฆผ ์ ์ก
sequenceDiagram
participant ์ ๊ท ์ฐธ๊ฐ์
participant ์๋ฒ
participant ๊ธฐ์กด ์ฐธ๊ฐ์
์ ๊ท ์ฐธ๊ฐ์ ->> ์๋ฒ: connection (์ ๊ท ์ฐธ๊ฐ์ ์ ์)
์๋ฒ ->> ๊ธฐ์กด ์ฐธ๊ฐ์: offerRequest (์ ๊ท ์ฐธ๊ฐ์ socketId ์ ๋ฌ)
loop ๊ฐ ๊ธฐ์กด ์ฐธ๊ฐ์์ ๋ํด
์ ๊ท ์ฐธ๊ฐ์ ->> ์๋ฒ: sendOffer (์ ๊ท ์ฐธ๊ฐ์์ offer์ ๋์ socketId)
์๋ฒ ->> ๊ธฐ์กด ์ฐธ๊ฐ์: answerRequest (์ ๊ท ์ฐธ๊ฐ์์ offer ์ ๋ฌ)
๊ธฐ์กด ์ฐธ๊ฐ์ ->> ๊ธฐ์กด ์ฐธ๊ฐ์: setRemoteDescription (์ ๊ท ์ฐธ๊ฐ์์ offer ๋ฑ๋ก)
๊ธฐ์กด ์ฐธ๊ฐ์ ->> ๊ธฐ์กด ์ฐธ๊ฐ์: createAnswer (answer ์์ฑ)
๊ธฐ์กด ์ฐธ๊ฐ์ ->> ์๋ฒ: sendAnswer (์ ๊ท ์ฐธ๊ฐ์์๊ฒ answer ์ ์ก)
์๋ฒ ->> ์ ๊ท ์ฐธ๊ฐ์: completeConnection (๊ธฐ์กด ์ฐธ๊ฐ์์ answer ์ ๋ฌ)
์ ๊ท ์ฐธ๊ฐ์ ->> ์ ๊ท ์ฐธ๊ฐ์: setRemoteDescription (๊ธฐ์กด ์ฐธ๊ฐ์์ answer ๋ฑ๋ก)
end
Note right of ์ ๊ท ์ฐธ๊ฐ์: Offer์ Answer ๊ตํ ์ดํ ICE ํ๋ณด ์์ฑ ๋ฐ ๊ตํ ์์
par ICE ํ๋ณด ๊ตํ
์ ๊ท ์ฐธ๊ฐ์ ->> ์๋ฒ: sendIceCandidate (์ ๊ท ์ฐธ๊ฐ์์ ICE ํ๋ณด์ ๋์ socketId)
์๋ฒ ->> ๊ธฐ์กด ์ฐธ๊ฐ์: setIceCandidate (์ ๊ท ์ฐธ๊ฐ์์ ICE ํ๋ณด ์ ๋ฌ)
๊ธฐ์กด ์ฐธ๊ฐ์ ->> ์๋ฒ: sendIceCandidate (๊ธฐ์กด ์ฐธ๊ฐ์์ ICE ํ๋ณด์ ๋์ socketId)
์๋ฒ ->> ์ ๊ท ์ฐธ๊ฐ์: setIceCandidate (๊ธฐ์กด ์ฐธ๊ฐ์์ ICE ํ๋ณด ์ ๋ฌ)
end
- ๐ง ํ ๋น๋ฉ ๋ฐ ๋ธ๋์น ์ ๋ต
- ๐ง ์ฃผ์ ์ ์ ๋ฐ ํ ํ๋ฆฟ ์์ฑ
- ๐ป ํ๋ก์ ํธ ์ด๋ฆ ๋ฐ ๋ก๊ณ ์ ํ๊ธฐ
- ๐ป ๊ธฐํ ์ธ๋ถ์ฌํญ ์ ํ๊ธฐ
- ๐ป ๊ธฐ์ ์คํ ์์
- ๐ป ๋ฐฑ๋ก๊ทธ ์ด์ ์์ฑ
- ๐ป ๊นํ๋ธ ์ํค
- ๐ ๊ธฐํ ๋ฐ MVP ์ ์ง ํ์
- 2๏ธโฃ ์คํ๋ฆฐํธ 2 ์ฃผ๊ฐ ๊ณํ
- ๐งญ ๊ณตํต ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ
- ๐ฆบ ์ด๋ฒคํธ ํ๋ฆ๋
- ๐ฅ ๋ฐฑ์๋ ์๊ทธ๋๋ง ์๋ฒ ๋ฌธ์
- ๐ฝ ์คํ๋ฆฐํธ 5โ 6 ๋ฐฉํฅ์ฑ
- ๐ ์ต์ข ๋ฐํ ์ค๋น
- ๐ฟ Github Actions CD(์๋๋ฐฐํฌ)
- ๐ API
- โซ forEach๋ async๋ฅผ ๊ธฐ๋ค๋ ค์ฃผ์ง ์๋๋ค
- ๐งช WebRTC SFU ๋ฐ๋ชจ
- ๐ TURN ์๋ฒ ์ธ์ฆ ๋ฐฉ์
- ๐ ๏ธ [ํธ๋ฌ๋ธ์ํ ] /socket.io ๊ฒฝ๋ก๋ก ์ง์์ ์ผ๋ก HTTP ์์ฒญ์ ๋ณด๋ด๋ ๋ฌธ์
- ๐น WebRTC ํ์ํ์์์ ์นด๋ฉ๋ผ ์ํ์ ๋ฐ๋ฅธ ์์คํ ์์ ์ฌ์ฉ๋ ๋ถ์
- ๐ฅ ๋์นญ NAT์์์ WebRTC ์ฐ๊ฒฐ ์๋ฆฝ ๊ณผ์
- ๐ ํ ์คํธ ์ฝ๋์ ๋ชจํน