Signatur
Beschreibung
Die Eigenschaft framesSent des Wörterbuchs RTCOutboundRtpStreamStats ist ein fortlaufend steigender Zähler (vorzeichenlose Ganzzahl). Er gibt an, wie viele vollständig codierte Frames über diesen RTP-Stream verschickt wurden. Ein Frame gilt als gesendet, sobald alle zugehörigen RTP-Pakete übertragen wurden.
Die Eigenschaft gibt es nur bei Video-Streams (kind === "video"). Bei Audio-Streams ist sie nicht vorhanden. Den Wert erhält man über RTCPeerConnection.getStats() oder RTCRtpSender.getStats(). Im zurückgegebenen RTCStatsReport sucht man dafür Einträge mit type === "outbound-rtp".
Richtig nützlich wird framesSent, wenn man zwei Messungen vergleicht. Teilt man die Differenz zweier Abfragen durch die vergangene Zeit (timestamp in Millisekunden), erhält man die tatsächlich gesendete Bildrate. Zusammen mit framesEncoded lässt sich erkennen, ob codierte Frames verworfen werden, bevor sie das Netz erreichen. Zusammen mit framesPerSecond oder qualityLimitationReason lassen sich Engpässe bei CPU oder Bandbreite aufspüren.
Rückgabewert
unsigned long): die Gesamtzahl der bisher gesendeten codierten Frames dieses RTP-Streams. Bei Audio-Streams ist der Wert undefined.Beispiele
Gesendete Frames aller Video-Streams auslesen
// pc ist eine bestehende RTCPeerConnection mit Video-Track
const logFramesSent = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type === 'outbound-rtp' && stats.kind === 'video') {
console.log(`SSRC ${stats.ssrc}: ${stats.framesSent} Frames gesendet`);
}
});
};
await logFramesSent(pc);
Tatsächliche Sende-Bildrate berechnen
// Bildrate aus der Differenz zweier Messungen berechnen
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
let previous = null;
const measureFps = async () => {
const report = await sender.getStats();
for (const stats of report.values()) {
if (stats.type !== 'outbound-rtp') continue;
if (previous) {
const frames = stats.framesSent - previous.framesSent;
const seconds = (stats.timestamp - previous.timestamp) / 1000;
console.log(`Gesendet: ${(frames / seconds).toFixed(1)} fps`);
}
previous = stats;
}
};
const timer = setInterval(measureFps, 1000);
// Später: clearInterval(timer);
Verlust zwischen Encoder und Versand erkennen
const checkEncoderGap = async (pc) => {
const report = await pc.getStats();
for (const s of report.values()) {
if (s.type === 'outbound-rtp' && s.kind === 'video') {
const gap = (s.framesEncoded ?? 0) - (s.framesSent ?? 0);
console.log({
rid: s.rid ?? '(kein Simulcast)',
framesEncoded: s.framesEncoded,
framesSent: s.framesSent,
nichtGesendet: gap,
limitierung: s.qualityLimitationReason
});
}
}
};
await checkEncoderGap(pc);
// Wichtig · Fallstricke
- Nur Video: Bei Audio-Streams gibt es
framesSentnicht. Prüfen Sie daher immerkind === "video"oder fangen Sieundefinedab, zum Beispiel mit?? 0. - Zähler, keine Rate: Der Wert steigt nur an. Für eine Bildrate braucht man zwei Messungen und muss die Differenz durch die Differenz der
timestamp-Werte teilen. Die Abfrage-Abstände selbst sind nicht exakt. - Simulcast: Bei Simulcast oder SVC gibt es pro Ebene einen eigenen
outbound-rtp-Eintrag mit eigenem Zähler. Diese Einträge unterscheiden sich überridbzw.ssrc. Wer die Summe braucht, muss selbst addieren. - Keine Angabe über den Empfang: Gesendet heißt nicht empfangen. Verluste im Netz sieht man erst in den Statistiken der Gegenseite (
remote-inbound-rtp,framesReceivedundframesDecodedbeim Empfänger). - Kompatibilität: Ältere Browser, vor allem Safari vor Version 15 und alte Firefox-Versionen, lieferten den Wert nicht oder nur im veralteten Eintrag vom Typ
track. Robuster Code sollte deshalb prüfen, ob die Eigenschaft vorhanden ist.