Signatur
Beschreibung
Die Eigenschaft frameWidth des Wörterbuchs RTCInboundRtpStreamStats liefert die Breite des zuletzt decodierten Video-Frames in Pixel. Du erhältst sie über RTCPeerConnection.getStats() oder RTCRtpReceiver.getStats() in Einträgen mit type === "inbound-rtp" und kind === "video".
Praktisch ist der Wert für die Überwachung der Empfangsqualität: Bei knapper Bandbreite oder hoher CPU-Last senkt der Sender (oder ein SFU) oft die Auflösung. Mit frameWidth und frameHeight siehst du, welche Auflösung tatsächlich ankommt. Das ist etwas anderes als die Größe des <video>-Elements oder als die Auflösung, die der Sender ursprünglich aufgenommen hat.
Bei Audio-Streams gibt es die Eigenschaft nicht. Solange noch kein Frame decodiert wurde, fehlt sie ebenfalls, der Wert ist dann undefined. Weil es nur eine Momentaufnahme ist, fragst du sie am besten regelmäßig ab, zum Beispiel jede Sekunde, und wertest Veränderungen über die Zeit aus.
Rückgabewert
unsigned long) mit der Breite des zuletzt decodierten Frames in Pixel. undefined, wenn noch kein Frame decodiert wurde oder der Stream kein Video ist.Beispiele
Aktuelle Empfangsauflösung auslesen
// pc ist eine verbundene RTCPeerConnection
const logInboundResolution = async (pc) => {
const stats = await pc.getStats();
stats.forEach((report) => {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
const { frameWidth, frameHeight } = report;
if (frameWidth !== undefined) {
console.log(`Empfangen: ${frameWidth}×${frameHeight}`);
} else {
console.log('Noch kein Frame decodiert');
}
}
});
};
await logInboundResolution(pc);
Auflösungswechsel überwachen (z. B. durch Bandbreitenanpassung)
const watchResolution = (receiver, onChange) => {
let lastWidth;
const timer = setInterval(async () => {
const stats = await receiver.getStats();
for (const report of stats.values()) {
if (report.type !== 'inbound-rtp' || report.kind !== 'video') continue;
const width = report.frameWidth;
if (width !== undefined && width !== lastWidth) {
onChange(lastWidth, width, report.frameHeight);
lastWidth = width;
}
}
}, 1000);
return () => clearInterval(timer); // Aufräumfunktion
};
const [videoReceiver] = pc.getReceivers().filter((r) => r.track.kind === 'video');
const stop = watchResolution(videoReceiver, (alt, neu, hoehe) => {
console.log(`Breite geändert: ${alt ?? '–'} → ${neu} (Höhe ${hoehe})`);
});
Qualitätswarnung bei zu niedriger Auflösung
const MIN_WIDTH = 640;
const checkQuality = async (pc) => {
const stats = await pc.getStats();
const video = [...stats.values()].find(
(r) => r.type === 'inbound-rtp' && r.kind === 'video'
);
if (video?.frameWidth && video.frameWidth < MIN_WIDTH) {
console.warn(`Niedrige Auflösung: ${video.frameWidth}px Breite`);
return false;
}
return true;
};
console.log(await checkQuality(pc));
// Wichtig · Fallstricke
- Nur Video: Bei
kind === "audio"gibt es die Eigenschaft nicht. Filtere deshalb immer nachkind. In älteren Browsern heißt das Feld nochmediaType. - Anfangs undefined: Direkt nach dem Verbindungsaufbau wurde oft noch kein Frame decodiert. Prüfe auf
undefined, bevor du rechnest. - Nicht die Anzeigegröße: Der Wert beschreibt den decodierten Frame. Die Größe auf dem Bildschirm hängt vom CSS und vom
<video>-Element ab. Die Größe des Videos selbst liefertHTMLVideoElement.videoWidth. - Drehung: Wird die Ausrichtung per RTP-Header-Erweiterung übertragen (z. B. bei Mobilgeräten im Hochformat), können Breite und Höhe im Vergleich zur angezeigten Ausrichtung vertauscht sein.
- Kompatibilität: In aktuellen Chromium-Browsern, Firefox und Safari verfügbar. Ältere Versionen haben den Wert im veralteten
track-Statistiktyp statt ininbound-rtpgemeldet. - Performance:
getStats()ist asynchron und nicht kostenlos. Ein Abfrageintervall von etwa 1 Sekunde reicht in der Regel aus.