지난 포스팅에서 설명했듯이 peer to peer 통신을 통해 화상/음성을 주고받을 수 있는 기술인 webRtc를 이용하여 실제 코드를 만들어 보겠습니다.
아래 코드를 실행시키면 좌측에는 본인, 우측에는 상대방이 표시됩니다. Call 버튼을 클릭하게 되면 상대방에서 접속요청을 보내게 되고, 요청을 받은 상대방 측에서는 자신의 peer 주소를 보내주어 통신을 하게됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body onload="showMyFace()">
<video id="yourVideo" autoplay muted playsinline></video>
<video id="friendsVideo" autoplay playsinline></video>
<br />
<button onclick="showFriendsFace()" type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-facetime-video" aria-hidden="true"></span> Call</button>
<script src="https://www.gstatic.com/firebasejs/4.9.0/firebase.js"></script>
</body>
</html>
|
cs |
아래의 config 는 Firebase Realtime Database 를 생성한 후에 Firebase 에서 제공하는 세팅값을 입력하는 부분입니다. Firebase 에 접속하여 데이터베이스를 생성하게 되면 얻을 수 있는 설정값입니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<script language = "javascript">
//Create an account on Firebase, and use the credentials they give you in place of the following
var config = {
apiKey: "AIeaS7AvL9f2MvHwVcghtVbGWtOk8AGcRmw6-Rqc",
authDomain: "webrtc-f0d24.firebaseapp.com",
databaseURL: "https://webrtc-f0d24.firebaseio.com",
projectId: "webrtc-f0d24",
storageBucket: "webrtc-f0d24.appspot.com",
messagingSenderId: "876347981221",
};
firebase.initializeApp(config);
var database = firebase.database().ref();
</script>
|
cs |
아래 코드의 var servers 에 세팅된 값은 공개 turn 서버의 주소를 사용한 코드입니다.
https://gist.github.com/sagivo/3a4b2f2c7ac6e1b5267c2f1f59ac6c6b 이 주소에 접속하면 공개서버를 다수 확인할 수 있습니다. RtcPeerConnection 함수를 이용하여 peer 커넥션을 위한 객체를 생성하게 됩니다
1
2
3
4
5
6
7
8
9
10
11
|
<script language = "javascript">
var yourVideo = document.getElementById("yourVideo");
var friendsVideo = document.getElementById("friendsVideo");
var yourId = Math.floor(Math.random()*1000000000);
var servers = {"urls": ["turn:13.250.13.83:3478?transport=udp"],"username": "YzYNCouZM1mhqhmseWk6","credential": "YzYNCouZM1mhqhmseWk6"}
var pc = new RTCPeerConnection(servers);
pc.onicecandidate = (event => event.candidate?sendMessage(yourId, JSON.stringify({'ice': event.candidate})):console.log("Sent All Ice") );
pc.onaddstream = (event => friendsVideo.srcObject = event.stream);
</script>
|
cs |
sendMessage 와 readmessage 함수는 peer 간에 서로 메시지를 주고 받기 위해 사용하는 함수 입니다. Firebase 연결을 통해 생성한 database 객체는 child_added 이벤트가 발생하면 readMessage()를 호출하게 됩니다. readMessage() 함수에서는 자신이 보낸 메시지인지 상대가 보낸 메시지인지를 구분하여 적절한 처리를 하게됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<script language = "javascript">
function sendMessage(senderId, data) {
var msg = database.push({ sender: senderId, message: data });
msg.remove();
}
function readMessage(data) {
var msg = JSON.parse(data.val().message);
var sender = data.val().sender;
if (sender != yourId) {
if (msg.ice != undefined)
pc.addIceCandidate(new RTCIceCandidate(msg.ice));
else if (msg.sdp.type == "offer")
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})));
else if (msg.sdp.type == "answer")
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
}
};
database.on('child_added', readMessage);
</script>
|
cs |
화면 로드시 showMyFace() 가 호출되어 getUserMedia()함수를 통해 자신의 영상정보를 읽어와서 뿌려주게 됩니다. Call 버튼을 클릭하게 되면 show FriendsFace()가 호출되어 sendMessage()함수를 통해 상대방에게 메시지를 보내게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<script language = "javascript">
database.on('child_added', readMessage);
function showMyFace() {
navigator.mediaDevices.getUserMedia({audio:true, video:true})
.then(stream => yourVideo.srcObject = stream)
.then(stream => pc.addStream(stream));
}
function showFriendsFace() {
pc.createOffer()
.then(offer => pc.setLocalDescription(offer) )
.then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})) );
}
</script>
|
cs |
코드가 그리 길지 않아서 css 와 javascript 코드를 모두 한페이지에 넣어두었습니다. 코드 중에서 config 값은 자신의 Firebase config 값으로 변경해야 동작하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="css/style.css">
<meta name="apple-mobile-web-app-capable" content="yes">
</head>
<body onload="showMyFace()">
<style>
video {
background-color: #ddd;
border-radius: 7px;
margin: 10px 0px 0px 10px;
width: 320px;
height: 240px;
}
button {
margin: 5px 0px 0px 10px !important;
width: 654px;
}
</style>
<video id="yourVideo" autoplay muted playsinline></video>
<video id="friendsVideo" autoplay muted playsinline></video>
<br />
<button onclick="showFriendsFace()" type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-facetime-video" aria-hidden="true"></span> Call</button>
<script src="https://www.gstatic.com/firebasejs/4.9.0/firebase.js"></script>
</body>
<script language = "javascript">
//Create an account on Firebase, and use the credentials they give you in place of the following
var config = {
apiKey: "AIeaS7AvL9f2MvHwVcghtVbGWtOk8AGcRmw6-Rqc",
authDomain: "webrtc-f0d24.firebaseapp.com",
databaseURL: "https://webrtc-f0d24.firebaseio.com",
projectId: "webrtc-f0d24",
storageBucket: "webrtc-f0d24.appspot.com",
messagingSenderId: "876347981221",
};
firebase.initializeApp(config);
var database = firebase.database().ref();
var yourVideo = document.getElementById("yourVideo");
var friendsVideo = document.getElementById("friendsVideo");
var yourId = Math.floor(Math.random()*1000000000);
//Create an account on Viagenie (http://numb.viagenie.ca/), and replace {'urls': 'turn:numb.viagenie.ca','credential': 'websitebeaver','username': 'websitebeaver@email.com'} with the information from your account
//var servers = {'iceServers': [{'urls': 'stun:stun.services.mozilla.com'}, {'urls': 'stun:stun.l.google.com:19302'}, {'urls': 'turn:numb.viagenie.ca','credential': 'beaver','username': 'webrtc.websitebeaver@gmail.com'}]};
//var servers = {'iceServers': [{'urls': 'turn:13.250.13.83:3478?transport=udp','YzYNCouZM1mhqhmseWk6': 'beaver','YzYNCouZM1mhqhmseWk6': 'webrtc.websitebeaver@gmail.com'}]};
// 아래 서버정보는 https://gist.github.com/sagivo/3a4b2f2c7ac6e1b5267c2f1f59ac6c6b 에서 참고했음
var servers = {"urls": ["turn:13.250.13.83:3478?transport=udp"],"username": "YzYNCouZM1mhqhmseWk6","credential": "YzYNCouZM1mhqhmseWk6"}
var pc = new RTCPeerConnection(servers);
pc.onicecandidate = (event => event.candidate?sendMessage(yourId, JSON.stringify({'ice': event.candidate})):console.log("Sent All Ice") );
pc.onaddstream = (event => friendsVideo.srcObject = event.stream);
function sendMessage(senderId, data) {
var msg = database.push({ sender: senderId, message: data });
msg.remove();
}
function readMessage(data) {
var msg = JSON.parse(data.val().message);
var sender = data.val().sender;
if (sender != yourId) {
if (msg.ice != undefined)
pc.addIceCandidate(new RTCIceCandidate(msg.ice));
else if (msg.sdp.type == "offer")
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})));
else if (msg.sdp.type == "answer")
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
}
};
database.on('child_added', readMessage);
function showMyFace() {
navigator.mediaDevices.getUserMedia({audio:true, video:true})
.then(stream => yourVideo.srcObject = stream)
.then(stream => pc.addStream(stream));
}
function showFriendsFace() {
pc.createOffer()
.then(offer => pc.setLocalDescription(offer) )
.then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})) );
}
</script>
</html>
|
cs |
참고 사이트
websitebeaver.com/insanely-simple-webrtc-video-chat-using-firebase-with-codepen-demo#demo
댓글 영역