Skip to content

๐Ÿฆบ ์ด๋ฒคํŠธ ํ๋ฆ„๋„

Junsang Yu edited this page Nov 12, 2024 · 1 revision

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์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹จ ์ƒ๊ฐ๋„ ๋“œ๋„ค์š”..?
  • ์ดˆ๊ธฐ ์ž…์žฅ์‹œ๋งŒ 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
Loading
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: ๋ฏธ๋””์–ด ์ŠคํŠธ๋ฆผ ์ „์†ก
Loading
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

Loading

๐Ÿš€ ํ”„๋กœ์ ํŠธ ๊ทœ์น™

๐Ÿš€ ํ”„๋กœ์ ํŠธ ๊ธฐํš

๐Ÿ“ฝ ๋ฐ์ผ๋ฆฌ ์Šคํฌ๋Ÿผ

๐Ÿ‘ฅ ๊ทธ๋ฃน ํšŒ๊ณ 

๐Ÿ““ ๋ฉ˜ํ† ๋ง ์ผ์ง€

๐Ÿ“† ํšŒ์˜๋ก

๐Ÿง ๊ฐœ๋ฐœ์ผ์ง€

Clone this wiki locally