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

Henrik Boström
Henrik Boström

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

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

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

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

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

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

לאובייקטים של הנתונים הסטטיסטיים יש מאפיין מזהה שמזהה באופן ייחודי את האובייקט הבסיסי בקריאות מרובות של getStats(). לאותו אובייקט יהיה אותו מזהה בכל פעם שמפעילים את השיטה. אפשרות זו שימושית לחישוב שיעור השינוי של מדדים (ראו דוגמה בקטע הבא). המזהים גם יוצרים קשרים של קובצי עזר. לדוגמה, אובייקט הנתונים הסטטיסטיים 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 Recordingr (RR) או בדוח RTCP Extended (XR).
  • remote-outbound-rtp: זרם RTP יוצא של נקודת קצה מרוחקת, שתואם לזרם RTP נכנס שנקודת הקצה הזו מקבלת (inbound-rtp). הוא נמדד בנקודת הקצה המרוחקת ומדווח בדוח RTCP Sender (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.framesDecodedinbound-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.fecPacketsReceivedinbound-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, השימוש בו יוצר חריג. אם אתם לא מצליחים להעביר את הקוד בזמן, גרסת המקור לניסיון של RTCPeerConnection API מבוסס קריאה חוזרת (callback) שמבוססת על getStats() תיתן לאתרים רשומים יותר זמן להעברה. עם אסימון מקור לניסיון, ניתן להמשיך להשתמש ב-getStats() API מהדור הקודם עד Chrome 121.