Skip to content

Commit

Permalink
Merge pull request #135 from tmddus2/feature/231122-webrtc-multi-client
Browse files Browse the repository at this point in the history
Feature(#133) ๋ฏธ๋””์–ด์„œ๋ฒ„์—์„œ ๋ฐœํ‘œ์ž MediaStream์„ ์ฐธ์—ฌ์ž์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.
  • Loading branch information
platinouss authored Nov 26, 2023
2 parents aa0f1ca + d3ab136 commit a593778
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 40 deletions.
74 changes: 55 additions & 19 deletions mediaServer/src/RelayServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ import { RTCIceCandidate, RTCPeerConnection } from 'wrtc';
export class RelayServer {
private readonly io;
private relayServerRTCPC: RTCPeerConnection;
private readonly socketToRoom: Map<string, string>;
// ์ ‘์†ํ•œ user์˜ RTCPeerConnection
private readonly receiveStreamRTCPC: Map<string, RTCPeerConnection>;
// receiveStreamRTCPC์—์„œ ๋ฐ›์€ MediaStream ์ €์žฅ
private readonly mediaStreams: Map<string, Array<{ id: string; stream: MediaStream }>>;
private readonly studentRTCPCs: Map<string, RTCPeerConnection>;
private presenterStream: any;

constructor(port: number) {
this.relayServerRTCPC = new RTCPeerConnection();
this.socketToRoom = new Map();
this.receiveStreamRTCPC = new Map();
this.mediaStreams = new Map();
this.studentRTCPCs = new Map();
this.io = new Server(port, {
cors: {
origin: '*',
Expand All @@ -24,7 +19,6 @@ export class RelayServer {
}

listen = (path: string, event: string, method: (socket: Socket) => void) => {
// TODO: API ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์ธ์ฆ ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
this.io.of(path).on(event, method);
};

Expand All @@ -33,23 +27,48 @@ export class RelayServer {
socket.on('presenterOffer', async (data) => {
const RTCPC = new RTCPeerConnection();
this.relayServerRTCPC = RTCPC;

RTCPC.ontrack = (event) => {
RTCPC.getTransceivers().forEach((receiver) => {
console.log(receiver);
});
const stream = event.streams[0];
console.log(stream);
this.presenterStream = stream;
};

this.getServerCandidate(socket, data.socketId);

await RTCPC.setRemoteDescription(data.SDP).then(() => console.log('sever remote description ์„ค์ • ์™„๋ฃŒ'));
await RTCPC.setRemoteDescription(data.SDP);
const SDP = await RTCPC.createAnswer();
socket.emit(`${data.socketId}-serverAnswer`, {
isStudent: false,
SDP: SDP
});
RTCPC.setLocalDescription(SDP);
});
} catch (e) {
console.log(e);
}
};

enterRoom = (socket: Socket) => {
try {
socket.on('studentOffer', async (data) => {
const socketId = data.socketId;
const RTCPC = new RTCPeerConnection();

this.presenterStream.getTracks().forEach((track: any) => {
RTCPC.addTrack(track);
});

this.studentRTCPCs.set(socketId, RTCPC);

this.exchangeCandidate(socket, socketId);

await RTCPC.setRemoteDescription(data.SDP);
const SDP = await RTCPC.createAnswer();
socket.emit(`${data.socketId}-serverAnswer`, {
isStudent: true,
SDP: SDP
});
RTCPC.setLocalDescription(SDP);
console.log(RTCPC);
});
} catch (e) {
console.log(e);
Expand All @@ -60,16 +79,33 @@ export class RelayServer {
try {
this.relayServerRTCPC.onicecandidate = (e) => {
if (e.candidate) {
console.log('์„œ๋ฒ„์˜ candidate ์ˆ˜์ง‘');
socket.emit(`${presenterSocketId}-serverCandidate`, {
candidate: e.candidate
});
}
};
socket.on('presenterCandidate', (data) => {
this.relayServerRTCPC
.addIceCandidate(new RTCIceCandidate(data.candidate))
.then(() => console.log('๋ฐœํ‘œ์ž๋กœ๋ถ€ํ„ฐ candidate ๋ฐ›์Œ'));
this.relayServerRTCPC.addIceCandidate(new RTCIceCandidate(data.candidate));
});
} catch (e) {
console.log(e);
}
};

exchangeCandidate = (socket: Socket, socketId: any) => {
try {
const RTCPC = this.studentRTCPCs.get(socketId);
if (RTCPC) {
RTCPC.onicecandidate = (e) => {
if (e.candidate) {
socket.emit(`${socketId}-serverCandidate`, {
candidate: e.candidate
});
}
};
}
socket.on('studentCandidate', (data) => {
RTCPC?.addIceCandidate(new RTCIceCandidate(data.candidate));
});
} catch (e) {
console.log(e);
Expand Down
2 changes: 1 addition & 1 deletion mediaServer/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ const PORT = 3000;

const relayServer = new RelayServer(PORT);
relayServer.listen('/create-room', 'connection', relayServer.createRoom);
// relayServer.listen('/enter-room', 'connection', relayServer.enterRoom);
relayServer.listen('/enter-room', 'connection', relayServer.enterRoom);
32 changes: 12 additions & 20 deletions mediaServer/test/presenter/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,33 @@ <h1>๋ฐœํ‘œ์ž</h1>
],
};

// TODO: ๋ฐœํ‘œ์ž ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฏธ๋””์–ด track ์„ค์ • & ํ™”๋ฉด์— ์˜์ƒ ์ถœ๋ ฅ
const start = async () => {
try {
startButton.disabled = true;
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
localStream = stream;
localVideo.srcObject = stream;
callButton.disabled = false;
console.log('1. ๋กœ์ปฌ stream ์ƒ์„ฑ ์™„๋ฃŒ');
presenterRTCPC = new RTCPeerConnection();
console.log('2. ๋กœ์ปฌ RTCPeerConnection ์ƒ์„ฑ ์™„๋ฃŒ');

if (localStream) {
console.log(localStream)
console.log('track ์ถ”๊ฐ€')
localStream.getTracks().forEach((track) => {
console.log(track);
localStream.getTracks().forEach((track) => {
presenterRTCPC.addTrack(track, localStream);
});
} else {
console.log('no stream')
}
} catch (e) {
alert(`getUserMedia() error: ${e.name}`)
console.log(e)
}
}

start().then(setStream)

async function setStream() {
console.log('setstream ์‹คํ–‰')
try {
await createPresenterOffer();
await setServerAnswer();
Expand All @@ -87,7 +81,6 @@ <h1>๋ฐœํ‘œ์ž</h1>
function getPresenterCandidate() {
presenterRTCPC.onicecandidate = (e) => {
if (e.candidate) {
console.log('๋ฐœํ‘œ์ž candidate ์ˆ˜์ง‘')
socket.emit('presenterCandidate', {
candidate: e.candidate,
presenterSocketId: socket.id
Expand All @@ -98,15 +91,16 @@ <h1>๋ฐœํ‘œ์ž</h1>

async function createPresenterOffer() {
try {
console.log('offer ์ƒ์„ฑ')
console.log(presenterRTCPC)
const SDP = await presenterRTCPC.createOffer();
console.log(presenterRTCPC)
const SDP = await presenterRTCPC.createOffer({
offerToReceiveAudio:true,
offerToReceiveVedio:true
});

socket.emit('presenterOffer', {
socketId: socket.id,
SDP: SDP
});
console.log('๋ฐœํ‘œ์ž localDescription ์„ค์ • ์™„๋ฃŒ');

presenterRTCPC.setLocalDescription(SDP);
getPresenterCandidate()
} catch (e) {
Expand All @@ -116,11 +110,9 @@ <h1>๋ฐœํ‘œ์ž</h1>

async function setServerAnswer() {
socket.on(`${socket.id}-serverAnswer`, (data) => {
console.log('remoteDescription ์„ค์ •์™„๋ฃŒ')
presenterRTCPC.setRemoteDescription(data.SDP)
})
socket.on(`${socket.id}-serverCandidate`, (data) => {
console.log('์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ candidate ๋ฐ›์Œ')
presenterRTCPC.addIceCandidate(new RTCIceCandidate(data.candidate))
});
}
Expand Down
115 changes: 115 additions & 0 deletions mediaServer/test/student/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="kr">
<head>
<meta charset="UTF-8">
<meta name="description" content="test WebRTC">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="../commons/css/main.css"/>
</head>
<body>
<div id="container">
<h1>์ฐธ์—ฌ์ž</h1>
<video id="localVideo" playsinline autoplay muted></video>
<canvas></canvas>
<div>
<button id="startButton">์‹œ์ž‘</button>
<button id="callButton">๋ฐฉ ์ƒ์„ฑ</button>
<button id="hangupButton">๋ฐฉ ๋‚˜๊ฐ€๊ธฐ</button>
</div>
</div>
<script type="module">
import { io } from "https://cdn.socket.io/4.4.1/socket.io.esm.min.js";
const socket = io('http://localhost:3000/enter-room');

const startButton = document.getElementById('startButton');
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;

const canvas = document.querySelector('canvas');
const localVideo = document.getElementById('localVideo');

let localStream = undefined;
let presenterRTCPC;
let tmp = []
const pc_config = {
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
]
},
],
};

const start = async () => {
try {
presenterRTCPC = new RTCPeerConnection();
const stream = new MediaStream();
localStream = stream;
} catch (e) {
console.log(e)
}
}

start().then(getStream)

async function getStream() {
try {
await createStudentOffer();
await setServerAnswer();
} catch (e) {
console.log(e);
}
}

function getStudentCandidate() {
presenterRTCPC.onicecandidate = (e) => {
if (e.candidate) {
socket.emit('studentCandidate', {
candidate: e.candidate,
studentSocketId: socket.id
});
}
}
}

async function createStudentOffer() {
try {
const SDP = await presenterRTCPC.createOffer({
offerToReceiveAudio:true,
offerToReceiveVideo:true
});
socket.emit('studentOffer', {
socketId: socket.id,
SDP: SDP
});

presenterRTCPC.setLocalDescription(SDP);
getStudentCandidate()
} catch (e) {
console.log(e);
}
}

async function setServerAnswer() {
socket.on(`${socket.id}-serverAnswer`, (data) => {
presenterRTCPC.setRemoteDescription(data.SDP)
})
socket.on(`${socket.id}-serverCandidate`, (data) => {
presenterRTCPC.addIceCandidate(new RTCIceCandidate(data.candidate))
});
}

presenterRTCPC.ontrack = (event) => {
localStream.addTrack(event.track)
localVideo.srcObject = localStream;
}

startButton.onclick = start;
</script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="../commons/js/streamVisualizer.js"></script>
</body>
</html>

0 comments on commit a593778

Please sign in to comment.