Browse Source

Preserve desktop camera cadence under timestamp jitter

master
evgeny 5 days ago
parent
commit
f3eed42c3f
  1. 15
      tools/chatgui/src/call_video_cadence.h
  2. 8
      tools/chatgui/src/call_video_engine.cpp
  3. 10
      tools/chatgui/tests/test_call_video_encoder.cpp

15
tools/chatgui/src/call_video_cadence.h

@ -0,0 +1,15 @@
#pragma once
#include <algorithm>
#include <cstdint>
// Сетка 30 fps с допуском половины периода: дрожание PTS не превращает 30 fps в 15.
class CallVideoCadence {
int64_t next = -1;
public:
bool accept(int64_t pts) {
constexpr int64_t period = 1000000 / 30;
if (next >= 0 && pts < next - period / 2) return false;
next = next < 0 ? pts + period : std::max(next + period, pts + period / 2);
return true;
}
};

8
tools/chatgui/src/call_video_engine.cpp

@ -1,6 +1,7 @@
#include "call_video_engine.h"
#include "call_video_encoder.h"
#include "call_video_decoder.h"
#include "call_video_cadence.h"
#include "../../../src/call/call_video_port.h"
#include "../../../src/call/call_audio.h"
#include "debug_config.h"
@ -174,7 +175,7 @@ void CallVideoEngine::transmit() {
int64_t reportAt = nowUs(), encodeMax = 0;
uint64_t captureBase = 0;
unsigned encodedCount = 0, keyCount = 0, sendBusy = 0, paced = 0;
int64_t pacedPts = -1;
CallVideoCadence cadence;
while (m->running) {
struct call_video_port_status state{};
if (call_video_port_status(m->id, &state) < 0) break;
@ -210,10 +211,6 @@ void CallVideoEngine::transmit() {
}
if (format.generation && state.have_peer && !call_video_format_supported(&caps, &state.peer, &format)) appliedRevision = 0;
if (!enabled || !state.have_peer || pts <= lastPts) { idle(); continue; }
if (lastPts >= 0 && pts - lastPts < 33333) {
if (pacedPts != pts) { pacedPts = pts; ++paced; }
idle(); continue;
}
if (revision != appliedRevision) {
if (lastKey >= 0 && pts - lastKey < 1000000) { idle(); continue; }
call_video_format next{format.generation + 1, (uint16_t)width, (uint16_t)height, 30, 0, (uint8_t)codec, 0, 4000000, 0};
@ -243,6 +240,7 @@ void CallVideoEngine::transmit() {
call_video_port_applied(m->id, format.generation, request, 0);
}
#ifdef HAVE_STREAM_CAPTURE
if (!cadence.accept(pts)) { lastPts = pts; ++paced; idle(); continue; }
QImage image = raw.toImage();
if (image.isNull()) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera frame conversion failed"); lastPts = pts; continue; }
LiveEncodedFrame output; CallVideoRateCompletion completion;

10
tools/chatgui/tests/test_call_video_encoder.cpp

@ -5,6 +5,7 @@
#include <clocale>
#include "../src/call_video_encoder.h"
#include "../src/call_video_decoder.h"
#include "../src/call_video_cadence.h"
#include "../../../src/call/call_video_bitstream.h"
extern "C" {
#include "debug_config.h"
@ -41,6 +42,15 @@ class CallVideoEncoderTest : public QObject {
return {17, 640, 360, 30, 0, (uint8_t)codec, (uint8_t)(codec == 1 ? 31 : 93), 4000000, (uint8_t)(codec == 1 ? 2 : 1)};
}
private slots:
void captureCadence() {
CallVideoCadence jittered, fast;
for (int i = 0; i < 300; i++) QVERIFY(jittered.accept(1000000 + i * 33333 + (i % 2 ? -1500 : 1500)));
int accepted = 0;
for (int i = 0; i < 600; i++) if (fast.accept(1000000 + i * 1000000LL / 60)) accepted++;
QCOMPARE(accepted, 300);
QVERIFY(jittered.accept(30000000)); // Долгая пауза не создаёт догоняющий burst.
QVERIFY(!jittered.accept(30001000));
}
void initTestCase() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_CALL, DEBUG_LEVEL_DEBUG);
av_log_set_level(AV_LOG_ERROR);

Loading…
Cancel
Save