fix webRTC

This commit is contained in:
2025-09-07 13:13:05 +00:00
parent c288c45138
commit 9bf372b972

View File

@@ -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 @@ export const webrtcService = {
},
/**
* Get WebRTC statistics
* Get WebRTC statistics (оптимизировано)
*/
async getConnectionStats(peerConnection) {
if (!peerConnection) return null
@@ -48,27 +285,27 @@ export const webrtcService = {
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 @@ export const webrtcService = {
},
/**
* 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 @@ export const webrtcService = {
// 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,11 +363,40 @@ export const webrtcService = {
// 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');
}
}
}