Signatur
Beschreibung
Die Eigenschaft availableOutgoingBitrate eines RTCIceCandidatePairStats-Objekts gibt an, wie viel ausgehende Kapazität die Netzwerkverbindung eines Kandidatenpaars gerade hat. Der Wert ist eine Zahl in Bit pro Sekunde. Der Browser berechnet ihn mit seiner Staukontrolle (z. B. GCC oder Transport-CC) und fasst dabei alle ausgehenden RTP-Streams zusammen, die über dieses Kandidatenpaar laufen.
Header von IP, UDP oder TCP sind in der Messung nicht enthalten. Sie entspricht damit etwa dem Wert TIAS (Transport Independent Application Specific Maximum) aus RFC 3890. In der Praxis zeigt sie, welche Bitrate Audio, Video und Datenkanäle gemeinsam nutzen können, bevor Pakete verloren gehen oder sich die Verzögerung erhöht.
Typische Einsatzfälle sind adaptive Bitraten-Steuerung (z. B. Auflösung oder maxBitrate eines RTCRtpSender anpassen), Anzeigen zur Verbindungsqualität in Videokonferenz-Apps und Telemetrie zur Fehlersuche bei schlechten Anrufen. Den Wert bekommst du über RTCPeerConnection.getStats(), indem du im zurückgegebenen RTCStatsReport den Eintrag mit type === "candidate-pair" suchst, der gerade genutzt wird.
Rückgabewert
undefined, wenn der Browser keine senderseitige Schätzung berechnet oder das Paar nicht das aktive bzw. nominierte Paar ist.Beispiele
Verfügbare Upload-Bandbreite des aktiven Kandidatenpaars auslesen
// pc ist eine bereits verbundene RTCPeerConnection
const getOutgoingBitrate = async (pc) => {
const report = await pc.getStats();
let selectedPairId;
// Das aktive Paar über die Transport-Statistik finden
report.forEach((stat) => {
if (stat.type === 'transport' && stat.selectedCandidatePairId) {
selectedPairId = stat.selectedCandidatePairId;
}
});
// Rückfall: nominiertes Paar im Zustand "succeeded"
const pair = selectedPairId
? report.get(selectedPairId)
: [...report.values()].find(
(s) => s.type === 'candidate-pair' && s.nominated && s.state === 'succeeded'
);
return pair?.availableOutgoingBitrate;
};
const bitrate = await getOutgoingBitrate(pc);
if (bitrate !== undefined) {
console.log(`Verfügbar: ${(bitrate / 1_000_000).toFixed(2)} Mbit/s`);
} else {
console.log('Keine Bandbreitenschätzung verfügbar');
}
Video-Bitrate dynamisch an die verfügbare Bandbreite anpassen
const adaptVideoBitrate = async (pc) => {
const report = await pc.getStats();
const pair = [...report.values()].find(
(s) => s.type === 'candidate-pair' && s.nominated && s.state === 'succeeded'
);
const available = pair?.availableOutgoingBitrate;
if (!available) return;
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
if (!sender) return;
const params = sender.getParameters();
if (!params.encodings?.length) return;
// 80 % der Kapazität für Video, Rest als Puffer für Audio/Daten
const target = Math.floor(available * 0.8);
params.encodings[0].maxBitrate = target;
await sender.setParameters(params);
console.log(`maxBitrate gesetzt auf ${Math.round(target / 1000)} kbit/s`);
};
// Alle 2 Sekunden neu bewerten
const timer = setInterval(() => adaptVideoBitrate(pc), 2000);
pc.addEventListener('connectionstatechange', () => {
if (pc.connectionState === 'closed') clearInterval(timer);
});
Qualitätsanzeige für eine Videokonferenz
const qualityLabel = (bps) => {
if (bps === undefined) return 'unbekannt';
if (bps >= 2_500_000) return 'sehr gut (HD)';
if (bps >= 1_000_000) return 'gut';
if (bps >= 300_000) return 'eingeschränkt';
return 'schlecht';
};
const updateIndicator = async (pc, element) => {
const report = await pc.getStats();
const pair = [...report.values()].find(
(s) => s.type === 'candidate-pair' && s.nominated && s.state === 'succeeded'
);
element.textContent = `Verbindung: ${qualityLabel(pair?.availableOutgoingBitrate)}`;
};
setInterval(() => updateIndicator(pc, document.querySelector('#quality')), 1000);
// Wichtig · Fallstricke
- Einheit: Der Wert ist in Bit pro Sekunde angegeben, nicht in Byte. Für kbit/s teilst du durch 1000, für Mbit/s durch 1 000 000.
- Nur für das aktive Paar sinnvoll: Bei nicht genutzten oder fehlgeschlagenen Kandidatenpaaren fehlt die Eigenschaft meist (
undefined). Nimm daher möglichst das Paar ausRTCTransportStats.selectedCandidatePairId. - Es ist eine Schätzung: Der Wert stammt aus der Staukontrolle und schwankt, besonders kurz nach dem Verbindungsaufbau. Er steigt oft erst dann, wenn tatsächlich Daten gesendet werden. Glätte ihn für Regelungen, z. B. mit einem gleitenden Mittelwert.
- Kein Overhead enthalten: IP-, UDP- und TCP-Header werden nicht mitgezählt. Die tatsächlich über die Leitung übertragene Datenmenge ist also etwas größer.
- Browser-Kompatibilität: Chrome, Edge und Safari unterstützen die Eigenschaft. Firefox liefert sie bislang nicht. Prüfe daher immer auf
undefinedund sieh eine Ersatzlösung vor, z. B. eine eigene Berechnung ausbytesSentüber die Zeit. - Rufe
getStats()nicht zu oft auf (Intervalle unter etwa 500 ms), denn jeder Aufruf erzeugt einen vollständigen Bericht und kostet Rechenzeit.