From 0e95d0178650e002ef55f181b07cca6947bc97be Mon Sep 17 00:00:00 2001 From: "dr.holy" Date: Sun, 7 Sep 2025 14:08:11 +0000 Subject: [PATCH] add av1 codec --- backend/requirements.txt | 1 + videocall-frontend/src/services/media.js | 9 +- videocall-frontend/src/services/webrtc.js | 531 ++++++++++++++++++---- videocall-frontend/src/stores/webrtc.js | 11 +- 4 files changed, 452 insertions(+), 100 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 3adf1e7..fb1e40d 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -15,6 +15,7 @@ pycparser==2.22 pypng==0.20220715.0 python-decouple==3.8 pytz==2025.2 +psutil==7.0.0 qrcode==7.4.2 redis==5.0.1 sqlparse==0.5.3 diff --git a/videocall-frontend/src/services/media.js b/videocall-frontend/src/services/media.js index 8a96bf6..c273332 100644 --- a/videocall-frontend/src/services/media.js +++ b/videocall-frontend/src/services/media.js @@ -55,14 +55,13 @@ export const mediaService = { // Default constraints let constraints = { video: { - width: { ideal: 1280, max: 1920 }, - height: { ideal: 720, max: 1080 }, - frameRate: { ideal: 30, max: 60 }, - }, + width: { ideal: 640, max: 1280 }, + height: { ideal: 480, max: 720 }, + frameRate: { ideal: 15, max: 30 } + }, audio: { echoCancellation: true, noiseSuppression: true, - autoGainControl: true, }, } diff --git a/videocall-frontend/src/services/webrtc.js b/videocall-frontend/src/services/webrtc.js index d60dcde..fd55e9b 100644 --- a/videocall-frontend/src/services/webrtc.js +++ b/videocall-frontend/src/services/webrtc.js @@ -1,25 +1,262 @@ +// // // src/services/webrtc.js - WebRTC utility functions +// // export const webrtcService = { +// // /** +// // * Get STUN/TURN server configuration +// // */ +// // getIceServerConfig() { +// // return { +// // iceServers: [ +// // { urls: 'stun:stun.l.google.com:19302' }, +// // { urls: 'stun:stun1.l.google.com:19302' }, +// // { urls: 'stun:stun2.l.google.com:19302' }, +// // // Add TURN servers for production +// // // { +// // // urls: 'turn:your-turn-server.com:3478', +// // // username: 'username', +// // // credential: 'password' +// // // } +// // ], +// // iceCandidatePoolSize: 10, +// // } +// // }, + +// // /** +// // * Test WebRTC support +// // */ +// // isWebRTCSupported() { +// // return !!( +// // window.RTCPeerConnection || +// // window.webkitRTCPeerConnection || +// // window.mozRTCPeerConnection +// // ) +// // }, + +// // /** +// // * Get WebRTC statistics +// // */ +// // async getConnectionStats(peerConnection) { +// // if (!peerConnection) return null + +// // try { +// // const stats = await peerConnection.getStats() +// // const result = { +// // video: {}, +// // audio: {}, +// // connection: {}, +// // } + +// // stats.forEach((report) => { +// // if (report.type === 'inbound-rtp' && report.mediaType === 'video') { +// // result.video.inbound = { +// // bytesReceived: report.bytesReceived, +// // packetsReceived: report.packetsReceived, +// // packetsLost: report.packetsLost, +// // frameWidth: report.frameWidth, +// // frameHeight: report.frameHeight, +// // framesPerSecond: report.framesPerSecond, +// // } +// // } else if (report.type === 'outbound-rtp' && report.mediaType === 'video') { +// // result.video.outbound = { +// // bytesSent: report.bytesSent, +// // packetsSent: report.packetsSent, +// // frameWidth: report.frameWidth, +// // frameHeight: report.frameHeight, +// // framesPerSecond: report.framesPerSecond, +// // } +// // } else if (report.type === 'candidate-pair' && report.state === 'succeeded') { +// // result.connection = { +// // currentRoundTripTime: report.currentRoundTripTime, +// // availableOutgoingBitrate: report.availableOutgoingBitrate, +// // bytesReceived: report.bytesReceived, +// // bytesSent: report.bytesSent, +// // } +// // } +// // }) + +// // return result +// // } catch (error) { +// // console.error('Failed to get connection stats:', error) +// // return null +// // } +// // }, + +// // /** +// // * Monitor connection quality +// // */ +// // createQualityMonitor(peerConnection, callback, interval = 5000) { +// // if (!peerConnection || typeof callback !== 'function') { +// // return null +// // } + +// // const monitor = setInterval(async () => { +// // try { +// // const stats = await this.getConnectionStats(peerConnection) +// // if (stats) { +// // const quality = this.calculateQuality(stats) +// // callback(quality, stats) +// // } +// // } catch (error) { +// // console.error('Quality monitoring error:', error) +// // } +// // }, interval) + +// // return monitor +// // }, + +// // /** +// // * Calculate connection quality score (0-100) +// // */ +// // calculateQuality(stats) { +// // let score = 100 + +// // // Reduce score based on packet loss +// // if (stats.video.inbound?.packetsLost && stats.video.inbound?.packetsReceived) { +// // const lossRate = stats.video.inbound.packetsLost / stats.video.inbound.packetsReceived +// // score -= lossRate * 50 // Up to 50 points for packet loss +// // } + +// // // Reduce score based on round trip time +// // if (stats.connection?.currentRoundTripTime) { +// // const rtt = stats.connection.currentRoundTripTime * 1000 // Convert to ms +// // if (rtt > 150) { +// // score -= Math.min(30, (rtt - 150) / 10) // Up to 30 points for high latency +// // } +// // } + +// // // Reduce score based on low frame rate +// // if (stats.video.inbound?.framesPerSecond) { +// // const fps = stats.video.inbound.framesPerSecond +// // if (fps < 15) { +// // score -= (15 - fps) * 2 // Up to 30 points for low FPS +// // } +// // } + +// // return Math.max(0, Math.min(100, Math.round(score))) +// // }, +// // } + + // // src/services/webrtc.js - WebRTC utility functions // export const webrtcService = { // /** -// * Get STUN/TURN server configuration +// * Get STUN/TURN server configuration (оптимизировано) // */ // getIceServerConfig() { // return { // iceServers: [ // { urls: 'stun:stun.l.google.com:19302' }, -// { urls: 'stun:stun1.l.google.com:19302' }, -// { urls: 'stun:stun2.l.google.com:19302' }, -// // Add TURN servers for production +// // Добавьте TURN сервер только при необходимости // // { // // urls: 'turn:your-turn-server.com:3478', // // username: 'username', // // credential: 'password' // // } // ], -// iceCandidatePoolSize: 10, +// iceCandidatePoolSize: 0, // Уменьшено для экономии ресурсов +// bundlePolicy: 'max-bundle', // Объединение каналов +// rtcpMuxPolicy: 'require' // Объединение RTCP и RTP // } // }, +// /** +// * Get optimized media constraints +// */ +// getOptimizedConstraints() { +// return { +// video: { +// width: { ideal: 640, max: 1280 }, +// height: { ideal: 480, max: 720 }, +// frameRate: { ideal: 15, max: 30 } +// }, +// audio: { +// sampleRate: 10000, +// channelCount: 1, +// echoCancellation: true, +// noiseSuppression: true +// } +// } +// }, + +// /** +// * Apply optimized parameters to video sender +// */ +// optimizeVideoSender(sender, quality = 'medium') { +// if (!sender || sender.track?.kind !== 'video') return; + +// const presets = { +// low: { +// maxBitrate: 150_000, +// scaleResolutionDownBy: 4.0, +// maxFramerate: 10 +// }, +// medium: { +// maxBitrate: 400_000, +// scaleResolutionDownBy: 2.0, +// maxFramerate: 15 +// }, +// high: { +// maxBitrate: 1_000_000, +// scaleResolutionDownBy: 1.0, +// maxFramerate: 30 +// } +// }; + +// const params = sender.getParameters(); +// params.encodings = [presets[quality] || presets.medium]; +// sender.setParameters(params); +// }, + +// /** +// * Apply simulcast for better quality adaptation +// */ +// enableSimulcast(sender) { +// if (!sender || sender.track?.kind !== 'video') return; + +// const params = sender.getParameters(); +// params.encodings = [ +// { rid: 'low', active: true, maxBitrate: 150_000, scaleResolutionDownBy: 4.0, maxFramerate: 10 }, +// { rid: 'medium', active: true, maxBitrate: 400_000, scaleResolutionDownBy: 2.0, maxFramerate: 15 }, +// { rid: 'high', active: true, maxBitrate: 1_000_000, scaleResolutionDownBy: 1.0, maxFramerate: 30 } +// ]; +// sender.setParameters(params); +// }, + +// /** +// * Prefer specific codec in SDP +// */ +// preferCodec(sdp, codec) { +// const codecInfo = this.extractCodecInfo(sdp, codec); +// if (!codecInfo) return sdp; + +// const { payload, rtpmap } = codecInfo; +// const lines = sdp.split('\r\n'); +// const mLineIndex = lines.findIndex(line => line.startsWith('m=video')); + +// if (mLineIndex === -1) return sdp; + +// // Изменяем порядок кодеков в m-line +// const mLineParts = lines[mLineIndex].split(' '); +// const payloads = mLineParts.slice(3); +// const newPayloads = [payload, ...payloads.filter(p => p !== payload)]; +// lines[mLineIndex] = [...mLineParts.slice(0, 3), ...newPayloads].join(' '); + +// return lines.join('\r\n'); +// }, + +// /** +// * Extract codec information from SDP +// */ +// extractCodecInfo(sdp, codecName) { +// const lines = sdp.split('\r\n'); +// for (let i = 0; i < lines.length; i++) { +// if (lines[i].includes(`a=rtpmap:`) && lines[i].toLowerCase().includes(codecName.toLowerCase())) { +// const payload = lines[i].split(' ')[0].split(':')[1]; +// return { payload, rtpmap: lines[i] }; +// } +// } +// return null; +// }, + // /** // * Test WebRTC support // */ @@ -32,7 +269,7 @@ // }, // /** -// * Get WebRTC statistics +// * Get WebRTC statistics (оптимизировано) // */ // async getConnectionStats(peerConnection) { // if (!peerConnection) return null @@ -48,27 +285,27 @@ // stats.forEach((report) => { // if (report.type === 'inbound-rtp' && report.mediaType === 'video') { // result.video.inbound = { -// bytesReceived: report.bytesReceived, -// packetsReceived: report.packetsReceived, -// packetsLost: report.packetsLost, -// frameWidth: report.frameWidth, -// frameHeight: report.frameHeight, -// framesPerSecond: report.framesPerSecond, +// bytesReceived: report.bytesReceived || 0, +// packetsReceived: report.packetsReceived || 0, +// packetsLost: report.packetsLost || 0, +// frameWidth: report.frameWidth || 0, +// frameHeight: report.frameHeight || 0, +// framesPerSecond: report.framesPerSecond || 0, // } // } else if (report.type === 'outbound-rtp' && report.mediaType === 'video') { // result.video.outbound = { -// bytesSent: report.bytesSent, -// packetsSent: report.packetsSent, -// frameWidth: report.frameWidth, -// frameHeight: report.frameHeight, -// framesPerSecond: report.framesPerSecond, +// bytesSent: report.bytesSent || 0, +// packetsSent: report.packetsSent || 0, +// frameWidth: report.frameWidth || 0, +// frameHeight: report.frameHeight || 0, +// framesPerSecond: report.framesPerSecond || 0, // } // } else if (report.type === 'candidate-pair' && report.state === 'succeeded') { // result.connection = { -// currentRoundTripTime: report.currentRoundTripTime, -// availableOutgoingBitrate: report.availableOutgoingBitrate, -// bytesReceived: report.bytesReceived, -// bytesSent: report.bytesSent, +// currentRoundTripTime: report.currentRoundTripTime || 0, +// availableOutgoingBitrate: report.availableOutgoingBitrate || 0, +// bytesReceived: report.bytesReceived || 0, +// bytesSent: report.bytesSent || 0, // } // } // }) @@ -81,9 +318,9 @@ // }, // /** -// * Monitor connection quality +// * Monitor connection quality (увеличен интервал) // */ -// createQualityMonitor(peerConnection, callback, interval = 5000) { +// createQualityMonitor(peerConnection, callback, interval = 10000) { // if (!peerConnection || typeof callback !== 'function') { // return null // } @@ -111,8 +348,8 @@ // // Reduce score based on packet loss // if (stats.video.inbound?.packetsLost && stats.video.inbound?.packetsReceived) { -// const lossRate = stats.video.inbound.packetsLost / stats.video.inbound.packetsReceived -// score -= lossRate * 50 // Up to 50 points for packet loss +// const lossRate = stats.video.inbound.packetsLost / Math.max(1, stats.video.inbound.packetsReceived) +// score -= Math.min(50, lossRate * 100) // Up to 50 points for packet loss // } // // Reduce score based on round trip time @@ -126,53 +363,76 @@ // // Reduce score based on low frame rate // if (stats.video.inbound?.framesPerSecond) { // const fps = stats.video.inbound.framesPerSecond -// if (fps < 15) { -// score -= (15 - fps) * 2 // Up to 30 points for low FPS +// if (fps < 10) { +// score -= (10 - fps) * 3 // Up to 30 points for very low FPS +// } else if (fps < 15) { +// score -= (15 - fps) * 2 // } // } // return Math.max(0, Math.min(100, Math.round(score))) // }, + +// /** +// * Adaptive quality control based on network conditions +// */ +// async adaptQuality(peerConnection, qualityCallback) { +// if (!peerConnection) return; + +// const senders = peerConnection.getSenders(); +// const videoSender = senders.find(sender => sender.track?.kind === 'video'); + +// if (!videoSender) return; + +// const stats = await this.getConnectionStats(peerConnection); +// if (!stats) return; + +// const quality = this.calculateQuality(stats); +// qualityCallback(quality); + +// // Адаптивное управление качеством +// if (quality < 30) { +// this.optimizeVideoSender(videoSender, 'low'); +// } else if (quality < 60) { +// this.optimizeVideoSender(videoSender, 'medium'); +// } else { +// this.optimizeVideoSender(videoSender, 'high'); +// } +// } // } +// src/services/webrtc.js - WebRTC utility functions with forced AV1 codec -// src/services/webrtc.js - WebRTC utility functions export const webrtcService = { /** - * Get STUN/TURN server configuration (оптимизировано) + * Get STUN/TURN server configuration */ getIceServerConfig() { return { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, - // Добавьте TURN сервер только при необходимости - // { - // urls: 'turn:your-turn-server.com:3478', - // username: 'username', - // credential: 'password' - // } ], - iceCandidatePoolSize: 0, // Уменьшено для экономии ресурсов - bundlePolicy: 'max-bundle', // Объединение каналов - rtcpMuxPolicy: 'require' // Объединение RTCP и RTP + iceCandidatePoolSize: 0, + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require' } }, /** - * Get optimized media constraints + * Get optimized media constraints for AV1 */ - getOptimizedConstraints() { + async getOptimizedConstraints() { return { video: { - width: { ideal: 640, max: 1280 }, - height: { ideal: 480, max: 720 }, - frameRate: { ideal: 15, max: 30 } - }, + width: { ideal: 640, max: 1280 }, + height: { ideal: 480, max: 720 }, + frameRate: { ideal: 15, max: 30 } + }, audio: { sampleRate: 10000, channelCount: 1, echoCancellation: true, - noiseSuppression: true + noiseSuppression: true, } } }, @@ -186,13 +446,13 @@ export const webrtcService = { const presets = { low: { maxBitrate: 150_000, - scaleResolutionDownBy: 4.0, - maxFramerate: 10 + scaleResolutionDownBy: 2.0, + maxFramerate: 15 }, medium: { maxBitrate: 400_000, - scaleResolutionDownBy: 2.0, - maxFramerate: 15 + scaleResolutionDownBy: 1.5, + maxFramerate: 25 }, high: { maxBitrate: 1_000_000, @@ -202,61 +462,89 @@ export const webrtcService = { }; const params = sender.getParameters(); - params.encodings = [presets[quality] || presets.medium]; + params.encodings = [presets[quality] || presets.high]; sender.setParameters(params); }, /** - * Apply simulcast for better quality adaptation + * Force AV1 codec in SDP */ - enableSimulcast(sender) { - if (!sender || sender.track?.kind !== 'video') return; + forceAV1Codec(sdp) { + // Найдем AV1 payload type + const av1Payload = this.findAV1Payload(sdp); + if (!av1Payload) { + console.warn('AV1 codec not found in SDP, falling back to default codecs'); + return sdp; + } - const params = sender.getParameters(); - params.encodings = [ - { rid: 'low', active: true, maxBitrate: 150_000, scaleResolutionDownBy: 4.0, maxFramerate: 10 }, - { rid: 'medium', active: true, maxBitrate: 400_000, scaleResolutionDownBy: 2.0, maxFramerate: 15 }, - { rid: 'high', active: true, maxBitrate: 1_000_000, scaleResolutionDownBy: 1.0, maxFramerate: 30 } - ]; - sender.setParameters(params); - }, - - /** - * Prefer specific codec in SDP - */ - preferCodec(sdp, codec) { - const codecInfo = this.extractCodecInfo(sdp, codec); - if (!codecInfo) return sdp; - - const { payload, rtpmap } = codecInfo; const lines = sdp.split('\r\n'); const mLineIndex = lines.findIndex(line => line.startsWith('m=video')); if (mLineIndex === -1) return sdp; - // Изменяем порядок кодеков в m-line + // Переставим AV1 на первое место в m-line const mLineParts = lines[mLineIndex].split(' '); const payloads = mLineParts.slice(3); - const newPayloads = [payload, ...payloads.filter(p => p !== payload)]; + const newPayloads = [av1Payload, ...payloads.filter(p => p !== av1Payload)]; lines[mLineIndex] = [...mLineParts.slice(0, 3), ...newPayloads].join(' '); - return lines.join('\r\n'); + // Удалим ненужные кодеки (опционально) + const filteredLines = this.removeUnwantedCodecs(lines, av1Payload); + + return filteredLines.join('\r\n'); }, /** - * Extract codec information from SDP + * Find AV1 payload type in SDP */ - extractCodecInfo(sdp, codecName) { + findAV1Payload(sdp) { const lines = sdp.split('\r\n'); - for (let i = 0; i < lines.length; i++) { - if (lines[i].includes(`a=rtpmap:`) && lines[i].toLowerCase().includes(codecName.toLowerCase())) { - const payload = lines[i].split(' ')[0].split(':')[1]; - return { payload, rtpmap: lines[i] }; + for (const line of lines) { + if (line.includes('a=rtpmap:') && line.toLowerCase().includes('av1')) { + const match = line.match(/a=rtpmap:(\d+) av1/i); + if (match) { + return match[1]; + } } } return null; }, + /** + * Remove unwanted codecs from SDP (keep only AV1) + */ + removeUnwantedCodecs(lines, av1Payload) { + const filteredLines = []; + let skipNext = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Пропускаем описания других кодеков + if (line.startsWith('a=rtpmap:') && !line.includes(`a=rtpmap:${av1Payload}`)) { + skipNext = true; + continue; + } + + if (line.startsWith('a=rtcp-fb:') && !line.includes(`a=rtcp-fb:${av1Payload}`)) { + continue; + } + + if (line.startsWith('a=fmtp:') && !line.includes(`a=fmtp:${av1Payload}`)) { + continue; + } + + if (skipNext && line.startsWith('a=')) { + skipNext = false; + continue; + } + + filteredLines.push(line); + } + + return filteredLines; + }, + /** * Test WebRTC support */ @@ -269,7 +557,56 @@ export const webrtcService = { }, /** - * Get WebRTC statistics (оптимизировано) + * Create peer connection with AV1 support + */ + async createPeerConnection(onIceCandidate, onTrack) { + const config = this.getIceServerConfig(); + const peerConnection = new RTCPeerConnection(config); + + peerConnection.onicecandidate = (event) => { + if (event.candidate) { + onIceCandidate(event.candidate); + } + }; + + peerConnection.ontrack = (event) => { + onTrack(event); + }; + + // Принудительно применяем AV1 к исходящим потокам + peerConnection.onnegotiationneeded = async () => { + try { + const offer = await peerConnection.createOffer(); + offer.sdp = this.forceAV1Codec(offer.sdp); + await peerConnection.setLocalDescription(offer); + } catch (error) { + console.error('Error forcing AV1 codec:', error); + } + }; + + return peerConnection; + }, + + /** + * Create offer with forced AV1 + */ + async createOfferWithAV1(peerConnection) { + const offer = await peerConnection.createOffer(); + offer.sdp = this.forceAV1Codec(offer.sdp); + return offer; + }, + + /** + * Create answer with forced AV1 + */ + async createAnswerWithAV1(peerConnection) { + const answer = await peerConnection.createAnswer(); + answer.sdp = this.forceAV1Codec(answer.sdp); + return answer; + }, + + /** + * Get WebRTC statistics */ async getConnectionStats(peerConnection) { if (!peerConnection) return null @@ -291,6 +628,7 @@ export const webrtcService = { frameWidth: report.frameWidth || 0, frameHeight: report.frameHeight || 0, framesPerSecond: report.framesPerSecond || 0, + codecId: report.codecId || '', } } else if (report.type === 'outbound-rtp' && report.mediaType === 'video') { result.video.outbound = { @@ -299,6 +637,7 @@ export const webrtcService = { frameWidth: report.frameWidth || 0, frameHeight: report.frameHeight || 0, framesPerSecond: report.framesPerSecond || 0, + codecId: report.codecId || '', } } else if (report.type === 'candidate-pair' && report.state === 'succeeded') { result.connection = { @@ -307,6 +646,15 @@ export const webrtcService = { bytesReceived: report.bytesReceived || 0, bytesSent: report.bytesSent || 0, } + } else if (report.type === 'codec') { + // Получаем информацию о кодеке + if (report.id === result.video.inbound?.codecId || report.id === result.video.outbound?.codecId) { + result.video.codec = { + mimeType: report.mimeType, + clockRate: report.clockRate, + sdpFmtpLine: report.sdpFmtpLine + }; + } } }) @@ -318,9 +666,9 @@ export const webrtcService = { }, /** - * Monitor connection quality (увеличен интервал) + * Monitor connection quality */ - createQualityMonitor(peerConnection, callback, interval = 10000) { + createQualityMonitor(peerConnection, callback, interval = 5000) { if (!peerConnection || typeof callback !== 'function') { return null } @@ -346,26 +694,31 @@ export const webrtcService = { calculateQuality(stats) { let score = 100 + // Проверяем, используется ли AV1 + if (stats.video.codec && stats.video.codec.mimeType) { + if (stats.video.codec.mimeType.toLowerCase().includes('av1')) { + score += 10; // Бонус за использование AV1 + } + } + // Reduce score based on packet loss if (stats.video.inbound?.packetsLost && stats.video.inbound?.packetsReceived) { const lossRate = stats.video.inbound.packetsLost / Math.max(1, stats.video.inbound.packetsReceived) - score -= Math.min(50, lossRate * 100) // Up to 50 points for packet loss + score -= Math.min(50, lossRate * 100) } // Reduce score based on round trip time if (stats.connection?.currentRoundTripTime) { - const rtt = stats.connection.currentRoundTripTime * 1000 // Convert to ms + const rtt = stats.connection.currentRoundTripTime * 1000 if (rtt > 150) { - score -= Math.min(30, (rtt - 150) / 10) // Up to 30 points for high latency + score -= Math.min(30, (rtt - 150) / 10) } } // Reduce score based on low frame rate if (stats.video.inbound?.framesPerSecond) { const fps = stats.video.inbound.framesPerSecond - if (fps < 10) { - score -= (10 - fps) * 3 // Up to 30 points for very low FPS - } else if (fps < 15) { + if (fps < 15) { score -= (15 - fps) * 2 } } @@ -374,7 +727,7 @@ export const webrtcService = { }, /** - * Adaptive quality control based on network conditions + * Adaptive quality control with AV1 */ async adaptQuality(peerConnection, qualityCallback) { if (!peerConnection) return; diff --git a/videocall-frontend/src/stores/webrtc.js b/videocall-frontend/src/stores/webrtc.js index 92a3c71..eb464dc 100644 --- a/videocall-frontend/src/stores/webrtc.js +++ b/videocall-frontend/src/stores/webrtc.js @@ -21,10 +21,10 @@ export const useWebRTCStore = defineStore('webrtc', () => { // Media constraints const mediaConstraints = ref({ video: { - width: { ideal: 1280, max: 1920 }, - height: { ideal: 720, max: 1080 }, - frameRate: { ideal: 30, max: 60 }, - }, + width: { ideal: 640, max: 1280 }, + height: { ideal: 480, max: 720 }, + frameRate: { ideal: 15, max: 30 } + }, audio: { echoCancellation: true, noiseSuppression: true, @@ -43,10 +43,9 @@ export const useWebRTCStore = defineStore('webrtc', () => { const rtcConfiguration = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, - { urls: 'stun:stun1.l.google.com:19302' }, // Add TURN servers here for production ], - iceCandidatePoolSize: 10, + iceCandidatePoolSize: 0, } // Actions