mirror of
https://git.hmsn.ink/call/client.git
synced 2026-03-20 00:02:15 +09:00
first
This commit is contained in:
126
src/webrtc/webrtcManager.js
Normal file
126
src/webrtc/webrtcManager.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// webrtcManager.js (수정본 — STUN 없이 ICE만)
|
||||
|
||||
export class WebRTCManager {
|
||||
constructor(signalUrl) {
|
||||
this.pc = null;
|
||||
this.localStream = null;
|
||||
this.remoteStream = null;
|
||||
this.onRemoteStream = null;
|
||||
this.signalUrl = signalUrl; // 시그널링 서버 주소
|
||||
this.ws = null;
|
||||
this.myId = null;
|
||||
this.peerId = null; // 상대방 ID (수동 입력 또는 자동 할당)
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.requestMicPermission()
|
||||
this.setupSignaling();
|
||||
}
|
||||
|
||||
async requestMicPermission() {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
|
||||
console.log('✅ 마이크 접근 성공');
|
||||
stream.getTracks().forEach(track => track.stop()); // 임시로 열었다가 닫음
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('❌ 마이크 권한 거부 또는 장치 없음:', err.message);
|
||||
alert('마이크 접근이 필요합니다. 브라우저 설정에서 마이크 권한을 허용해주세요.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
setupSignaling() {
|
||||
this.ws = new WebSocket(this.signalUrl);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('🔌 시그널링 서버 연결됨');
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === 'your-id') {
|
||||
this.myId = msg.id;
|
||||
console.log('내 ID:', this.myId);
|
||||
} else if (msg.type === 'offer') {
|
||||
this.setRemoteDescription(msg.sdp);
|
||||
this.addIceCandidateQueue.forEach(c => this.pc.addIceCandidate(c));
|
||||
this.addIceCandidateQueue = [];
|
||||
} else if (msg.type === 'answer') {
|
||||
this.setRemoteDescription(msg.sdp);
|
||||
} else if (msg.type === 'candidate') {
|
||||
this.addIceCandidate(msg.candidate);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
createPeerConnection() {
|
||||
// ✅ STUN 서버 없이 빈 설정 — 인트라넷 내부 IP로 직접 연결
|
||||
this.pc = new RTCPeerConnection({});
|
||||
|
||||
this.localStream.getTracks().forEach(track => {
|
||||
this.pc.addTrack(track, this.localStream);
|
||||
});
|
||||
|
||||
this.pc.ontrack = (event) => {
|
||||
this.remoteStream = event.streams[0];
|
||||
if (this.onRemoteStream) this.onRemoteStream(this.remoteStream);
|
||||
};
|
||||
|
||||
// ICE Candidate → 시그널링으로 전송
|
||||
this.pc.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
console.log('📤 ICE Candidate:', event.candidate.candidate);
|
||||
this.ws.send(JSON.stringify({
|
||||
target: this.peerId,
|
||||
type: 'candidate',
|
||||
candidate: event.candidate
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async createOffer(targetId) {
|
||||
this.peerId = targetId;
|
||||
this.createPeerConnection();
|
||||
const offer = await this.pc.createOffer();
|
||||
await this.pc.setLocalDescription(offer);
|
||||
|
||||
this.ws.send(JSON.stringify({
|
||||
target: targetId,
|
||||
type: 'offer',
|
||||
sdp: offer
|
||||
}));
|
||||
console.log('📤 Offer 전송 완료');
|
||||
}
|
||||
|
||||
async setRemoteDescription(sdp) {
|
||||
await this.pc.setRemoteDescription(new RTCSessionDescription(sdp));
|
||||
if (sdp.type === 'offer') {
|
||||
const answer = await this.pc.createAnswer();
|
||||
await this.pc.setLocalDescription(answer);
|
||||
this.ws.send(JSON.stringify({
|
||||
target: this.peerId || 'unknown',
|
||||
type: 'answer',
|
||||
sdp: answer
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async addIceCandidate(candidate) {
|
||||
if (this.pc && this.pc.remoteDescription) {
|
||||
await this.pc.addIceCandidate(new RTCIceCandidate(candidate));
|
||||
} else {
|
||||
// SDP 설정 전이라면 큐에 저장
|
||||
this.addIceCandidateQueue = this.addIceCandidateQueue || [];
|
||||
this.addIceCandidateQueue.push(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
hangup() {
|
||||
this.pc?.close();
|
||||
this.localStream?.getTracks().forEach(t => t.stop());
|
||||
this.ws?.close();
|
||||
console.log('📞 통화 종료');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user