WebRTC: מדריך להעברה של getStats() מדור קודם

Henrik Boström
Henrik Boström

הגרסה הקודמת של WebRTC API עם getStats() תוסר מ-Chrome 117, ולכן אפליקציות שמשתמשות בו יצטרכו לעבור ל-API הרגיל. במאמר הזה נסביר איך להעביר את הקוד ומה צריך לעשות אם נדרש זמן נוסף לביצוע השינוי.

בעבר היו שתי גרסאות מתחרות של WebRTC getStats() API. ה-API הקודם של getStats() , שמעדכן את תהליך הסטנדרטיזציה ומקבל ארגומנט קריאה חוזרת, ואת ה-API הסטנדרטי שנתמך בתפוצה רחבה שמחזיר הבטחה.

ה-API הסטנדרטי הוא יותר עשיר בתכונות, ויש בו מדדים מוגדרים היטב המתועדים באופן ציבורי במפרט W3C מזהים עבור Statistics API של WebRTC. המפרט כולל תיאורים של כל מדד שמופיע במדריך הזה, והרבה נושאים נוספים.

החל מגרסה 117 של Chrome, ה-API הקודם של getStats() יגרום לחריגה בערוץ ההפצה היציבה (הזרקה החריגה תושק בהדרגה). המדריך הזה יעזור לכם לעבור בקלות ל-API הרגיל.

נתונים סטטיסטיים מדור קודם לעומת נתונים סטטיסטיים רגילים

הרשימה המלאה של סוגי נתונים סטטיסטיים סטנדרטיים זמינה במפרט RTCStatsType במפרט. כולל אילו הגדרות מילון של נתונים סטטיסטיים מתארות את המדדים שנאספו עבור כל סוג.

לכל האובייקטים של הנתונים הסטטיסטיים יש מאפיין מזהה שמזהה באופן ייחודי את האובייקט הבסיסי בכמה קריאות של getStats(). לאותו אובייקט יהיה אותו מזהה בכל קריאה ל-method. האפשרות הזו שימושית לחישוב שיעור שינוי המדדים (דוגמה לכך בקטע הבא). המזהים גם יוצרים קשרי גומלין בין קובצי עזר. לדוגמה, אובייקט הנתונים הסטטיסטיים outbound-rtp מפנה לאובייקט הנתונים הסטטיסטיים המשויך של media-source באמצעות המאפיין outbound-rtp.mediaSourceId. אם תציירו את כל קשרי הגומלין של ...Id, יוצג תרשים.

ה-API מהדור הקודם כולל את סוגי הנתונים הסטטיסטיים הבאים, שתואמים לסוגים הרגילים הבאים:


סוג מדור קודם

סוג רגיל
ssrc
מייצג זרם RTP ומדדים לגבי MediaStreamTrack המשויך.


הסוגים הרגילים לכך הם inbound-rtp (לקבלה של זרמי RTP ושל MediaStreamTrack המרוחקים המשויכים אליהם), outbound-rtp (לשליחת זרמי RTP) ו-media-source (למדדי MediaStreamTrack מקומיים המשויכים לזרם RTP שנשלח). מדדי סטרימינג של RTP מכילים גם מידע על המקודד או המפענח שמשמשים את זרם ה-RTP.
VideoBwe
מדדים להערכת רוחב הפס, קצב העברת הנתונים ליעד, קצב העברת הנתונים של המקודד וקצב העברת הנתונים בפועל. סוגי המדדים האלה הם חלק ממדדי RTP (outbound-rtp ו-inbound-rtp) ומדדי צמד מועמד ל-ICE (candidate-pair).
googComponent
מייצג את התעבורה (ICE ו-DTLS). הגרסה הרגילה היא transport.
localcandidate and remotecandidate
מייצג מועמד ל-ICE. הגרסה הרגילה היא local-candidate ו-remote-candidate.
googCandidatePair
מייצג זוג מועמד ל-ICE, שהוא צמד בין מועמד מקומי למועמד מרוחק. הגרסה הרגילה היא candidate-pair.
googCertificate
מייצג אישור שמשמש את תעבורת DTLS. הגרסה הרגילה היא certificate.
googLibjingleSession
מייצג את RTCPeerConnection. אומנם התוכן של התקן לא ממופה לשום דבר בתקן, אבל למעשה תקן כולל סוג שמשויך לRTCPeerConnection: peer-connection.

חסר ב-API הקודם

הסוגים הבאים של נתונים סטטיסטיים נוספו ל-API הרגיל שאין להם סוג תואם מדור קודם:
  • codec: קודק שנמצא כרגע בשימוש של זרם RTP לקידוד או לפענוח. זו קבוצת משנה של רכיבי הקודק שנכללו במשא ומתן ב-SDP.
  • remote-inbound-rtp: שידור RTP נכנס של נקודת קצה מרוחקת שתואם לזרם RTP היוצא שנקודת הקצה הזו שולחת (outbound-rtp). היא נמדדת בנקודת הקצה המרוחקת ומדווחת בדוח מקלט RTCP (RR) או בדוח מורחב של RTCP (XR).
  • remote-outbound-rtp: שידור RTP יוצא של נקודת קצה מרחוק שתואם לשידור RTP נכנס שנקודת הקצה הזו מקבלת (inbound-rtp). הוא נמדד בנקודת הקצה המרוחקת ומדווח בדוח שולח RTCP (SR).
  • media-playout: מדדים לגבי ההפעלה של MediaStreamTrack מרחוק שמשויך לשידור RTP נכנס (inbound-rtp).
  • data-channel: מייצג RTCDataChannel.

מיפוי המדדים הקודמים למדדים רגילים

המיפוי הזה נועד לעזור למפתחים לגלות איזה מדד מדור קודם תואם לכל מדד סטנדרטי, אבל חשוב לזכור שהמדד התואם עשוי להשתמש ביחידות שונות או לבטא אותו כמונה כולל ולא כערך מיידי. יש לעיין במפרט להגדרות המדדים.
ב-API הרגיל עדיף לחשוף ספירות כוללים במקום תעריפים. כלומר, כדי לקבל את הקצב התואם (למשל, קצב העברת הנתונים) כמו ב-API הקודם, האפליקציה צריכה לחשב את הקצב הממוצע על ידי בדיקת דלתא בין שתי קריאות של getStats(). לדוגמה:

// Periodically (e.g. every second or every 10 seconds)...
const currReport = await pc.getStats();
// Calculate bitrate since the last getStats() call.
// Handling of undefined is omitted for clarity.
const currOutboundRtp = currReport.values().find(s => s.type == 'outbound-rtp');
const prevOutboundRtp = prevReport.get(currOutboundRtp.id);
const deltaBits = (currOutboundRtp.bytesSent - prevOutboundRtp.bytesSent) * 8;
const deltaSeconds = (currOutboundRtp.timestamp - prevOutboundRtp.timestamp) / 1000;
logBitrateMeasurement(deltaBits / deltaSeconds);
// Remember the report for next time.
prevReport = currReport;

חישוב שיעורים וממוצעים בעצמכם עשוי להיראות כמו שלב נוסף מסורבל, אבל הצד ההפוך הוא שמאפשר לכם לקבל ממוצעים לפי כל מרווח זמן שרוצים. להפעלת ה-API הרגיל בתדירות נמוכה יותר ממה שהייתם צריכים לעשות עם ה-API מהדור הקודם יש כמה יתרונות לשיפור הביצועים.

המדד הקודם
googCertificate
תכתובת רגילה
certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
המדד הקודם
googComponent
תכתובת רגילה
transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
המדד הקודם
localcandidate
תכתובת רגילה
local-candidate או candidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (חיפוש הפוך candidate-pair דרך candidate-pair.localCandidateId)
.portNumber local-candidate.port
.networkType local-candidate.networkType
.ipAddress local-candidate.address
.stunKeepaliveResponsesReceived candidate-pair.responsesReceived
.stunKeepaliveRttTotal candidate-pair.totalRoundTripTime
.transport local-candidate.protocol
.candidateType local-candidate.candidateType
.priority local-candidate.priority
המדד הקודם
remotecandidate
תכתובת רגילה
remote-candidate
בדיוק כמו localcandidate למעלה. בדיוק כמו local-candidate למעלה.
המדד הקודם
googCandidatePair
תכתובת רגילה
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(חיפוש remote-candidate דרך
candidate-pair.remoteCandidateId)
.googReadable googReadable הוא ערך בוליאני שמשקף אם הגדלנו לאחרונה את candidate-pair.requestsReceived או את candidate-pair.responsesReceived
.googLocalAddress local-candidate.address
(חיפוש local-candidate דרך
candidate-pair.localCandidateId)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType בדיוק כמו local-candidate.protocol ו-remote-candidate.protocol.
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable הוא ערך בוליאני שמשקף אם הגדלנו לאחרונה את candidate-pair.responsesReceived
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection החיבור הפעיל מתייחס לשני המועמדים שנבחרו כרגע על ידי התעבורה, למשל candidate-pair.id == transport.selectedCandidatePairId
.packetsDiscardedOnSend candidate-pair.packetsDiscardedOnSend
.bytesReceived candidate-pair.bytesReceived
.responsesReceived candidate-pair.responsesReceived
.remoteCandidateId candidate-pair.remoteCandidateId
.localCandidateId candidate-pair.localCandidateId
.bytesSent candidate-pair.bytesSent
.packetsSent candidate-pair.packetsSent
.bytesReceived candidate-pair.bytesReceived
.bytesReceived candidate-pair.bytesReceived
המדד הקודם
ssrc
תכתובת רגילה
inbound-rtp, outbound-rtp, media-source
.audioInputLevel media-source.audioLevel. המדד הקודם נמצא בטווח [0..32768] אבל המדד הרגיל נמצא בטווח [0..1].
.audioOutputLevel
inbound-rtp.audioLevel. המדד הקודם נמצא בטווח [0..32768] אבל המדד הרגיל נמצא בטווח [0..1].
.packetsLost inbound-rtp.packetsLost
.googTrackId media-source.trackIdentifier למעבדי MediaStreamTrack מקומיים ו-inbound-rtp.trackIdentifier למעבדי MediaStreamTrack מרוחקים
.googRtt remote-inbound-rtp.roundTripTime (ראו outbound-rtp.remoteId)
.googEchoCancellationReturnLossEnhancement inbound-rtp.echoReturnLossEnhancement
.googCodecName שם הקודק הוא סוג המשנה של "סוג/סוג משנה" סוג mime, codec.mimeType (ניתן לעיין ב-inbound-rtp.codecId וב-outbound-rtp.codecId)
.transportId inbound-rtp.transportId וגם outbound-rtp.transportId
.mediaType inbound-rtp.kind וגם outbound-rtp.kind או media-source.kind
.googEchoCancellationReturnLoss inbound-rtp.echoReturnLoss
.totalAudioEnergy inbound-rtp.totalAudioEnergy וגם media-source.totalAudioEnergy
ssrc.totalSamplesDuration inbound-rtp.totalSamplesDuration וגם media-source.totalSamplesDuration
.ssrc inbound-rtp.ssrc וגם outbound-rtp.ssrc
.googJitterReceived inbound-rtp.jitter
.packetsSent outbound-rtp.packetsSent
.bytesSent outbound-rtp.bytesSent
.googContentType inbound-rtp.contentType וגם outbound-rtp.contentType
.googFrameWidthInput media-source.width
.googFrameHeightInput media-source.height
.googFrameRateInput media-source.framesPerSecond
.googFrameWidthSent outbound-rtp.frameWidth
.googFrameHeightSent outbound-rtp.frameHeight
.googFrameRateSent
ה-FPS בשליחה הוא קצב השינוי של outbound-rtp.framesSent, אבל בפועל הוא מיושם בתור outbound-rtp.framesPerSecond, שמקודד FPS.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
שיעור השינוי של inbound-rtp.framesDecoded
.googFrameRateOutput
שיעור השינוי של inbound-rtp.framesDecoded - inbound-rtp.framesDropped
.hugeFramesSent outbound-rtp.hugeFramesSent
.qpSum

inbound-rtp.qpSum וגם outbound-rtp.qpSum

.framesEncoded outbound-rtp.framesEncoded
.googAvgEncodeMs

outbound-rtp.totalEncodeTime / outbound-rtp.framesEncoded

.codecImplementationName

inbound-rtp.decoderImplementation וגם outbound-rtp.encoderImplementation

.googCpuLimitedResolution
True אם outbound-rtp.qualityLimitationReason == "cpu"
.googBandwidthLimitedResolution
True אם outbound-rtp.qualityLimitationReason == "bandwidth"
.googAdaptationChanges
המדד הקודם סופר את מספר הפעמים שהרזולוציה או קצב הפריימים השתנו מסיבות הקשורות ל-qualityLimitationReason. אפשר להסיק את זה ממדדים אחרים (למשל, רזולוציית השליחה או קצב הפריימים שונים מרזולוציית המקור או מקצב הפריימים), אבל משך הזמן שבו היינו מוגבלים, outbound-rtp.qualityLimitationDurations, עשוי להיות שימושי יותר מהתדירות שבה הרזולוציה או קצב הפריימים השתנו מחדש.
.googNacksReceived inbound-rtp.nackCount
.googNacksSent inbound-rtp.nackCount
.googPlisReceived inbound-rtp.pliCount
.googPlisSent inbound-rtp.pliCount
.googFirsReceived inbound-rtp.firCount
.googFirsSent inbound-rtp.firCount
.googSecondaryDecodedRate
היחס האחרון בין החבילות שמכילות תיקון שגיאות: inbound-rtp.fecPacketsReceived-inbound-rtp.fecPacketsDiscarded
.packetsReceived inbound-rtp.packetsReceived
.googJitterBufferMs inbound-rtp.jitterBufferDelay / inbound-rtp.jitterBufferEmittedCount
.googTargetDelayMs (סרטון) inbound-rtp.jitterBufferTargetDelay / inbound-rtp.jitterBufferEmittedCount
.googPreferredJitterBufferMs (אודיו) inbound-rtp.jitterBufferTargetDelay / inbound-rtp.jitterBufferEmittedCount
.googExpandRate
היחס האחרון בין דגימות מוסתרות: inbound-rtp.concealedSamples / inbound-rtp.totalSamplesReceived
.googSpeechExpandRate היחס האחרון בין דגימות מוסתרות בזמן השידור לא היה שקט: מתוך (inbound-rtp.concealedSamples - inbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate היחס האחרון בין הדגימות שנמחקו כדי להאיץ את מהירות ההפעלה: inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
היחס האחרון בין הדגימות שסוננו כדי להאט את מהירות ההפעלה: inbound-rtp.insertedSamplesForDeceleration / inbound-rtp.totalSamplesReceived
.googSecondaryDiscardedRate inbound-rtp.fecPacketsDiscarded
.bytesReceived inbound-rtp.bytesReceived
s.googCurrentDelayMs inbound-rtp.jitterBufferDelay + media-playout.totalPlayoutDelay
.googDecodeMs inbound-rtp.totalDecodeTime / inbound-rtp.framesDecoded
.googTimingFrameInfo
המדד היחיד שנותר מ-goog. inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
המדד הקודם
VideoBwe
תכתובת רגילה
outbound-rtp וגם candidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate כערך מיידי או outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded בממוצע
.googActualEncBitrate הבייטים שהמקודד מפיק הם הבייטים של המטען הייעודי (Payload), לא כולל העברות חוזרות: קצב השינוי של outbound-rtp.bytesSent - outbound-rtp.retransmittedBytesSent
.googBucketDelay outbound-rtp.totalPacketSendDelay / outbound-rtp.packetsSent
.googTransmitBitrate קצב השינוי של outbound-rtp.headerBytesSent + outbound-rtp.bytesSent עבור קצב העברת הנתונים של שידור לכל RTP, candidate-pair.bytesSent עבור קצב העברת נתונים מועמד לכל ICE, או transport.bytesSent בשביל קצב העברת נתונים לכל תחבורה
.googRetransmitBitrate טווח השינוי של outbound-rtp.retransmittedBytesSent
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

ה-API הרגיל מבוסס על סימולטנות

אם אתם משתמשים ב-Simulcast, יכול להיות שתשימו לב שה-API הקודם מדווח רק על SSRC אחד, גם כשאתם משתמשים בסימולטנות כדי לשלוח (לדוגמה) שלושה זרמי RTP עם שלושה זרמי SSRC נפרדים.

ה-API הרגיל לא חולק את המגבלה הזו, והוא יחזיר שלושה אובייקטים של נתונים סטטיסטיים מסוג outbound-rtp, אחד לכל אחד מה-SSRC. המשמעות היא שאתם יכולים לנתח כל שידור RTP בנפרד, אבל המשמעות היא גם שכדי לקבל את קצב העברת הנתונים הכולל של כל שידורי RTP, תצטרכו לצבור אותם בעצמכם.

סטרימינג של SVC או שידורי RTP עם כמה שכבות מרחביות שהוגדרו באמצעות ה-API של scalabilityMode עדיין מופיעים כ-outbound-rtp יחיד כי הם נשלחים דרך SSRC יחיד.

אם נדרש לכם עוד זמן להעברה

כשמסירים את ה-API הקודם מ-Chrome 117, השימוש בו יגרום לחריגה. אם לא תוכלו להעביר את הקוד בזמן, גרסת המקור לניסיון של API getStats() של RTCPeerConnection מבוססת קריאה חוזרת מעניקה לאתרים רשומים זמן נוסף לביצוע ההעברה. עם אסימון מקור לניסיון, ניתן להמשיך להשתמש ב-API הקודם של getStats() עד Chrome 121.