| 2337 | }; |
| 2338 | |
| 2339 | static AudioHealthStatus formatAudioHealth(const NetworkDiagnosticsSample& sample) |
| 2340 | { |
| 2341 | if (sample.audioStreamCount <= 0) { |
| 2342 | return {AudioHealthState::Waiting, "Waiting for RX audio"}; |
| 2343 | } |
| 2344 | if (sample.audioLastPacketAgeMs > 1000) { |
| 2345 | return {AudioHealthState::NoRecentAudio, "No recent RX audio"}; |
| 2346 | } |
| 2347 | if (sample.audioFeedRateHz <= 0.0) { |
| 2348 | return {AudioHealthState::Measuring, "Measuring RX audio"}; |
| 2349 | } |
| 2350 | |
| 2351 | const bool underfed = sample.audioFeedDeficitMs >= 20.0 |
| 2352 | || sample.audioFeedRateHz < AudioEngine::DEFAULT_SAMPLE_RATE * 0.95; |
| 2353 | if (underfed) { |
| 2354 | return {AudioHealthState::SlowDelivery, "Audio stream is arriving too slowly"}; |
| 2355 | } |
| 2356 | const bool recurringLatePackets = sample.audioLatePacketsPerSecond >= 2.0 |
| 2357 | || (sample.audioLatePacketsPerSecond >= 1.0 && sample.audioFeedDeficitMs >= 10.0); |
| 2358 | if (recurringLatePackets) { |
| 2359 | return {AudioHealthState::RepeatedLatePackets, "Audio packets are repeatedly late"}; |
| 2360 | } |
| 2361 | if (sample.audioPacketGaps > 0) { |
| 2362 | return {AudioHealthState::PacketGaps, "Audio packet gaps detected"}; |
| 2363 | } |
| 2364 | if (sample.underrunsPerSecond > 0.0) { |
| 2365 | return {AudioHealthState::BufferLow, "Playback buffer ran low recently"}; |
| 2366 | } |
| 2367 | return {AudioHealthState::Healthy, "Healthy"}; |
| 2368 | } |
| 2369 | |
| 2370 | static QString audioHealthStyle(AudioHealthState state) |
| 2371 | { |