여기서는 실제 turn, stun 등의 시그널링과 실제 화상통신에 대한 것을 정리하는 내용을 다룬다.
WEBRTC 는 p2p 통신을 하기 위해 사전에 미리 sdp 나 turn 서버를 서로 알고 있어야 한다.
어떠한 텍스트 정보를 전달할 수 있는 통신수단(예: 소켓, 웹소켓 등)의 종류는 어떤것이라도 상관없다.
여기서는 websocket 을 사용했다.
실제 보드 개발에는 socket 을 쓰면 될것이다.
이 서버는 단순히 접속을 하면 room 을 만들고 서로 준비가 된 상태에서 한 client가 broadcast 를 하게 되면, 나머지 client 에서 그 데이터를 받아서 joinRequest 를 보내면서 통신이 시작된다.
var fs = require('fs');
var _static = require('node-static');
var file = new _static.Server('./public');
var options = {
key: fs.readFileSync('fake-keys/privatekey.pem'),
cert: fs.readFileSync('fake-keys/certificate.pem')
};
var app = require('https').createServer(options, function(request, response) {
request.addListener('end', function() {
file.serve(request, response);
}).resume();
});
var WebSocketServer = require('websocket').server;
new WebSocketServer({
httpServer: app,
autoAcceptConnections: false
}).on('request', onRequest);
var CHANNELS = { };
function onRequest(socket) {
var origin = socket.origin + socket.resource;
var websocket = socket.accept(null, origin);
websocket.on('message', function(message) {
if (message.type === 'utf8') {
onMessage(JSON.parse(message.utf8Data), websocket);
}
});
websocket.on('close', function() {
truncateChannels(websocket);
});
}
function onMessage(message, websocket) {
//console.log("from client ...", message);
if (message.open)
onOpen(message, websocket);
else
sendMessage(message, websocket);
}
function onOpen(message, websocket) {
var channel = CHANNELS[message.channel];
if (channel)
CHANNELS[message.channel][channel.length] = websocket;
else
CHANNELS[message.channel] = [websocket];
//console.log("CHANNELS!!!!!!!!!!", CHANNELS);
}
function sendMessage(message, websocket) {
message.data = JSON.stringify(message.data);
var channel = CHANNELS[message.channel];
if (!channel) {
console.error('no such channel exists');
return;
}
for (var i = 0; i < channel.length; i++) {
if (channel[i] && channel[i] != websocket) {
try {
console.log(i, " 에게 ", message.data);
channel[i].sendUTF(message.data);
} catch(e) {
}
}
}
}
function swapArray(arr) {
var swapped = [],
length = arr.length;
for (var i = 0; i < length; i++) {
if (arr[i])
swapped[swapped.length] = arr[i];
}
return swapped;
}
function truncateChannels(websocket) {
for (var channel in CHANNELS) {
var _channel = CHANNELS[channel];
for (var i = 0; i < _channel.length; i++) {
if (_channel[i] == websocket)
delete _channel[i];
}
CHANNELS[channel] = swapArray(_channel);
if (CHANNELS && CHANNELS[channel] && !CHANNELS[channel].length)
delete CHANNELS[channel];
}
}
app.listen(12034);
console.log('Please open SSL URL: https://localhost:12034/');
상단의 내용이 서버 내용의 끝이다.
두 브라우저가 통신을 하기위해 클라이언트 코드가 필요하다.
위 흐름을 구현한 코드.
<button id="start-broadcasting" class="setup">Start Transmitting Yourself!</button>
<div id="videos-container">
<video width="470" id="remoteView" autoplay></video>
<video width="470" id="localView" autoplay></video>
</div>
<script>
var channel = location.href.replace( /\/|:|#|%|\.|\[|\]/g , '');
var websocket = new WebSocket('wss://' + document.domain + ':12034');
websocket.onopen = function() {
websocket.push(JSON.stringify({
open: true,
channel: channel
}));
};
var pc;
var isFirefox = !!navigator.mozGetUserMedia;
var isChrome = !!navigator.webkitGetUserMedia;
var STUN = {
url: isChrome ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121'
};
var TURN = {
url: 'turn:[email protected]:80',
credential: 'homeo'
};
var iceServers = {
iceServers: [STUN]
};
if (isChrome) {
if (parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) >= 28)
TURN = {
url: 'turn:turn.bistri.com:80',
credential: 'homeo',
username: 'homeo'
};
iceServers.iceServers = [STUN, TURN];
}
var optionalArgument = {
optional: [{
DtlsSrtpKeyAgreement: true
}]
};
function start() {
pc = new RTCPeerConnection(iceServers, optionalArgument);
pc.onicecandidate = function (evt) {
console.log("onicecandidate");
if (evt.candidate) {
websocket.send({
candidate: evt.candidate
})
}
};
pc.onnegotiationneeded = function () {
console.log("onnegotiationneeded");
pc.createOffer(localDescCreated, logError);
}
pc.onaddstream = function (evt) {
console.log("onaddstream");
var removeView = document.getElementById("remoteView");
remoteView.src = URL.createObjectURL(evt.stream);
};
navigator.getUserMedia({
'audio': true,
'video': true
}, function (stream) {
var localView = document.getElementById("localView");
var video = localView;
video.src = URL.createObjectURL(stream);
video.controls = true;
video.muted = true;
pc.addStream(stream);
}, logError);
}
function localDescCreated(desc) {
console.log("localDescCreated");
pc.setLocalDescription(desc, function () {
console.log("setLocalDescCreated CallBack");
websocket.send({
sdp: pc.localDescription
});
}, logError);
}
function logError(error) {
log(error.name + ': ' + error.message);
}
websocket.onmessage = function(e) {
var message = JSON.parse(e.data);
if(message.broadcast) {
websocket.send({
joinRequest: true,
});
window.alert("it will join them");
start();
}
if(message.joinRequest) {
start();
window.alert("they join us");
}
if (message.sdp) {
console.log("sdp 받았다.");
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
// if we received an offer, we need to answer
if (pc.remoteDescription.type == 'offer') {
console.log("createAnswer");
pc.createAnswer(localDescCreated, logError);
}
}, logError);
}
if(message.candidate) {
console.log("candidate!!!", message);
pc.addIceCandidate(new RTCIceCandidate(message.candidate));
}
};
websocket.push = websocket.send;
websocket.send = function(data) {
websocket.push(JSON.stringify({
data: data,
channel: channel
}));
};
document.querySelector('#start-broadcasting').onclick = function() {
websocket.send({broadcast:true});
};
</script>
로그 결과
pc1
onnegotiationneeded
localDescCreated
setLocalDescCreated Callback
onicecandidate [7]
sdp 받았다.
candidate!!!
sdp 받았다.
createAnswer
onaddstream
localDescCreated
setLocalDescCreated Callback
pc2
sdp 받았다.
candidate!!![6]
createAnswer
onaddstream
localDescCreated
setLocalDescCreated CallBack
onicecandidate
onnegotiationneeded
localDescCreated
setLocalDescCreated CallBack
sdp 받았다.
server
0 ' 에게 ' '{"broadcast":true}'
1 ' 에게 ' '{"joinRequest":true}'
1 ' 에게 ' '{"sdp":{"type":"offer","sdp":"v=0\\r\\no=- 2448330732196048433 2 IN IP4 127.0.0.1\\r\\ns=-\\r\\nt=0 0\\r\\na=group:BUNDLE audio video\\r\\na=msid-semantic: WMS B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\\r\\nc=IN IP4 0.0.0.0\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=ice-ufrag:XhEn\\r\\na=ice-pwd:nbtxJI5UNjyL0IQ52EOQ8cCg\\r\\na=ice-options:trickle\\r\\na=fingerprint:sha-256 BB:9B:A9:90:33:E9:4F:07:7E:58:0A:9D:09:13:E1:9D:A1:CE:85:1A:FB:D2:5D:81:CA:74:6F:C2:43:39:66:0D\\r\\na=setup:actpass\\r\\na=mid:audio\\r\\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtpmap:111 opus/48000/2\\r\\na=rtcp-fb:111 transport-cc\\r\\na=fmtp:111 minptime=10;useinbandfec=1\\r\\na=rtpmap:103 ISAC/16000\\r\\na=rtpmap:104 ISAC/32000\\r\\na=rtpmap:9 G722/8000\\r\\na=rtpmap:0 PCMU/8000\\r\\na=rtpmap:8 PCMA/8000\\r\\na=rtpmap:106 CN/32000\\r\\na=rtpmap:105 CN/16000\\r\\na=rtpmap:13 CN/8000\\r\\na=rtpmap:110 telephone-event/48000\\r\\na=rtpmap:112 telephone-event/32000\\r\\na=rtpmap:113 telephone-event/16000\\r\\na=rtpmap:126 telephone-event/8000\\r\\na=ssrc:4099229577 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:4099229577 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH e90476d3-2275-4fee-a0cd-5c7e5f859fb3\\r\\na=ssrc:4099229577 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:4099229577 label:e90476d3-2275-4fee-a0cd-5c7e5f859fb3\\r\\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\\r\\nc=IN IP4 0.0.0.0\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=ice-ufrag:XhEn\\r\\na=ice-pwd:nbtxJI5UNjyL0IQ52EOQ8cCg\\r\\na=ice-options:trickle\\r\\na=fingerprint:sha-256 BB:9B:A9:90:33:E9:4F:07:7E:58:0A:9D:09:13:E1:9D:A1:CE:85:1A:FB:D2:5D:81:CA:74:6F:C2:43:39:66:0D\\r\\na=setup:actpass\\r\\na=mid:video\\r\\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\na=extmap:4 urn:3gpp:video-orientation\\r\\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\\r\\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtcp-rsize\\r\\na=rtpmap:96 VP8/90000\\r\\na=rtcp-fb:96 ccm fir\\r\\na=rtcp-fb:96 nack\\r\\na=rtcp-fb:96 nack pli\\r\\na=rtcp-fb:96 goog-remb\\r\\na=rtcp-fb:96 transport-cc\\r\\na=rtpmap:98 VP9/90000\\r\\na=rtcp-fb:98 ccm fir\\r\\na=rtcp-fb:98 nack\\r\\na=rtcp-fb:98 nack pli\\r\\na=rtcp-fb:98 goog-remb\\r\\na=rtcp-fb:98 transport-cc\\r\\na=rtpmap:100 H264/90000\\r\\na=rtcp-fb:100 ccm fir\\r\\na=rtcp-fb:100 nack\\r\\na=rtcp-fb:100 nack pli\\r\\na=rtcp-fb:100 goog-remb\\r\\na=rtcp-fb:100 transport-cc\\r\\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\na=rtpmap:102 red/90000\\r\\na=rtpmap:127 ulpfec/90000\\r\\na=rtpmap:97 rtx/90000\\r\\na=fmtp:97 apt=96\\r\\na=rtpmap:99 rtx/90000\\r\\na=fmtp:99 apt=98\\r\\na=rtpmap:101 rtx/90000\\r\\na=fmtp:101 apt=100\\r\\na=rtpmap:125 rtx/90000\\r\\na=fmtp:125 apt=102\\r\\na=ssrc-group:FID 809685964 3334537262\\r\\na=ssrc:809685964 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:809685964 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH 2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:809685964 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:809685964 label:2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:3334537262 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:3334537262 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH 2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:3334537262 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:3334537262 label:2e5d5bb5-895e-497b-97dd-99e101895177\\r\\n"}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:2437072876 1 udp 2122260223 192.168.1.2 54332 typ host generation 0 ufrag XhEn network-id 1","sdpMid":"audio","sdpMLineIndex":0}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:2437072876 1 udp 2122260223 192.168.1.2 53379 typ host generation 0 ufrag XhEn network-id 1","sdpMid":"video","sdpMLineIndex":1}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:3753982748 1 tcp 1518280447 192.168.1.2 9 typ host tcptype active generation 0 ufrag XhEn network-id 1","sdpMid":"audio","sdpMLineIndex":0}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:3753982748 1 tcp 1518280447 192.168.1.2 9 typ host tcptype active generation 0 ufrag XhEn network-id 1","sdpMid":"video","sdpMLineIndex":1}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:941443129 1 udp 1686052607 119.203.5.62 54332 typ srflx raddr 192.168.1.2 rport 54332 generation 0 ufrag XhEn network-id 1","sdpMid":"audio","sdpMLineIndex":0}}'
1 ' 에게 ' '{"candidate":{"candidate":"candidate:941443129 1 udp 1686052607 119.203.5.62 53379 typ srflx raddr 192.168.1.2 rport 53379 generation 0 ufrag XhEn network-id 1","sdpMid":"video","sdpMLineIndex":1}}'
0 ' 에게 ' '{"sdp":{"type":"answer","sdp":"v=0\\r\\no=- 7934811631185428798 2 IN IP4 127.0.0.1\\r\\ns=-\\r\\nt=0 0\\r\\na=group:BUNDLE audio video\\r\\na=msid-semantic: WMS\\r\\nm=audio 59683 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\\r\\nc=IN IP4 192.168.1.22\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=candidate:514002099 1 udp 2122260223 192.168.1.22 59683 typ host generation 0 network-id 1\\r\\na=ice-ufrag:hVSq\\r\\na=ice-pwd:KPenDaJMEF/PUqHVws+WQLKy\\r\\na=fingerprint:sha-256 EF:F8:CD:07:C8:44:E8:78:9D:69:00:4A:28:E1:17:44:FE:9A:20:5F:D9:00:AD:F2:85:38:A5:36:88:6C:18:D4\\r\\na=setup:active\\r\\na=mid:audio\\r\\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\na=recvonly\\r\\na=rtcp-mux\\r\\na=rtpmap:111 opus/48000/2\\r\\na=rtcp-fb:111 transport-cc\\r\\na=fmtp:111 minptime=10;useinbandfec=1\\r\\na=rtpmap:103 ISAC/16000\\r\\na=rtpmap:104 ISAC/32000\\r\\na=rtpmap:9 G722/8000\\r\\na=rtpmap:0 PCMU/8000\\r\\na=rtpmap:8 PCMA/8000\\r\\na=rtpmap:106 CN/32000\\r\\na=rtpmap:105 CN/16000\\r\\na=rtpmap:13 CN/8000\\r\\na=rtpmap:110 telephone-event/48000\\r\\na=rtpmap:112 telephone-event/32000\\r\\na=rtpmap:113 telephone-event/16000\\r\\na=rtpmap:126 telephone-event/8000\\r\\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\\r\\nc=IN IP4 0.0.0.0\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=ice-ufrag:hVSq\\r\\na=ice-pwd:KPenDaJMEF/PUqHVws+WQLKy\\r\\na=fingerprint:sha-256 EF:F8:CD:07:C8:44:E8:78:9D:69:00:4A:28:E1:17:44:FE:9A:20:5F:D9:00:AD:F2:85:38:A5:36:88:6C:18:D4\\r\\na=setup:active\\r\\na=mid:video\\r\\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\na=extmap:4 urn:3gpp:video-orientation\\r\\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\\r\\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\\r\\na=recvonly\\r\\na=rtcp-mux\\r\\na=rtcp-rsize\\r\\na=rtpmap:96 VP8/90000\\r\\na=rtcp-fb:96 ccm fir\\r\\na=rtcp-fb:96 nack\\r\\na=rtcp-fb:96 nack pli\\r\\na=rtcp-fb:96 goog-remb\\r\\na=rtcp-fb:96 transport-cc\\r\\na=rtpmap:98 VP9/90000\\r\\na=rtcp-fb:98 ccm fir\\r\\na=rtcp-fb:98 nack\\r\\na=rtcp-fb:98 nack pli\\r\\na=rtcp-fb:98 goog-remb\\r\\na=rtcp-fb:98 transport-cc\\r\\na=rtpmap:100 H264/90000\\r\\na=rtcp-fb:100 ccm fir\\r\\na=rtcp-fb:100 nack\\r\\na=rtcp-fb:100 nack pli\\r\\na=rtcp-fb:100 goog-remb\\r\\na=rtcp-fb:100 transport-cc\\r\\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\na=rtpmap:102 red/90000\\r\\na=rtpmap:127 ulpfec/90000\\r\\na=rtpmap:97 rtx/90000\\r\\na=fmtp:97 apt=96\\r\\na=rtpmap:99 rtx/90000\\r\\na=fmtp:99 apt=98\\r\\na=rtpmap:101 rtx/90000\\r\\na=fmtp:101 apt=100\\r\\na=rtpmap:125 rtx/90000\\r\\na=fmtp:125 apt=102\\r\\n"}}'
0 ' 에게 ' '{"candidate":{"candidate":"candidate:514002099 1 udp 2122260223 192.168.1.22 59683 typ host generation 0 ufrag hVSq network-id 1","sdpMid":"audio","sdpMLineIndex":0}}'
0 ' 에게 ' '{"sdp":{"type":"offer","sdp":"v=0\\r\\no=- 7934811631185428798 3 IN IP4 127.0.0.1\\r\\ns=-\\r\\nt=0 0\\r\\na=group:BUNDLE audio video\\r\\na=msid-semantic: WMS jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl\\r\\nm=audio 59683 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\\r\\nc=IN IP4 192.168.1.22\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=candidate:514002099 1 udp 2122260223 192.168.1.22 59683 typ host generation 0 network-id 1\\r\\na=ice-ufrag:hVSq\\r\\na=ice-pwd:KPenDaJMEF/PUqHVws+WQLKy\\r\\na=fingerprint:sha-256 EF:F8:CD:07:C8:44:E8:78:9D:69:00:4A:28:E1:17:44:FE:9A:20:5F:D9:00:AD:F2:85:38:A5:36:88:6C:18:D4\\r\\na=setup:actpass\\r\\na=mid:audio\\r\\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtpmap:111 opus/48000/2\\r\\na=rtcp-fb:111 transport-cc\\r\\na=fmtp:111 minptime=10;useinbandfec=1\\r\\na=rtpmap:103 ISAC/16000\\r\\na=rtpmap:104 ISAC/32000\\r\\na=rtpmap:9 G722/8000\\r\\na=rtpmap:0 PCMU/8000\\r\\na=rtpmap:8 PCMA/8000\\r\\na=rtpmap:106 CN/32000\\r\\na=rtpmap:105 CN/16000\\r\\na=rtpmap:13 CN/8000\\r\\na=rtpmap:110 telephone-event/48000\\r\\na=rtpmap:112 telephone-event/32000\\r\\na=rtpmap:113 telephone-event/16000\\r\\na=rtpmap:126 telephone-event/8000\\r\\na=ssrc:2162542293 cname:RHwVAqg7NxlDh2Ll\\r\\na=ssrc:2162542293 msid:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl 55cb3c32-2a54-4580-93d1-54fec125f40d\\r\\na=ssrc:2162542293 mslabel:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl\\r\\na=ssrc:2162542293 label:55cb3c32-2a54-4580-93d1-54fec125f40d\\r\\nm=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\\r\\nc=IN IP4 0.0.0.0\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=ice-ufrag:hVSq\\r\\na=ice-pwd:KPenDaJMEF/PUqHVws+WQLKy\\r\\na=fingerprint:sha-256 EF:F8:CD:07:C8:44:E8:78:9D:69:00:4A:28:E1:17:44:FE:9A:20:5F:D9:00:AD:F2:85:38:A5:36:88:6C:18:D4\\r\\na=setup:actpass\\r\\na=mid:video\\r\\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\na=extmap:4 urn:3gpp:video-orientation\\r\\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\\r\\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtcp-rsize\\r\\na=rtpmap:96 VP8/90000\\r\\na=rtcp-fb:96 ccm fir\\r\\na=rtcp-fb:96 nack\\r\\na=rtcp-fb:96 nack pli\\r\\na=rtcp-fb:96 goog-remb\\r\\na=rtcp-fb:96 transport-cc\\r\\na=rtpmap:98 VP9/90000\\r\\na=rtcp-fb:98 ccm fir\\r\\na=rtcp-fb:98 nack\\r\\na=rtcp-fb:98 nack pli\\r\\na=rtcp-fb:98 goog-remb\\r\\na=rtcp-fb:98 transport-cc\\r\\na=rtpmap:100 H264/90000\\r\\na=rtcp-fb:100 ccm fir\\r\\na=rtcp-fb:100 nack\\r\\na=rtcp-fb:100 nack pli\\r\\na=rtcp-fb:100 goog-remb\\r\\na=rtcp-fb:100 transport-cc\\r\\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\na=rtpmap:102 red/90000\\r\\na=rtpmap:127 ulpfec/90000\\r\\na=rtpmap:97 rtx/90000\\r\\na=fmtp:97 apt=96\\r\\na=rtpmap:99 rtx/90000\\r\\na=fmtp:99 apt=98\\r\\na=rtpmap:101 rtx/90000\\r\\na=fmtp:101 apt=100\\r\\na=rtpmap:125 rtx/90000\\r\\na=fmtp:125 apt=102\\r\\na=ssrc-group:FID 3759801015 1863437275\\r\\na=ssrc:3759801015 cname:RHwVAqg7NxlDh2Ll\\r\\na=ssrc:3759801015 msid:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl b3a1e4d9-898f-4d05-81bd-d0d6b1cdc14b\\r\\na=ssrc:3759801015 mslabel:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl\\r\\na=ssrc:3759801015 label:b3a1e4d9-898f-4d05-81bd-d0d6b1cdc14b\\r\\na=ssrc:1863437275 cname:RHwVAqg7NxlDh2Ll\\r\\na=ssrc:1863437275 msid:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl b3a1e4d9-898f-4d05-81bd-d0d6b1cdc14b\\r\\na=ssrc:1863437275 mslabel:jtp6GtnFckLUocKIin4MT3PydrljbA14xGgl\\r\\na=ssrc:1863437275 label:b3a1e4d9-898f-4d05-81bd-d0d6b1cdc14b\\r\\n"}}'
1 ' 에게 ' '{"sdp":{"type":"answer","sdp":"v=0\\r\\no=- 2448330732196048433 3 IN IP4 127.0.0.1\\r\\ns=-\\r\\nt=0 0\\r\\na=group:BUNDLE audio video\\r\\na=msid-semantic: WMS B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\nm=audio 54332 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\\r\\nc=IN IP4 119.203.5.62\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=candidate:2437072876 1 udp 2122260223 192.168.1.2 54332 typ host generation 0 network-id 1\\r\\na=candidate:3753982748 1 tcp 1518280447 192.168.1.2 9 typ host tcptype active generation 0 network-id 1\\r\\na=candidate:941443129 1 udp 1686052607 119.203.5.62 54332 typ srflx raddr 192.168.1.2 rport 54332 generation 0 network-id 1\\r\\na=ice-ufrag:XhEn\\r\\na=ice-pwd:nbtxJI5UNjyL0IQ52EOQ8cCg\\r\\na=ice-options:trickle\\r\\na=fingerprint:sha-256 BB:9B:A9:90:33:E9:4F:07:7E:58:0A:9D:09:13:E1:9D:A1:CE:85:1A:FB:D2:5D:81:CA:74:6F:C2:43:39:66:0D\\r\\na=setup:passive\\r\\na=mid:audio\\r\\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtpmap:111 opus/48000/2\\r\\na=rtcp-fb:111 transport-cc\\r\\na=fmtp:111 minptime=10;useinbandfec=1\\r\\na=rtpmap:103 ISAC/16000\\r\\na=rtpmap:104 ISAC/32000\\r\\na=rtpmap:9 G722/8000\\r\\na=rtpmap:0 PCMU/8000\\r\\na=rtpmap:8 PCMA/8000\\r\\na=rtpmap:106 CN/32000\\r\\na=rtpmap:105 CN/16000\\r\\na=rtpmap:13 CN/8000\\r\\na=rtpmap:110 telephone-event/48000\\r\\na=rtpmap:112 telephone-event/32000\\r\\na=rtpmap:113 telephone-event/16000\\r\\na=rtpmap:126 telephone-event/8000\\r\\na=ssrc:4099229577 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:4099229577 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH e90476d3-2275-4fee-a0cd-5c7e5f859fb3\\r\\na=ssrc:4099229577 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:4099229577 label:e90476d3-2275-4fee-a0cd-5c7e5f859fb3\\r\\nm=video 53379 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\\r\\nc=IN IP4 119.203.5.62\\r\\na=rtcp:9 IN IP4 0.0.0.0\\r\\na=candidate:2437072876 1 udp 2122260223 192.168.1.2 53379 typ host generation 0 network-id 1\\r\\na=candidate:3753982748 1 tcp 1518280447 192.168.1.2 9 typ host tcptype active generation 0 network-id 1\\r\\na=candidate:941443129 1 udp 1686052607 119.203.5.62 53379 typ srflx raddr 192.168.1.2 rport 53379 generation 0 network-id 1\\r\\na=ice-ufrag:XhEn\\r\\na=ice-pwd:nbtxJI5UNjyL0IQ52EOQ8cCg\\r\\na=ice-options:trickle\\r\\na=fingerprint:sha-256 BB:9B:A9:90:33:E9:4F:07:7E:58:0A:9D:09:13:E1:9D:A1:CE:85:1A:FB:D2:5D:81:CA:74:6F:C2:43:39:66:0D\\r\\na=setup:passive\\r\\na=mid:video\\r\\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\na=extmap:4 urn:3gpp:video-orientation\\r\\na=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\\r\\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\\r\\na=sendrecv\\r\\na=rtcp-mux\\r\\na=rtcp-rsize\\r\\na=rtpmap:96 VP8/90000\\r\\na=rtcp-fb:96 ccm fir\\r\\na=rtcp-fb:96 nack\\r\\na=rtcp-fb:96 nack pli\\r\\na=rtcp-fb:96 goog-remb\\r\\na=rtcp-fb:96 transport-cc\\r\\na=rtpmap:98 VP9/90000\\r\\na=rtcp-fb:98 ccm fir\\r\\na=rtcp-fb:98 nack\\r\\na=rtcp-fb:98 nack pli\\r\\na=rtcp-fb:98 goog-remb\\r\\na=rtcp-fb:98 transport-cc\\r\\na=rtpmap:100 H264/90000\\r\\na=rtcp-fb:100 ccm fir\\r\\na=rtcp-fb:100 nack\\r\\na=rtcp-fb:100 nack pli\\r\\na=rtcp-fb:100 goog-remb\\r\\na=rtcp-fb:100 transport-cc\\r\\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\na=rtpmap:102 red/90000\\r\\na=rtpmap:127 ulpfec/90000\\r\\na=rtpmap:97 rtx/90000\\r\\na=fmtp:97 apt=96\\r\\na=rtpmap:99 rtx/90000\\r\\na=fmtp:99 apt=98\\r\\na=rtpmap:101 rtx/90000\\r\\na=fmtp:101 apt=100\\r\\na=rtpmap:125 rtx/90000\\r\\na=fmtp:125 apt=102\\r\\na=ssrc-group:FID 809685964 3334537262\\r\\na=ssrc:809685964 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:809685964 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH 2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:809685964 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:809685964 label:2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:3334537262 cname:KxMjmPUWeatN6Kzu\\r\\na=ssrc:3334537262 msid:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH 2e5d5bb5-895e-497b-97dd-99e101895177\\r\\na=ssrc:3334537262 mslabel:B6GweMac79bGvdfnNYjVoLxufXP6nbq1s4JH\\r\\na=ssrc:3334537262 label:2e5d5bb5-895e-497b-97dd-99e101895177\\r\\n"}}'
위 로그를 보면 결국 소켓은 json 으로 되어있는 candidate 정보와 sdp 정보만 전달해주면 된다.
