Browse Source

Expose desktop output latency and verify hardware clock transitions

master
evgeny 5 days ago
parent
commit
e1bb12d16a
  1. 1
      tools/chatgui/src/call_audio_engine.h
  2. 14
      tools/chatgui/src/callwindow.cpp
  3. 26
      tools/chatgui/tests/test_call_timeline.cpp

1
tools/chatgui/src/call_audio_engine.h

@ -33,6 +33,7 @@ public:
bool isActive() const { return m_active.load(); }
quint64 callId() const { return m_callId.load(); }
VoiceAudioIo::OutputLatency outputLatency() const { return m_voice.outputLatency(); }
/* Mute: пир слышит тишину (отправляем нулевые кадры вместо захваченного PCM). */
void setMuted(bool muted);

14
tools/chatgui/src/callwindow.cpp

@ -506,10 +506,20 @@ void CallWindow::updateDuration() {
void CallWindow::setAudioStats(int rtt, int buffer, int tempo, int dropped, int underruns,
int minimum, int maximum, int reserve) {
m_statsLabel->setText(QCoreApplication::translate("ChatGui",
QString text = QCoreApplication::translate("ChatGui",
"Buffer: %1 ms · Reserve: %2 ms\nOver 10 s: minimum %3 · maximum %4 ms\nSpeed: %5× · RTT: %6 "
"ms\nBuffer drops: %7 · Underruns: %8")
.arg(buffer).arg(reserve).arg(minimum).arg(maximum)
.arg(tempo / 100.0, 0, 'f', 2).arg(rtt).arg(dropped).arg(underruns));
.arg(tempo / 100.0, 0, 'f', 2).arg(rtt).arg(dropped).arg(underruns);
auto* audio = CallAudioEngine::instance();
if (audio->callId() == m_callId) {
auto output = audio->outputLatency();
auto value = [](int ms) { return ms >= 0 ? QString::number(ms) : QStringLiteral("—"); };
QString total = value(output.outputMs);
if (output.outputMs >= 0 && !output.measured) total.prepend(QStringLiteral("~"));
text += QLatin1Char('\n') + QCoreApplication::translate("ChatGui", "Audio output: %1 ms (includes app queue: %2 ms)")
.arg(total).arg(value(output.queueMs));
}
m_statsLabel->setText(text);
m_statsLabel->setVisible(m_state == Active);
}

26
tools/chatgui/tests/test_call_timeline.cpp

@ -53,6 +53,30 @@ static void test_mix() {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: weighted overlap, input boundary, source discontinuity");
}
static void test_hardware_anchor_transition() {
CallTimeline map;
map.reset(1);
for (unsigned i = 0; i < 20; ++i) {
map.feed(1000000 + i * 20000, 960, 1);
map.source({SourceKind::Copy, SourceError::None, i * 960, i * 960, 0, 960});
map.read(960);
}
map.progress(1, 9600, 1020000, 1000000, false);
auto before = map.clock(1000000);
CHECK(before.valid && before.played_samples == 8640 && !before.measured);
// Новая аппаратная метка обнаружила ещё 100мс HAL: не регрессируем позицию, ждём реальный звук.
map.progress(1, 10560, 1140000, 1020000, true);
auto after = map.clock(1020000);
CHECK(after.valid && after.measured && after.pts_us == before.pts_us);
CHECK(map.clock(1100000).pts_us == before.pts_us);
CHECK(map.clock(1120000).pts_us > before.pts_us);
CHECK(!map.clock(1300000).valid);
// После смены выхода позиция написанного PCM продолжается, меняется лишь физический срок.
map.progress(1, 19200, 1320000, 1300000, true);
CHECK(map.clock(1300000).valid && map.clock(1300000).pts_us >= after.pts_us);
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: hardware timestamp acquisition, route change, no clock regression");
}
static void test_long_run() {
CallTimeline map;
map.reset(1);
@ -108,6 +132,6 @@ static void test_failure_and_recovery() {
int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
test_boundaries(); test_mix(); test_long_run(); test_failure_and_recovery();
test_boundaries(); test_mix(); test_hardware_anchor_transition(); test_long_run(); test_failure_and_recovery();
return 0;
}

Loading…
Cancel
Save