Буферизация 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 [sharedSecret, setSharedSecret] = useState<string>("");
const iceServersRef = useRef<RTCIceServer[]>([]);
const remoteAudioRef = useRef<HTMLAudioElement>(null);
const iceCandidatesBufferRef = useRef<RTCIceCandidate[]>([]);
useEffect(() => {
/**
/**
* Нам нужно получить ICE серверы для установки соединения из разных сетей
* Получаем их от сервера
*/
let packet = new PacketIceServers();
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) => {
let iceServers = packet.getIceServers();
@@ -118,6 +143,15 @@ export function CallProvider(props : CallProviderProps) {
*/
const sdp = JSON.parse(packet.getSdpOrCandidate());
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");
return;
}
@@ -127,6 +161,14 @@ export function CallProvider(props : CallProviderProps) {
*/
const candidate = JSON.parse(packet.getSdpOrCandidate());
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));
info("Received WebRTC ICE candidate and added to peer connection");
return;
@@ -165,13 +207,7 @@ export function CallProvider(props : CallProviderProps) {
/**
* Сбросили звонок
*/
setActiveCall("");
setCallState(CallState.ENDED);
setShowCallView(false);
setSessionKeys(null);
setSharedSecret("");
setDuration(0);
roleRef.current = null;
end();
return;
}
if(signalType == SignalType.CALL){
@@ -276,7 +312,12 @@ export function CallProvider(props : CallProviderProps) {
* При получении медиа-трека с другой стороны
*/
console.info("TRACK RECV!!!!!");
if(remoteAudioRef.current){
console.info(event.streams);
remoteAudioRef.current.srcObject = event.streams[0];
}
}
/**
* Запрашиваем Аудио поток с микрофона и добавляем его в PeerConnection, чтобы другая сторона могла его получить и воспроизвести,
* когда мы установим WebRTC соединение
@@ -291,7 +332,7 @@ export function CallProvider(props : CallProviderProps) {
* Отправляем свой оффер другой стороне
*/
let offer = await peerConnectionRef.current.createOffer();
peerConnectionRef.current.setLocalDescription(offer);
await peerConnectionRef.current.setLocalDescription(offer);
let offerSignal = new PacketWebRTC();
offerSignal.setSignalType(WebRTCSignalType.OFFER);
offerSignal.setSdpOrCandidate(JSON.stringify(offer));
@@ -325,6 +366,11 @@ export function CallProvider(props : CallProviderProps) {
packetSignal.setDst(activeCall);
packetSignal.setSignalType(SignalType.END_CALL);
send(packetSignal);
end();
}
const end = () => {
peerConnectionRef.current?.close();
peerConnectionRef.current = null;
roomIdRef.current = "";
setActiveCall("");
@@ -386,6 +432,7 @@ export function CallProvider(props : CallProviderProps) {
return (
<CallContext.Provider value={context}>
{props.children}
<audio ref={remoteAudioRef} autoPlay playsInline style={{ display: 'none' }} />
{showCallView && <Call context={context}></Call>}
</CallContext.Provider>
)