github.com/livekit/protocol@v1.16.1-0.20240517185851-47e4c6bba773/utils/rtpstats.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package utils 16 17 import ( 18 "time" 19 20 "github.com/livekit/protocol/livekit" 21 "google.golang.org/protobuf/types/known/timestamppb" 22 ) 23 24 func AggregateRTPStats(statsList []*livekit.RTPStats, gapHistogramSize int) *livekit.RTPStats { 25 if len(statsList) == 0 { 26 return nil 27 } 28 29 startTime := time.Time{} 30 endTime := time.Time{} 31 32 packets := uint32(0) 33 bytes := uint64(0) 34 headerBytes := uint64(0) 35 packetsLost := uint32(0) 36 packetsDuplicate := uint32(0) 37 bytesDuplicate := uint64(0) 38 headerBytesDuplicate := uint64(0) 39 packetsPadding := uint32(0) 40 bytesPadding := uint64(0) 41 headerBytesPadding := uint64(0) 42 packetsOutOfOrder := uint32(0) 43 frames := uint32(0) 44 keyFrames := uint32(0) 45 lastKeyFrame := time.Time{} 46 jitter := 0.0 47 maxJitter := float64(0) 48 gapHistogram := make(map[int32]uint32, gapHistogramSize) 49 nacks := uint32(0) 50 nackAcks := uint32(0) 51 nackMisses := uint32(0) 52 nackRepeated := uint32(0) 53 plis := uint32(0) 54 lastPli := time.Time{} 55 layerLockPlis := uint32(0) 56 lastLayerLockPli := time.Time{} 57 firs := uint32(0) 58 lastFir := time.Time{} 59 rtt := uint32(0) 60 maxRtt := uint32(0) 61 62 for _, stats := range statsList { 63 if startTime.IsZero() || startTime.After(stats.StartTime.AsTime()) { 64 startTime = stats.StartTime.AsTime() 65 } 66 67 if endTime.IsZero() || endTime.Before(stats.EndTime.AsTime()) { 68 endTime = stats.EndTime.AsTime() 69 } 70 71 packets += stats.Packets 72 bytes += stats.Bytes 73 headerBytes += stats.HeaderBytes 74 75 packetsLost += stats.PacketsLost 76 77 packetsDuplicate += stats.PacketsDuplicate 78 bytesDuplicate += stats.BytesDuplicate 79 headerBytesDuplicate += stats.HeaderBytesDuplicate 80 81 packetsPadding += stats.PacketsPadding 82 bytesPadding += stats.BytesPadding 83 headerBytesPadding += stats.HeaderBytesPadding 84 85 packetsOutOfOrder += stats.PacketsOutOfOrder 86 87 frames += stats.Frames 88 89 keyFrames += stats.KeyFrames 90 if lastKeyFrame.IsZero() || lastKeyFrame.Before(stats.LastKeyFrame.AsTime()) { 91 lastKeyFrame = stats.LastKeyFrame.AsTime() 92 } 93 94 jitter += stats.JitterCurrent 95 if stats.JitterMax > maxJitter { 96 maxJitter = stats.JitterMax 97 } 98 99 for burst, count := range stats.GapHistogram { 100 gapHistogram[burst] += count 101 } 102 103 nacks += stats.Nacks 104 nackAcks += stats.NackAcks 105 nackMisses += stats.NackMisses 106 nackRepeated += stats.NackRepeated 107 108 plis += stats.Plis 109 if lastPli.IsZero() || lastPli.Before(stats.LastPli.AsTime()) { 110 lastPli = stats.LastPli.AsTime() 111 } 112 113 layerLockPlis += stats.LayerLockPlis 114 if lastLayerLockPli.IsZero() || lastLayerLockPli.Before(stats.LastLayerLockPli.AsTime()) { 115 lastLayerLockPli = stats.LastLayerLockPli.AsTime() 116 } 117 118 firs += stats.Firs 119 if lastFir.IsZero() || lastPli.Before(stats.LastFir.AsTime()) { 120 lastFir = stats.LastFir.AsTime() 121 } 122 123 rtt += stats.RttCurrent 124 if stats.RttMax > maxRtt { 125 maxRtt = stats.RttMax 126 } 127 } 128 129 if endTime.IsZero() { 130 endTime = time.Now() 131 } 132 elapsed := endTime.Sub(startTime).Seconds() 133 134 packetLostRate := float64(packetsLost) / elapsed 135 packetLostPercentage := float32(packetsLost) / (float32(packets) + float32(packetsLost)) * 100.0 136 137 packetRate := float64(packets) / elapsed 138 packetDuplicateRate := float64(packetsDuplicate) / elapsed 139 packetPaddingRate := float64(packetsPadding) / elapsed 140 141 bitrate := float64(bytes) * 8.0 / elapsed 142 bitrateDuplicate := float64(bytesDuplicate) * 8.0 / elapsed 143 bitratePadding := float64(bytesPadding) * 8.0 / elapsed 144 145 frameRate := float64(frames) / elapsed 146 147 return &livekit.RTPStats{ 148 StartTime: timestamppb.New(startTime), 149 EndTime: timestamppb.New(endTime), 150 Duration: elapsed, 151 Packets: packets, 152 PacketRate: packetRate, 153 Bytes: bytes, 154 HeaderBytes: headerBytes, 155 Bitrate: bitrate, 156 PacketsLost: packetsLost, 157 PacketLossRate: packetLostRate, 158 PacketLossPercentage: packetLostPercentage, 159 PacketsDuplicate: packetsDuplicate, 160 PacketDuplicateRate: packetDuplicateRate, 161 BytesDuplicate: bytesDuplicate, 162 HeaderBytesDuplicate: headerBytesDuplicate, 163 BitrateDuplicate: bitrateDuplicate, 164 PacketsPadding: packetsPadding, 165 PacketPaddingRate: packetPaddingRate, 166 BytesPadding: bytesPadding, 167 HeaderBytesPadding: headerBytesPadding, 168 BitratePadding: bitratePadding, 169 PacketsOutOfOrder: packetsOutOfOrder, 170 Frames: frames, 171 FrameRate: frameRate, 172 KeyFrames: keyFrames, 173 LastKeyFrame: timestamppb.New(lastKeyFrame), 174 JitterCurrent: jitter / float64(len(statsList)), 175 JitterMax: maxJitter, 176 GapHistogram: gapHistogram, 177 Nacks: nacks, 178 NackAcks: nackAcks, 179 NackMisses: nackMisses, 180 NackRepeated: nackRepeated, 181 Plis: plis, 182 LastPli: timestamppb.New(lastPli), 183 LayerLockPlis: layerLockPlis, 184 LastLayerLockPli: timestamppb.New(lastLayerLockPli), 185 Firs: firs, 186 LastFir: timestamppb.New(lastFir), 187 RttCurrent: rtt / uint32(len(statsList)), 188 RttMax: maxRtt, 189 // no aggregation for drift calculations 190 } 191 }