Буферизация ICE кандидатов (для избежания гонки)

This commit is contained in:
RoyceDa
2026-03-15 17:22:48 +02:00
parent f57ec484e3
commit ab57303eb6
2 changed files with 59 additions and 12 deletions

View File

@@ -71,15 +71,40 @@ export function CallProvider(props : CallProviderProps) {
const roleRef = useRef<CallRole | null>(null); const roleRef = useRef<CallRole | null>(null);
const [sharedSecret, setSharedSecret] = useState<string>(""); const [sharedSecret, setSharedSecret] = useState<string>("");
const iceServersRef = useRef<RTCIceServer[]>([]); const iceServersRef = useRef<RTCIceServer[]>([]);
const remoteAudioRef = useRef<HTMLAudioElement>(null);
const iceCandidatesBufferRef = useRef<RTCIceCandidate[]>([]);
useEffect(() => { useEffect(() => {
/** /**
* Нам нужно получить ICE серверы для установки соединения из разных сетей * Нам нужно получить ICE серверы для установки соединения из разных сетей
* Получаем их от сервера * Получаем их от сервера
*/ */
let packet = new PacketIceServers(); let packet = new PacketIceServers();
send(packet); send(packet);
}, []);
//debug
setInterval(async () => {
if(callState == CallState.ACTIVE){
if(peerConnectionRef.current){
const stats = await peerConnectionRef.current.getStats();
stats.forEach((report) => {
if (report.type === "inbound-rtp" && !report.isRemote) {
const kind = (report as any).kind || (report as any).mediaType;
const bytesReceived = (report as any).bytesReceived ?? 0;
const packetsReceived = (report as any).packetsReceived ?? 0;
const packetsLost = (report as any).packetsLost ?? 0;
console.log(
`[inbound ${kind}] bytesReceived=${bytesReceived}, packetsReceived=${packetsReceived}, packetsLost=${packetsLost}`
);
}
});
}
}
}, 2000);
}, [callState, peerConnectionRef]);
usePacket(28, async (packet: PacketIceServers) => { usePacket(28, async (packet: PacketIceServers) => {
let iceServers = packet.getIceServers(); let iceServers = packet.getIceServers();
@@ -118,6 +143,15 @@ export function CallProvider(props : CallProviderProps) {
*/ */
const sdp = JSON.parse(packet.getSdpOrCandidate()); const sdp = JSON.parse(packet.getSdpOrCandidate());
await peerConnectionRef.current?.setRemoteDescription(new RTCSessionDescription(sdp)); await peerConnectionRef.current?.setRemoteDescription(new RTCSessionDescription(sdp));
if(iceCandidatesBufferRef.current.length > 0){
/**
* У нас есть буферизированные ICE кандидаты, которые мы получили до установки удаленного описания, теперь мы можем их добавить в PeerConnection
*/
for(let i = 0; i < iceCandidatesBufferRef.current.length; i++){
await peerConnectionRef.current?.addIceCandidate(iceCandidatesBufferRef.current[i]);
}
iceCandidatesBufferRef.current = [];
}
info("Received WebRTC answer and set remote description"); info("Received WebRTC answer and set remote description");
return; return;
} }
@@ -127,6 +161,14 @@ export function CallProvider(props : CallProviderProps) {
*/ */
const candidate = JSON.parse(packet.getSdpOrCandidate()); const candidate = JSON.parse(packet.getSdpOrCandidate());
console.info(candidate); console.info(candidate);
if(peerConnectionRef.current?.remoteDescription == null){
/**
* Удаленное описание еще не установлено, буферизуем кандидата, чтобы добавить его после установки удаленного описания
*/
iceCandidatesBufferRef.current.push(new RTCIceCandidate(candidate));
info("Received WebRTC ICE candidate but remote description is not set yet, buffering candidate");
return;
}
await peerConnectionRef.current?.addIceCandidate(new RTCIceCandidate(candidate)); await peerConnectionRef.current?.addIceCandidate(new RTCIceCandidate(candidate));
info("Received WebRTC ICE candidate and added to peer connection"); info("Received WebRTC ICE candidate and added to peer connection");
return; return;
@@ -165,13 +207,7 @@ export function CallProvider(props : CallProviderProps) {
/** /**
* Сбросили звонок * Сбросили звонок
*/ */
setActiveCall(""); end();
setCallState(CallState.ENDED);
setShowCallView(false);
setSessionKeys(null);
setSharedSecret("");
setDuration(0);
roleRef.current = null;
return; return;
} }
if(signalType == SignalType.CALL){ if(signalType == SignalType.CALL){
@@ -276,7 +312,12 @@ export function CallProvider(props : CallProviderProps) {
* При получении медиа-трека с другой стороны * При получении медиа-трека с другой стороны
*/ */
console.info("TRACK RECV!!!!!"); console.info("TRACK RECV!!!!!");
if(remoteAudioRef.current){
console.info(event.streams);
remoteAudioRef.current.srcObject = event.streams[0];
}
} }
/** /**
* Запрашиваем Аудио поток с микрофона и добавляем его в PeerConnection, чтобы другая сторона могла его получить и воспроизвести, * Запрашиваем Аудио поток с микрофона и добавляем его в PeerConnection, чтобы другая сторона могла его получить и воспроизвести,
* когда мы установим WebRTC соединение * когда мы установим WebRTC соединение
@@ -291,7 +332,7 @@ export function CallProvider(props : CallProviderProps) {
* Отправляем свой оффер другой стороне * Отправляем свой оффер другой стороне
*/ */
let offer = await peerConnectionRef.current.createOffer(); let offer = await peerConnectionRef.current.createOffer();
peerConnectionRef.current.setLocalDescription(offer); await peerConnectionRef.current.setLocalDescription(offer);
let offerSignal = new PacketWebRTC(); let offerSignal = new PacketWebRTC();
offerSignal.setSignalType(WebRTCSignalType.OFFER); offerSignal.setSignalType(WebRTCSignalType.OFFER);
offerSignal.setSdpOrCandidate(JSON.stringify(offer)); offerSignal.setSdpOrCandidate(JSON.stringify(offer));
@@ -325,6 +366,11 @@ export function CallProvider(props : CallProviderProps) {
packetSignal.setDst(activeCall); packetSignal.setDst(activeCall);
packetSignal.setSignalType(SignalType.END_CALL); packetSignal.setSignalType(SignalType.END_CALL);
send(packetSignal); send(packetSignal);
end();
}
const end = () => {
peerConnectionRef.current?.close();
peerConnectionRef.current = null; peerConnectionRef.current = null;
roomIdRef.current = ""; roomIdRef.current = "";
setActiveCall(""); setActiveCall("");
@@ -386,6 +432,7 @@ export function CallProvider(props : CallProviderProps) {
return ( return (
<CallContext.Provider value={context}> <CallContext.Provider value={context}>
{props.children} {props.children}
<audio ref={remoteAudioRef} autoPlay playsInline style={{ display: 'none' }} />
{showCallView && <Call context={context}></Call>} {showCallView && <Call context={context}></Call>}
</CallContext.Provider> </CallContext.Provider>
) )

View File

@@ -1,7 +1,7 @@
export const SERVERS = [ export const SERVERS = [
//'wss://cdn.rosetta-im.com', //'wss://cdn.rosetta-im.com',
//'ws://10.211.55.2:3000', 'ws://10.211.55.2:3000',
'ws://192.168.6.82:3000', //'ws://192.168.6.82:3000',
//'wss://wss.rosetta.im' //'wss://wss.rosetta.im'
]; ];