'use strict'; // Test is based on the following editor draft: // webrtc-pc 20171130 // webrtc-stats 20171122 // This file depends on dictionary-helper.js which should // be loaded from the main HTML file. /* [webrtc-stats] 6.1. RTCStatsType enum enum RTCStatsType { "codec", "inbound-rtp", "outbound-rtp", "remote-inbound-rtp", "remote-outbound-rtp", "csrc", "peer-connection", "data-channel", "stream", "track", "transport", "candidate-pair", "local-candidate", "remote-candidate", "certificate", "ice-server" }; */ const statsValidatorTable = { 'codec': validateCodecStats, 'inbound-rtp': validateInboundRtpStreamStats, 'outbound-rtp': validateOutboundRtpStreamStats, 'remote-inbound-rtp': validateRemoteInboundRtpStreamStats, 'remote-outbound-rtp': validateRemoteOutboundRtpStreamStats, 'media-source': validateMediaSourceStats, 'csrc': validateContributingSourceStats, 'peer-connection': validatePeerConnectionStats, 'data-channel': validateDataChannelStats, 'transceiver': validateTransceiverStats, 'sender': validateSenderStats, 'receiver': validateReceiverStats, 'transport': validateTransportStats, 'candidate-pair': validateIceCandidatePairStats, 'local-candidate': validateIceCandidateStats, 'remote-candidate': validateIceCandidateStats, 'certificate': validateCertificateStats, 'ice-server': validateIceServerStats }; // Validate that the stats objects in a stats report // follows the respective definitions. // Stats objects with unknown type are ignored and // only basic validation is done. function validateStatsReport(statsReport) { for(const [id, stats] of statsReport.entries()) { assert_equals(stats.id, id, 'expect stats.id to be the same as the key in statsReport'); const validator = statsValidatorTable[stats.type]; if(validator) { validator(statsReport, stats); } else { validateRtcStats(statsReport, stats); } } } // Assert that the stats report have stats objects of // given types function assert_stats_report_has_stats(statsReport, statsTypes) { const hasTypes = new Set([...statsReport.values()] .map(stats => stats.type)); for(const type of statsTypes) { assert_true(hasTypes.has(type), `Expect statsReport to contain stats object of type ${type}`); } } function findStatsFromReport(statsReport, predicate, message) { for (const stats of statsReport.values()) { if (predicate(stats)) { return stats; } } assert_unreached(message || 'none of stats in statsReport satisfy given condition') } // Get stats object of type that is expected to be // found in the statsReport function getRequiredStats(statsReport, type) { for(const stats of statsReport.values()) { if(stats.type === type) { return stats; } } assert_unreached(`required stats of type ${type} is not found in stats report`); } // Get stats object by the stats ID. // This is used to retreive other stats objects // linked to a stats object function getStatsById(statsReport, statsId) { assert_true(statsReport.has(statsId), `Expect stats report to have stats object with id ${statsId}`); return statsReport.get(statsId); } // Validate an ID field in a stats object by making sure // that the linked stats object is found in the stats report // and have the type field value same as expected type // It doesn't validate the other fields of the linked stats // as validateStatsReport already does all validations function validateIdField(statsReport, stats, field, type) { assert_string_field(stats, field); const linkedStats = getStatsById(statsReport, stats[field]); assert_equals(linkedStats.type, type, `Expect linked stats object to have type ${type}`); } function validateOptionalIdField(statsReport, stats, field, type) { if(stats[field] !== undefined) { validateIdField(statsReport, stats, field, type); } } /* [webrtc-pc] 8.4. RTCStats Dictionary dictionary RTCStats { required DOMHighResTimeStamp timestamp; required RTCStatsType type; required DOMString id; }; */ function validateRtcStats(statsReport, stats) { assert_number_field(stats, 'timestamp'); assert_string_field(stats, 'type'); assert_string_field(stats, 'id'); } /* [webrtc-stats] 7.1. RTCRtpStreamStats dictionary dictionary RTCRtpStreamStats : RTCStats { unsigned long ssrc; DOMString kind; DOMString transportId; DOMString codecId; }; kind of type DOMString Either "audio" or "video". [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCRtpStreamStats, with attributes ssrc, kind, transportId, codecId */ function validateRtpStreamStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_unsigned_int_field(stats, 'ssrc'); assert_string_field(stats, 'kind'); assert_enum_field(stats, 'kind', ['audio', 'video']) validateIdField(statsReport, stats, 'transportId', 'transport'); validateIdField(statsReport, stats, 'codecId', 'codec'); } /* [webrtc-stats] 7.2. RTCCodecStats dictionary dictionary RTCCodecStats : RTCStats { required unsigned long payloadType; RTCCodecType codecType; required DOMString transportId; required DOMString mimeType; unsigned long clockRate; unsigned long channels; DOMString sdpFmtpLine; }; enum RTCCodecType { "encode", "decode", }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCCodecStats, with attributes payloadType, codecType, mimeType, clockRate, channels, sdpFmtpLine */ function validateCodecStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_unsigned_int_field(stats, 'payloadType'); assert_optional_enum_field(stats, 'codecType', ['encode', 'decode']); validateOptionalIdField(statsReport, stats, 'transportId', 'transport'); assert_string_field(stats, 'mimeType'); assert_unsigned_int_field(stats, 'clockRate'); if (stats.kind === 'audio') { assert_unsigned_int_field(stats, 'channels'); } assert_string_field(stats, 'sdpFmtpLine'); } /* [webrtc-stats] 7.3. RTCReceivedRtpStreamStats dictionary dictionary RTCReceivedRtpStreamStats : RTCRtpStreamStats { unsigned long long packetsReceived; long long packetsLost; double jitter; unsigned long long packetsDiscarded; unsigned long long packetsRepaired; unsigned long long burstPacketsLost; unsigned long long burstPacketsDiscarded; unsigned long burstLossCount; unsigned long burstDiscardCount; double burstLossRate; double burstDiscardRate; double gapLossRate; double gapDiscardRate; unsigned long framesDropped; unsigned long partialFramesLost; unsigned long fullFramesLost; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCReceivedRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes packetsReceived, packetsLost, jitter, packetsDiscarded, framesDropped */ function validateReceivedRtpStreamStats(statsReport, stats) { validateRtpStreamStats(statsReport, stats); assert_unsigned_int_field(stats, 'packetsReceived'); assert_unsigned_int_field(stats, 'packetsLost'); assert_number_field(stats, 'jitter'); assert_unsigned_int_field(stats, 'packetsDiscarded'); assert_unsigned_int_field(stats, 'framesDropped'); assert_optional_unsigned_int_field(stats, 'packetsRepaired'); assert_optional_unsigned_int_field(stats, 'burstPacketsLost'); assert_optional_unsigned_int_field(stats, 'burstPacketsDiscarded'); assert_optional_unsigned_int_field(stats, 'burstLossCount'); assert_optional_unsigned_int_field(stats, 'burstDiscardCount'); assert_optional_number_field(stats, 'burstLossRate'); assert_optional_number_field(stats, 'burstDiscardRate'); assert_optional_number_field(stats, 'gapLossRate'); assert_optional_number_field(stats, 'gapDiscardRate'); assert_optional_unsigned_int_field(stats, 'partialFramesLost'); assert_optional_unsigned_int_field(stats, 'fullFramesLost'); } /* [webrtc-stats] 7.4. RTCInboundRtpStreamStats dictionary dictionary RTCInboundRtpStreamStats : RTCReceivedRtpStreamStats { DOMString trackId; DOMString receiverId; DOMString remoteId; unsigned long framesDecoded; unsigned long keyFramesDecoded; unsigned long frameWidth; unsigned long frameHeight; unsigned long frameBitDepth; double framesPerSecond; unsigned long long qpSum; double totalDecodeTime; double totalInterFrameDelay; double totalSquaredInterFrameDelay; boolean voiceActivityFlag; DOMHighResTimeStamp lastPacketReceivedTimestamp; double averageRtcpInterval; unsigned long long headerBytesReceived; unsigned long long fecPacketsReceived; unsigned long long fecPacketsDiscarded; unsigned long long bytesReceived; unsigned long long packetsFailedDecryption; unsigned long long packetsDuplicated; record perDscpPacketsReceived; unsigned long nackCount; unsigned long firCount; unsigned long pliCount; unsigned long sliCount; DOMHighResTimeStamp estimatedPlayoutTimestamp; double jitterBufferDelay; unsigned long long jitterBufferEmittedCount; unsigned long long totalSamplesReceived; unsigned long long samplesDecodedWithSilk; unsigned long long samplesDecodedWithCelt; unsigned long long concealedSamples; unsigned long long silentConcealedSamples; unsigned long long concealmentEvents; unsigned long long insertedSamplesForDeceleration; unsigned long long removedSamplesForAcceleration; double audioLevel; double totalAudioEnergy; double totalSamplesDuration; unsigned long framesReceived; DOMString decoderImplementation; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCInboundRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes receiverId, remoteId, framesDecoded, nackCount, framesReceived, bytesReceived, totalAudioEnergy, totalSampleDuration */ function validateInboundRtpStreamStats(statsReport, stats) { validateReceivedRtpStreamStats(statsReport, stats); validateOptionalIdField(statsReport, stats, 'trackId', 'track'); validateIdField(statsReport, stats, 'receiverId', 'receiver'); validateIdField(statsReport, stats, 'remoteId', 'remote-outbound-rtp'); assert_unsigned_int_field(stats, 'framesDecoded'); assert_optional_unsigned_int_field(stats, 'keyFramesDecoded'); assert_optional_unsigned_int_field(stats, 'frameWidth'); assert_optional_unsigned_int_field(stats, 'frameHeight'); assert_optional_unsigned_int_field(stats, 'frameBitDepth'); assert_optional_number_field(stats, 'framesPerSecond'); assert_optional_unsigned_int_field(stats, 'qpSum'); assert_optional_number_field(stats, 'totalDecodeTime'); assert_optional_number_field(stats, 'totalInterFrameDelay'); assert_optional_number_field(stats, 'totalSquaredInterFrameDelay'); assert_optional_boolean_field(stats, 'voiceActivityFlag'); assert_optional_number_field(stats, 'lastPacketReceivedTimeStamp'); assert_optional_number_field(stats, 'averageRtcpInterval'); assert_optional_unsigned_int_field(stats, 'fecPacketsReceived'); assert_optional_unsigned_int_field(stats, 'fecPacketsDiscarded'); assert_unsigned_int_field(stats, 'bytesReceived'); assert_optional_unsigned_int_field(stats, 'packetsFailedDecryption'); assert_optional_unsigned_int_field(stats, 'packetsDuplicated'); assert_optional_dict_field(stats, 'perDscpPacketsReceived'); if (stats['perDscpPacketsReceived']) { Object.keys(stats['perDscpPacketsReceived']) .forEach(k => assert_equals(typeof k, 'string', 'Expect keys of perDscpPacketsReceived to be strings') ); Object.values(stats['perDscpPacketsReceived']) .forEach(v => assert_true(Number.isInteger(v) && (v >= 0), 'Expect values of perDscpPacketsReceived to be strings') ); } assert_unsigned_int_field(stats, 'nackCount'); assert_optional_unsigned_int_field(stats, 'firCount'); assert_optional_unsigned_int_field(stats, 'pliCount'); assert_optional_unsigned_int_field(stats, 'sliCount'); assert_optional_number_field(stats, 'estimatedPlayoutTimestamp'); assert_optional_number_field(stats, 'jitterBufferDelay'); assert_optional_unsigned_int_field(stats, 'jitterBufferEmittedCount'); assert_optional_unsigned_int_field(stats, 'totalSamplesReceived'); assert_optional_unsigned_int_field(stats, 'samplesDecodedWithSilk'); assert_optional_unsigned_int_field(stats, 'samplesDecodedWithCelt'); assert_optional_unsigned_int_field(stats, 'concealedSamples'); assert_optional_unsigned_int_field(stats, 'silentConcealedSamples'); assert_optional_unsigned_int_field(stats, 'concealmentEvents'); assert_optional_unsigned_int_field(stats, 'insertedSamplesForDeceleration'); assert_optional_unsigned_int_field(stats, 'removedSamplesForAcceleration'); assert_optional_number_field(stats, 'audioLevel'); assert_optional_number_field(stats, 'totalAudioEnergy'); assert_optional_number_field(stats, 'totalSamplesDuration'); assert_unsigned_int_field(stats, 'framesReceived'); assert_optional_string_field(stats, 'decoderImplementation'); assert_optional_boolean_field(stats, 'powerEfficientDecoder'); } /* [webrtc-stats] 7.5. RTCRemoteInboundRtpStreamStats dictionary dictionary RTCRemoteInboundRtpStreamStats : RTCReceivedRtpStreamStats { DOMString localId; double roundTripTime; double totalRoundTripTime; double fractionLost; unsigned long long reportsReceived; unsigned long long roundTripTimeMeasurements; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCRemoteInboundRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes localId, roundTripTime */ function validateRemoteInboundRtpStreamStats(statsReport, stats) { validateReceivedRtpStreamStats(statsReport, stats); validateIdField(statsReport, stats, 'localId', 'outbound-rtp'); assert_number_field(stats, 'roundTripTime'); assert_optional_number_field(stats, 'totalRoundTripTime'); assert_optional_number_field(stats, 'fractionLost'); assert_optional_unsigned_int_field(stats, 'reportsReceived'); assert_optional_unsigned_int_field(stats, 'roundTripTimeMeasurements'); } /* [webrtc-stats] 7.6. RTCSentRtpStreamStats dictionary dictionary RTCSentRtpStreamStats : RTCRtpStreamStats { unsigned long packetsSent; unsigned long long bytesSent; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCSentRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes packetsSent, bytesSent */ function validateSentRtpStreamStats(statsReport, stats) { validateRtpStreamStats(statsReport, stats); assert_unsigned_int_field(stats, 'packetsSent'); assert_unsigned_int_field(stats, 'bytesSent'); } /* [webrtc-stats] 7.7. RTCOutboundRtpStreamStats dictionary dictionary RTCOutboundRtpStreamStats : RTCSentRtpStreamStats { DOMString mediaSourceId; DOMString senderId; DOMString remoteId; DOMString rid; DOMHighResTimeStamp lastPacketSentTimestamp; unsigned long long headerBytesSent; unsigned long packetsDiscardedOnSend; unsigned long long bytesDiscardedOnSend; unsigned long fecPacketsSent; unsigned long long retransmittedPacketsSent; unsigned long long retransmittedBytesSent; double targetBitrate; unsigned long long totalEncodedBytesTarget; unsigned long frameWidth; unsigned long frameHeight; unsigned long frameBitDepth; double framesPerSecond; unsigned long framesSent; unsigned long hugeFramesSent; unsigned long framesEncoded; unsigned long keyFramesEncoded; unsigned long framesDiscardedOnSend; unsigned long long qpSum; unsigned long long totalSamplesSent; unsigned long long samplesEncodedWithSilk; unsigned long long samplesEncodedWithCelt; boolean voiceActivityFlag; double totalEncodeTime; double totalPacketSendDelay; double averageRtcpInterval; RTCQualityLimitationReason qualityLimitationReason; record qualityLimitationDurations; unsigned long qualityLimitationResolutionChanges; record perDscpPacketsSent; unsigned long nackCount; unsigned long firCount; unsigned long pliCount; unsigned long sliCount; DOMString encoderImplementation; }; Obsolete members: partial dictionary RTCOutboundStreamStats { DOMString trackId; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCOutboundRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes senderId, remoteId, framesEncoded, nackCount, framesSent */ function validateOutboundRtpStreamStats(statsReport, stats) { validateSentRtpStreamStats(statsReport, stats) validateOptionalIdField(statsReport, stats, 'mediaSourceId', 'media-source'); validateIdField(statsReport, stats, 'senderId', 'sender'); validateIdField(statsReport, stats, 'remoteId', 'remote-inbound-rtp'); assert_optional_string_field(stats, 'rid'); assert_optional_number_field(stats, 'lastPacketSentTimestamp'); assert_optional_unsigned_int_field(stats, 'headerBytesSent'); assert_optional_unsigned_int_field(stats, 'packetsDiscardedOnSend'); assert_optional_unsigned_int_field(stats, 'bytesDiscardedOnSend'); assert_optional_unsigned_int_field(stats, 'fecPacketsSent'); assert_optional_unsigned_int_field(stats, 'retransmittedPacketsSent'); assert_optional_unsigned_int_field(stats, 'retransmittedBytesSent'); assert_optional_number_field(stats, 'targetBitrate'); assert_optional_unsigned_int_field(stats, 'totalEncodedBytesTarget'); if (stats['kind'] === 'video') { assert_optional_unsigned_int_field(stats, 'frameWidth'); assert_optional_unsigned_int_field(stats, 'frameHeight'); assert_optional_unsigned_int_field(stats, 'frameBitDepth'); assert_optional_number_field(stats, 'framesPerSecond'); assert_unsigned_int_field(stats, 'framesSent'); assert_optional_unsigned_int_field(stats, 'hugeFramesSent'); assert_unsigned_int_field(stats, 'framesEncoded'); assert_optional_unsigned_int_field(stats, 'keyFramesEncoded'); assert_optional_unsigned_int_field(stats, 'framesDiscardedOnSend'); assert_optional_unsigned_int_field(stats, 'qpSum'); } else if (stats['kind'] === 'audio') { assert_optional_unsigned_int_field(stats, 'totalSamplesSent'); assert_optional_unsigned_int_field(stats, 'samplesEncodedWithSilk'); assert_optional_unsigned_int_field(stats, 'samplesEncodedWithCelt'); assert_optional_boolean_field(stats, 'voiceActivityFlag'); } assert_optional_number_field(stats, 'totalEncodeTime'); assert_optional_number_field(stats, 'totalPacketSendDelay'); assert_optional_number_field(stats, 'averageRTCPInterval'); if (stats['kind'] === 'video') { assert_optional_enum_field(stats, 'qualityLimitationReason', ['none', 'cpu', 'bandwidth', 'other']); assert_optional_dict_field(stats, 'qualityLimitationDurations'); if (stats['qualityLimitationDurations']) { Object.keys(stats['qualityLimitationDurations']) .forEach(k => assert_equals(typeof k, 'string', 'Expect keys of qualityLimitationDurations to be strings') ); Object.values(stats['qualityLimitationDurations']) .forEach(v => assert_equals(typeof num, 'number', 'Expect values of qualityLimitationDurations to be numbers') ); } assert_optional_unsigned_int_field(stats, 'qualityLimitationResolutionChanges'); } assert_unsigned_int_field(stats, 'nackCount'); assert_optional_dict_field(stats, 'perDscpPacketsSent'); if (stats['perDscpPacketsSent']) { Object.keys(stats['perDscpPacketsSent']) .forEach(k => assert_equals(typeof k, 'string', 'Expect keys of perDscpPacketsSent to be strings') ); Object.values(stats['perDscpPacketsSent']) .forEach(v => assert_true(Number.isInteger(v) && (v >= 0), 'Expect values of perDscpPacketsSent to be strings') ); } assert_optional_unsigned_int_field(stats, 'firCount'); assert_optional_unsigned_int_field(stats, 'pliCount'); assert_optional_unsigned_int_field(stats, 'sliCount'); assert_optional_string_field(stats, 'encoderImplementation'); assert_optional_boolean_field(stats, 'powerEfficientEncoder'); assert_optional_string_field(stats, 'scalabilityMode'); // Obsolete stats validateOptionalIdField(statsReport, stats, 'trackId', 'track'); } /* [webrtc-stats] 7.8. RTCRemoteOutboundRtpStreamStats dictionary dictionary RTCRemoteOutboundRtpStreamStats : RTCSentRtpStreamStats { DOMString localId; DOMHighResTimeStamp remoteTimestamp; unsigned long long reportsSent; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCRemoteOutboundRtpStreamStats, with all required attributes from its inherited dictionaries, and also attributes localId, remoteTimestamp */ function validateRemoteOutboundRtpStreamStats(statsReport, stats) { validateSentRtpStreamStats(statsReport, stats); validateIdField(statsReport, stats, 'localId', 'inbound-rtp'); assert_number_field(stats, 'remoteTimeStamp'); assert_optional_unsigned_int_field(stats, 'reportsSent'); } /* [webrtc-stats] 7.11 RTCMediaSourceStats dictionary dictionary RTCMediaSourceStats : RTCStats { DOMString trackIdentifier; DOMString kind; }; dictionary RTCAudioSourceStats : RTCMediaSourceStats { double audioLevel; double totalAudioEnergy; double totalSamplesDuration; double echoReturnLoss; double echoReturnLossEnhancement; }; dictionary RTCVideoSourceStats : RTCMediaSourceStats { unsigned long width; unsigned long height; unsigned long bitDepth; unsigned long frames; // see https://github.com/w3c/webrtc-stats/issues/540 double framesPerSecond; }; [webrtc-pc] 8.6. Mandatory To Implement Stats RTCMediaSourceStats with attributes trackIdentifier, kind RTCAudioSourceStats, with all required attributes from its inherited dictionaries and totalAudioEnergy, totalSamplesDuration RTCVideoSourceStats, with all required attributes from its inherited dictionaries and width, height, framesPerSecond */ function validateMediaSourceStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_string_field(stats, 'trackIdentifier'); assert_enum_field(stats, 'kind', ['audio', 'video']); if (stats.kind === 'audio') { assert_optional_number_field(stats, 'audioLevel'); assert_number_field(stats, 'totalAudioEnergy'); assert_number_field(stats, 'totalSamplesDuration'); assert_optional_number_field(stats, 'echoReturnLoss'); assert_optional_number_field(stats, 'echoReturnLossEnhancement'); } else if (stats.kind === 'video') { assert_unsigned_int_field(stats, 'width'); assert_unsigned_int_field(stats, 'height'); assert_optional_unsigned_int_field(stats, 'bitDpeth'); assert_optional_unsigned_int_field(stats, 'frames'); assert_number_field(stats, 'framesPerSecond'); } } /* [webrtc-stats] 7.9. RTCRTPContributingSourceStats dictionary RTCRTPContributingSourceStats : RTCStats { unsigned long contributorSsrc; DOMString inboundRtpStreamId; unsigned long packetsContributedTo; double audioLevel; }; */ function validateContributingSourceStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_optional_unsigned_int_field(stats, 'contributorSsrc'); validateOptionalIdField(statsReport, stats, 'inboundRtpStreamId', 'inbound-rtp'); assert_optional_unsigned_int_field(stats, 'packetsContributedTo'); assert_optional_number_field(stats, 'audioLevel'); } /* [webrtc-stats] 7.10. RTCPeerConnectionStats dictionary dictionary RTCPeerConnectionStats : RTCStats { unsigned long dataChannelsOpened; unsigned long dataChannelsClosed; unsigned long dataChannelsRequested; unsigned long dataChannelsAccepted; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCPeerConnectionStats, with attributes dataChannelsOpened, dataChannelsClosed */ function validatePeerConnectionStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_unsigned_int_field(stats, 'dataChannelsOpened'); assert_unsigned_int_field(stats, 'dataChannelsClosed'); assert_optional_unsigned_int_field(stats, 'dataChannelsRequested'); assert_optional_unsigned_int_field(stats, 'dataChannelsAccepted'); } /* [webrtc-stats] 7.16 RTCRtpTransceiverStats dictionary dictionary RTCRtpTransceiverStats { DOMString senderId; DOMString receiverId; DOMString mid; }; */ function validateTransceiverStats(statsReport, stats) { validateRtcStats(statsReport, stats); validateOptionalIdField(statsReport, stats, 'senderId', 'sender'); validateOptionalIdField(statsReport, stats, 'receiverId', 'sender'); assert_optional_string_field(stats, 'mid'); } /* [webrtc-stats] dictionary RTCMediaHandlerStats : RTCStats { DOMString trackIdentifier; boolean remoteSource; boolean ended; DOMString kind; RTCPriorityType priority; }; dictionary RTCVideoHandlerStats : RTCMediaHandlerStats { }; dictionary RTCAudioHandlerStats : RTCMediaHandlerStats { }; Used from validateSenderStats and validateReceiverStats [webrtc-priority] enum RTCPriorityType { "very-low", "low", "medium", "high" }; [webrtc-pc] MTI: RTCMediaHandlerStats with attributes trackIdentifier RTCAudioHandlerStats, with all required attributes from its inherited dictionaries RTCVideoHandlerStats, with all required attributes from its inherited dictionaries */ function validateMediaHandlerStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_string_field(stats, 'trackIdentifier'); assert_optional_boolean_field(stats, 'remoteSource'); assert_optional_boolean_field(stats, 'ended'); assert_optional_string_field(stats, 'kind'); assert_enum_field(stats, 'priority', ['very-low', 'low', 'medium', 'high']); } /* [webrtc-stats] dictionary RTCAudioSenderStats : RTCAudioHandlerStats { DOMString mediaSourceId; }; dictionary RTCVideoSenderStats : RTCVideoHandlerStats { DOMString mediaSourceId; }; [webrtc-pc] MTI: RTCVideoSenderStats, with all required attributes from its inherited dictionaries */ function validateSenderStats(statsReport, stats) { validateMediaHandlerStats(statsReport, stats); validateOptionalIdField(statsReport, stats, 'mediaSourceId', 'media-source'); } /* [webrtc-stats] dictionary RTCAudioReceiverStats : RTCAudioHandlerStats { }; dictionary RTCVideoReceiverStats : RTCVideoHandlerStats { }; [webrtc-pc] MTI: RTCVideoReceiverStats, with all required attributes from its inherited dictionaries */ function validateReceiverStats(statsReport, stats) { validateMediaHandlerStats(statsReport, stats); } /* [webrtc-stats] 7.13. RTCDataChannelStats dictionary dictionary RTCDataChannelStats : RTCStats { DOMString label; DOMString protocol; // see https://github.com/w3c/webrtc-stats/issues/541 unsigned short dataChannelIdentifier; DOMString transportId; RTCDataChannelState state; unsigned long messagesSent; unsigned long long bytesSent; unsigned long messagesReceived; unsigned long long bytesReceived; }; [webrtc-pc] 6.2. RTCDataChannel enum RTCDataChannelState { "connecting", "open", "closing", "closed" }; 8.6. Mandatory To Implement Stats - RTCDataChannelStats, with attributes label, protocol, datachannelIdentifier, state, messagesSent, bytesSent, messagesReceived, bytesReceived */ function validateDataChannelStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_string_field(stats, 'label'); assert_string_field(stats, 'protocol'); assert_unsigned_int_field(stats, 'dataChannelIdentifier'); validateOptionalIdField(statsReport, stats, 'transportId', 'transport'); assert_enum_field(stats, 'state', ['connecting', 'open', 'closing', 'closed']); assert_unsigned_int_field(stats, 'messagesSent'); assert_unsigned_int_field(stats, 'bytesSent'); assert_unsigned_int_field(stats, 'messagesReceived'); assert_unsigned_int_field(stats, 'bytesReceived'); } /* [webrtc-stats] 7.14. RTCTransportStats dictionary dictionary RTCTransportStats : RTCStats { unsigned long long packetsSent; unsigned long long packetsReceived; unsigned long long bytesSent; unsigned long long bytesReceived; DOMString rtcpTransportStatsId; RTCIceRole iceRole; RTCDtlsTransportState dtlsState; DOMString selectedCandidatePairId; DOMString localCertificateId; DOMString remoteCertificateId; DOMString tlsVersion; DOMString dtlsCipher; DOMString srtpCipher; DOMString tlsGroup; unsigned long selectedCandidatePairChanges; }; [webrtc-pc] 5.5. RTCDtlsTransportState Enum enum RTCDtlsTransportState { "new", "connecting", "connected", "closed", "failed" }; 5.6. RTCIceRole Enum enum RTCIceRole { "unknown", "controlling", "controlled" }; 8.6. Mandatory To Implement Stats - RTCTransportStats, with attributes bytesSent, bytesReceived, selectedCandidatePairId, localCertificateId, remoteCertificateId */ function validateTransportStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_optional_unsigned_int_field(stats, 'packetsSent'); assert_optional_unsigned_int_field(stats, 'packetsReceived'); assert_unsigned_int_field(stats, 'bytesSent'); assert_unsigned_int_field(stats, 'bytesReceived'); validateOptionalIdField(statsReport, stats, 'rtcpTransportStatsId', 'transport'); assert_optional_enum_field(stats, 'iceRole', ['unknown', 'controlling', 'controlled']); assert_optional_enum_field(stats, 'dtlsState', ['new', 'connecting', 'connected', 'closed', 'failed']); validateIdField(statsReport, stats, 'selectedCandidatePairId', 'candidate-pair'); validateIdField(statsReport, stats, 'localCertificateId', 'certificate'); validateIdField(statsReport, stats, 'remoteCertificateId', 'certificate'); assert_optional_string_field(stats, 'tlsVersion'); assert_optional_string_field(stats, 'dtlsCipher'); assert_optional_string_field(stats, 'srtpCipher'); assert_optional_string_field(stats, 'tlsGroup'); assert_optional_unsigned_int_field(stats, 'selectedCandidatePairChanges'); } /* [webrtc-stats] 7.15. RTCIceCandidateStats dictionary dictionary RTCIceCandidateStats : RTCStats { required DOMString transportId; DOMString? address; long port; DOMString protocol; RTCIceCandidateType candidateType; long priority; DOMString url; DOMString relayProtocol; }; [webrtc-pc] 4.8.1.3. RTCIceCandidateType Enum enum RTCIceCandidateType { "host", "srflx", "prflx", "relay" }; 8.6. Mandatory To Implement Stats - RTCIceCandidateStats, with attributes address, port, protocol, candidateType, url */ function validateIceCandidateStats(statsReport, stats) { validateRtcStats(statsReport, stats); validateIdField(statsReport, stats, 'transportId', 'transport'); // The address is mandatory to implement, but is allowed to be null // when hidden for privacy reasons. if (stats.address != null) { // Departure from strict spec reading: // This field is populated in a racy manner in Chrome. // We allow it to be present or not present for the time being. // TODO(https://bugs.chromium.org/1092721): Become consistent. assert_optional_string_field(stats, 'address'); } assert_unsigned_int_field(stats, 'port'); assert_string_field(stats, 'protocol'); assert_enum_field(stats, 'candidateType', ['host', 'srflx', 'prflx', 'relay']); assert_optional_int_field(stats, 'priority'); // The url field is mandatory for local candidates gathered from // a STUN or TURN server, and MUST NOT be present otherwise. // TODO(hta): Improve checking. assert_optional_string_field(stats, 'url'); assert_optional_string_field(stats, 'relayProtocol'); } /* [webrtc-stats] 7.16. RTCIceCandidatePairStats dictionary dictionary RTCIceCandidatePairStats : RTCStats { DOMString transportId; DOMString localCandidateId; DOMString remoteCandidateId; RTCStatsIceCandidatePairState state; boolean nominated; unsigned long packetsSent; unsigned long packetsReceived; unsigned long long bytesSent; unsigned long long bytesReceived; DOMHighResTimeStamp lastPacketSentTimestamp; DOMHighResTimeStamp lastPacketReceivedTimestamp; DOMHighResTimeStamp firstRequestTimestamp; DOMHighResTimeStamp lastRequestTimestamp; DOMHighResTimeStamp lastResponseTimestamp; double totalRoundTripTime; double currentRoundTripTime; double availableOutgoingBitrate; double availableIncomingBitrate; unsigned long circuitBreakerTriggerCount; unsigned long long requestsReceived; unsigned long long requestsSent; unsigned long long responsesReceived; unsigned long long responsesSent; unsigned long long retransmissionsReceived; unsigned long long retransmissionsSent; unsigned long long consentRequestsSent; DOMHighResTimeStamp consentExpiredTimestamp; unsigned long packetsDiscardedOnSend; unsigned long long bytesDiscardedOnSend; }; enum RTCStatsIceCandidatePairState { "frozen", "waiting", "in-progress", "failed", "succeeded" }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCIceCandidatePairStats, with attributes transportId, localCandidateId, remoteCandidateId, state, nominated, bytesSent, bytesReceived, totalRoundTripTime, currentRoundTripTime // not including priority per https://github.com/w3c/webrtc-pc/issues/2457 */ function validateIceCandidatePairStats(statsReport, stats) { validateRtcStats(statsReport, stats); validateIdField(statsReport, stats, 'transportId', 'transport'); validateIdField(statsReport, stats, 'localCandidateId', 'local-candidate'); validateIdField(statsReport, stats, 'remoteCandidateId', 'remote-candidate'); assert_enum_field(stats, 'state', ['frozen', 'waiting', 'in-progress', 'failed', 'succeeded']); assert_boolean_field(stats, 'nominated'); assert_optional_unsigned_int_field(stats, 'packetsSent'); assert_optional_unsigned_int_field(stats, 'packetsReceived'); assert_unsigned_int_field(stats, 'bytesSent'); assert_unsigned_int_field(stats, 'bytesReceived'); assert_optional_number_field(stats, 'lastPacketSentTimestamp'); assert_optional_number_field(stats, 'lastPacketReceivedTimestamp'); assert_optional_number_field(stats, 'firstRequestTimestamp'); assert_optional_number_field(stats, 'lastRequestTimestamp'); assert_optional_number_field(stats, 'lastResponseTimestamp'); assert_number_field(stats, 'totalRoundTripTime'); assert_number_field(stats, 'currentRoundTripTime'); assert_optional_number_field(stats, 'availableOutgoingBitrate'); assert_optional_number_field(stats, 'availableIncomingBitrate'); assert_optional_unsigned_int_field(stats, 'circuitBreakerTriggerCount'); assert_optional_unsigned_int_field(stats, 'requestsReceived'); assert_optional_unsigned_int_field(stats, 'requestsSent'); assert_optional_unsigned_int_field(stats, 'responsesReceived'); assert_optional_unsigned_int_field(stats, 'responsesSent'); assert_optional_unsigned_int_field(stats, 'retransmissionsReceived'); assert_optional_unsigned_int_field(stats, 'retransmissionsSent'); assert_optional_unsigned_int_field(stats, 'consentRequestsSent'); assert_optional_number_field(stats, 'consentExpiredTimestamp'); assert_optional_unsigned_int_field(stats, 'packetsDiscardedOnSend'); assert_optional_unsigned_int_field(stats, 'bytesDiscardedOnSend'); } /* [webrtc-stats] 7.17. RTCCertificateStats dictionary dictionary RTCCertificateStats : RTCStats { DOMString fingerprint; DOMString fingerprintAlgorithm; DOMString base64Certificate; DOMString issuerCertificateId; }; [webrtc-pc] 8.6. Mandatory To Implement Stats - RTCCertificateStats, with attributes fingerprint, fingerprintAlgorithm, base64Certificate, issuerCertificateId */ function validateCertificateStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_string_field(stats, 'fingerprint'); assert_string_field(stats, 'fingerprintAlgorithm'); assert_string_field(stats, 'base64Certificate'); assert_optional_string_field(stats, 'issuerCertificateId'); } /* [webrtc-stats] 7.30. RTCIceServerStats dictionary dictionary RTCIceServerStats : RTCStats { DOMString url; long port; DOMString protocol; unsigned long totalRequestsSent; unsigned long totalResponsesReceived; double totalRoundTripTime; }; */ function validateIceServerStats(statsReport, stats) { validateRtcStats(statsReport, stats); assert_optional_string_field(stats, 'url'); assert_optional_int_field(stats, 'port'); assert_optional_string_field(stats, 'protocol'); assert_optional_unsigned_int_field(stats, 'totalRequestsSent'); assert_optional_unsigned_int_field(stats, 'totalResponsesReceived'); assert_optional_number_field(stats, 'totalRoundTripTime'); }