From b0f09e0a1ff68644b03fdd8a15b81f173302ba3d Mon Sep 17 00:00:00 2001 From: bangae1 Date: Mon, 20 Oct 2025 23:02:04 +0900 Subject: [PATCH] Aa --- agent-renderer.js | 112 ++++++++++++++++------------------------------ main.js | 33 +++++++++----- 2 files changed, 62 insertions(+), 83 deletions(-) diff --git a/agent-renderer.js b/agent-renderer.js index e6b810e..75e670e 100644 --- a/agent-renderer.js +++ b/agent-renderer.js @@ -1,7 +1,6 @@ // agent-renderer.js — 브라우저 환경에서 실행됨 → WebRTC 사용 가능 let peerConnection = null; -let socket = null; let currentStream = null; // 메인 프로세스로부터 명령 수신 (IPC) @@ -9,85 +8,52 @@ console.log('electronAPI:', window.electronAPI); console.log('desktopCapturer:', window.desktopCapturer); window.electronAPI.send('displays', { types: ['screen'] }); -window.electronAPI.receive('start-webrtc', async (payload) => { - try { - const { offer, displayId, signalingServer, targetId } = payload; - console.log('start webrtc'); - // 1. Socket 연결 (렌더러에서도 가능) - socket = io(signalingServer); - socket.on('availableDisplays', async (sources) => { - const source = sources.find(s => { - console.log(s.id, displayId) - return s.id === displayId - }); - if (!source) throw new Error('디스플레이 없음'); +console.log('start webrtc'); +// 1. Socket 연결 (렌더러에서도 가능) - const stream = await navigator.mediaDevices.getUserMedia({ - audio: false, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: source.id, - minWidth: 1920, - minHeight: 1080 - } - } - }); - currentStream = stream; +window.electronAPI.receive('start-webrtc', async (data) => { + const source = data.sources.find(s => { + return s.id === data.displayId + }); + // 3. WebRTC 연결 + peerConnection = new RTCPeerConnection({ + iceServers: [{urls: 'stun:stun.l.google.com:19302'}] + }); - // 3. WebRTC 연결 - peerConnection = new RTCPeerConnection({ - iceServers: [{urls: 'stun:stun.l.google.com:19302'}] - }); + const stream = await navigator.mediaDevices.getUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: source.id, + minWidth: 1920, + minHeight: 1080 + } + } + }); + currentStream = stream; + stream.getTracks().forEach(track => { + console.log('track', track, stream); + peerConnection.addTrack(track, stream) + }); - stream.getTracks().forEach(track => { - console.log('track', track, stream); - peerConnection.addTrack(track, stream) - }); - await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); - const answer = await peerConnection.createAnswer(); - await peerConnection.setLocalDescription(answer); - - // 4. Answer 전송 - socket.emit('webrtcSignal', { - targetId: targetId, - type: 'answer', - answer - }); - - // 5. ICE candidate - peerConnection.onicecandidate = (e) => { - if (e.candidate) { - socket.emit('webrtcSignal', { - targetId: targetId, - type: 'icecandidate', - candidate: e.candidate - }); - } - }; - - // 6. 입력 이벤트 수신 (robotjs는 메인 프로세스에서 실행 권장) - socket.on('inputEvent', (data) => { - window.electronApi.send('input-event-from-renderer', data); - }); - - - console.log('✅ WebRTC 시작됨 (렌더러)'); - }) - - // 2. 화면 스트림 캡처 - socket.emit('requestDisplays', 'psn14020') - - // const sources = await window.desktopCapturer.getSources({ types: ['screen'] }); - - } catch (err) { - console.error('❌ WebRTC 실패:', err); - window.electronApi.send('webrtc-error', err.message); - } + const offer = await peerConnection.createOffer({offerToReceiveVideo: true}) + await peerConnection.setLocalDescription(offer); + window.electronAPI.send('offer', {type:'offer', targetId: data.targetId, offer}) }); +window.electronAPI.receive('displays', async (payload) => { + window.desktopCapturer.getSources(['display']) +}); + + +window.electronAPI.receive('answer', async (data) => { + console.log('answer', data) + await peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer)); + console.log('connect') +}) window.electronAPI.receive('webrtcSignal', async (data) => { try { diff --git a/main.js b/main.js index 89d11fa..353113b 100644 --- a/main.js +++ b/main.js @@ -16,6 +16,7 @@ let socket = null; let peerConnection = null; let displayId = null; let offer = null; +let sources = null; // 자동 시작 설정 app.setLoginItemSettings({ @@ -54,10 +55,9 @@ function createTray() { // 🖥️ 사용 가능한 디스플레이 목록 전송 async function sendAvailableDisplays() { try { - const sources = await desktopCapturer.getSources({ types: ['screen'] }); + sources = await desktopCapturer.getSources({ types: ['screen'] }); const source = sources[0]; displayId = source.id - console.log('sources',sources) const displays = sources.map(source => ({ id: source.id, name: source.name, @@ -104,17 +104,22 @@ function connectToSignaling() { reconnectionDelay: 2000 }); - socket.on('connect', () => { + socket.on('connect', (data) => { console.log('시그널링 서버 연결됨. 직원 등록 중...'); socket.emit('register', EMPLOYEE_ID); sendAvailableDisplays(); // 연결 시 디스플레이 목록 전송 + + // rendererWindow.webContents.send('connection', data); }); - socket.on('controlRequest', async (data) => { + + /*연결 요청 -> webrtc 시작 -> offer 전송*/ + socket.on('responseControl', async (data) => { + console.log(data) const result = await dialog.showMessageBox({ type: 'question', title: '원격 연결 요청', - message: `관리자로부터 원격 제어 요청이 왔습니다.\n허용하시겠습니까? ${displayId}`, + message: `관리자로부터 원격 제어 요청이 왔습니다.\n허용하시겠습니까? ${data.displayId}`, buttons: ['허용', '거부'], defaultId: 1, cancelId: 1 @@ -123,16 +128,20 @@ function connectToSignaling() { if (result.response === 0) { // ✅ 렌더러 프로세스에 WebRTC 시작 명령 전달 rendererWindow.webContents.send('start-webrtc', { - offer: data.offer, - displayId, - signalingServer: SIGNALING_SERVER, - targetId: data.from + displayId: data.displayId, + targetId: data.targetId, + sources }); } else { - socket.emit('webrtcSignal', { targetId: data.from, data: { type: 'reject' } }); + socket.emit('webrtcSignal', { targetId: data.targetId, data: { type: 'reject' } }); } }); + socket.on('answer', async (data) => { + rendererWindow.webContents.send('answer', data); + console.log('answer', data); + }) + // WebRTC 신호 수신 (ICE candidate 등) socket.on('webrtcSignal', async (data) => { rendererWindow.webContents.send('webrtcSignal', data); @@ -186,3 +195,7 @@ ipcMain.on('input-event-from-renderer', (event, data) => { } }) + +ipcMain.on('offer', (event, data) => { + socket.emit('offer', data); +}) \ No newline at end of file