Browse Source

Suspend hardware clock during output replacement without resetting media

master
evgeny 3 days ago
parent
commit
0a56c01e29
  1. 11
      src/call/call_timeline.cpp
  2. 1
      src/call/call_timeline.h
  3. 21
      tools/chatgui/tests/test_call_timeline.cpp

11
src/call/call_timeline.cpp

@ -143,10 +143,19 @@ void CallTimeline::progress(uint64_t deviceEpoch, uint64_t position, int64_t atU
while (output.count > 1 && output.front().first + output.front().count <= minimumPosition) output.pop();
}
/* Устройство меняется, но PCM/PTS, монотонная позиция и сетевой резерв сохраняются. */
void CallTimeline::suspend_device(uint64_t deviceEpoch) {
if (deviceEpoch != epoch) return;
anchorReceived = -1;
DEBUG_INFO(DEBUG_CATEGORY_CALL, "audio clock device suspended epoch=%llu position=%llu written=%llu",
(unsigned long long)epoch, (unsigned long long)anchorPosition, (unsigned long long)written);
}
call_clock_snapshot CallTimeline::clock(int64_t nowUs) {
call_clock_snapshot result{};
result.epoch = epoch; result.written_samples = written; result.pts_us = lastPts; result.measured = measured;
if (outputFailed || deviceFailed || !haveAnchor || !output.count || nowUs < anchorReceived || nowUs - anchorReceived > 250000) return result;
if (outputFailed || deviceFailed || !haveAnchor || !output.count || anchorReceived < 0 ||
nowUs < anchorReceived || nowUs - anchorReceived > 250000) return result;
uint64_t pending = anchorTime > nowUs ? (uint64_t)std::ceil((anchorTime - nowUs) / sampleUs) : 0;
uint64_t position = pending > anchorPosition ? 0 : anchorPosition - pending;
position = std::max(position, minimumPosition);

1
src/call/call_timeline.h

@ -18,6 +18,7 @@ public:
void read(unsigned samples);
void silence(unsigned samples);
void progress(uint64_t epoch, uint64_t position, int64_t at_us, int64_t received_us, bool measured);
void suspend_device(uint64_t epoch);
call_clock_snapshot clock(int64_t now_us);
private:
struct Span {

21
tools/chatgui/tests/test_call_timeline.cpp

@ -130,8 +130,27 @@ static void test_failure_and_recovery() {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: regressing/impossible device head, overread, bounded overflow, reset recovery");
}
static void test_device_reopen() {
CallTimeline map;
map.reset(7); map.feed(1000000, 1920, 1);
map.source({SourceKind::Copy, SourceError::None, 0, 0, 0, 1920}); map.read(1920);
map.progress(7, 960, 30000, 10000, true);
auto before = map.clock(20000); CHECK(before.valid);
map.suspend_device(6); CHECK(map.clock(20000).valid);
map.suspend_device(7); CHECK(!map.clock(20001).valid);
CHECK(map.clock(20001).written_samples == 1920);
map.progress(7, 1920, 70000, 50000, true);
auto after = map.clock(50000);
CHECK(after.valid && after.epoch == 7 && after.pts_us >= before.pts_us);
CHECK(after.played_samples == 960);
map.suspend_device(7);
map.progress(7, 1000, 80000, 60000, true); // Пересоздание не разрешает регрессию общей позиции.
CHECK(!map.clock(60000).valid);
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: device reopen preserves PCM/epoch and rejects old/regressing anchors");
}
int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
test_boundaries(); test_mix(); test_hardware_anchor_transition(); test_long_run(); test_failure_and_recovery();
test_boundaries(); test_mix(); test_hardware_anchor_transition(); test_long_run(); test_failure_and_recovery(); test_device_reopen();
return 0;
}

Loading…
Cancel
Save