WebRTC - Twilio

WebRTC와 twilio를 연동해 봅시다.

작성자

홍세민

홍세민

MarcusHong

Twilio NETWORK TRAVERSAL

Twilio는 전화와 관련된 클라우드 서비스를 제공하는 업체인데, NETWORK TRAVERSAL 서비스를 통해 STUN/TURN 서버를 제공한다. (https://www.twilio.com/stun-turn) 가격은 저렴한 편이며, 커버리지가 전세계 이므로 좋은 옵션중에 하나라고 생각한다.

동작방식

Web에서 RTCPeerConnection를 초기화할 때 Signaling Server에서 Twilio로 요청해 받은 token에 있는 iceServers를 사용한다.

Signaling Server

token에서는 callback으로 생성한 token을 반환한다.

const twilio = require('twilio')(twilio.accountSid, twilio.authToken)
const io = require('socket.io')

io.sockets.on('connection', function(socket) {
  socket.on('join', (room) => {
    const clients = io.sockets.adapter.rooms[room]
    const numClients = (typeof clients !== 'undefined') ? clients.length : 0
    if (numClients > 1){
      return callback('already_full')
    }
    else if(numClients === 1) {
      socket.join(room)
      io.in(room).emit('ready')
    }
    else {
      socket.join(room)
    }
    callback()
  })

  socket.on('token', (callback) => {
    twilio.tokens.create()
      .then(token => callback(token))
  })

  socket.on('offer', (data) => {
    const {room, candidate} = data
    socket.to(room).emit('offer', candidate)
  })

  socket.on('answer', (data) => {
    const {room, candidate} = data
    socket.to(room).emit('answer', candidate)
  })

  socket.on('candidate', (data) => {
    const {room, candidate} = data
    socket.to(room).emit('candidate', candidate)
  })
})

main.js

offer를 받았을 때와 startCall를 할 때 token을 요청하는 부분을 추가한다.

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');

const roomName = document.getElementById('roomName');

const startButton = document.getElementById('startButton');
startButton.addEventListener('click', initRTC, false);

const callButton = document.getElementById('callButton');
callButton.addEventListener('click', startCall, false);

const hangupButton = document.getElementById('hangupButton');
hangupButton.addEventListener('click', hangupCall, false);

function initRTC() {
  const socket = io();
  const roomName = options.roomName
  socket.on('connect', () => {
    navigator.mediaDevices.getUserMedia({
      video: {
        width: 640,
        height: 480,
        facingMode: 'user'
      },
      audio: true
    })
      .then(mediaStream => {
        localVideo.srcObject = mediaStream
        localVideo.volume = 0
        localStream = mediaStream
      });

    socket.emit('join', roomName.value, (err) => {
      if (err) alert(err);
      else {
        socket.on('offer', offer => {
          onToken(token);
          createAnswer(offer);
        });
        socket.on('candidate', (candidate) => {
          if (peerConnection) peerConnection.addIceCandidate(candidate);
        });
        socket.on('answer', onAnswer);
      }
    });
  });

  function onToken(token) {
    const iceServers = [];
    for (let i = 0; i < token.iceServers.length; i++) {
      const server = token.iceServers[i];
      iceServers.push({urls: [server.url], username: server.username, credential: server.credential});
    }
    peerConnection = new RTCPeerConnection({iceServers: iceServers})
    peerConnection.addEventListener('icecandidate', handleConnection)
    peerConnection.addEventListener('addstream', gotRemoteMediaStream)
    peerConnection.addStream(localStream)
  }

  function startCall () {
    socket.emit('token', token => {
      onToken(token);
      createOffer();
    });
  }

  function hangupCall() {
    if (peerConnection) {
      peerConnection.close()
      peerConnection = null
    }
  }

  function onAnswer(answer) {
    peerConnection.setRemoteDescription(answer)
      .catch(e => console.log(e));
  };

  function createOffer() {
    peerConnection.createOffer()
      .then(offer => {
        peerConnection.setLocalDescription(offer)
        return offer
      })
      .then(offer => socket.emit('offer', {room: roomName, offer}));
  };

  function createAnswer(description) {
    peerConnection.setRemoteDescription(description)
      .then(() => peerConnection.createAnswer())
      .then((answer) => {
        peerConnection.setLocalDescription(answer)
        return answer;
      })
      .then((answer) => socket.emit('answer', {room: roomName, answer}))
      .catch(e => console.log(e));
  };

  function gotRemoteMediaStream(event) {
    const mediaStream = event.stream;
    remoteVideo.srcObject = mediaStream;
    remoteStream = mediaStream;
  };

  function handleConnection(event) {
    const iceCandidate = event.candidate;
    socket.emit('candidate', {room: roomName, candidate: iceCandidate});
  };
};

Tags : webrtc twilio 

comments powered by Disqus