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  }