From 891d96fe0e5e86df51f6586040d9e3e5eac9d748 Mon Sep 17 00:00:00 2001 From: Artem Tarasiuk Date: Thu, 3 Oct 2024 19:38:20 +0300 Subject: [PATCH] Support more data from webrtc stats --- icetransportstate.go | 56 +++++++- stats.go | 284 ++++++++++++++++++++++++++++++++++++- stats_go_test.go | 326 ++++++++++++++++++++++++++++--------------- 3 files changed, 546 insertions(+), 120 deletions(-) diff --git a/icetransportstate.go b/icetransportstate.go index f62495e1cac..645c5c7746a 100644 --- a/icetransportstate.go +++ b/icetransportstate.go @@ -49,22 +49,53 @@ const ( ICETransportStateClosed ) +const ( + iceTransportStateNewStr = "new" + iceTransportStateCheckingStr = "checking" + iceTransportStateConnectedStr = "connected" + iceTransportStateCompletedStr = "completed" + iceTransportStateFailedStr = "failed" + iceTransportStateDisconnectedStr = "disconnected" + iceTransportStateClosedStr = "closed" +) + +func newICETransportState(raw string) ICETransportState { + switch raw { + case iceTransportStateNewStr: + return ICETransportStateNew + case iceTransportStateCheckingStr: + return ICETransportStateChecking + case iceTransportStateConnectedStr: + return ICETransportStateConnected + case iceTransportStateCompletedStr: + return ICETransportStateCompleted + case iceTransportStateFailedStr: + return ICETransportStateFailed + case iceTransportStateDisconnectedStr: + return ICETransportStateDisconnected + case iceTransportStateClosedStr: + return ICETransportStateClosed + default: + return ICETransportStateUnknown + } +} + func (c ICETransportState) String() string { switch c { case ICETransportStateNew: - return "new" + return iceTransportStateNewStr case ICETransportStateChecking: - return "checking" + return iceTransportStateCheckingStr case ICETransportStateConnected: - return "connected" + return iceTransportStateConnectedStr case ICETransportStateCompleted: - return "completed" + return iceTransportStateCompletedStr case ICETransportStateFailed: - return "failed" + return iceTransportStateFailedStr case ICETransportStateDisconnected: - return "disconnected" + return iceTransportStateDisconnectedStr case ICETransportStateClosed: - return "closed" + return iceTransportStateClosedStr default: return ErrUnknownType.Error() } @@ -111,3 +142,14 @@ func (c ICETransportState) toICE() ice.ConnectionState { return ice.ConnectionStateUnknown } } + +// MarshalText implements encoding.TextMarshaler +func (c ICETransportState) MarshalText() ([]byte, error) { + return []byte(c.String()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler +func (c *ICETransportState) UnmarshalText(b []byte) error { + *c = newICETransportState(string(b)) + return nil +} diff --git a/stats.go b/stats.go index 93c51fe141d..abb0b2ed9ee 100644 --- a/stats.go +++ b/stats.go @@ -275,6 +275,10 @@ func unmarshalCodecStats(b []byte) (CodecStats, error) { // InboundRTPStreamStats contains statistics for an inbound RTP stream that is // currently received with this PeerConnection object. type InboundRTPStreamStats struct { + // Mid represents a mid value of RTPTransceiver owning this stream, if that value is not + // null. Otherwise, this member is not present. + Mid string `json:"mid"` + // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` @@ -309,10 +313,92 @@ type InboundRTPStreamStats struct { // received by the sender. This metric is only valid for video and is sent by receiver. PLICount uint32 `json:"pliCount"` + // TotalProcessingDelay is the sum of the time, in seconds, each audio sample or video frame + // takes from the time the first RTP packet is received (reception timestamp) and to the time + // the corresponding sample or frame is decoded (decoded timestamp). At this point the audio + // sample or video frame is ready for playout by the MediaStreamTrack. Typically ready for + // playout here means after the audio sample or video frame is fully decoded by the decoder. + TotalProcessingDelay float64 `json:"totalProcessingDelay"` + // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets // received by the sender and is sent by receiver. NACKCount uint32 `json:"nackCount"` + // JitterBufferDelay is the sum of the time, in seconds, each audio sample or a video frame + // takes from the time the first packet is received by the jitter buffer (ingest timestamp) + // to the time it exits the jitter buffer (emit timestamp). The average jitter buffer delay + // can be calculated by dividing the JitterBufferDelay with the JitterBufferEmittedCount. + JitterBufferDelay float64 `json:"jitterBufferDelay"` + + // JitterBufferTargetDelay is increased by the target jitter buffer delay every time a sample is emitted + // by the jitter buffer. The added target is the target delay, in seconds, at the time that + // the sample was emitted from the jitter buffer. To get the average target delay, + // divide by JitterBufferEmittedCount + JitterBufferTargetDelay float64 `json:"jitterBufferTargetDelay"` + + // JitterBufferEmittedCount is the total number of audio samples or video frames that + // have come out of the jitter buffer (increasing jitterBufferDelay). + JitterBufferEmittedCount uint64 `json:"jitterBufferEmittedCount"` + + // JitterBufferMinimumDelay works the same way as jitterBufferTargetDelay, except that + // it is not affected by external mechanisms that increase the jitter buffer target delay, + // such as jitterBufferTarget, AV sync, or any other mechanisms. This metric is purely + // based on the network characteristics such as jitter and packet loss, and can be seen + // as the minimum obtainable jitter buffer delay if no external factors would affect it. + // The metric is updated every time JitterBufferEmittedCount is updated. + JitterBufferMinimumDelay float64 `json:"jitterBufferMinimumDelay"` + + // TotalSamplesReceived is the total number of samples that have been received on + // this RTP stream. This includes concealedSamples. Does not exist for video. + TotalSamplesReceived uint64 `json:"totalSamplesReceived"` + + // ConcealedSamples is the total number of samples that are concealed samples. + // A concealed sample is a sample that was replaced with synthesized samples generated + // locally before being played out. Examples of samples that have to be concealed are + // samples from lost packets (reported in packetsLost) or samples from packets that + // arrive too late to be played out (reported in packetsDiscarded). Does not exist for video. + ConcealedSamples uint64 `json:"concealedSamples"` + + // SilentConcealedSamples is the total number of concealed samples inserted that + // are "silent". Playing out silent samples results in silence or comfort noise. + // This is a subset of concealedSamples. Does not exist for video. + SilentConcealedSamples uint64 `json:"silentConcealedSamples"` + + // ConcealmentEvents increases every time a concealed sample is synthesized after + // a non-concealed sample. That is, multiple consecutive concealed samples will increase + // the concealedSamples count multiple times but is a single concealment event. + // Does not exist for video. + ConcealmentEvents uint64 `json:"concealmentEvents"` + + // InsertedSamplesForDeceleration is increased by the difference between the number of + // samples received and the number of samples played out when playout is slowed down. + // If playout is slowed down by inserting samples, this will be the number of inserted samples. + // Does not exist for video. + InsertedSamplesForDeceleration uint64 `json:"insertedSamplesForDeceleration"` + + // RemovedSamplesForAcceleration is increased by the difference between the number of + // samples received and the number of samples played out when playout is sped up. If speedup + // is achieved by removing samples, this will be the count of samples removed. + // Does not exist for video. + RemovedSamplesForAcceleration uint64 `json:"removedSamplesForAcceleration"` + + // AudioLevel represents the audio level of the receiving track.. + // + // The value is a value between 0..1 (linear), where 1.0 represents 0 dBov, + // 0 represents silence, and 0.5 represents approximately 6 dBSPL change in + // the sound pressure level from 0 dBov. Does not exist for video. + AudioLevel float64 `json:"audioLevel"` + + // TotalAudioEnergy represents the audio energy of the receiving track. It is calculated + // by duration * Math.pow(energy/maxEnergy, 2) for each audio sample received (and thus + // counted by TotalSamplesReceived). Does not exist for video. + TotalAudioEnergy float64 `json:"totalAudioEnergy"` + + // TotalSamplesDuration represents the total duration in seconds of all samples that have been + // received (and thus counted by TotalSamplesReceived). Can be used with totalAudioEnergy to + // compute an average audio level over different intervals. Does not exist for video. + TotalSamplesDuration float64 `json:"totalSamplesDuration"` + // SLICount counts the total number of Slice Loss Indication (SLI) packets received // by the sender. This metric is only valid for video and is sent by receiver. SLICount uint32 `json:"sliCount"` @@ -321,6 +407,25 @@ type InboundRTPStreamStats struct { // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats. QPSum uint64 `json:"qpSum"` + // TotalDecodeTime is the total number of seconds that have been spent decoding the FramesDecoded + // frames of this stream. The average decode time can be calculated by dividing this value + // with FramesDecoded. The time it takes to decode one frame is the time passed between + // feeding the decoder a frame and the decoder returning decoded data for that frame. + TotalDecodeTime float64 `json:"totalDecodeTime"` + + // TotalInterFrameDelay is the sum of the interframe delays in seconds between consecutively + // rendered frames, recorded just after a frame has been rendered. The interframe delay variance + // be calculated from TotalInterFrameDelay, TotalSquaredInterFrameDelay, and FramesRendered according + // to the formula: (TotalSquaredInterFrameDelay - TotalInterFrameDelay^2 / FramesRendered) / FramesRendered. + // Does not exist for audio. + TotalInterFrameDelay float64 `json:"totalInterFrameDelay"` + + // TotalSquaredInterFrameDelay is the sum of the squared interframe delays in seconds + // between consecutively rendered frames, recorded just after a frame has been rendered. + // See TotalInterFrameDelay for details on how to calculate the interframe delay variance. + // Does not exist for audio. + TotalSquaredInterFrameDelay float64 `json:"totalSquaredInterFrameDelay"` + // PacketsReceived is the total number of RTP packets received for this SSRC. PacketsReceived uint32 `json:"packetsReceived"` @@ -383,11 +488,41 @@ type InboundRTPStreamStats struct { // i.e., frames that would be displayed if no frames are dropped. Only valid for video. FramesDecoded uint32 `json:"framesDecoded"` + // KeyFramesDecoded represents the total number of key frames, such as key frames in + // VP8 [RFC6386] or IDR-frames in H.264 [RFC6184], successfully decoded for this RTP + // media stream. This is a subset of FramesDecoded. FramesDecoded - KeyFramesDecoded + // gives you the number of delta frames decoded. Does not exist for audio. + KeyFramesDecoded uint32 `json:"keyFramesDecoded"` + + // FramesRendered represents the total number of frames that have been rendered. + // It is incremented just after a frame has been rendered. Does not exist for audio. + FramesRendered uint32 `json:"framesRendered"` + + // FramesDropped is the total number of frames dropped prior to decode or dropped + // because the frame missed its display deadline for this receiver's track. + // The measurement begins when the receiver is created and is a cumulative metric + // as defined in Appendix A (g) of [RFC7004]. Does not exist for audio. + FramesDropped uint32 `json:"framesDropped"` + + // FrameWidth represents the width of the last decoded frame. Before the first + // frame is decoded this member does not exist. Does not exist for audio. + FrameWidth uint32 `json:"frameWidth"` + + // FrameHeight represents the height of the last decoded frame. Before the first + // frame is decoded this member does not exist. Does not exist for audio. + FrameHeight uint32 `json:"frameHeight"` + // LastPacketReceivedTimestamp represents the timestamp at which the last packet was // received for this SSRC. This differs from Timestamp, which represents the time // at which the statistics were generated by the local endpoint. LastPacketReceivedTimestamp StatsTimestamp `json:"lastPacketReceivedTimestamp"` + // HeaderBytesReceived is the total number of RTP header and padding bytes received for this SSRC. + // This includes retransmissions. This does not include the size of transport layer headers such + // as IP or UDP. headerBytesReceived + bytesReceived equals the number of bytes received as + // payload over the transport. + HeaderBytesReceived uint64 `json:"headerBytesReceived"` + // AverageRTCPInterval is the average RTCP interval between two consecutive compound RTCP packets. // This is calculated by the sending endpoint when sending compound RTCP reports. // Compound packets must contain at least a RTCP RR or SR packet and an SDES packet @@ -398,9 +533,22 @@ type InboundRTPStreamStats struct { // This counter can also be incremented when receiving FEC packets in-band with media packets (e.g., with Opus). FECPacketsReceived uint32 `json:"fecPacketsReceived"` + // FECPacketsDiscarded is the total number of RTP FEC packets received for this SSRC where the + // error correction payload was discarded by the application. This may happen + // 1. if all the source packets protected by the FEC packet were received or already + // recovered by a separate FEC packet, or + // 2. if the FEC packet arrived late, i.e., outside the recovery window, and the + // lost RTP packets have already been skipped during playout. + // This is a subset of FECPacketsReceived. + FECPacketsDiscarded uint64 `json:"fecPacketsDiscarded"` + // BytesReceived is the total number of bytes received for this SSRC. BytesReceived uint64 `json:"bytesReceived"` + // FramesReceived represents the total number of complete frames received on this RTP stream. + // This metric is incremented when the complete frame is received. Does not exist for audio. + FramesReceived uint32 `json:"framesReceived"` + // PacketsFailedDecryption is the cumulative number of RTP packets that failed // to be decrypted. These packets are not counted by PacketsDiscarded. PacketsFailedDecryption uint32 `json:"packetsFailedDecryption"` @@ -453,6 +601,18 @@ const ( // OutboundRTPStreamStats contains statistics for an outbound RTP stream that is // currently sent with this PeerConnection object. type OutboundRTPStreamStats struct { + // Mid represents a mid value of RTPTransceiver owning this stream, if that value is not + // null. Otherwise, this member is not present. + Mid string `json:"mid"` + + // Rid only exists if a rid has been set for this RTP stream. + // Must not exist for audio. + Rid string `json:"rid"` + + // MediaSourceID is the identifier of the stats object representing the track currently + // attached to the sender of this stream, an RTCMediaSourceStats. + MediaSourceID string `json:"mediaSourceId"` + // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` @@ -479,6 +639,23 @@ type OutboundRTPStreamStats struct { // to produce the CodecStats associated with this RTP stream. CodecID string `json:"codecId"` + // HeaderBytesSent is the total number of RTP header and padding bytes sent for this SSRC. This does not + // include the size of transport layer headers such as IP or UDP. + // HeaderBytesSent + BytesSent equals the number of bytes sent as payload over the transport. + HeaderBytesSent uint64 `json:"headerBytesSent"` + + // RetransmittedPacketsSent is the total number of packets that were retransmitted for this SSRC. + // This is a subset of packetsSent. If RTX is not negotiated, retransmitted packets are sent + // over this ssrc. If RTX was negotiated, retransmitted packets are sent over a separate SSRC + // but is still accounted for here. + RetransmittedPacketsSent uint64 `json:"retransmittedPacketsSent"` + + // RetransmittedBytesSent is the total number of bytes that were retransmitted for this SSRC, + // only including payload bytes. This is a subset of bytesSent. If RTX is not negotiated, + // retransmitted bytes are sent over this ssrc. If RTX was negotiated, retransmitted bytes + // are sent over a separate SSRC but is still accounted for here. + RetransmittedBytesSent uint64 `json:"retransmittedBytesSent"` + // FIRCount counts the total number of Full Intra Request (FIR) packets received // by the sender. This metric is only valid for video and is sent by receiver. FIRCount uint32 `json:"firCount"` @@ -547,10 +724,47 @@ type OutboundRTPStreamStats struct { // It is measured in bits per second and the bitrate is calculated over a 1 second window. TargetBitrate float64 `json:"targetBitrate"` + // TotalEncodedBytesTarget is increased by the target frame size in bytes every time + // a frame has been encoded. The actual frame size may be bigger or smaller than this number. + // This value goes up every time framesEncoded goes up. + TotalEncodedBytesTarget uint64 `json:"totalEncodedBytesTarget"` + + // FrameWidth represents the width of the last encoded frame. The resolution of the + // encoded frame may be lower than the media source. Before the first frame is encoded + // this member does not exist. Does not exist for audio. + FrameWidth uint32 `json:"frameWidth"` + + // FrameHeight represents the height of the last encoded frame. The resolution of the + // encoded frame may be lower than the media source. Before the first frame is encoded + // this member does not exist. Does not exist for audio. + FrameHeight uint32 `json:"frameHeight"` + + // FramesPerSecond is the number of encoded frames during the last second. This may be + // lower than the media source frame rate. Does not exist for audio. + FramesPerSecond float64 `json:"framesPerSecond"` + + // FramesSent represents the total number of frames sent on this RTP stream. Does not exist for audio. + FramesSent uint32 `json:"framesSent"` + + // HugeFramesSent represents the total number of huge frames sent by this RTP stream. + // Huge frames, by definition, are frames that have an encoded size at least 2.5 times + // the average size of the frames. The average size of the frames is defined as the + // target bitrate per second divided by the target FPS at the time the frame was encoded. + // These are usually complex to encode frames with a lot of changes in the picture. + // This can be used to estimate, e.g slide changes in the streamed presentation. + // Does not exist for audio. + HugeFramesSent uint32 `json:"hugeFramesSent"` + // FramesEncoded represents the total number of frames successfully encoded for this RTP media stream. // Only valid for video. FramesEncoded uint32 `json:"framesEncoded"` + // KeyFramesEncoded represents the total number of key frames, such as key frames in VP8 [RFC6386] or + // IDR-frames in H.264 [RFC6184], successfully encoded for this RTP media stream. This is a subset of + // FramesEncoded. FramesEncoded - KeyFramesEncoded gives you the number of delta frames encoded. + // Does not exist for audio. + KeyFramesEncoded uint32 `json:"keyFramesEncoded"` + // TotalEncodeTime is the total number of seconds that has been spent encoding the // framesEncoded frames of this stream. The average encode time can be calculated by // dividing this value with FramesEncoded. The time it takes to encode one frame is the @@ -558,6 +772,12 @@ type OutboundRTPStreamStats struct { // for that frame. This does not include any additional time it may take to packetize the resulting data. TotalEncodeTime float64 `json:"totalEncodeTime"` + // TotalPacketSendDelay is the total number of seconds that packets have spent buffered + // locally before being transmitted onto the network. The time is measured from when + // a packet is emitted from the RTP packetizer until it is handed over to the OS network socket. + // This measurement is added to totalPacketSendDelay when packetsSent is incremented. + TotalPacketSendDelay float64 `json:"totalPacketSendDelay"` + // AverageRTCPInterval is the average RTCP interval between two consecutive compound RTCP // packets. This is calculated by the sending endpoint when sending compound RTCP reports. // Compound packets must contain at least a RTCP RR or SR packet and an SDES packet with the CNAME item. @@ -572,9 +792,20 @@ type OutboundRTPStreamStats struct { // for all QualityLimitationReason types, including "none". Only valid for video. QualityLimitationDurations map[string]float64 `json:"qualityLimitationDurations"` + // QualityLimitationResolutionChanges is the number of times that the resolution has changed + // because we are quality limited (qualityLimitationReason has a value other than "none"). + // The counter is initially zero and increases when the resolution goes up or down. + // For example, if a 720p track is sent as 480p for some time and then recovers to 720p, + // qualityLimitationResolutionChanges will have the value 2. Does not exist for audio. + QualityLimitationResolutionChanges uint32 `json:"qualityLimitationResolutionChanges"` + // PerDSCPPacketsSent is the total number of packets sent for this SSRC, per DSCP. // DSCPs are identified as decimal integers in string form. PerDSCPPacketsSent map[string]uint32 `json:"perDscpPacketsSent"` + + // Active indicates whether this RTP stream is configured to be sent or disabled. Note that an + // active stream can still not be sending, e.g. when being limited by network conditions. + Active bool `json:"active"` } func (s OutboundRTPStreamStats) statsMarker() {} @@ -692,8 +923,20 @@ type RemoteInboundRTPStreamStats struct { // RTCP timestamps in the RTCP Receiver Report (RR) and measured in seconds. RoundTripTime float64 `json:"roundTripTime"` + // TotalRoundTripTime represents the cumulative sum of all round trip time measurements + // in seconds since the beginning of the session. The individual round trip time is calculated + // based on the RTCP timestamps in the RTCP Receiver Report (RR) [RFC3550], hence requires + // a DLSR value other than 0. The average round trip time can be computed from + // TotalRoundTripTime by dividing it by RoundTripTimeMeasurements. + TotalRoundTripTime float64 `json:"totalRoundTripTime"` + // FractionLost is the fraction packet loss reported for this SSRC. FractionLost float64 `json:"fractionLost"` + + // RoundTripTimeMeasurements represents the total number of RTCP RR blocks received for this SSRC + // that contain a valid round trip time. This counter will not increment if the RoundTripTime can + // not be calculated because no RTCP Receiver Report with a DLSR value other than 0 has been received. + RoundTripTimeMeasurements uint64 `json:"roundTripTimeMeasurements"` } func (s RemoteInboundRTPStreamStats) statsMarker() {} @@ -791,6 +1034,28 @@ type RemoteOutboundRTPStreamStats struct { // Sender Report (SR) packet, which reflects the remote endpoint's clock. // That clock may not be synchronized with the local clock. RemoteTimestamp StatsTimestamp `json:"remoteTimestamp"` + + // ReportsSent represents the total number of RTCP Sender Report (SR) blocks sent for this SSRC. + ReportsSent uint64 `json:"reportsSent"` + + // RoundTripTime is estimated round trip time for this SSRC based on the latest + // RTCP Sender Report (SR) that contains a DLRR report block as defined in [RFC3611]. + // The Calculation of the round trip time is defined in section 4.5. of [RFC3611]. + // Does not exist if the latest SR does not contain the DLRR report block, or if the last RR timestamp + // in the DLRR report block is zero, or if the delay since last RR value in the DLRR report block is zero. + RoundTripTime float64 `json:"roundTripTime"` + + // TotalRoundTripTime represents the cumulative sum of all round trip time measurements in seconds + // since the beginning of the session. The individual round trip time is calculated based on the DLRR + // report block in the RTCP Sender Report (SR) [RFC3611]. This counter will not increment if the + // RoundTripTime can not be calculated. The average round trip time can be computed from + // TotalRoundTripTime by dividing it by RoundTripTimeMeasurements. + TotalRoundTripTime float64 `json:"totalRoundTripTime"` + + // RoundTripTimeMeasurements represents the total number of RTCP Sender Report (SR) blocks + // received for this SSRC that contain a DLRR report block that can derive a valid round trip time + // according to [RFC3611]. This counter will not increment if the RoundTripTime can not be calculated. + RoundTripTimeMeasurements uint64 `json:"roundTripTimeMeasurements"` } func (s RemoteOutboundRTPStreamStats) statsMarker() {} @@ -1636,12 +1901,16 @@ type TransportStats struct { RTCPTransportStatsID string `json:"rtcpTransportStatsId"` // ICERole is set to the current value of the "role" attribute of the underlying - // DTLSTransport's "transport". + // DTLSTransport's "iceTransport". ICERole ICERole `json:"iceRole"` // DTLSState is set to the current value of the "state" attribute of the underlying DTLSTransport. DTLSState DTLSTransportState `json:"dtlsState"` + // ICEState is set to the current value of the "state" attribute of the underlying + // RTCIceTransport's "state". + ICEState ICETransportState `json:"iceState"` + // SelectedCandidatePairID is a unique identifier that is associated to the object // that was inspected to produce the ICECandidatePairStats associated with this transport. SelectedCandidatePairID string `json:"selectedCandidatePairId"` @@ -1857,6 +2126,19 @@ type ICECandidatePairStats struct { // ConsentExpiredTimestamp represents the timestamp at which the latest valid // STUN binding response expired. ConsentExpiredTimestamp StatsTimestamp `json:"consentExpiredTimestamp"` + + // PacketsDiscardedOnSend retpresents the total number of packets for this candidate pair + // that have been discarded due to socket errors, i.e. a socket error occurred + // when handing the packets to the socket. This might happen due to various reasons, + // including full buffer or no available memory. + PacketsDiscardedOnSend uint32 `json:"packetsDiscardedOnSend"` + + // BytesDiscardedOnSend represents the total number of bytes for this candidate pair + // that have been discarded due to socket errors, i.e. a socket error occurred + // when handing the packets containing the bytes to the socket. This might happen due + // to various reasons, including full buffer or no available memory. + // Calculated as defined in [RFC3550] section 6.4.1. + BytesDiscardedOnSend uint32 `json:"bytesDiscardedOnSend"` } func (s ICECandidatePairStats) statsMarker() {} diff --git a/stats_go_test.go b/stats_go_test.go index d4529230bd3..9ebbce30812 100644 --- a/stats_go_test.go +++ b/stats_go_test.go @@ -80,47 +80,74 @@ func getStatsSamples() []statSample { } ` inboundRTPStreamStats := InboundRTPStreamStats{ - Timestamp: 1688978831527.718, - ID: "IT01A2184088143", - Type: StatsTypeInboundRTP, - SSRC: 2184088143, - Kind: "audio", - TransportID: "T01", - CodecID: "CIT01_111_minptime=10;useinbandfec=1", - FIRCount: 1, - PLICount: 2, - NACKCount: 3, - SLICount: 4, - QPSum: 5, - PacketsReceived: 6, - PacketsLost: 7, - Jitter: 8, - PacketsDiscarded: 9, - PacketsRepaired: 10, - BurstPacketsLost: 11, - BurstPacketsDiscarded: 12, - BurstLossCount: 13, - BurstDiscardCount: 14, - BurstLossRate: 15, - BurstDiscardRate: 16, - GapLossRate: 17, - GapDiscardRate: 18, - TrackID: "d57dbc4b-484b-4b40-9088-d3150e3a2010", - ReceiverID: "R01", - RemoteID: "ROA2184088143", - FramesDecoded: 17, - LastPacketReceivedTimestamp: 1689668364374.181, - AverageRTCPInterval: 18, - FECPacketsReceived: 19, - BytesReceived: 20, - PacketsFailedDecryption: 21, - PacketsDuplicated: 22, + Mid: "1", + Timestamp: 1688978831527.718, + ID: "IT01A2184088143", + Type: StatsTypeInboundRTP, + SSRC: 2184088143, + Kind: "audio", + TransportID: "T01", + CodecID: "CIT01_111_minptime=10;useinbandfec=1", + FIRCount: 1, + PLICount: 2, + TotalProcessingDelay: 23, + NACKCount: 3, + JitterBufferDelay: 24, + JitterBufferTargetDelay: 25, + JitterBufferEmittedCount: 26, + JitterBufferMinimumDelay: 27, + TotalSamplesReceived: 28, + ConcealedSamples: 29, + SilentConcealedSamples: 30, + ConcealmentEvents: 31, + InsertedSamplesForDeceleration: 32, + RemovedSamplesForAcceleration: 33, + AudioLevel: 34, + TotalAudioEnergy: 35, + TotalSamplesDuration: 36, + SLICount: 4, + QPSum: 5, + TotalDecodeTime: 37, + TotalInterFrameDelay: 38, + TotalSquaredInterFrameDelay: 39, + PacketsReceived: 6, + PacketsLost: 7, + Jitter: 8, + PacketsDiscarded: 9, + PacketsRepaired: 10, + BurstPacketsLost: 11, + BurstPacketsDiscarded: 12, + BurstLossCount: 13, + BurstDiscardCount: 14, + BurstLossRate: 15, + BurstDiscardRate: 16, + GapLossRate: 17, + GapDiscardRate: 18, + TrackID: "d57dbc4b-484b-4b40-9088-d3150e3a2010", + ReceiverID: "R01", + RemoteID: "ROA2184088143", + FramesDecoded: 17, + KeyFramesDecoded: 40, + FramesRendered: 41, + FramesDropped: 42, + FrameWidth: 43, + FrameHeight: 44, + LastPacketReceivedTimestamp: 1689668364374.181, + HeaderBytesReceived: 45, + AverageRTCPInterval: 18, + FECPacketsReceived: 19, + FECPacketsDiscarded: 46, + BytesReceived: 20, + FramesReceived: 47, + PacketsFailedDecryption: 21, + PacketsDuplicated: 22, PerDSCPPacketsReceived: map[string]uint32{ "123": 23, }, } inboundRTPStreamStatsJSON := ` { + "mid": "1", "timestamp": 1688978831527.718, "id": "IT01A2184088143", "type": "inbound-rtp", @@ -130,9 +157,26 @@ func getStatsSamples() []statSample { "codecId": "CIT01_111_minptime=10;useinbandfec=1", "firCount": 1, "pliCount": 2, + "totalProcessingDelay": 23, "nackCount": 3, + "jitterBufferDelay": 24, + "jitterBufferTargetDelay": 25, + "jitterBufferEmittedCount": 26, + "jitterBufferMinimumDelay": 27, + "totalSamplesReceived": 28, + "concealedSamples": 29, + "silentConcealedSamples": 30, + "concealmentEvents": 31, + "insertedSamplesForDeceleration": 32, + "removedSamplesForAcceleration": 33, + "audioLevel": 34, + "totalAudioEnergy": 35, + "totalSamplesDuration": 36, "sliCount": 4, "qpSum": 5, + "totalDecodeTime": 37, + "totalInterFrameDelay": 38, + "totalSquaredInterFrameDelay": 39, "packetsReceived": 6, "packetsLost": 7, "jitter": 8, @@ -150,10 +194,18 @@ func getStatsSamples() []statSample { "receiverId": "R01", "remoteId": "ROA2184088143", "framesDecoded": 17, + "keyFramesDecoded": 40, + "framesRendered": 41, + "framesDropped": 42, + "frameWidth": 43, + "frameHeight": 44, "lastPacketReceivedTimestamp": 1689668364374.181, + "headerBytesReceived": 45, "averageRtcpInterval": 18, "fecPacketsReceived": 19, + "fecPacketsDiscarded": 46, "bytesReceived": 20, + "framesReceived": 47, "packetsFailedDecryption": 21, "packetsDuplicated": 22, "perDscpPacketsReceived": { @@ -162,44 +214,63 @@ func getStatsSamples() []statSample { } ` outboundRTPStreamStats := OutboundRTPStreamStats{ - Timestamp: 1688978831527.718, - Type: StatsTypeOutboundRTP, - ID: "OT01A2184088143", - SSRC: 2184088143, - Kind: "audio", - TransportID: "T01", - CodecID: "COT01_111_minptime=10;useinbandfec=1", - FIRCount: 1, - PLICount: 2, - NACKCount: 3, - SLICount: 4, - QPSum: 5, - PacketsSent: 6, - PacketsDiscardedOnSend: 7, - FECPacketsSent: 8, - BytesSent: 9, - BytesDiscardedOnSend: 10, - TrackID: "d57dbc4b-484b-4b40-9088-d3150e3a2010", - SenderID: "S01", - RemoteID: "ROA2184088143", - LastPacketSentTimestamp: 11, - TargetBitrate: 12, - FramesEncoded: 13, - TotalEncodeTime: 14, - AverageRTCPInterval: 15, - QualityLimitationReason: "cpu", + Mid: "1", + Rid: "hi", + MediaSourceID: "SA5", + Timestamp: 1688978831527.718, + Type: StatsTypeOutboundRTP, + ID: "OT01A2184088143", + SSRC: 2184088143, + Kind: "audio", + TransportID: "T01", + CodecID: "COT01_111_minptime=10;useinbandfec=1", + HeaderBytesSent: 24, + RetransmittedPacketsSent: 25, + RetransmittedBytesSent: 26, + FIRCount: 1, + PLICount: 2, + NACKCount: 3, + SLICount: 4, + QPSum: 5, + PacketsSent: 6, + PacketsDiscardedOnSend: 7, + FECPacketsSent: 8, + BytesSent: 9, + BytesDiscardedOnSend: 10, + TrackID: "d57dbc4b-484b-4b40-9088-d3150e3a2010", + SenderID: "S01", + RemoteID: "ROA2184088143", + LastPacketSentTimestamp: 11, + TargetBitrate: 12, + TotalEncodedBytesTarget: 27, + FrameWidth: 28, + FrameHeight: 29, + FramesPerSecond: 30, + FramesSent: 31, + HugeFramesSent: 32, + FramesEncoded: 13, + KeyFramesEncoded: 33, + TotalEncodeTime: 14, + TotalPacketSendDelay: 34, + AverageRTCPInterval: 15, + QualityLimitationReason: "cpu", QualityLimitationDurations: map[string]float64{ "none": 16, "cpu": 17, "bandwidth": 18, "other": 19, }, + QualityLimitationResolutionChanges: 35, PerDSCPPacketsSent: map[string]uint32{ "123": 23, }, + Active: true, } outboundRTPStreamStatsJSON := ` { + "mid": "1", + "rid": "hi", + "mediaSourceId": "SA5", "timestamp": 1688978831527.718, "type": "outbound-rtp", "id": "OT01A2184088143", @@ -207,6 +278,9 @@ func getStatsSamples() []statSample { "kind": "audio", "transportId": "T01", "codecId": "COT01_111_minptime=10;useinbandfec=1", + "headerBytesSent": 24, + "retransmittedPacketsSent": 25, + "retransmittedBytesSent": 26, "firCount": 1, "pliCount": 2, "nackCount": 3, @@ -222,8 +296,16 @@ func getStatsSamples() []statSample { "remoteId": "ROA2184088143", "lastPacketSentTimestamp": 11, "targetBitrate": 12, + "totalEncodedBytesTarget": 27, + "frameWidth": 28, + "frameHeight": 29, + "framesPerSecond": 30, + "framesSent": 31, + "hugeFramesSent": 32, "framesEncoded": 13, + "keyFramesEncoded": 33, "totalEncodeTime": 14, + "totalPacketSendDelay": 34, "averageRtcpInterval": 15, "qualityLimitationReason": "cpu", "qualityLimitationDurations": { @@ -232,40 +314,44 @@ func getStatsSamples() []statSample { "bandwidth": 18, "other": 19 }, + "qualityLimitationResolutionChanges": 35, "perDscpPacketsSent": { "123": 23 - } + }, + "active": true } ` remoteInboundRTPStreamStats := RemoteInboundRTPStreamStats{ - Timestamp: 1688978831527.718, - Type: StatsTypeRemoteInboundRTP, - ID: "RIA2184088143", - SSRC: 2184088143, - Kind: "audio", - TransportID: "T01", - CodecID: "COT01_111_minptime=10;useinbandfec=1", - FIRCount: 1, - PLICount: 2, - NACKCount: 3, - SLICount: 4, - QPSum: 5, - PacketsReceived: 6, - PacketsLost: 7, - Jitter: 8, - PacketsDiscarded: 9, - PacketsRepaired: 10, - BurstPacketsLost: 11, - BurstPacketsDiscarded: 12, - BurstLossCount: 13, - BurstDiscardCount: 14, - BurstLossRate: 15, - BurstDiscardRate: 16, - GapLossRate: 17, - GapDiscardRate: 18, - LocalID: "RIA2184088143", - RoundTripTime: 19, - FractionLost: 20, + Timestamp: 1688978831527.718, + Type: StatsTypeRemoteInboundRTP, + ID: "RIA2184088143", + SSRC: 2184088143, + Kind: "audio", + TransportID: "T01", + CodecID: "COT01_111_minptime=10;useinbandfec=1", + FIRCount: 1, + PLICount: 2, + NACKCount: 3, + SLICount: 4, + QPSum: 5, + PacketsReceived: 6, + PacketsLost: 7, + Jitter: 8, + PacketsDiscarded: 9, + PacketsRepaired: 10, + BurstPacketsLost: 11, + BurstPacketsDiscarded: 12, + BurstLossCount: 13, + BurstDiscardCount: 14, + BurstLossRate: 15, + BurstDiscardRate: 16, + GapLossRate: 17, + GapDiscardRate: 18, + LocalID: "RIA2184088143", + RoundTripTime: 19, + TotalRoundTripTime: 21, + FractionLost: 20, + RoundTripTimeMeasurements: 22, } remoteInboundRTPStreamStatsJSON := ` { @@ -296,29 +382,35 @@ func getStatsSamples() []statSample { "gapDiscardRate": 18, "localId": "RIA2184088143", "roundTripTime": 19, - "fractionLost": 20 + "totalRoundTripTime": 21, + "fractionLost": 20, + "roundTripTimeMeasurements": 22 } ` remoteOutboundRTPStreamStats := RemoteOutboundRTPStreamStats{ - Timestamp: 1688978831527.718, - Type: StatsTypeRemoteOutboundRTP, - ID: "ROA2184088143", - SSRC: 2184088143, - Kind: "audio", - TransportID: "T01", - CodecID: "CIT01_111_minptime=10;useinbandfec=1", - FIRCount: 1, - PLICount: 2, - NACKCount: 3, - SLICount: 4, - QPSum: 5, - PacketsSent: 1259, - PacketsDiscardedOnSend: 6, - FECPacketsSent: 7, - BytesSent: 92654, - BytesDiscardedOnSend: 8, - LocalID: "IT01A2184088143", - RemoteTimestamp: 1689668361298, + Timestamp: 1688978831527.718, + Type: StatsTypeRemoteOutboundRTP, + ID: "ROA2184088143", + SSRC: 2184088143, + Kind: "audio", + TransportID: "T01", + CodecID: "CIT01_111_minptime=10;useinbandfec=1", + FIRCount: 1, + PLICount: 2, + NACKCount: 3, + SLICount: 4, + QPSum: 5, + PacketsSent: 1259, + PacketsDiscardedOnSend: 6, + FECPacketsSent: 7, + BytesSent: 92654, + BytesDiscardedOnSend: 8, + LocalID: "IT01A2184088143", + RemoteTimestamp: 1689668361298, + ReportsSent: 9, + RoundTripTime: 10, + TotalRoundTripTime: 11, + RoundTripTimeMeasurements: 12, } remoteOutboundRTPStreamStatsJSON := ` { @@ -340,7 +432,11 @@ func getStatsSamples() []statSample { "bytesSent": 92654, "bytesDiscardedOnSend": 8, "localId": "IT01A2184088143", - "remoteTimestamp": 1689668361298 + "remoteTimestamp": 1689668361298, + "reportsSent": 9, + "roundTripTime": 10, + "totalRoundTripTime": 11, + "roundTripTimeMeasurements": 12 } ` csrcStats := RTPContributingSourceStats{ @@ -708,6 +804,7 @@ func getStatsSamples() []statSample { RTCPTransportStatsID: "T01", ICERole: ICERoleControlling, DTLSState: DTLSTransportStateConnected, + ICEState: ICETransportStateConnected, SelectedCandidatePairID: "CPxIhBDNnT_sPDhy1TB", LocalCertificateID: "CFF4:4F:C4:C7:F3:31:6C:B9:D5:AD:19:64:05:9F:2F:E9:00:70:56:1E:BA:92:29:3A:08:CE:1B:27:CF:2D:AB:24", RemoteCertificateID: "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", @@ -726,6 +823,7 @@ func getStatsSamples() []statSample { "rtcpTransportStatsId": "T01", "iceRole": "controlling", "dtlsState": "connected", + "iceState": "connected", "selectedCandidatePairId": "CPxIhBDNnT_sPDhy1TB", "localCertificateId": "CFF4:4F:C4:C7:F3:31:6C:B9:D5:AD:19:64:05:9F:2F:E9:00:70:56:1E:BA:92:29:3A:08:CE:1B:27:CF:2D:AB:24", "remoteCertificateId": "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", @@ -764,6 +862,8 @@ func getStatsSamples() []statSample { RetransmissionsSent: 20, ConsentRequestsSent: 21, ConsentExpiredTimestamp: 22, + PacketsDiscardedOnSend: 23, + BytesDiscardedOnSend: 24, } iceCandidatePairStatsJSON := ` { @@ -796,7 +896,9 @@ func getStatsSamples() []statSample { "retransmissionsReceived": 19, "retransmissionsSent": 20, "consentRequestsSent": 21, - "consentExpiredTimestamp": 22 + "consentExpiredTimestamp": 22, + "packetsDiscardedOnSend": 23, + "bytesDiscardedOnSend": 24 } ` localIceCandidateStats := ICECandidateStats{