PeerJS로 WebRTC 쉽게 사용해보기

민경민·2020년 8월 13일
2
post-thumbnail

1. 영상통화 서비스를 만들자!🤳

  본 포스트는, 글쓴이가 영상통화 웹사이트를 만들면서 개인적 정리/참고를 위해서 작성되었습니다. 앞으로도 놀지말고 빨리빨리 만들자 이말이야...💻


2. WebRTC란❓

  WebRTC(Web Real Time Communication)의 약자이다. WebRTC는 통신 표준으로, 웹 브라우저간 실시간 영상/음성/데이터 통신을 가능하게 해준다. 즉 별도의 플러그인이나 프로그램이 필요 없다! WebRTC는 P2P 구조로 되어있다. 기기간 통신하기 때문에 서버의 성능이 크게 필요하지 않다. WebRTC는 구글에 제안되었다. 이후 Mozilla, Opera, MS 등의 기관들이 표준 확립에 참여하고있다. WebRTC 공식 웹사이트


2. PeerJS는 또 무엇?🤔

PeerJS는 WebRTC를 쉽게 사용하기 위해 만들어진 wrapper이다. ID만 지정해주면 손쉽게 P2P 연결을 처리해준다! PeerJS 공식 웹사이트


3. 아무튼 내가 사용한 것들😅

  1. 배포 서버는 aws EC2
  2. 웹 프레임워크는 Express.js
  3. WebRTC 이외의 실시간 통신은 Socket.IO
  4. Let's Encrypt로 https 적용(크롬 기준 WebRTC에 https가 필요)
  5. PeerJS 서버는 공식 서버를 사용

  간단한 결과는 다음과 같다. 왼쪽 부터 데스크탑, 아이패드, 아이폰으로 캡쳐한 영상이다. 😎


4. 샘플코드❕

  • 우선 프로젝트 다이렉토리로 이동해서 의존 패키지를 설치한다
npm init -y
npm i uuid socket.io express ejs
npm i -g peer

  • server.js
const express = require('express');
const app = express();
const { v4: uuidV4 } = require('uuid');

const fs = require('fs');
const https = require('https');
const server = https.createServer(
  {
    key: fs.readFileSync('/etc/letsencrypt/live/your.domain.here/privkey.pem'),
    cert: fs.readFileSync('/etc/letsencrypt/live/your.domain.here/cert.pem'),
    ca: fs.readFileSync('/etc/letsencrypt/live/your.domain.here/chain.pem'),
    requestCert: false,
    rejectUnauthorized: false,
  },
  app
);

const io = require('socket.io')(server);

app.set('view engine', 'ejs');
app.use(express.static('public'));

app.get('/', (req, res) => {
  res.redirect(`/${uuidV4()}`);
});

app.get('/:room', (req, res) => {
  res.render('room', { roomId: req.params.room });
});

io.on('connection', (socket) => {
  socket.on('join-room', (roomId, userId) => {
    socket.join(roomId);
    socket.to(roomId).broadcast.emit('user-connected', userId);

    socket.on('disconnect', () => {
      socket.to(roomId).broadcast.emit('user-disconnected', userId);
    });
  });
});

server.listen(portNumber);

  • room.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <script>
      const ROOM_ID = '<%= roomId %>';
    </script>
    <script
      defer
      src="https://unpkg.com/peerjs@1.2.0/dist/peerjs.min.js"
    ></script>
    <script src="/socket.io/socket.io.js" defer></script>
    <script src="script.js" defer></script>
    <title>Document</title>
    <style>
      #video-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, 300px);
        grid-auto-rows: 300px;
      }
      video {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    </style>
  </head>
  <body>
    <div id="video-grid"></div>
  </body>
</html>

  • script.js
const socket = io('/');
const videoGrid = document.getElementById('video-grid');
const myPeer = new Peer();
const myVideo = document.createElement('video');
myVideo.muted = true;
const peers = {};
navigator.mediaDevices
  .getUserMedia({
    video: true,
    audio: true,
  })
  .then((stream) => {
    addVideoStream(myVideo, stream);

    myPeer.on('call', (call) => {
      call.answer(stream);
      const video = document.createElement('video');
      call.on('stream', (userVideoStream) => {
        addVideoStream(video, userVideoStream);
      });
    });

    socket.on('user-connected', (userId) => {
      connectToNewUser(userId, stream);
    });
  });

socket.on('user-disconnected', (userId) => {
  if (peers[userId]) peers[userId].close();
});

myPeer.on('open', (id) => {
  socket.emit('join-room', ROOM_ID, id);
});

function connectToNewUser(userId, stream) {
  const call = myPeer.call(userId, stream);
  const video = document.createElement('video');
  call.on('stream', (userVideoStream) => {
    addVideoStream(video, userVideoStream);
  });
  call.on('close', () => {
    video.remove();
  });

  peers[userId] = call;
}

function addVideoStream(video, stream) {
  video.srcObject = stream;
  video.addEventListener('loadedmetadata', () => {
    video.play();
  });
  videoGrid.append(video);
}

5. 참고 및 링크 🖥️

youtube 튜토리얼 (영어)

Github

profile
I build stuff.

2개의 댓글

comment-user-thumbnail
2020년 11월 25일

안녕하세요. 좋은 글 감사합니다.
WebRTC를 적용한 웹 사이트를 하이브리드 앱으로 뽑고
이후 서비스 내부에서 음성채팅을 구현하려고 합니다.

이런 경우 상대방이 폰에 앱을 설치했다는 가정하에
누군가 음성채팅을 연결하고자 할때 연결을 시도한다는 알람이 가게끔 할 수 있나요? (ex 보이스톡)

답글 달기
comment-user-thumbnail
2021년 1월 16일

감사합니다!!!!~
nodejs 잘 몰라서 로컬코드를 서버로 호스팅하게 바꾸는 거에서 계속 막혔는데,
올려주신 호스팅 서버용 코드 덕분에 성공할 수 있었습니다!!

답글 달기