// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT package webrtc import ( "encoding/json" "fmt" "sync" "time" "github.com/pion/ice/v2" ) // A Stats object contains a set of statistics copies out of a monitored component // of the WebRTC stack at a specific time. type Stats interface { statsMarker() } // UnmarshalStatsJSON unmarshals a Stats object from JSON func UnmarshalStatsJSON(b []byte) (Stats, error) { type typeJSON struct { Type StatsType `json:"type"` } typeHolder := typeJSON{} err := json.Unmarshal(b, &typeHolder) if err != nil { return nil, fmt.Errorf("unmarshal json type: %w", err) } switch typeHolder.Type { case StatsTypeCodec: return unmarshalCodecStats(b) case StatsTypeInboundRTP: return unmarshalInboundRTPStreamStats(b) case StatsTypeOutboundRTP: return unmarshalOutboundRTPStreamStats(b) case StatsTypeRemoteInboundRTP: return unmarshalRemoteInboundRTPStreamStats(b) case StatsTypeRemoteOutboundRTP: return unmarshalRemoteOutboundRTPStreamStats(b) case StatsTypeCSRC: return unmarshalCSRCStats(b) case StatsTypeMediaSource: return unmarshalMediaSourceStats(b) case StatsTypeMediaPlayout: return unmarshalMediaPlayoutStats(b) case StatsTypePeerConnection: return unmarshalPeerConnectionStats(b) case StatsTypeDataChannel: return unmarshalDataChannelStats(b) case StatsTypeStream: return unmarshalStreamStats(b) case StatsTypeTrack: return unmarshalTrackStats(b) case StatsTypeSender: return unmarshalSenderStats(b) case StatsTypeReceiver: return unmarshalReceiverStats(b) case StatsTypeTransport: return unmarshalTransportStats(b) case StatsTypeCandidatePair: return unmarshalICECandidatePairStats(b) case StatsTypeLocalCandidate, StatsTypeRemoteCandidate: return unmarshalICECandidateStats(b) case StatsTypeCertificate: return unmarshalCertificateStats(b) case StatsTypeSCTPTransport: return unmarshalSCTPTransportStats(b) default: return nil, fmt.Errorf("type: %w", ErrUnknownType) } } // StatsType indicates the type of the object that a Stats object represents. type StatsType string const ( // StatsTypeCodec is used by CodecStats. StatsTypeCodec StatsType = "codec" // StatsTypeInboundRTP is used by InboundRTPStreamStats. StatsTypeInboundRTP StatsType = "inbound-rtp" // StatsTypeOutboundRTP is used by OutboundRTPStreamStats. StatsTypeOutboundRTP StatsType = "outbound-rtp" // StatsTypeRemoteInboundRTP is used by RemoteInboundRTPStreamStats. StatsTypeRemoteInboundRTP StatsType = "remote-inbound-rtp" // StatsTypeRemoteOutboundRTP is used by RemoteOutboundRTPStreamStats. StatsTypeRemoteOutboundRTP StatsType = "remote-outbound-rtp" // StatsTypeCSRC is used by RTPContributingSourceStats. StatsTypeCSRC StatsType = "csrc" // StatsTypeMediaSource is used by AudioSourceStats or VideoSourceStats depending on kind. StatsTypeMediaSource = "media-source" // StatsTypeMediaPlayout is used by AudioPlayoutStats. StatsTypeMediaPlayout StatsType = "media-playout" // StatsTypePeerConnection used by PeerConnectionStats. StatsTypePeerConnection StatsType = "peer-connection" // StatsTypeDataChannel is used by DataChannelStats. StatsTypeDataChannel StatsType = "data-channel" // StatsTypeStream is used by MediaStreamStats. StatsTypeStream StatsType = "stream" // StatsTypeTrack is used by SenderVideoTrackAttachmentStats and SenderAudioTrackAttachmentStats depending on kind. StatsTypeTrack StatsType = "track" // StatsTypeSender is used by the AudioSenderStats or VideoSenderStats depending on kind. StatsTypeSender StatsType = "sender" // StatsTypeReceiver is used by the AudioReceiverStats or VideoReceiverStats depending on kind. StatsTypeReceiver StatsType = "receiver" // StatsTypeTransport is used by TransportStats. StatsTypeTransport StatsType = "transport" // StatsTypeCandidatePair is used by ICECandidatePairStats. StatsTypeCandidatePair StatsType = "candidate-pair" // StatsTypeLocalCandidate is used by ICECandidateStats for the local candidate. StatsTypeLocalCandidate StatsType = "local-candidate" // StatsTypeRemoteCandidate is used by ICECandidateStats for the remote candidate. StatsTypeRemoteCandidate StatsType = "remote-candidate" // StatsTypeCertificate is used by CertificateStats. StatsTypeCertificate StatsType = "certificate" // StatsTypeSCTPTransport is used by SCTPTransportStats StatsTypeSCTPTransport StatsType = "sctp-transport" ) // MediaKind indicates the kind of media (audio or video) type MediaKind string const ( // MediaKindAudio indicates this is audio stats MediaKindAudio MediaKind = "audio" // MediaKindVideo indicates this is video stats MediaKindVideo MediaKind = "video" ) // StatsTimestamp is a timestamp represented by the floating point number of // milliseconds since the epoch. type StatsTimestamp float64 // Time returns the time.Time represented by this timestamp. func (s StatsTimestamp) Time() time.Time { millis := float64(s) nanos := int64(millis * float64(time.Millisecond)) return time.Unix(0, nanos).UTC() } func statsTimestampFrom(t time.Time) StatsTimestamp { return StatsTimestamp(t.UnixNano() / int64(time.Millisecond)) } func statsTimestampNow() StatsTimestamp { return statsTimestampFrom(time.Now()) } // StatsReport collects Stats objects indexed by their ID. type StatsReport map[string]Stats type statsReportCollector struct { collectingGroup sync.WaitGroup report StatsReport mux sync.Mutex } func newStatsReportCollector() *statsReportCollector { return &statsReportCollector{report: make(StatsReport)} } func (src *statsReportCollector) Collecting() { src.collectingGroup.Add(1) } func (src *statsReportCollector) Collect(id string, stats Stats) { src.mux.Lock() defer src.mux.Unlock() src.report[id] = stats src.collectingGroup.Done() } func (src *statsReportCollector) Done() { src.collectingGroup.Done() } func (src *statsReportCollector) Ready() StatsReport { src.collectingGroup.Wait() src.mux.Lock() defer src.mux.Unlock() return src.report } // CodecType specifies whether a CodecStats objects represents a media format // that is being encoded or decoded type CodecType string const ( // CodecTypeEncode means the attached CodecStats represents a media format that // is being encoded, or that the implementation is prepared to encode. CodecTypeEncode CodecType = "encode" // CodecTypeDecode means the attached CodecStats represents a media format // that the implementation is prepared to decode. CodecTypeDecode CodecType = "decode" ) // CodecStats contains statistics for a codec that is currently being used by RTP streams // being sent or received by this PeerConnection object. type CodecStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // PayloadType as used in RTP encoding or decoding PayloadType PayloadType `json:"payloadType"` // CodecType of this CodecStats CodecType CodecType `json:"codecType"` // TransportID is the unique identifier of the transport on which this codec is // being used, which can be used to look up the corresponding TransportStats object. TransportID string `json:"transportId"` // MimeType is the codec MIME media type/subtype. e.g., video/vp8 or equivalent. MimeType string `json:"mimeType"` // ClockRate represents the media sampling rate. ClockRate uint32 `json:"clockRate"` // Channels is 2 for stereo, missing for most other cases. Channels uint8 `json:"channels"` // SDPFmtpLine is the a=fmtp line in the SDP corresponding to the codec, // i.e., after the colon following the PT. SDPFmtpLine string `json:"sdpFmtpLine"` // Implementation identifies the implementation used. This is useful for diagnosing // interoperability issues. Implementation string `json:"implementation"` } func (s CodecStats) statsMarker() {} func unmarshalCodecStats(b []byte) (CodecStats, error) { var codecStats CodecStats err := json.Unmarshal(b, &codecStats) if err != nil { return CodecStats{}, fmt.Errorf("unmarshal codec stats: %w", err) } return codecStats, nil } // InboundRTPStreamStats contains statistics for an inbound RTP stream that is // currently received with this PeerConnection object. type InboundRTPStreamStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // SSRC is the 32-bit unsigned integer value used to identify the source of the // stream of RTP packets that this stats object concerns. SSRC SSRC `json:"ssrc"` // Kind is either "audio" or "video" Kind string `json:"kind"` // It is a unique identifier that is associated to the object that was inspected // to produce the TransportStats associated with this RTP stream. TransportID string `json:"transportId"` // CodecID is a unique identifier that is associated to the object that was inspected // to produce the CodecStats associated with this RTP stream. CodecID string `json:"codecId"` // 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"` // PLICount counts the total number of Picture Loss Indication (PLI) packets // received by the sender. This metric is only valid for video and is sent by receiver. PLICount uint32 `json:"pliCount"` // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets // received by the sender and is sent by receiver. NACKCount uint32 `json:"nackCount"` // 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"` // QPSum is the sum of the QP values of frames passed. The count of frames is // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats. QPSum uint64 `json:"qpSum"` // PacketsReceived is the total number of RTP packets received for this SSRC. PacketsReceived uint32 `json:"packetsReceived"` // PacketsLost is the total number of RTP packets lost for this SSRC. Note that // because of how this is estimated, it can be negative if more packets are received than sent. PacketsLost int32 `json:"packetsLost"` // Jitter is the packet jitter measured in seconds for this SSRC Jitter float64 `json:"jitter"` // PacketsDiscarded is the cumulative number of RTP packets discarded by the jitter // buffer due to late or early-arrival, i.e., these packets are not played out. // RTP packets discarded due to packet duplication are not reported in this metric. PacketsDiscarded uint32 `json:"packetsDiscarded"` // PacketsRepaired is the cumulative number of lost RTP packets repaired after applying // an error-resilience mechanism. It is measured for the primary source RTP packets // and only counted for RTP packets that have no further chance of repair. PacketsRepaired uint32 `json:"packetsRepaired"` // BurstPacketsLost is the cumulative number of RTP packets lost during loss bursts. BurstPacketsLost uint32 `json:"burstPacketsLost"` // BurstPacketsDiscarded is the cumulative number of RTP packets discarded during discard bursts. BurstPacketsDiscarded uint32 `json:"burstPacketsDiscarded"` // BurstLossCount is the cumulative number of bursts of lost RTP packets. BurstLossCount uint32 `json:"burstLossCount"` // BurstDiscardCount is the cumulative number of bursts of discarded RTP packets. BurstDiscardCount uint32 `json:"burstDiscardCount"` // BurstLossRate is the fraction of RTP packets lost during bursts to the // total number of RTP packets expected in the bursts. BurstLossRate float64 `json:"burstLossRate"` // BurstDiscardRate is the fraction of RTP packets discarded during bursts to // the total number of RTP packets expected in bursts. BurstDiscardRate float64 `json:"burstDiscardRate"` // GapLossRate is the fraction of RTP packets lost during the gap periods. GapLossRate float64 `json:"gapLossRate"` // GapDiscardRate is the fraction of RTP packets discarded during the gap periods. GapDiscardRate float64 `json:"gapDiscardRate"` // TrackID is the identifier of the stats object representing the receiving track, // a ReceiverAudioTrackAttachmentStats or ReceiverVideoTrackAttachmentStats. TrackID string `json:"trackId"` // ReceiverID is the stats ID used to look up the AudioReceiverStats or VideoReceiverStats // object receiving this stream. ReceiverID string `json:"receiverId"` // RemoteID is used for looking up the remote RemoteOutboundRTPStreamStats object // for the same SSRC. RemoteID string `json:"remoteId"` // FramesDecoded represents the total number of frames correctly decoded for this SSRC, // i.e., frames that would be displayed if no frames are dropped. Only valid for video. FramesDecoded uint32 `json:"framesDecoded"` // 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"` // 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. AverageRTCPInterval float64 `json:"averageRtcpInterval"` // FECPacketsReceived is the total number of RTP FEC packets received for this SSRC. // This counter can also be incremented when receiving FEC packets in-band with media packets (e.g., with Opus). FECPacketsReceived uint32 `json:"fecPacketsReceived"` // BytesReceived is the total number of bytes received for this SSRC. BytesReceived uint64 `json:"bytesReceived"` // PacketsFailedDecryption is the cumulative number of RTP packets that failed // to be decrypted. These packets are not counted by PacketsDiscarded. PacketsFailedDecryption uint32 `json:"packetsFailedDecryption"` // PacketsDuplicated is the cumulative number of packets discarded because they // are duplicated. Duplicate packets are not counted in PacketsDiscarded. // // Duplicated packets have the same RTP sequence number and content as a previously // received packet. If multiple duplicates of a packet are received, all of them are counted. // An improved estimate of lost packets can be calculated by adding PacketsDuplicated to PacketsLost. PacketsDuplicated uint32 `json:"packetsDuplicated"` // PerDSCPPacketsReceived is the total number of packets received for this SSRC, // per Differentiated Services code point (DSCP) [RFC2474]. DSCPs are identified // as decimal integers in string form. Note that due to network remapping and bleaching, // these numbers are not expected to match the numbers seen on sending. Not all // OSes make this information available. PerDSCPPacketsReceived map[string]uint32 `json:"perDscpPacketsReceived"` } func (s InboundRTPStreamStats) statsMarker() {} func unmarshalInboundRTPStreamStats(b []byte) (InboundRTPStreamStats, error) { var inboundRTPStreamStats InboundRTPStreamStats err := json.Unmarshal(b, &inboundRTPStreamStats) if err != nil { return InboundRTPStreamStats{}, fmt.Errorf("unmarshal inbound rtp stream stats: %w", err) } return inboundRTPStreamStats, nil } // QualityLimitationReason lists the reason for limiting the resolution and/or framerate. // Only valid for video. type QualityLimitationReason string const ( // QualityLimitationReasonNone means the resolution and/or framerate is not limited. QualityLimitationReasonNone QualityLimitationReason = "none" // QualityLimitationReasonCPU means the resolution and/or framerate is primarily limited due to CPU load. QualityLimitationReasonCPU QualityLimitationReason = "cpu" // QualityLimitationReasonBandwidth means the resolution and/or framerate is primarily limited due to congestion cues during bandwidth estimation. Typical, congestion control algorithms use inter-arrival time, round-trip time, packet or other congestion cues to perform bandwidth estimation. QualityLimitationReasonBandwidth QualityLimitationReason = "bandwidth" // QualityLimitationReasonOther means the resolution and/or framerate is primarily limited for a reason other than the above. QualityLimitationReasonOther QualityLimitationReason = "other" ) // OutboundRTPStreamStats contains statistics for an outbound RTP stream that is // currently sent with this PeerConnection object. type OutboundRTPStreamStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // SSRC is the 32-bit unsigned integer value used to identify the source of the // stream of RTP packets that this stats object concerns. SSRC SSRC `json:"ssrc"` // Kind is either "audio" or "video" Kind string `json:"kind"` // It is a unique identifier that is associated to the object that was inspected // to produce the TransportStats associated with this RTP stream. TransportID string `json:"transportId"` // CodecID is a unique identifier that is associated to the object that was inspected // to produce the CodecStats associated with this RTP stream. CodecID string `json:"codecId"` // 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"` // PLICount counts the total number of Picture Loss Indication (PLI) packets // received by the sender. This metric is only valid for video and is sent by receiver. PLICount uint32 `json:"pliCount"` // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets // received by the sender and is sent by receiver. NACKCount uint32 `json:"nackCount"` // 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"` // QPSum is the sum of the QP values of frames passed. The count of frames is // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats. QPSum uint64 `json:"qpSum"` // PacketsSent is the total number of RTP packets sent for this SSRC. PacketsSent uint32 `json:"packetsSent"` // PacketsDiscardedOnSend is the total number of RTP packets for this SSRC 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"` // FECPacketsSent is the total number of RTP FEC packets sent for this SSRC. // This counter can also be incremented when sending FEC packets in-band with // media packets (e.g., with Opus). FECPacketsSent uint32 `json:"fecPacketsSent"` // BytesSent is the total number of bytes sent for this SSRC. BytesSent uint64 `json:"bytesSent"` // BytesDiscardedOnSend is the total number of bytes for this SSRC 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. BytesDiscardedOnSend uint64 `json:"bytesDiscardedOnSend"` // TrackID is the identifier of the stats object representing the current track // attachment to the sender of this stream, a SenderAudioTrackAttachmentStats // or SenderVideoTrackAttachmentStats. TrackID string `json:"trackId"` // SenderID is the stats ID used to look up the AudioSenderStats or VideoSenderStats // object sending this stream. SenderID string `json:"senderId"` // RemoteID is used for looking up the remote RemoteInboundRTPStreamStats object // for the same SSRC. RemoteID string `json:"remoteId"` // LastPacketSentTimestamp represents the timestamp at which the last packet was // sent for this SSRC. This differs from timestamp, which represents the time at // which the statistics were generated by the local endpoint. LastPacketSentTimestamp StatsTimestamp `json:"lastPacketSentTimestamp"` // TargetBitrate is the current target bitrate configured for this particular SSRC // and is the Transport Independent Application Specific (TIAS) bitrate [RFC3890]. // Typically, the target bitrate is a configuration parameter provided to the codec's // encoder and does not count the size of the IP or other transport layers like TCP or UDP. // It is measured in bits per second and the bitrate is calculated over a 1 second window. TargetBitrate float64 `json:"targetBitrate"` // FramesEncoded represents the total number of frames successfully encoded for this RTP media stream. // Only valid for video. FramesEncoded uint32 `json:"framesEncoded"` // 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 // time passed between feeding the encoder a frame and the encoder returning encoded data // for that frame. This does not include any additional time it may take to packetize the resulting data. TotalEncodeTime float64 `json:"totalEncodeTime"` // 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. AverageRTCPInterval float64 `json:"averageRtcpInterval"` // QualityLimitationReason is the current reason for limiting the resolution and/or framerate, // or "none" if not limited. Only valid for video. QualityLimitationReason QualityLimitationReason `json:"qualityLimitationReason"` // QualityLimitationDurations is record of the total time, in seconds, that this // stream has spent in each quality limitation state. The record includes a mapping // for all QualityLimitationReason types, including "none". Only valid for video. QualityLimitationDurations map[string]float64 `json:"qualityLimitationDurations"` // 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"` } func (s OutboundRTPStreamStats) statsMarker() {} func unmarshalOutboundRTPStreamStats(b []byte) (OutboundRTPStreamStats, error) { var outboundRTPStreamStats OutboundRTPStreamStats err := json.Unmarshal(b, &outboundRTPStreamStats) if err != nil { return OutboundRTPStreamStats{}, fmt.Errorf("unmarshal outbound rtp stream stats: %w", err) } return outboundRTPStreamStats, nil } // RemoteInboundRTPStreamStats contains statistics for the remote endpoint's inbound // RTP stream corresponding to an outbound stream that is currently sent with this // PeerConnection object. It is measured at the remote endpoint and reported in an RTCP // Receiver Report (RR) or RTCP Extended Report (XR). type RemoteInboundRTPStreamStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // SSRC is the 32-bit unsigned integer value used to identify the source of the // stream of RTP packets that this stats object concerns. SSRC SSRC `json:"ssrc"` // Kind is either "audio" or "video" Kind string `json:"kind"` // It is a unique identifier that is associated to the object that was inspected // to produce the TransportStats associated with this RTP stream. TransportID string `json:"transportId"` // CodecID is a unique identifier that is associated to the object that was inspected // to produce the CodecStats associated with this RTP stream. CodecID string `json:"codecId"` // 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"` // PLICount counts the total number of Picture Loss Indication (PLI) packets // received by the sender. This metric is only valid for video and is sent by receiver. PLICount uint32 `json:"pliCount"` // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets // received by the sender and is sent by receiver. NACKCount uint32 `json:"nackCount"` // 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"` // QPSum is the sum of the QP values of frames passed. The count of frames is // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats. QPSum uint64 `json:"qpSum"` // PacketsReceived is the total number of RTP packets received for this SSRC. PacketsReceived uint32 `json:"packetsReceived"` // PacketsLost is the total number of RTP packets lost for this SSRC. Note that // because of how this is estimated, it can be negative if more packets are received than sent. PacketsLost int32 `json:"packetsLost"` // Jitter is the packet jitter measured in seconds for this SSRC Jitter float64 `json:"jitter"` // PacketsDiscarded is the cumulative number of RTP packets discarded by the jitter // buffer due to late or early-arrival, i.e., these packets are not played out. // RTP packets discarded due to packet duplication are not reported in this metric. PacketsDiscarded uint32 `json:"packetsDiscarded"` // PacketsRepaired is the cumulative number of lost RTP packets repaired after applying // an error-resilience mechanism. It is measured for the primary source RTP packets // and only counted for RTP packets that have no further chance of repair. PacketsRepaired uint32 `json:"packetsRepaired"` // BurstPacketsLost is the cumulative number of RTP packets lost during loss bursts. BurstPacketsLost uint32 `json:"burstPacketsLost"` // BurstPacketsDiscarded is the cumulative number of RTP packets discarded during discard bursts. BurstPacketsDiscarded uint32 `json:"burstPacketsDiscarded"` // BurstLossCount is the cumulative number of bursts of lost RTP packets. BurstLossCount uint32 `json:"burstLossCount"` // BurstDiscardCount is the cumulative number of bursts of discarded RTP packets. BurstDiscardCount uint32 `json:"burstDiscardCount"` // BurstLossRate is the fraction of RTP packets lost during bursts to the // total number of RTP packets expected in the bursts. BurstLossRate float64 `json:"burstLossRate"` // BurstDiscardRate is the fraction of RTP packets discarded during bursts to // the total number of RTP packets expected in bursts. BurstDiscardRate float64 `json:"burstDiscardRate"` // GapLossRate is the fraction of RTP packets lost during the gap periods. GapLossRate float64 `json:"gapLossRate"` // GapDiscardRate is the fraction of RTP packets discarded during the gap periods. GapDiscardRate float64 `json:"gapDiscardRate"` // LocalID is used for looking up the local OutboundRTPStreamStats object for the same SSRC. LocalID string `json:"localId"` // RoundTripTime is the estimated round trip time for this SSRC based on the // RTCP timestamps in the RTCP Receiver Report (RR) and measured in seconds. RoundTripTime float64 `json:"roundTripTime"` // FractionLost is the fraction packet loss reported for this SSRC. FractionLost float64 `json:"fractionLost"` } func (s RemoteInboundRTPStreamStats) statsMarker() {} func unmarshalRemoteInboundRTPStreamStats(b []byte) (RemoteInboundRTPStreamStats, error) { var remoteInboundRTPStreamStats RemoteInboundRTPStreamStats err := json.Unmarshal(b, &remoteInboundRTPStreamStats) if err != nil { return RemoteInboundRTPStreamStats{}, fmt.Errorf("unmarshal remote inbound rtp stream stats: %w", err) } return remoteInboundRTPStreamStats, nil } // RemoteOutboundRTPStreamStats contains statistics for the remote endpoint's outbound // RTP stream corresponding to an inbound stream that is currently received with this // PeerConnection object. It is measured at the remote endpoint and reported in an // RTCP Sender Report (SR). type RemoteOutboundRTPStreamStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // SSRC is the 32-bit unsigned integer value used to identify the source of the // stream of RTP packets that this stats object concerns. SSRC SSRC `json:"ssrc"` // Kind is either "audio" or "video" Kind string `json:"kind"` // It is a unique identifier that is associated to the object that was inspected // to produce the TransportStats associated with this RTP stream. TransportID string `json:"transportId"` // CodecID is a unique identifier that is associated to the object that was inspected // to produce the CodecStats associated with this RTP stream. CodecID string `json:"codecId"` // 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"` // PLICount counts the total number of Picture Loss Indication (PLI) packets // received by the sender. This metric is only valid for video and is sent by receiver. PLICount uint32 `json:"pliCount"` // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets // received by the sender and is sent by receiver. NACKCount uint32 `json:"nackCount"` // 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"` // QPSum is the sum of the QP values of frames passed. The count of frames is // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats. QPSum uint64 `json:"qpSum"` // PacketsSent is the total number of RTP packets sent for this SSRC. PacketsSent uint32 `json:"packetsSent"` // PacketsDiscardedOnSend is the total number of RTP packets for this SSRC 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"` // FECPacketsSent is the total number of RTP FEC packets sent for this SSRC. // This counter can also be incremented when sending FEC packets in-band with // media packets (e.g., with Opus). FECPacketsSent uint32 `json:"fecPacketsSent"` // BytesSent is the total number of bytes sent for this SSRC. BytesSent uint64 `json:"bytesSent"` // BytesDiscardedOnSend is the total number of bytes for this SSRC 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. BytesDiscardedOnSend uint64 `json:"bytesDiscardedOnSend"` // LocalID is used for looking up the local InboundRTPStreamStats object for the same SSRC. LocalID string `json:"localId"` // RemoteTimestamp represents the remote timestamp at which these statistics were // sent by the remote endpoint. This differs from timestamp, which represents the // time at which the statistics were generated or received by the local endpoint. // The RemoteTimestamp, if present, is derived from the NTP timestamp in an RTCP // 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"` } func (s RemoteOutboundRTPStreamStats) statsMarker() {} func unmarshalRemoteOutboundRTPStreamStats(b []byte) (RemoteOutboundRTPStreamStats, error) { var remoteOutboundRTPStreamStats RemoteOutboundRTPStreamStats err := json.Unmarshal(b, &remoteOutboundRTPStreamStats) if err != nil { return RemoteOutboundRTPStreamStats{}, fmt.Errorf("unmarshal remote outbound rtp stream stats: %w", err) } return remoteOutboundRTPStreamStats, nil } // RTPContributingSourceStats contains statistics for a contributing source (CSRC) that contributed // to an inbound RTP stream. type RTPContributingSourceStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // ContributorSSRC is the SSRC identifier of the contributing source represented // by this stats object. It is a 32-bit unsigned integer that appears in the CSRC // list of any packets the relevant source contributed to. ContributorSSRC SSRC `json:"contributorSsrc"` // InboundRTPStreamID is the ID of the InboundRTPStreamStats object representing // the inbound RTP stream that this contributing source is contributing to. InboundRTPStreamID string `json:"inboundRtpStreamId"` // PacketsContributedTo is the total number of RTP packets that this contributing // source contributed to. This value is incremented each time a packet is counted // by InboundRTPStreamStats.packetsReceived, and the packet's CSRC list contains // the SSRC identifier of this contributing source, ContributorSSRC. PacketsContributedTo uint32 `json:"packetsContributedTo"` // AudioLevel is present if the last received RTP packet that this source contributed // to contained an [RFC6465] mixer-to-client audio level header extension. The value // of audioLevel is 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. AudioLevel float64 `json:"audioLevel"` } func (s RTPContributingSourceStats) statsMarker() {} func unmarshalCSRCStats(b []byte) (RTPContributingSourceStats, error) { var csrcStats RTPContributingSourceStats err := json.Unmarshal(b, &csrcStats) if err != nil { return RTPContributingSourceStats{}, fmt.Errorf("unmarshal csrc stats: %w", err) } return csrcStats, nil } // AudioSourceStats represents an audio track that is attached to one or more senders. type AudioSourceStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TrackIdentifier represents the id property of the track. TrackIdentifier string `json:"trackIdentifier"` // Kind is "audio" Kind string `json:"kind"` // AudioLevel represents the output audio level of the 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. // // If the track is sourced from an Receiver, does no audio processing, has a // constant level, and has a volume setting of 1.0, the audio level is expected // to be the same as the audio level of the source SSRC, while if the volume setting // is 0.5, the AudioLevel is expected to be half that value. AudioLevel float64 `json:"audioLevel"` // TotalAudioEnergy is the total energy of all the audio samples sent/received // for this object, calculated by duration * Math.pow(energy/maxEnergy, 2) for // each audio sample seen. TotalAudioEnergy float64 `json:"totalAudioEnergy"` // TotalSamplesDuration represents the total duration in seconds of all samples // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived). // Can be used with TotalAudioEnergy to compute an average audio level over different intervals. TotalSamplesDuration float64 `json:"totalSamplesDuration"` // EchoReturnLoss is only present while the sender is sending a track sourced from // a microphone where echo cancellation is applied. Calculated in decibels. EchoReturnLoss float64 `json:"echoReturnLoss"` // EchoReturnLossEnhancement is only present while the sender is sending a track // sourced from a microphone where echo cancellation is applied. Calculated in decibels. EchoReturnLossEnhancement float64 `json:"echoReturnLossEnhancement"` // DroppedSamplesDuration represents the total duration, in seconds, of samples produced by the device that got // dropped before reaching the media source. Only applicable if this media source is backed by an audio capture device. DroppedSamplesDuration float64 `json:"droppedSamplesDuration"` // DroppedSamplesEvents is the number of dropped samples events. This counter increases every time a sample is // dropped after a non-dropped sample. That is, multiple consecutive dropped samples will increase // droppedSamplesDuration multiple times but is a single dropped samples event. DroppedSamplesEvents uint64 `json:"droppedSamplesEvents"` // TotalCaptureDelay is the total delay, in seconds, for each audio sample between the time the sample was emitted // by the capture device and the sample reaching the source. This can be used together with totalSamplesCaptured to // calculate the average capture delay per sample. Only applicable if the audio source represents an audio capture device. TotalCaptureDelay float64 `json:"totalCaptureDelay"` // TotalSamplesCaptured is the total number of captured samples reaching the audio source, i.e. that were not dropped // by the capture pipeline. The frequency of the media source is not necessarily the same as the frequency of encoders // later in the pipeline. Only applicable if the audio source represents an audio capture device. TotalSamplesCaptured uint64 `json:"totalSamplesCaptured"` } func (s AudioSourceStats) statsMarker() {} // VideoSourceStats represents a video track that is attached to one or more senders. type VideoSourceStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TrackIdentifier represents the id property of the track. TrackIdentifier string `json:"trackIdentifier"` // Kind is "video" Kind string `json:"kind"` // Width is width of the last frame originating from this source in pixels. Width uint32 `json:"width"` // Height is height of the last frame originating from this source in pixels. Height uint32 `json:"height"` // Frames is the total number of frames originating from this source. Frames uint32 `json:"frames"` // FramesPerSecond is the number of frames originating from this source, measured during the last second. FramesPerSecond float64 `json:"framesPerSecond"` } func (s VideoSourceStats) statsMarker() {} func unmarshalMediaSourceStats(b []byte) (Stats, error) { type kindJSON struct { Kind string `json:"kind"` } kindHolder := kindJSON{} err := json.Unmarshal(b, &kindHolder) if err != nil { return nil, fmt.Errorf("unmarshal json kind: %w", err) } switch MediaKind(kindHolder.Kind) { case MediaKindAudio: var mediaSourceStats AudioSourceStats err := json.Unmarshal(b, &mediaSourceStats) if err != nil { return nil, fmt.Errorf("unmarshal audio source stats: %w", err) } return mediaSourceStats, nil case MediaKindVideo: var mediaSourceStats VideoSourceStats err := json.Unmarshal(b, &mediaSourceStats) if err != nil { return nil, fmt.Errorf("unmarshal video source stats: %w", err) } return mediaSourceStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) } } // AudioPlayoutStats represents one playout path - if the same playout stats object is referenced by multiple // RTCInboundRtpStreamStats this is an indication that audio mixing is happening in which case sample counters in this // stats object refer to the samples after mixing. Only applicable if the playout path represents an audio device. type AudioPlayoutStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Kind is "audio" Kind string `json:"kind"` // SynthesizedSamplesDuration is measured in seconds and is incremented each time an audio sample is synthesized by // this playout path. This metric can be used together with totalSamplesDuration to calculate the percentage of played // out media being synthesized. If the playout path is unable to produce audio samples on time for device playout, // samples are synthesized to be playout out instead. Synthesization typically only happens if the pipeline is // underperforming. Samples synthesized by the RTCInboundRtpStreamStats are not counted for here, but in // InboundRtpStreamStats.concealedSamples. SynthesizedSamplesDuration float64 `json:"synthesizedSamplesDuration"` // SynthesizedSamplesEvents is the number of synthesized samples events. This counter increases every time a sample // is synthesized after a non-synthesized sample. That is, multiple consecutive synthesized samples will increase // synthesizedSamplesDuration multiple times but is a single synthesization samples event. SynthesizedSamplesEvents uint64 `json:"synthesizedSamplesEvents"` // TotalSamplesDuration represents the total duration in seconds of all samples // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived). // Can be used with TotalAudioEnergy to compute an average audio level over different intervals. TotalSamplesDuration float64 `json:"totalSamplesDuration"` // When audio samples are pulled by the playout device, this counter is incremented with the estimated delay of the // playout path for that audio sample. The playout delay includes the delay from being emitted to the actual time of // playout on the device. This metric can be used together with totalSamplesCount to calculate the average // playout delay per sample. TotalPlayoutDelay float64 `json:"totalPlayoutDelay"` // When audio samples are pulled by the playout device, this counter is incremented with the number of samples // emitted for playout. TotalSamplesCount uint64 `json:"totalSamplesCount"` } func (s AudioPlayoutStats) statsMarker() {} func unmarshalMediaPlayoutStats(b []byte) (Stats, error) { var audioPlayoutStats AudioPlayoutStats err := json.Unmarshal(b, &audioPlayoutStats) if err != nil { return nil, fmt.Errorf("unmarshal audio playout stats: %w", err) } return audioPlayoutStats, nil } // PeerConnectionStats contains statistics related to the PeerConnection object. type PeerConnectionStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // DataChannelsOpened represents the number of unique DataChannels that have // entered the "open" state during their lifetime. DataChannelsOpened uint32 `json:"dataChannelsOpened"` // DataChannelsClosed represents the number of unique DataChannels that have // left the "open" state during their lifetime (due to being closed by either // end or the underlying transport being closed). DataChannels that transition // from "connecting" to "closing" or "closed" without ever being "open" // are not counted in this number. DataChannelsClosed uint32 `json:"dataChannelsClosed"` // DataChannelsRequested Represents the number of unique DataChannels returned // from a successful createDataChannel() call on the PeerConnection. If the // underlying data transport is not established, these may be in the "connecting" state. DataChannelsRequested uint32 `json:"dataChannelsRequested"` // DataChannelsAccepted represents the number of unique DataChannels signaled // in a "datachannel" event on the PeerConnection. DataChannelsAccepted uint32 `json:"dataChannelsAccepted"` } func (s PeerConnectionStats) statsMarker() {} func unmarshalPeerConnectionStats(b []byte) (PeerConnectionStats, error) { var pcStats PeerConnectionStats err := json.Unmarshal(b, &pcStats) if err != nil { return PeerConnectionStats{}, fmt.Errorf("unmarshal pc stats: %w", err) } return pcStats, nil } // DataChannelStats contains statistics related to each DataChannel ID. type DataChannelStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Label is the "label" value of the DataChannel object. Label string `json:"label"` // Protocol is the "protocol" value of the DataChannel object. Protocol string `json:"protocol"` // DataChannelIdentifier is the "id" attribute of the DataChannel object. DataChannelIdentifier int32 `json:"dataChannelIdentifier"` // TransportID the ID of the TransportStats object for transport used to carry this datachannel. TransportID string `json:"transportId"` // State is the "readyState" value of the DataChannel object. State DataChannelState `json:"state"` // MessagesSent represents the total number of API "message" events sent. MessagesSent uint32 `json:"messagesSent"` // BytesSent represents the total number of payload bytes sent on this // datachannel not including headers or padding. BytesSent uint64 `json:"bytesSent"` // MessagesReceived represents the total number of API "message" events received. MessagesReceived uint32 `json:"messagesReceived"` // BytesReceived represents the total number of bytes received on this // datachannel not including headers or padding. BytesReceived uint64 `json:"bytesReceived"` } func (s DataChannelStats) statsMarker() {} func unmarshalDataChannelStats(b []byte) (DataChannelStats, error) { var dataChannelStats DataChannelStats err := json.Unmarshal(b, &dataChannelStats) if err != nil { return DataChannelStats{}, fmt.Errorf("unmarshal data channel stats: %w", err) } return dataChannelStats, nil } // MediaStreamStats contains statistics related to a specific MediaStream. type MediaStreamStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // StreamIdentifier is the "id" property of the MediaStream StreamIdentifier string `json:"streamIdentifier"` // TrackIDs is a list of the identifiers of the stats object representing the // stream's tracks, either ReceiverAudioTrackAttachmentStats or ReceiverVideoTrackAttachmentStats. TrackIDs []string `json:"trackIds"` } func (s MediaStreamStats) statsMarker() {} func unmarshalStreamStats(b []byte) (MediaStreamStats, error) { var streamStats MediaStreamStats err := json.Unmarshal(b, &streamStats) if err != nil { return MediaStreamStats{}, fmt.Errorf("unmarshal stream stats: %w", err) } return streamStats, nil } // AudioSenderStats represents the stats about one audio sender of a PeerConnection // object for which one calls GetStats. // // It appears in the stats as soon as the RTPSender is added by either AddTrack // or AddTransceiver, or by media negotiation. type AudioSenderStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TrackIdentifier represents the id property of the track. TrackIdentifier string `json:"trackIdentifier"` // RemoteSource is true if the source is remote, for instance if it is sourced // from another host via a PeerConnection. False otherwise. Only applicable for 'track' stats. RemoteSource bool `json:"remoteSource"` // Ended reflects the "ended" state of the track. Ended bool `json:"ended"` // Kind is "audio" Kind string `json:"kind"` // AudioLevel represents the output audio level of the 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. // // If the track is sourced from an Receiver, does no audio processing, has a // constant level, and has a volume setting of 1.0, the audio level is expected // to be the same as the audio level of the source SSRC, while if the volume setting // is 0.5, the AudioLevel is expected to be half that value. // // For outgoing audio tracks, the AudioLevel is the level of the audio being sent. AudioLevel float64 `json:"audioLevel"` // TotalAudioEnergy is the total energy of all the audio samples sent/received // for this object, calculated by duration * Math.pow(energy/maxEnergy, 2) for // each audio sample seen. TotalAudioEnergy float64 `json:"totalAudioEnergy"` // VoiceActivityFlag represents whether the last RTP packet sent or played out // by this track contained voice activity or not based on the presence of the // V bit in the extension header, as defined in [RFC6464]. // // This value indicates the voice activity in the latest RTP packet played out // from a given SSRC, and is defined in RTPSynchronizationSource.voiceActivityFlag. VoiceActivityFlag bool `json:"voiceActivityFlag"` // TotalSamplesDuration represents the total duration in seconds of all samples // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived). // Can be used with TotalAudioEnergy to compute an average audio level over different intervals. TotalSamplesDuration float64 `json:"totalSamplesDuration"` // EchoReturnLoss is only present while the sender is sending a track sourced from // a microphone where echo cancellation is applied. Calculated in decibels. EchoReturnLoss float64 `json:"echoReturnLoss"` // EchoReturnLossEnhancement is only present while the sender is sending a track // sourced from a microphone where echo cancellation is applied. Calculated in decibels. EchoReturnLossEnhancement float64 `json:"echoReturnLossEnhancement"` // TotalSamplesSent is the total number of samples that have been sent by this sender. TotalSamplesSent uint64 `json:"totalSamplesSent"` } func (s AudioSenderStats) statsMarker() {} // SenderAudioTrackAttachmentStats object represents the stats about one attachment // of an audio MediaStreamTrack to the PeerConnection object for which one calls GetStats. // // It appears in the stats as soon as it is attached (via AddTrack, via AddTransceiver, // via ReplaceTrack on an RTPSender object). // // If an audio track is attached twice (via AddTransceiver or ReplaceTrack), there // will be two SenderAudioTrackAttachmentStats objects, one for each attachment. // They will have the same "TrackIdentifier" attribute, but different "ID" attributes. // // If the track is detached from the PeerConnection (via removeTrack or via replaceTrack), // it continues to appear, but with the "ObjectDeleted" member set to true. type SenderAudioTrackAttachmentStats AudioSenderStats func (s SenderAudioTrackAttachmentStats) statsMarker() {} // VideoSenderStats represents the stats about one video sender of a PeerConnection // object for which one calls GetStats. // // It appears in the stats as soon as the sender is added by either AddTrack or // AddTransceiver, or by media negotiation. type VideoSenderStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Kind is "video" Kind string `json:"kind"` // FramesCaptured represents the total number of frames captured, before encoding, // for this RTPSender (or for this MediaStreamTrack, if type is "track"). For example, // if type is "sender" and this sender's track represents a camera, then this is the // number of frames produced by the camera for this track while being sent by this sender, // combined with the number of frames produced by all tracks previously attached to this // sender while being sent by this sender. Framerates can vary due to hardware limitations // or environmental factors such as lighting conditions. FramesCaptured uint32 `json:"framesCaptured"` // FramesSent represents the total number of frames sent by this RTPSender // (or for this MediaStreamTrack, if type is "track"). FramesSent uint32 `json:"framesSent"` // HugeFramesSent represents the total number of huge frames sent by this RTPSender // (or for this MediaStreamTrack, if type is "track"). 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. If a huge frame is also a key frame, // then both counters HugeFramesSent and KeyFramesSent are incremented. HugeFramesSent uint32 `json:"hugeFramesSent"` // KeyFramesSent represents the total number of key frames sent by this RTPSender // (or for this MediaStreamTrack, if type is "track"), such as Infra-frames in // VP8 [RFC6386] or I-frames in H.264 [RFC6184]. This is a subset of FramesSent. // FramesSent - KeyFramesSent gives you the number of delta frames sent. KeyFramesSent uint32 `json:"keyFramesSent"` } func (s VideoSenderStats) statsMarker() {} // SenderVideoTrackAttachmentStats represents the stats about one attachment of a // video MediaStreamTrack to the PeerConnection object for which one calls GetStats. // // It appears in the stats as soon as it is attached (via AddTrack, via AddTransceiver, // via ReplaceTrack on an RTPSender object). // // If a video track is attached twice (via AddTransceiver or ReplaceTrack), there // will be two SenderVideoTrackAttachmentStats objects, one for each attachment. // They will have the same "TrackIdentifier" attribute, but different "ID" attributes. // // If the track is detached from the PeerConnection (via RemoveTrack or via ReplaceTrack), // it continues to appear, but with the "ObjectDeleted" member set to true. type SenderVideoTrackAttachmentStats VideoSenderStats func (s SenderVideoTrackAttachmentStats) statsMarker() {} func unmarshalSenderStats(b []byte) (Stats, error) { type kindJSON struct { Kind string `json:"kind"` } kindHolder := kindJSON{} err := json.Unmarshal(b, &kindHolder) if err != nil { return nil, fmt.Errorf("unmarshal json kind: %w", err) } switch MediaKind(kindHolder.Kind) { case MediaKindAudio: var senderStats AudioSenderStats err := json.Unmarshal(b, &senderStats) if err != nil { return nil, fmt.Errorf("unmarshal audio sender stats: %w", err) } return senderStats, nil case MediaKindVideo: var senderStats VideoSenderStats err := json.Unmarshal(b, &senderStats) if err != nil { return nil, fmt.Errorf("unmarshal video sender stats: %w", err) } return senderStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) } } func unmarshalTrackStats(b []byte) (Stats, error) { type kindJSON struct { Kind string `json:"kind"` } kindHolder := kindJSON{} err := json.Unmarshal(b, &kindHolder) if err != nil { return nil, fmt.Errorf("unmarshal json kind: %w", err) } switch MediaKind(kindHolder.Kind) { case MediaKindAudio: var trackStats SenderAudioTrackAttachmentStats err := json.Unmarshal(b, &trackStats) if err != nil { return nil, fmt.Errorf("unmarshal audio track stats: %w", err) } return trackStats, nil case MediaKindVideo: var trackStats SenderVideoTrackAttachmentStats err := json.Unmarshal(b, &trackStats) if err != nil { return nil, fmt.Errorf("unmarshal video track stats: %w", err) } return trackStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) } } // AudioReceiverStats contains audio metrics related to a specific receiver. type AudioReceiverStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Kind is "audio" Kind string `json:"kind"` // AudioLevel represents the output audio level of the 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. // // If the track is sourced from an Receiver, does no audio processing, has a // constant level, and has a volume setting of 1.0, the audio level is expected // to be the same as the audio level of the source SSRC, while if the volume setting // is 0.5, the AudioLevel is expected to be half that value. // // For outgoing audio tracks, the AudioLevel is the level of the audio being sent. AudioLevel float64 `json:"audioLevel"` // TotalAudioEnergy is the total energy of all the audio samples sent/received // for this object, calculated by duration * Math.pow(energy/maxEnergy, 2) for // each audio sample seen. TotalAudioEnergy float64 `json:"totalAudioEnergy"` // VoiceActivityFlag represents whether the last RTP packet sent or played out // by this track contained voice activity or not based on the presence of the // V bit in the extension header, as defined in [RFC6464]. // // This value indicates the voice activity in the latest RTP packet played out // from a given SSRC, and is defined in RTPSynchronizationSource.voiceActivityFlag. VoiceActivityFlag bool `json:"voiceActivityFlag"` // TotalSamplesDuration represents the total duration in seconds of all samples // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived). // Can be used with TotalAudioEnergy to compute an average audio level over different intervals. TotalSamplesDuration float64 `json:"totalSamplesDuration"` // EstimatedPlayoutTimestamp is the estimated playout time of this receiver's // track. The playout time is the NTP timestamp of the last playable sample that // has a known timestamp (from an RTCP SR packet mapping RTP timestamps to NTP // timestamps), extrapolated with the time elapsed since it was ready to be played out. // This is the "current time" of the track in NTP clock time of the sender and // can be present even if there is no audio currently playing. // // This can be useful for estimating how much audio and video is out of // sync for two tracks from the same source: // AudioTrackStats.EstimatedPlayoutTimestamp - VideoTrackStats.EstimatedPlayoutTimestamp EstimatedPlayoutTimestamp StatsTimestamp `json:"estimatedPlayoutTimestamp"` // JitterBufferDelay is the sum of the time, in seconds, each sample takes from // the time it is received and to the time it exits the jitter buffer. // This increases upon samples exiting, having completed their time in the buffer // (incrementing JitterBufferEmittedCount). The average jitter buffer delay can // be calculated by dividing the JitterBufferDelay with the JitterBufferEmittedCount. JitterBufferDelay float64 `json:"jitterBufferDelay"` // JitterBufferEmittedCount is the total number of samples that have come out // of the jitter buffer (increasing JitterBufferDelay). JitterBufferEmittedCount uint64 `json:"jitterBufferEmittedCount"` // TotalSamplesReceived is the total number of samples that have been received // by this receiver. This includes ConcealedSamples. TotalSamplesReceived uint64 `json:"totalSamplesReceived"` // ConcealedSamples is the total number of samples that are concealed samples. // A concealed sample is a sample that is based on data that was synthesized // to conceal packet loss and does not represent incoming data. ConcealedSamples uint64 `json:"concealedSamples"` // ConcealmentEvents is the number of concealment events. This counter 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. ConcealmentEvents uint64 `json:"concealmentEvents"` } func (s AudioReceiverStats) statsMarker() {} // VideoReceiverStats contains video metrics related to a specific receiver. type VideoReceiverStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Kind is "video" Kind string `json:"kind"` // FrameWidth represents the width of the last processed frame for this track. // Before the first frame is processed this attribute is missing. FrameWidth uint32 `json:"frameWidth"` // FrameHeight represents the height of the last processed frame for this track. // Before the first frame is processed this attribute is missing. FrameHeight uint32 `json:"frameHeight"` // FramesPerSecond represents the nominal FPS value before the degradation preference // is applied. It is the number of complete frames in the last second. For sending // tracks it is the current captured FPS and for the receiving tracks it is the // current decoding framerate. FramesPerSecond float64 `json:"framesPerSecond"` // EstimatedPlayoutTimestamp is the estimated playout time of this receiver's // track. The playout time is the NTP timestamp of the last playable sample that // has a known timestamp (from an RTCP SR packet mapping RTP timestamps to NTP // timestamps), extrapolated with the time elapsed since it was ready to be played out. // This is the "current time" of the track in NTP clock time of the sender and // can be present even if there is no audio currently playing. // // This can be useful for estimating how much audio and video is out of // sync for two tracks from the same source: // AudioTrackStats.EstimatedPlayoutTimestamp - VideoTrackStats.EstimatedPlayoutTimestamp EstimatedPlayoutTimestamp StatsTimestamp `json:"estimatedPlayoutTimestamp"` // JitterBufferDelay is the sum of the time, in seconds, each sample takes from // the time it is received and to the time it exits the jitter buffer. // This increases upon samples exiting, having completed their time in the buffer // (incrementing JitterBufferEmittedCount). The average jitter buffer delay can // be calculated by dividing the JitterBufferDelay with the JitterBufferEmittedCount. JitterBufferDelay float64 `json:"jitterBufferDelay"` // JitterBufferEmittedCount is the total number of samples that have come out // of the jitter buffer (increasing JitterBufferDelay). JitterBufferEmittedCount uint64 `json:"jitterBufferEmittedCount"` // FramesReceived Represents the total number of complete frames received for // this receiver. This metric is incremented when the complete frame is received. FramesReceived uint32 `json:"framesReceived"` // KeyFramesReceived represents the total number of complete key frames received // for this MediaStreamTrack, such as Infra-frames in VP8 [RFC6386] or I-frames // in H.264 [RFC6184]. This is a subset of framesReceived. `framesReceived - keyFramesReceived` // gives you the number of delta frames received. This metric is incremented when // the complete key frame is received. It is not incremented if a partial key // frames is received and sent for decoding, i.e., the frame could not be recovered // via retransmission or FEC. KeyFramesReceived uint32 `json:"keyFramesReceived"` // FramesDecoded represents the total number of frames correctly decoded for this // SSRC, i.e., frames that would be displayed if no frames are dropped. FramesDecoded uint32 `json:"framesDecoded"` // FramesDropped is the total number of frames dropped predecode or dropped // because the frame missed its display deadline for this receiver's track. FramesDropped uint32 `json:"framesDropped"` // The cumulative number of partial frames lost. This metric is incremented when // the frame is sent to the decoder. If the partial frame is received and recovered // via retransmission or FEC before decoding, the FramesReceived counter is incremented. PartialFramesLost uint32 `json:"partialFramesLost"` // FullFramesLost is the cumulative number of full frames lost. FullFramesLost uint32 `json:"fullFramesLost"` } func (s VideoReceiverStats) statsMarker() {} func unmarshalReceiverStats(b []byte) (Stats, error) { type kindJSON struct { Kind string `json:"kind"` } kindHolder := kindJSON{} err := json.Unmarshal(b, &kindHolder) if err != nil { return nil, fmt.Errorf("unmarshal json kind: %w", err) } switch MediaKind(kindHolder.Kind) { case MediaKindAudio: var receiverStats AudioReceiverStats err := json.Unmarshal(b, &receiverStats) if err != nil { return nil, fmt.Errorf("unmarshal audio receiver stats: %w", err) } return receiverStats, nil case MediaKindVideo: var receiverStats VideoReceiverStats err := json.Unmarshal(b, &receiverStats) if err != nil { return nil, fmt.Errorf("unmarshal video receiver stats: %w", err) } return receiverStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) } } // TransportStats contains transport statistics related to the PeerConnection object. type TransportStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // PacketsSent represents the total number of packets sent over this transport. PacketsSent uint32 `json:"packetsSent"` // PacketsReceived represents the total number of packets received on this transport. PacketsReceived uint32 `json:"packetsReceived"` // BytesSent represents the total number of payload bytes sent on this PeerConnection // not including headers or padding. BytesSent uint64 `json:"bytesSent"` // BytesReceived represents the total number of bytes received on this PeerConnection // not including headers or padding. BytesReceived uint64 `json:"bytesReceived"` // RTCPTransportStatsID is the ID of the transport that gives stats for the RTCP // component If RTP and RTCP are not multiplexed and this record has only // the RTP component stats. RTCPTransportStatsID string `json:"rtcpTransportStatsId"` // ICERole is set to the current value of the "role" attribute of the underlying // DTLSTransport's "transport". ICERole ICERole `json:"iceRole"` // DTLSState is set to the current value of the "state" attribute of the underlying DTLSTransport. DTLSState DTLSTransportState `json:"dtlsState"` // 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"` // LocalCertificateID is the ID of the CertificateStats for the local certificate. // Present only if DTLS is negotiated. LocalCertificateID string `json:"localCertificateId"` // LocalCertificateID is the ID of the CertificateStats for the remote certificate. // Present only if DTLS is negotiated. RemoteCertificateID string `json:"remoteCertificateId"` // DTLSCipher is the descriptive name of the cipher suite used for the DTLS transport, // as defined in the "Description" column of the IANA cipher suite registry. DTLSCipher string `json:"dtlsCipher"` // SRTPCipher is the descriptive name of the protection profile used for the SRTP // transport, as defined in the "Profile" column of the IANA DTLS-SRTP protection // profile registry. SRTPCipher string `json:"srtpCipher"` } func (s TransportStats) statsMarker() {} func unmarshalTransportStats(b []byte) (TransportStats, error) { var transportStats TransportStats err := json.Unmarshal(b, &transportStats) if err != nil { return TransportStats{}, fmt.Errorf("unmarshal transport stats: %w", err) } return transportStats, nil } // StatsICECandidatePairState is the state of an ICE candidate pair used in the // ICECandidatePairStats object. type StatsICECandidatePairState string func toStatsICECandidatePairState(state ice.CandidatePairState) (StatsICECandidatePairState, error) { switch state { case ice.CandidatePairStateWaiting: return StatsICECandidatePairStateWaiting, nil case ice.CandidatePairStateInProgress: return StatsICECandidatePairStateInProgress, nil case ice.CandidatePairStateFailed: return StatsICECandidatePairStateFailed, nil case ice.CandidatePairStateSucceeded: return StatsICECandidatePairStateSucceeded, nil default: // NOTE: this should never happen[tm] err := fmt.Errorf("%w: %s", errStatsICECandidateStateInvalid, state.String()) return StatsICECandidatePairState("Unknown"), err } } const ( // StatsICECandidatePairStateFrozen means a check for this pair hasn't been // performed, and it can't yet be performed until some other check succeeds, // allowing this pair to unfreeze and move into the Waiting state. StatsICECandidatePairStateFrozen StatsICECandidatePairState = "frozen" // StatsICECandidatePairStateWaiting means a check has not been performed for // this pair, and can be performed as soon as it is the highest-priority Waiting // pair on the check list. StatsICECandidatePairStateWaiting StatsICECandidatePairState = "waiting" // StatsICECandidatePairStateInProgress means a check has been sent for this pair, // but the transaction is in progress. StatsICECandidatePairStateInProgress StatsICECandidatePairState = "in-progress" // StatsICECandidatePairStateFailed means a check for this pair was already done // and failed, either never producing any response or producing an unrecoverable // failure response. StatsICECandidatePairStateFailed StatsICECandidatePairState = "failed" // StatsICECandidatePairStateSucceeded means a check for this pair was already // done and produced a successful result. StatsICECandidatePairStateSucceeded StatsICECandidatePairState = "succeeded" ) // ICECandidatePairStats contains ICE candidate pair statistics related // to the ICETransport objects. type ICECandidatePairStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TransportID is a unique identifier that is associated to the object that // was inspected to produce the TransportStats associated with this candidate pair. TransportID string `json:"transportId"` // LocalCandidateID is a unique identifier that is associated to the object // that was inspected to produce the ICECandidateStats for the local candidate // associated with this candidate pair. LocalCandidateID string `json:"localCandidateId"` // RemoteCandidateID is a unique identifier that is associated to the object // that was inspected to produce the ICECandidateStats for the remote candidate // associated with this candidate pair. RemoteCandidateID string `json:"remoteCandidateId"` // State represents the state of the checklist for the local and remote // candidates in a pair. State StatsICECandidatePairState `json:"state"` // Nominated is true when this valid pair that should be used for media // if it is the highest-priority one amongst those whose nominated flag is set Nominated bool `json:"nominated"` // PacketsSent represents the total number of packets sent on this candidate pair. PacketsSent uint32 `json:"packetsSent"` // PacketsReceived represents the total number of packets received on this candidate pair. PacketsReceived uint32 `json:"packetsReceived"` // BytesSent represents the total number of payload bytes sent on this candidate pair // not including headers or padding. BytesSent uint64 `json:"bytesSent"` // BytesReceived represents the total number of payload bytes received on this candidate pair // not including headers or padding. BytesReceived uint64 `json:"bytesReceived"` // LastPacketSentTimestamp represents the timestamp at which the last packet was // sent on this particular candidate pair, excluding STUN packets. LastPacketSentTimestamp StatsTimestamp `json:"lastPacketSentTimestamp"` // LastPacketReceivedTimestamp represents the timestamp at which the last packet // was received on this particular candidate pair, excluding STUN packets. LastPacketReceivedTimestamp StatsTimestamp `json:"lastPacketReceivedTimestamp"` // FirstRequestTimestamp represents the timestamp at which the first STUN request // was sent on this particular candidate pair. FirstRequestTimestamp StatsTimestamp `json:"firstRequestTimestamp"` // LastRequestTimestamp represents the timestamp at which the last STUN request // was sent on this particular candidate pair. The average interval between two // consecutive connectivity checks sent can be calculated with // (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent. LastRequestTimestamp StatsTimestamp `json:"lastRequestTimestamp"` // LastResponseTimestamp represents the timestamp at which the last STUN response // was received on this particular candidate pair. LastResponseTimestamp StatsTimestamp `json:"lastResponseTimestamp"` // TotalRoundTripTime represents the sum of all round trip time measurements // in seconds since the beginning of the session, based on STUN connectivity // check responses (ResponsesReceived), including those that reply to requests // that are sent in order to verify consent. The average round trip time can // be computed from TotalRoundTripTime by dividing it by ResponsesReceived. TotalRoundTripTime float64 `json:"totalRoundTripTime"` // CurrentRoundTripTime represents the latest round trip time measured in seconds, // computed from both STUN connectivity checks, including those that are sent // for consent verification. CurrentRoundTripTime float64 `json:"currentRoundTripTime"` // AvailableOutgoingBitrate is calculated by the underlying congestion control // by combining the available bitrate for all the outgoing RTP streams using // this candidate pair. The bitrate measurement does not count the size of the // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined // in RFC 3890, i.e., it is measured in bits per second and the bitrate is calculated // over a 1 second window. AvailableOutgoingBitrate float64 `json:"availableOutgoingBitrate"` // AvailableIncomingBitrate is calculated by the underlying congestion control // by combining the available bitrate for all the incoming RTP streams using // this candidate pair. The bitrate measurement does not count the size of the // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined // in RFC 3890, i.e., it is measured in bits per second and the bitrate is // calculated over a 1 second window. AvailableIncomingBitrate float64 `json:"availableIncomingBitrate"` // CircuitBreakerTriggerCount represents the number of times the circuit breaker // is triggered for this particular 5-tuple, ceasing transmission. CircuitBreakerTriggerCount uint32 `json:"circuitBreakerTriggerCount"` // RequestsReceived represents the total number of connectivity check requests // received (including retransmissions). It is impossible for the receiver to // tell whether the request was sent in order to check connectivity or check // consent, so all connectivity checks requests are counted here. RequestsReceived uint64 `json:"requestsReceived"` // RequestsSent represents the total number of connectivity check requests // sent (not including retransmissions). RequestsSent uint64 `json:"requestsSent"` // ResponsesReceived represents the total number of connectivity check responses received. ResponsesReceived uint64 `json:"responsesReceived"` // ResponsesSent represents the total number of connectivity check responses sent. // Since we cannot distinguish connectivity check requests and consent requests, // all responses are counted. ResponsesSent uint64 `json:"responsesSent"` // RetransmissionsReceived represents the total number of connectivity check // request retransmissions received. RetransmissionsReceived uint64 `json:"retransmissionsReceived"` // RetransmissionsSent represents the total number of connectivity check // request retransmissions sent. RetransmissionsSent uint64 `json:"retransmissionsSent"` // ConsentRequestsSent represents the total number of consent requests sent. ConsentRequestsSent uint64 `json:"consentRequestsSent"` // ConsentExpiredTimestamp represents the timestamp at which the latest valid // STUN binding response expired. ConsentExpiredTimestamp StatsTimestamp `json:"consentExpiredTimestamp"` } func (s ICECandidatePairStats) statsMarker() {} func unmarshalICECandidatePairStats(b []byte) (ICECandidatePairStats, error) { var iceCandidatePairStats ICECandidatePairStats err := json.Unmarshal(b, &iceCandidatePairStats) if err != nil { return ICECandidatePairStats{}, fmt.Errorf("unmarshal ice candidate pair stats: %w", err) } return iceCandidatePairStats, nil } // ICECandidateStats contains ICE candidate statistics related to the ICETransport objects. type ICECandidateStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TransportID is a unique identifier that is associated to the object that // was inspected to produce the TransportStats associated with this candidate. TransportID string `json:"transportId"` // NetworkType represents the type of network interface used by the base of a // local candidate (the address the ICE agent sends from). Only present for // local candidates; it's not possible to know what type of network interface // a remote candidate is using. // // Note: // This stat only tells you about the network interface used by the first "hop"; // it's possible that a connection will be bottlenecked by another type of network. // For example, when using Wi-Fi tethering, the networkType of the relevant candidate // would be "wifi", even when the next hop is over a cellular connection. // // DEPRECATED. Although it may still work in some browsers, the networkType property was deprecated for // preserving privacy. NetworkType NetworkType `json:"networkType,omitempty"` // IP is the IP address of the candidate, allowing for IPv4 addresses and // IPv6 addresses, but fully qualified domain names (FQDNs) are not allowed. IP string `json:"ip"` // Port is the port number of the candidate. Port int32 `json:"port"` // Protocol is one of udp and tcp. Protocol string `json:"protocol"` // CandidateType is the "Type" field of the ICECandidate. CandidateType ICECandidateType `json:"candidateType"` // Priority is the "Priority" field of the ICECandidate. Priority int32 `json:"priority"` // URL is the URL of the TURN or STUN server indicated in the that translated // this IP address. It is the URL address surfaced in an PeerConnectionICEEvent. URL string `json:"url"` // RelayProtocol is the protocol used by the endpoint to communicate with the // TURN server. This is only present for local candidates. Valid values for // the TURN URL protocol is one of udp, tcp, or tls. RelayProtocol string `json:"relayProtocol"` // Deleted is true if the candidate has been deleted/freed. For host candidates, // this means that any network resources (typically a socket) associated with the // candidate have been released. For TURN candidates, this means the TURN allocation // is no longer active. // // Only defined for local candidates. For remote candidates, this property is not applicable. Deleted bool `json:"deleted"` } func (s ICECandidateStats) statsMarker() {} func unmarshalICECandidateStats(b []byte) (ICECandidateStats, error) { var iceCandidateStats ICECandidateStats err := json.Unmarshal(b, &iceCandidateStats) if err != nil { return ICECandidateStats{}, fmt.Errorf("unmarshal ice candidate stats: %w", err) } return iceCandidateStats, nil } // CertificateStats contains information about a certificate used by an ICETransport. type CertificateStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // Fingerprint is the fingerprint of the certificate. Fingerprint string `json:"fingerprint"` // FingerprintAlgorithm is the hash function used to compute the certificate fingerprint. For instance, "sha-256". FingerprintAlgorithm string `json:"fingerprintAlgorithm"` // Base64Certificate is the DER-encoded base-64 representation of the certificate. Base64Certificate string `json:"base64Certificate"` // IssuerCertificateID refers to the stats object that contains the next certificate // in the certificate chain. If the current certificate is at the end of the chain // (i.e. a self-signed certificate), this will not be set. IssuerCertificateID string `json:"issuerCertificateId"` } func (s CertificateStats) statsMarker() {} func unmarshalCertificateStats(b []byte) (CertificateStats, error) { var certificateStats CertificateStats err := json.Unmarshal(b, &certificateStats) if err != nil { return CertificateStats{}, fmt.Errorf("unmarshal certificate stats: %w", err) } return certificateStats, nil } // SCTPTransportStats contains information about a certificate used by an SCTPTransport. type SCTPTransportStats struct { // Timestamp is the timestamp associated with this object. Timestamp StatsTimestamp `json:"timestamp"` // Type is the object's StatsType Type StatsType `json:"type"` // ID is a unique id that is associated with the component inspected to produce // this Stats object. Two Stats objects will have the same ID if they were produced // by inspecting the same underlying object. ID string `json:"id"` // TransportID is the identifier of the object that was inspected to produce the // RTCTransportStats for the DTLSTransport and ICETransport supporting the SCTP transport. TransportID string `json:"transportId"` // SmoothedRoundTripTime is the latest smoothed round-trip time value, corresponding to spinfo_srtt defined in [RFC6458] // but converted to seconds. If there has been no round-trip time measurements yet, this value is undefined. SmoothedRoundTripTime float64 `json:"smoothedRoundTripTime"` // CongestionWindow is the latest congestion window, corresponding to spinfo_cwnd defined in [RFC6458]. CongestionWindow uint32 `json:"congestionWindow"` // ReceiverWindow is the latest receiver window, corresponding to sstat_rwnd defined in [RFC6458]. ReceiverWindow uint32 `json:"receiverWindow"` // MTU is the latest maximum transmission unit, corresponding to spinfo_mtu defined in [RFC6458]. MTU uint32 `json:"mtu"` // UNACKData is the number of unacknowledged DATA chunks, corresponding to sstat_unackdata defined in [RFC6458]. UNACKData uint32 `json:"unackData"` // BytesSent represents the total number of bytes sent on this SCTPTransport BytesSent uint64 `json:"bytesSent"` // BytesReceived represents the total number of bytes received on this SCTPTransport BytesReceived uint64 `json:"bytesReceived"` } func (s SCTPTransportStats) statsMarker() {} func unmarshalSCTPTransportStats(b []byte) (SCTPTransportStats, error) { var sctpTransportStats SCTPTransportStats if err := json.Unmarshal(b, &sctpTransportStats); err != nil { return SCTPTransportStats{}, fmt.Errorf("unmarshal sctp transport stats: %w", err) } return sctpTransportStats, nil }