|
|
|
|
@ -11,10 +11,10 @@ double CallTimeline::at(const Span& s, double position) {
|
|
|
|
|
|
|
|
|
|
void CallTimeline::reset(uint64_t newEpoch) { |
|
|
|
|
input.clear(); ready.clear(); output.clear(); |
|
|
|
|
inputWritten = generated = consumed = written = anchorPosition = minimumPosition = lastSourceEpoch = 0; |
|
|
|
|
inputWritten = generated = consumed = written = anchorPosition = minimumPosition = 0; |
|
|
|
|
epoch = newEpoch; anchorTime = anchorReceived = 0; lastPts = -1; |
|
|
|
|
endPts = 0; endEpoch = 0; sourceError = 0; |
|
|
|
|
outputFailed = haveAnchor = measured = endValid = false; |
|
|
|
|
outputFailed = deviceFailed = haveAnchor = measured = endValid = false; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio timeline reset epoch=%llu", (unsigned long long)epoch); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -78,6 +78,17 @@ void CallTimeline::source(const soundtouch::SourceSpan& s) {
|
|
|
|
|
|
|
|
|
|
void CallTimeline::appendOutput(const Span& s) { |
|
|
|
|
if (outputFailed) return; |
|
|
|
|
// Источники без PTS и длинные локальные паузы не расходуют по записи на каждый pull.
|
|
|
|
|
if (output.count) { |
|
|
|
|
Span& previous = output.data[(output.head + output.count - 1) % output.data.size()]; |
|
|
|
|
bool sameHold = previous.pts == previous.end && s.pts == s.end && previous.end == s.pts; |
|
|
|
|
if (previous.first + previous.count == s.first && previous.epoch == s.epoch && previous.valid == s.valid |
|
|
|
|
&& (!s.valid || sameHold) && previous.count <= UINT32_MAX - s.count) { |
|
|
|
|
previous.count += s.count; previous.end = s.end; |
|
|
|
|
endPts = s.end; endEpoch = s.epoch; endValid = s.valid; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!output.push(s)) { |
|
|
|
|
outputFailed = true; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio timeline output overflow epoch=%llu written=%llu anchor=%llu spans=%zu", |
|
|
|
|
@ -118,12 +129,12 @@ void CallTimeline::silence(unsigned samples) {
|
|
|
|
|
|
|
|
|
|
/* at_us — время конца известного устройству участка; будущая отметка учитывает очередь backend. */ |
|
|
|
|
void CallTimeline::progress(uint64_t deviceEpoch, uint64_t position, int64_t atUs, int64_t receivedUs, bool isMeasured) { |
|
|
|
|
if (deviceEpoch != epoch) return; |
|
|
|
|
if (deviceEpoch != epoch || deviceFailed) return; |
|
|
|
|
if (position > written || atUs < 0 || receivedUs < 0 || (haveAnchor && (position < anchorPosition || receivedUs < anchorReceived))) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "audio clock invalid device progress epoch=%llu position=%llu written=%llu time=%lld received=%lld", |
|
|
|
|
(unsigned long long)epoch, (unsigned long long)position, (unsigned long long)written, |
|
|
|
|
(long long)atUs, (long long)receivedUs); |
|
|
|
|
haveAnchor = false; return; |
|
|
|
|
deviceFailed = true; haveAnchor = false; return; |
|
|
|
|
} |
|
|
|
|
anchorPosition = position; anchorTime = atUs; anchorReceived = receivedUs; measured = isMeasured; haveAnchor = true; |
|
|
|
|
uint64_t pending = atUs > receivedUs ? (uint64_t)std::ceil((atUs - receivedUs) / sampleUs) : 0; |
|
|
|
|
@ -135,7 +146,7 @@ void CallTimeline::progress(uint64_t deviceEpoch, uint64_t position, int64_t atU
|
|
|
|
|
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 || !haveAnchor || !output.count || nowUs < anchorReceived || nowUs - anchorReceived > 250000) return result; |
|
|
|
|
if (outputFailed || deviceFailed || !haveAnchor || !output.count || 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); |
|
|
|
|
@ -147,7 +158,7 @@ call_clock_snapshot CallTimeline::clock(int64_t nowUs) {
|
|
|
|
|
int64_t pts = (int64_t)std::llround(at(s, position)); |
|
|
|
|
if (pts < 0 || (lastPts >= 0 && pts < lastPts - 100000)) return result; |
|
|
|
|
result.held_back_us = pts < lastPts ? (uint32_t)(lastPts - pts) : 0; |
|
|
|
|
lastPts = std::max(pts, lastPts); lastSourceEpoch = s.epoch; |
|
|
|
|
lastPts = std::max(pts, lastPts); |
|
|
|
|
result.pts_us = lastPts; result.valid = 1; |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|