Signatur
Beschreibung
Das Event gatheringstatechange wird an einem RTCIceTransport ausgelöst, sobald sich seine Eigenschaft gatheringState ändert. Diese Eigenschaft zeigt an, wie weit das Sammeln der lokalen ICE-Kandidaten für genau diesen einen Transport ist. Mögliche Werte:
"new": Das Sammeln hat noch nicht begonnen."gathering": Es werden gerade Kandidaten gesammelt (Host, STUN/srflx, TURN/relay)."complete": Das Sammeln ist abgeschlossen. Bis zu einem ICE-Restart kommen keine weiteren Kandidaten hinzu.
Einen Handler registrierst du mit addEventListener("gatheringstatechange", handler) oder über die Eigenschaft ongatheringstatechange. Das Event ist ein einfaches Event ohne zusätzliche Daten. Den neuen Zustand liest du im Handler aus event.target.gatheringState. Das Event lässt sich nicht abbrechen und steigt im DOM nicht auf (es „bubbelt“ nicht).
An den RTCIceTransport kommst du über RTCRtpSender.transport.iceTransport, RTCRtpReceiver.transport.iceTransport oder RTCSctpTransport.transport.iceTransport. Das Event ist sinnvoll, wenn du die Kandidatensammlung pro Transport genau beobachten willst, etwa ohne BUNDLE oder zur Fehlersuche. In den meisten Anwendungen reicht das zusammengefasste Event icegatheringstatechange an RTCPeerConnection, das den Zustand über alle Transporte hinweg meldet.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Event-Name: "gatheringstatechange". |
|
| $handler Pflicht | (event: Event) => void | Callback, der bei jeder Zustandsänderung aufgerufen wird. Den aktuellen Wert liefert event.target.gatheringState. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie once oder signal (zum Entfernen per AbortController). |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler erhält ein Event, dessen target der betroffene RTCIceTransport ist.Beispiele
Kandidatensammlung eines Senders beobachten
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const sender = pc.addTrack(stream.getAudioTracks()[0], stream);
// transport ist erst nach setLocalDescription gesetzt
await pc.setLocalDescription(await pc.createOffer());
const iceTransport = sender.transport?.iceTransport;
if (iceTransport) {
console.log('Start:', iceTransport.gatheringState);
iceTransport.addEventListener('gatheringstatechange', (event) => {
console.log('ICE-Gathering:', event.target.gatheringState);
});
}
on-Property nutzen und Handler wieder entfernen
const pc = new RTCPeerConnection();
const channel = pc.createDataChannel('chat');
await pc.setLocalDescription(await pc.createOffer());
const iceTransport = pc.sctp?.transport.iceTransport;
// Variante 1: on-Property
iceTransport.ongatheringstatechange = () => {
console.log('Zustand:', iceTransport.gatheringState);
};
// Variante 2: benannter Listener, später wieder entfernen
const onGathering = () => {
if (iceTransport.gatheringState === 'complete') {
console.log('Fertig gesammelt');
iceTransport.removeEventListener('gatheringstatechange', onGathering);
iceTransport.ongatheringstatechange = null;
}
};
iceTransport.addEventListener('gatheringstatechange', onGathering);
Auf vollständige Sammlung warten (mit Timeout und Fallback)
const waitForGatheringComplete = (pc, sender, timeoutMs = 5000) =>
new Promise((resolve) => {
// Fallback auf RTCPeerConnection, wenn RTCIceTransport fehlt
const target = sender?.transport?.iceTransport ?? pc;
const stateOf = () => target === pc ? pc.iceGatheringState : target.gatheringState;
const eventName = target === pc ? 'icegatheringstatechange' : 'gatheringstatechange';
if (stateOf() === 'complete') return resolve('complete');
const controller = new AbortController();
const timer = setTimeout(() => { controller.abort(); resolve('timeout'); }, timeoutMs);
target.addEventListener(eventName, () => {
if (stateOf() === 'complete') {
clearTimeout(timer);
controller.abort();
resolve('complete');
}
}, { signal: controller.signal });
});
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc.addTrack(stream.getVideoTracks()[0], stream);
await pc.setLocalDescription(await pc.createOffer());
console.log(await waitForGatheringComplete(pc, sender));
// Jetzt enthält pc.localDescription.sdp alle Kandidaten (non-trickle ICE)
// Wichtig · Fallstricke
- Transport erst später verfügbar:
sender.transportistnull, bissetLocalDescription()bzw.setRemoteDescription()gelaufen ist. Registrierst du den Listener zu spät, hast du den Wechsel zu"gathering"eventuell schon verpasst. Prüfe deshalb zuerst den aktuellengatheringState. - BUNDLE: Mit dem üblichen
bundlePolicy: "max-bundle"teilen sich alle Sender und Empfänger denselbenRTCIceTransport. Bei mehreren Listenern wird das Event dann für jeden von ihnen ausgelöst. - Nicht verwechseln:
icegatheringstatechangeanRTCPeerConnectionfasst den Zustand aller Transporte zusammen und wird von allen Browsern unterstützt.gatheringstatechangegilt nur für einen einzelnen Transport. - ICE-Restart: Nach
pc.restartIce()kann der Zustand wieder von"complete"auf"gathering"wechseln. Ein Handler mitonce: truebekommt das nicht mit. - Browser-Kompatibilität:
RTCIceTransportund dieses Event gibt es in Chromium-Browsern und Safari. Firefox unterstützt sie nur eingeschränkt bzw. erst in neueren Versionen. Prüfe daher vorher mitsender.transport?.iceTransportund greif sonst aufRTCPeerConnectionzurück. - Das Event enthält keine Kandidaten. Die einzelnen Kandidaten liefert das Event
icecandidateanRTCPeerConnection.