WebRTC
WebRTC (Web Real-Time Communication)는 웹 브라우저 간에 플러그인의 도움 없이 서로 통신할 수 있도록 설계된 API이다. W3C에서 제시된 초안이며, 음성 통화, 영상 통화, P2P 파일 공유 등으로 활용될 수 있다.
Categories
- Object Real-time Communications (ORTC)
- WebRTC:NativeCode
- WebRTC:NativeCode:GnArgumentList
- WebRTC:API (Web browser, JavaScript 코드 API)
- WebRTC:IsolatedNetwork: 격리된 네트워크 환경 (Offline) 일 때 WebRTC 사용 방법.
- WebRTC:Connectivity
- WebRTC HTTP
About Streaming
- Media Streaming
- Media Streaming#Demo video: Streaming 샘플 비디오 URL 모음
- Low Latency Streaming
Terms
- Network address translation (NAT)
- Session Traversal Utilities for NAT (STUN)
- Traversal Using Relays around NAT (TURN)
- Interactive Connectivity Establishment (ICE)
- Session Description Protocol (SDP)
- JavaScript Session Establishment Protocol (JSEP) - Offer/Answer 플로우 그림 있음.
- Trickle ICE
Tools
- Firefox:Plugin:DevToolsMediaPanel
- Chrome: chrome://webrtc-internals/
- Firefox: about:webrtc
Server/libraries
- Overview of WebRTC Open Source Media Servers
- Best 5 open source WebRTC Media Servers
- libjingle
- aiortc (python)
- jitsi (Java)
- Pion (Go)
- Janus (Open Source) WebRTC Gateway
- LibSourcey
- Kurento Media Server
- Licode
- Medooze
- wowza - Pricing 참고 자료
- Mediasoup
- PJSIP
- Jam - WebRTC 기반의 라이브 오디오 서비스 (클럽하우스의 오픈소스 대체제)
- Jitsi Video Bridge
- Open WebRTC Toolkit (OWT)
- Ant Media Server - Pricing 참고 자료
- Open-EasyRTC - https://github.com/open-easyrtc/open-easyrtc
- openVidu - https://openvidu.io/
- Galene - https://github.com/jech/galene
- SaltyRTC - https://github.com/saltyrtc
- ion media server - https://github.com/ionorg/ion
- OvenMediaEngine - https://airensoft.com/ome
- Temasys - https://temasys.github.io/
- JSCommunicator - https://jscommunicator.org/
- PeerJS Server and Library - https://peerjs.com/
- WebRTC-Streamer - 임베디드 장비에 설치할만한... (Docker 이미지도 있다)
Client
- webrtc-adapter - adapter.js is a shim to insulate apps from spec changes and prefix differences.
- OvenPlayer (HTML5 player)
Libraries
-
webrtcsink - https://github.com/centricular/webrtcsinkIMPORTANT: this repository is now archived - libwebrtc - Google's WebRTC implementation in a single static library.
Use case
WebRTC는 기본적으로 P2P 프로토콜로서, 대규모의 미디어 방송 서비스를 구축하거나 나 컨텐츠 가공이 필요한 경우와 같이, 서비스 목적에 따라 별도의 중앙 서버를 구성해야 할 필요가 있다. 아키텍처는 유즈케이스에 따라 다음과 같은 구성을 고려해 볼 수 있다.
- P2P (Peer to Peer)
- 중앙 서버 없이 종단 간 직접 연결 방식은 비용 측면에서 유리하나, 피어 수가 증가(mesh structure)할수록 시스템과 네트워크의 높은 capacity를 요구한다. 1:1 또는 소규모 미디어 교환에 적합하다.
- SFU (Selective Forwarding Unit)
- 중앙 서버를 통해 종단 간 미디어 트래픽을 중계하는 중앙 서버 방식으로, 각 피어 연결 할당 및 decrypt/encrypt 처리 비용 정도를 감수한다. 영상 방송과 같은 1:M(or minimum-N:M) 스트리밍 서비스 구조에 적합하다.
- MCU (Multi-point Control Unit)
- 다수의 송출 미디어를 중앙 서버에서 혼합(muxing) 또는 가공(transcoding)하여 수신측으로 전달하는 중앙 서버 방식으로, 클라이언트와 네트워크의 부담이 현저히 줄어드는 반면 중앙 서버의 높은 컴퓨팅 파워가 요구된다.
- | P2P | SFU | MCU |
Client Uplink | High | Low | Low |
Client Downlink | High | Low | High |
Client CPU Usage | High | Low | Medium |
Server CPU Usage | - | High | Low |
Possible Latency | Depends on Network Bandwidth | Depends on CPU Power | Depends on Network Bandwidth |
Transcode Capability | - | Yes | No |
Simulcast/SVC Capability | - | - | Yes |
ICE candidate
네트워크 연결에 관한 정보를 담고있음 UDP랑 TCP 두 가지가 존재. 일반적으로 UDP를 사용하게 되어있음. 유저 한명은 controlling agent로, 다른 유저는 controlled agent로 배정됨.
- controlling agent가 어떤 candidate 쌍을 사용할지 결정
- ICE 리셋 전까지는 결정된 candidate 쌍을 사용하게됨
UDP 타입
속도가 빠름고 MediaStream에 장애가 발생해도 상대적으로 더 쉽게 복구 가능
-
host
: 유저의 실제 IP 주소 사용 -
prflx
: peer reflexive, 두 유저간에 존재하는 Symmetric NAT에서 발생한 IP 주소 사용 -
srflx
: server reflexive, STUN/TURN 서버가 생성한 IP 주소 사용 -
relay
: TURN 서버에서 생성한 IP 주소 사용
TCP 타입
-
active
: outbound 연결 시도 -
passive
: 연결을 받도록 설정 -
so
: 유저간에 연결을 동시에 개통하도록 시도
WebRTC Server streaming
Yes it is possible as the server can be one of the peers in that peer-to-peer session. If you respect the protocols and send the video in SRTP packets using VP8, the browser will play it. To help you build these components on other applications or servers, you can check this page and this project as a guide.
Now, comparing WebRTC with other streaming services... It will depend on several variables like the Codec or the protocol. But, for instance, comparing WebRTC (SRTP over UDP with VP8 Codec) against Flash (RTMP over TCP with H264 Codec), I would say that WebRTC wins.
- The player will be Flash Player against the native <code><video>
</code> tag.
- The transport would be TCP against UDP.
But of course, everything depends on what you are sending to the client.
SDP Offer/Answer
자세한 내용은 JavaScript Session Establishment Protocol (JSEP) 항목 참조.
TypeScript 개발 설정
no-undef error
RTCConfiguration같은 심볼을 사용하면 다음과 같이 ESLint 에러(no-undef)가 나타날 수 있다:
TypeScript의 lib의 dom에 해당 심볼 정의가 존재한다. compilerOptions
에 다음과 같이 추가한다:
혹은 타입 심볼을 설치하고,
compilerOptions
에 다음과 같이 타입을 추가한다:
그래도 안된다면 ESLint에 전역 심볼을 등록해 주면 된다:
"eslintConfig": {
"root": true,
"env": {
"browser": true,
"node": true
},
"globals": {
"RTCIceGatheringState": true,
"RTCIceServer": true,
"RTCConfiguration": true,
"RTCSessionDescriptionInit": true,
"RTCErrorEvent": true
},
Sample URLs
Troubleshooting
stun:localhost is not connect
Session Traversal Utilities for NAT#stun:localhost is not connect 항목 참조.
ICE failed, your TURN server appears to be broken, see about:webrtc for more details
만약 STUN 또는 TURN 서버가 로컬 호스트에 있고, Firefox를 사용한다면 about:config
페이지 에서 아래 설정을 바꾼다.
ICE failed, add a TURN server and see about:webrtc for more details
Firefox에서 RTCPeerConnection.onicecandidate
이벤트에서 candidate
의 host 정보가 127.0.0.1
과 같은 형태로 들어가거나 event.candidate
는 Null이 아니지만 event.candidate.candidate
의 값이 빈 문자열""
일 경우 접속 URL을 확인하는 것이 좋다. 0.0.0.0
과 같은 주소로 접속하면 안된다.
정상적일경우:
RTCIceCandidate { candidate: "candidate:0 1 UDP 2122252543 192.168.1.10 50077 typ host", sdpMid: "2", sdpMLineIndex: 2, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 192.168.1.10 9 typ host tcptype active", sdpMid: "2", sdpMLineIndex: 2, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:0 1 UDP 2122252543 192.168.1.10 49330 typ host", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 192.168.1.10 9 typ host tcptype active", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:0 2 UDP 2122252542 192.168.1.10 50790 typ host", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:2 2 TCP 2105524478 192.168.1.10 9 typ host tcptype active", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:0 1 UDP 2122252543 192.168.1.10 64160 typ host", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 192.168.1.10 9 typ host tcptype active", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:0 2 UDP 2122252542 192.168.1.10 58270 typ host", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "2c97a5b3" }
RTCIceCandidate { candidate: "candidate:2 2 TCP 2105524478 192.168.1.10 9 typ host tcptype active", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "2c97a5b3" }
비정상적일경우:
RTCIceCandidate { candidate: "candidate:0 1 UDP 2122252543 127.0.0.1 49837 typ host", sdpMid: "2", sdpMLineIndex: 2, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 127.0.0.1 9 typ host tcptype active", sdpMid: "2", sdpMLineIndex: 2, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 127.0.0.1 9 typ host tcptype active", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "candidate:2 2 TCP 2105524478 127.0.0.1 9 typ host tcptype active", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "candidate:2 1 TCP 2105524479 127.0.0.1 9 typ host tcptype active", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "candidate:2 2 TCP 2105524478 127.0.0.1 9 typ host tcptype active", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "4ecc8976" }
RTCIceCandidate { candidate: "", sdpMid: "1", sdpMLineIndex: 1, usernameFragment: "4ecc8976" }
원격 Peer 에 Close 신호 보내기
- Stackoverflow - Detecting that the peer's browser was closed in a webrtc videochat
- Stackoverflow - Why does calling
RTCPeerConnection.close()
not sendclosed
event? - Stackoverflow - WebRTC issue on closing peer connection in have-local-offer state
peer를 close 했을 때, 원격 peer 는 바로 close 를 수신하지 못한다. (UDP로 연결되었을 경우가 대표적인 예시) 이는 ICE 로 시그널링하기 때문이다. ICE로 시그널링할 경우 ICE 확인을 위한 시간이 약 5초 정도 필요하다.
바로 close 하고싶다면 JSEP와 같은 방법으로 직접 close 해야 한다.
아니면, SCTP로 연결되는 DataChannel을 생성한 후, DataChannel 이 닫히면 종료하도록 하면 된다.
See also
Favorite site
-
WebRTC website - WebRTC Home - WebRTC
- Google 그룹스 - discuss-webrtc
- Wikipedia (en) WebRTC에 대한 설명
- WebRTC 기술 약어 모음집 - Mozilla 웹 기술 블로그
- RTCS 실시간 웹 서비스를 위한 도전 1
- WebRTC in the real world: STUN, TURN and signaling
- Getting Started with WebRTC
Documentation
Guide
- [추천] JIYUN.ME | WebRTC 개요 4
- WebRTC Basic – cryingnavi – 이런저런 개발 이야기
- [추천] 7 ways to stream RTSP on the page 5
- Embedding a WebRTC player for live broadcasts to a website
- [추천] Browser-based WebRTC stream from RTSP IP camera with low latency
- [추천] Kurento - Interoperating WebRTC and IP cameras (Kurento example)
- Migrating your native/mobile application to Unified Plan/WebRTC 1 6
- WebRTC - Full Stack Python (tutorials, resources 모음)
- WebRTC: Configure Your Own TURN/STUN Server (red5, TURN, STUN, WebRTC)
Understanding WebRTC and ICE
- webrtc-story-01 - WebRTC 시동걸기 | Doublem.org
- webrtc-story-02 - WebRTC 시그널링 서버 구현하기 | Doublem.org
- WebRTC는 어떻게 실시간으로 데이터를 교환할 수 있을까? - 재그지그의 개발 블로그
- clux/sdp-transform: A simple parser/writer for the Session Description Protocol
- The signaling server · webRTC-kor
Video Chat Tutorials
Article
- WebRTC and the Early API
- WebRTC는 어떻게 실시간으로 데이터를 교환할 수 있을까? - 재그지그의 개발 블로그
- [그림] WebRTC란? (STUN과 TURN 서버의 이해) (2)
Demo
- Github - WebRTC C++ sample
- Github - WebRTC native C++ to browser PeerConnection example
- Github - WebRTC Nuts and Bolts
- WebRTC가 실제로 어떻게 동작하는지 코드와 예제를 통해 전체적으로 배우기
- 웹페이지 에서 웹캠을 초기화하고 백엔드와 통신하는 각 단계들을 로그 및 출력물과 함께 볼 수 있음
- Front 는 TypeScript, 백엔드는 Go 로 구현한 오픈소스