mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-18 02:10:59 +03:00
fix(audio): throttled debug logs (#16172)
* fix(audio): throttled debug logs Signed-off-by: fufesou <linlong1266@gmail.com> * fix(audio): preserve contention counts across throttled reports Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -2250,7 +2250,9 @@ impl AudioBuffer {
|
|||||||
let occupied = lock.occupied_len();
|
let occupied = lock.occupied_len();
|
||||||
drop(lock);
|
drop(lock);
|
||||||
if let Some((discarded, generation)) = discard {
|
if let Some((discarded, generation)) = discard {
|
||||||
log::debug!(
|
hbb_common::throttled_log!(
|
||||||
|
audio_playback::AUDIO_PLAYBACK_LOG_INTERVAL,
|
||||||
|
debug,
|
||||||
"Audio buffer capacity discard: samples={discarded}, generation={generation}"
|
"Audio buffer capacity discard: samples={discarded}, generation={generation}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2441,7 +2443,7 @@ impl AudioHandler {
|
|||||||
}
|
}
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
if self.simple.is_none() {
|
if self.simple.is_none() {
|
||||||
log::debug!("PulseAudio simple binding does not exists");
|
log::trace!("PulseAudio simple binding does not exists");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.audio_decoder.as_mut().map(|(d, buffer)| {
|
self.audio_decoder.as_mut().map(|(d, buffer)| {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use hbb_common::{log, thiserror};
|
use hbb_common::{log, log_throttle::LogThrottle, thiserror};
|
||||||
use ringbuf::{ring_buffer::RbBase, Rb};
|
use ringbuf::{ring_buffer::RbBase, Rb};
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||||
@@ -6,6 +6,8 @@ use std::sync::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub(super) const UNDERRUN_DECLICK_MS: usize = 5;
|
pub(super) const UNDERRUN_DECLICK_MS: usize = 5;
|
||||||
|
pub(super) const AUDIO_PLAYBACK_LOG_INTERVAL: std::time::Duration =
|
||||||
|
std::time::Duration::from_secs(5);
|
||||||
const MILLISECONDS_PER_SECOND: usize = 1_000;
|
const MILLISECONDS_PER_SECOND: usize = 1_000;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@@ -36,17 +38,30 @@ pub(super) struct AudioPlaybackRecovery {
|
|||||||
output_frame: Vec<f32>,
|
output_frame: Vec<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub(super) struct AudioPlaybackStatus {
|
pub(super) struct AudioPlaybackStatus {
|
||||||
pub(super) ready: AtomicBool,
|
pub(super) ready: AtomicBool,
|
||||||
contentions: AtomicUsize,
|
contentions: AtomicUsize,
|
||||||
|
contention_log_throttle: LogThrottle,
|
||||||
buffer_poisoned: AtomicBool,
|
buffer_poisoned: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for AudioPlaybackStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ready: AtomicBool::new(false),
|
||||||
|
contentions: AtomicUsize::new(0),
|
||||||
|
contention_log_throttle: LogThrottle::new(AUDIO_PLAYBACK_LOG_INTERVAL),
|
||||||
|
buffer_poisoned: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AudioPlaybackStatus {
|
impl AudioPlaybackStatus {
|
||||||
pub(super) fn report_errors(&self) {
|
pub(super) fn report_errors(&self) {
|
||||||
let contentions = self.contentions.swap(0, Ordering::Relaxed);
|
if self.contentions.load(Ordering::Relaxed) != 0
|
||||||
if contentions != 0 {
|
&& self.contention_log_throttle.due().is_some()
|
||||||
|
{
|
||||||
|
let contentions = self.contentions.swap(0, Ordering::Relaxed);
|
||||||
log::debug!("Audio playback PCM buffer contention: callbacks={contentions}");
|
log::debug!("Audio playback PCM buffer contention: callbacks={contentions}");
|
||||||
}
|
}
|
||||||
if self.buffer_poisoned.swap(false, Ordering::Relaxed) {
|
if self.buffer_poisoned.swap(false, Ordering::Relaxed) {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use super::{AudioPlaybackConfig, AudioPlaybackError, AudioPlaybackRecovery, AudioPlaybackWriter};
|
use super::{
|
||||||
|
AudioPlaybackConfig, AudioPlaybackError, AudioPlaybackRecovery, AudioPlaybackStatus,
|
||||||
|
AudioPlaybackWriter,
|
||||||
|
};
|
||||||
use ringbuf::{ring_buffer::RbBase, Rb};
|
use ringbuf::{ring_buffer::RbBase, Rb};
|
||||||
use std::{
|
use std::{
|
||||||
sync::{atomic::Ordering, mpsc, Arc, Mutex},
|
sync::{atomic::Ordering, mpsc, Arc, Mutex},
|
||||||
@@ -216,3 +219,44 @@ fn poisoned_playback_buffer_reports_once_without_panicking_in_the_callback() {
|
|||||||
assert_eq!(writer.status.contentions.load(Ordering::Relaxed), 0);
|
assert_eq!(writer.status.contentions.load(Ordering::Relaxed), 0);
|
||||||
assert!(!writer.status.ready.load(Ordering::Acquire));
|
assert!(!writer.status.ready.load(Ordering::Acquire));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contention_counts_accumulate_until_the_next_report() {
|
||||||
|
let status = AudioPlaybackStatus::default();
|
||||||
|
status.report_errors();
|
||||||
|
status.contentions.fetch_add(1, Ordering::Relaxed);
|
||||||
|
status.report_errors();
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), 0);
|
||||||
|
|
||||||
|
let mut total = 0;
|
||||||
|
for callbacks in [3, 7, 2] {
|
||||||
|
total += callbacks;
|
||||||
|
status.contentions.fetch_add(callbacks, Ordering::Relaxed);
|
||||||
|
status.report_errors();
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), total);
|
||||||
|
}
|
||||||
|
status.buffer_poisoned.store(true, Ordering::Relaxed);
|
||||||
|
status.report_errors();
|
||||||
|
assert!(!status.buffer_poisoned.load(Ordering::Relaxed));
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), total);
|
||||||
|
|
||||||
|
std::thread::sleep(super::AUDIO_PLAYBACK_LOG_INTERVAL);
|
||||||
|
status.report_errors();
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contention_reporting_is_independent_between_playbacks() {
|
||||||
|
let first = AudioPlaybackStatus::default();
|
||||||
|
let second = AudioPlaybackStatus::default();
|
||||||
|
for status in [&first, &second] {
|
||||||
|
status.contentions.fetch_add(1, Ordering::Relaxed);
|
||||||
|
status.report_errors();
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), 0);
|
||||||
|
}
|
||||||
|
for status in [&first, &second] {
|
||||||
|
status.contentions.fetch_add(1, Ordering::Relaxed);
|
||||||
|
status.report_errors();
|
||||||
|
assert_eq!(status.contentions.load(Ordering::Relaxed), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ impl CaptureStatsReporter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let stats = std::mem::take(&mut self.pending);
|
let stats = std::mem::take(&mut self.pending);
|
||||||
log::debug!(
|
log::trace!(
|
||||||
"Audio capture PCM handoff stats: observed_max_queued_packets={}, approx_queued_audio_ms={}, capacity_packets={}",
|
"Audio capture PCM handoff stats: observed_max_queued_packets={}, approx_queued_audio_ms={}, capacity_packets={}",
|
||||||
stats.max_queued_packets,
|
stats.max_queued_packets,
|
||||||
stats.max_queued_packets.saturating_mul(CAPTURE_PACKET_MS),
|
stats.max_queued_packets.saturating_mul(CAPTURE_PACKET_MS),
|
||||||
|
|||||||
Reference in New Issue
Block a user