@ -106,6 +106,23 @@ function PeerConnectionAnalyzer() {
video : new AverageStatValue ( 5 , STAT_VALUE_TYPE . CUMULATIVE ) ,
}
this . _stagedPackets = {
audio : [ ] ,
video : [ ] ,
}
this . _stagedPacketsLost = {
audio : [ ] ,
video : [ ] ,
}
this . _stagedRoundTripTime = {
audio : [ ] ,
video : [ ] ,
}
this . _stagedTimestamps = {
audio : [ ] ,
video : [ ] ,
}
this . _analysisEnabled = {
audio : true ,
video : true ,
@ -462,7 +479,114 @@ PeerConnectionAnalyzer.prototype = {
}
} ,
/ * *
* Adds the stats reported by the browser to the average stats used to do
* the analysis .
*
* The stats reported by the browser can sometimes stall for a second ( or
* more , but typically they stall only for a single report ) . When that
* happens the stats are still reported , but with the same number of packets
* as in the previous report ( timestamp and round trip time are updated ,
* though ) . In that case the given stats are not added yet to the average
* stats ; they are kept on hold until more stats are provided by the browser
* and it can be determined if the previous stats were stalled or not . If
* they were stalled the previous and new stats are distributed , and if they
* were not they are added as is to the average stats .
*
* @ param { string } kind the type of the stats ( "audio" or "video" )
* @ param { number } packets the cumulative number of packets
* @ param { number } packetsLost the cumulative number of lost packets
* @ param { number } timestamp the cumulative timestamp
* @ param { number } roundTripTime the relative round trip time
* /
_addStats ( kind , packets , packetsLost , timestamp , roundTripTime ) {
if ( this . _stagedPackets [ kind ] . length === 0 ) {
if ( packets !== this . _packets [ kind ] . getLastRawValue ( ) ) {
this . _commitStats ( kind , packets , packetsLost , timestamp , roundTripTime )
} else {
this . _stageStats ( kind , packets , packetsLost , timestamp , roundTripTime )
}
return
}
this . _stageStats ( kind , packets , packetsLost , timestamp , roundTripTime )
// If the packets have changed now it is assumed that the previous stats
// were stalled.
if ( packets > 0 ) {
this . _distributeStagedStats ( kind )
}
while ( this . _stagedPackets [ kind ] . length > 0 ) {
const stagedPackets = this . _stagedPackets [ kind ] . shift ( )
const stagedPacketsLost = this . _stagedPacketsLost [ kind ] . shift ( )
const stagedTimestamp = this . _stagedTimestamps [ kind ] . shift ( )
const stagedRoundTripTime = this . _stagedRoundTripTime [ kind ] . shift ( )
this . _commitStats ( kind , stagedPackets , stagedPacketsLost , stagedTimestamp , stagedRoundTripTime )
}
} ,
_stageStats ( kind , packets , packetsLost , timestamp , roundTripTime ) {
this . _stagedPackets [ kind ] . push ( packets )
this . _stagedPacketsLost [ kind ] . push ( packetsLost )
this . _stagedTimestamps [ kind ] . push ( timestamp )
this . _stagedRoundTripTime [ kind ] . push ( roundTripTime )
} ,
/ * *
* Distributes the values of the staged stats proportionately to their
* timestamps .
*
* Once the stats unstall the new stats are a sum of the values that should
* have been reported before and the actual new values . The stats typically
* stall for just a second , but they can stall for an arbitrary length too .
* Due to this the staged stats need to be distributed based on their
* timestamps .
*
* @ param { string } kind the type of the stats ( "audio" or "video" )
* /
_distributeStagedStats ( kind ) {
let packetsBase = this . _packets [ kind ] . getLastRawValue ( )
let packetsLostBase = this . _packetsLost [ kind ] . getLastRawValue ( )
let timestampsBase = this . _timestamps [ kind ] . getLastRawValue ( )
let packetsTotal = 0
let packetsLostTotal = 0
let timestampsTotal = 0
for ( let i = 0 ; i < this . _stagedPackets [ kind ] . length ; i ++ ) {
packetsTotal += ( this . _stagedPackets [ kind ] [ i ] - packetsBase )
packetsBase = this . _stagedPackets [ kind ] [ i ]
packetsLostTotal += ( this . _stagedPacketsLost [ kind ] [ i ] - packetsLostBase )
packetsLostBase = this . _stagedPacketsLost [ kind ] [ i ]
timestampsTotal += ( this . _stagedTimestamps [ kind ] [ i ] - timestampsBase )
timestampsBase = this . _stagedTimestamps [ kind ] [ i ]
}
packetsBase = this . _packets [ kind ] . getLastRawValue ( )
packetsLostBase = this . _packetsLost [ kind ] . getLastRawValue ( )
timestampsBase = this . _timestamps [ kind ] . getLastRawValue ( )
for ( let i = 0 ; i < this . _stagedPackets [ kind ] . length ; i ++ ) {
const weight = ( this . _stagedTimestamps [ kind ] [ i ] - timestampsBase ) / timestampsTotal
timestampsBase = this . _stagedTimestamps [ kind ] [ i ]
this . _stagedPackets [ kind ] [ i ] = packetsBase + packetsTotal * weight
packetsBase = this . _stagedPackets [ kind ] [ i ]
this . _stagedPacketsLost [ kind ] [ i ] = packetsLostBase + packetsLostTotal * weight
packetsLostBase = this . _stagedPacketsLost [ kind ] [ i ]
// Timestamps and round trip time are not distributed, as those
// values are properly updated even if the stats are stalled.
}
} ,
_commitStats ( kind , packets , packetsLost , timestamp , roundTripTime ) {
if ( packets >= 0 ) {
this . _packets [ kind ] . add ( packets )
}
@ -474,7 +598,9 @@ PeerConnectionAnalyzer.prototype = {
// are got from the helper object.
// If there were no transmitted packets in the last stats the ratio
// is higher than 1 both to signal that and to force the quality
// towards a very bad quality faster, but not immediately.
// towards "no transmitted data" faster, but not immediately.
// However, note that the quality will immediately change to "very
// bad quality".
let packetsLostRatio = 1.5
if ( this . _packets [ kind ] . getLastRelativeValue ( ) > 0 ) {
packetsLostRatio = this . _packetsLost [ kind ] . getLastRelativeValue ( ) / this . _packets [ kind ] . getLastRelativeValue ( )
@ -514,6 +640,13 @@ PeerConnectionAnalyzer.prototype = {
return CONNECTION_QUALITY . UNKNOWN
}
// The stats might be in a temporary stall and the analysis is on hold
// until further stats arrive, so until that happens the last known
// state is returned again.
if ( this . _stagedPackets [ kind ] . length > 0 ) {
return this . _connectionQuality [ kind ]
}
const packetsLostRatioWeightedAverage = packetsLostRatio . getWeightedAverage ( )
if ( packetsLostRatioWeightedAverage >= 1 ) {
this . _logStats ( kind , 'No transmitted data, packet lost ratio: ' + packetsLostRatioWeightedAverage )