Prevent delayed camera startup from shifting video into the future
This commit is contained in:
1 parent
ac3801761e
commit
4c2d8aa4cc
3 files changed
+57
-14
No files matched your search
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Первый callback может опоздать при запуске камеры. Его задержка не должна навсегда сдвигать PTS в будущее.
|
||||
struct CallVideoCaptureClock {
|
||||
int64_t source = -1, offset = 0, correction = 0;
|
||||
int64_t map(int64_t next, int64_t arrival) {
|
||||
if (next < 0) { source = -1; return arrival; }
|
||||
if (source < 0 || next < source) offset = arrival - next;
|
||||
source = next;
|
||||
int64_t pts = next + offset;
|
||||
if (pts > arrival) {
|
||||
correction += pts - arrival;
|
||||
offset -= pts - arrival;
|
||||
pts = arrival;
|
||||
}
|
||||
return pts;
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "call_video_encoder.h"
|
||||
#include "call_video_decoder.h"
|
||||
#include "call_video_cadence.h"
|
||||
#include "call_video_capture_clock.h"
|
||||
#include "call_camera_rate.h"
|
||||
#include "../../../src/call/call_video_port.h"
|
||||
#include "../../../src/call/call_audio.h"
|
||||
@@ -32,7 +33,8 @@ struct CallVideoEngine::Impl {
|
||||
uint64_t id = 0, revision = 1;
|
||||
bool enabled = false;
|
||||
int width = 640, height = 480, codec = 0;
|
||||
int64_t pts = -1, backend = -1, anchor = -1;
|
||||
int64_t pts = -1;
|
||||
CallVideoCaptureClock captureClock;
|
||||
uint64_t captured = 0;
|
||||
QImage shown;
|
||||
uint64_t shownSerial = 0, deliveredSerial = 0;
|
||||
@@ -109,7 +111,7 @@ void CallVideoEngine::closeCamera() {
|
||||
if (m->camera) { disconnect(m->camera.get(), nullptr, this, nullptr); m->camera->stop(); }
|
||||
m->cameraRate.restore();
|
||||
m->capture.reset(); m->camera.reset(); m->sink.reset();
|
||||
{ std::lock_guard<std::mutex> guard(m->lock); m->raw = {}; m->pts = m->backend = m->anchor = -1; }
|
||||
{ std::lock_guard<std::mutex> guard(m->lock); m->raw = {}; m->pts = -1; m->captureClock = {}; }
|
||||
#endif
|
||||
m->previewPts = -1; m->previewVisible = false; m->previewReportAt = nowUs(); m->previewFrames = 0; m->previewMaxUs = 0;
|
||||
emit localImageChanged({});
|
||||
@@ -152,15 +154,12 @@ void CallVideoEngine::setVideo(bool enabled, const QByteArray& cameraId, int wid
|
||||
if (!frame.isValid() || !m->running) return;
|
||||
std::lock_guard<std::mutex> guard(m->lock);
|
||||
if (revision != m->revision) return;
|
||||
int64_t pts = nowUs();
|
||||
if (frame.startTime() >= 0) {
|
||||
if (m->backend < 0) {
|
||||
m->backend = frame.startTime(); m->anchor = pts;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera clock source=%lld monotonic=%lld calibration=arrival-estimate",
|
||||
(long long)m->backend, (long long)m->anchor);
|
||||
}
|
||||
pts = m->anchor + frame.startTime() - m->backend;
|
||||
}
|
||||
const int64_t arrival = nowUs(), source = frame.startTime();
|
||||
if (m->pts < 0) DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera clock source=%lld monotonic=%lld calibration=arrival-upper-bound",
|
||||
(long long)source, (long long)arrival);
|
||||
if (m->captureClock.source >= 0 && source < m->captureClock.source)
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera clock reset source=%lld previous=%lld", (long long)source, (long long)m->captureClock.source);
|
||||
const int64_t pts = m->captureClock.map(source, arrival);
|
||||
if (pts > m->pts) { m->raw = frame; m->pts = pts; ++m->captured; }
|
||||
}, Qt::DirectConnection);
|
||||
connect(m->camera.get(), &QCamera::errorOccurred, this, [this](QCamera::Error, const QString& error) {
|
||||
@@ -201,13 +200,14 @@ void CallVideoEngine::transmit() {
|
||||
struct call_video_port_status state{};
|
||||
if (call_video_port_status(m->id, &state) < 0) break;
|
||||
if (state.error) { m->report(tr("Core rejected video format")); break; }
|
||||
uint64_t revision, captured; bool enabled; int width, height, codec; int64_t pts;
|
||||
uint64_t revision, captured; bool enabled; int width, height, codec; int64_t pts, clockCorrection;
|
||||
#ifdef HAVE_STREAM_CAPTURE
|
||||
QVideoFrame raw;
|
||||
#endif
|
||||
{ std::lock_guard<std::mutex> guard(m->lock);
|
||||
revision = m->revision; enabled = m->enabled; width = m->width; height = m->height; codec = m->codec; pts = m->pts;
|
||||
captured = m->captured;
|
||||
clockCorrection = m->captureClock.correction;
|
||||
#ifdef HAVE_STREAM_CAPTURE
|
||||
raw = m->raw;
|
||||
#endif
|
||||
@@ -216,10 +216,10 @@ void CallVideoEngine::transmit() {
|
||||
if (now - reportAt >= 1000000) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL,
|
||||
"video capture flow call=%llu interval_us=%lld captured=%llu encoded=%u keys=%u send_busy=%u paced=%u "
|
||||
"encode_max_us=%lld capture_age_us=%lld pts=%lld",
|
||||
"encode_max_us=%lld capture_age_us=%lld pts=%lld clock_correction_us=%lld",
|
||||
(unsigned long long)m->id, (long long)(now - reportAt), (unsigned long long)(captured - captureBase),
|
||||
encodedCount, keyCount, sendBusy, paced, (long long)encodeMax,
|
||||
(long long)(pts >= 0 ? now - pts : 0), (long long)pts);
|
||||
(long long)(pts >= 0 ? now - pts : 0), (long long)pts, (long long)clockCorrection);
|
||||
reportAt = now; captureBase = captured; encodedCount = keyCount = sendBusy = paced = 0; encodeMax = 0;
|
||||
}
|
||||
if (revision != userRevision) { userRevision = revision; excluded = 0; }
|
||||
|
||||
Reference in new issue
Block a user