Browse Source

Ограничить камеру звонка до 30 fps перед конвертацией

stream-multiview
evgeny 6 days ago
parent
commit
21e4821e22
  1. 14
      tools/chatgui/src/call_camera_worker.cpp
  2. 11
      tools/chatgui/src/call_video_engine.cpp
  3. 14
      tools/chatgui/tests/test_call_camera_capture.cpp

14
tools/chatgui/src/call_camera_worker.cpp

@ -2,6 +2,7 @@
#include "call_camera_protocol.h" #include "call_camera_protocol.h"
#include "call_camera_buffer.h" #include "call_camera_buffer.h"
#include "call_video_capture_clock.h" #include "call_video_capture_clock.h"
#include "call_video_cadence.h"
#include "call_camera_rate.h" #include "call_camera_rate.h"
#include "../../../src/call/call_video_playout.h" #include "../../../src/call/call_video_playout.h"
#include "debug_config.h" #include "debug_config.h"
@ -79,11 +80,12 @@ class CameraWorker : public QObject {
std::shared_ptr<Pending> pending; std::shared_ptr<Pending> pending;
QMetaObject::Connection captureConnection, errorConnection; QMetaObject::Connection captureConnection, errorConnection;
CallVideoCaptureClock clock; CallVideoCaptureClock clock;
CallVideoCadence cadence;
quint64 generation = 0, inFlight = 0; quint64 generation = 0, inFlight = 0;
bool sending = false; bool sending = false;
int width = 640, height = 480; int width = 640, height = 480;
qint64 lastPts = -1, reportAt = 0, convertMax = 0; qint64 lastPts = -1, reportAt = 0, convertMax = 0;
unsigned frames = 0; unsigned frames = 0, paced = 0;
void send(const QByteArray& data) { void send(const QByteArray& data) {
if (socket.write(data) != data.size()) { if (socket.write(data) != data.size()) {
@ -113,6 +115,8 @@ class CameraWorker : public QObject {
DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera skips old frame age_us=%lld pts=%lld last=%lld", DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera skips old frame age_us=%lld pts=%lld last=%lld",
(long long)(started - pts), (long long)pts, (long long)lastPts); return; (long long)(started - pts), (long long)pts, (long long)lastPts); return;
} }
// Ограничиваем поток до дорогой конвертации и IPC; preview и TX получают те же 30 fps.
if (!cadence.accept(pts)) { ++paced; return; }
stage(QStringLiteral("convert")); stage(QStringLiteral("convert"));
QImage image = raw.toImage(); QImage image = raw.toImage();
if (image.isNull()) { if (image.isNull()) {
@ -133,9 +137,9 @@ class CameraWorker : public QObject {
stage(QStringLiteral("release")); image = {}; raw = {}; stage(QStringLiteral("running")); stage(QStringLiteral("release")); image = {}; raw = {}; stage(QStringLiteral("running"));
convertMax = std::max(convertMax, call_video_playout_now_us() - started); convertMax = std::max(convertMax, call_video_playout_now_us() - started);
if (started - reportAt >= 1000000) { if (started - reportAt >= 1000000) {
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "camera flow frames=%u convert_max_us=%lld age_us=%lld clock_correction_us=%lld", DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "camera flow frames=%u paced=%u convert_max_us=%lld age_us=%lld clock_correction_us=%lld",
frames, (long long)convertMax, (long long)(converted - pts), (long long)clock.correction); frames, paced, (long long)convertMax, (long long)(converted - pts), (long long)clock.correction);
reportAt = started; convertMax = 0; frames = 0; reportAt = started; convertMax = 0; frames = paced = 0;
} }
} }
bool receive(quint8 type, quint64 revision, QDataStream& in) { bool receive(quint8 type, quint64 revision, QDataStream& in) {
@ -152,7 +156,7 @@ class CameraWorker : public QObject {
} else if (type == Configure) { } else if (type == Configure) {
bool enabled = false; QByteArray device; qint32 w = 0, h = 0; in >> enabled >> device >> w >> h; bool enabled = false; QByteArray device; qint32 w = 0, h = 0; in >> enabled >> device >> w >> h;
if (in.status() != QDataStream::Ok || w < 1 || h < 1 || w > 1280 || h > 720 || !pixels) return false; if (in.status() != QDataStream::Ok || w < 1 || h < 1 || w > 1280 || h > 720 || !pixels) return false;
stop(); generation = revision; width = w; height = h; clock = {}; lastPts = -1; stop(); generation = revision; width = w; height = h; clock = {}; cadence = {}; lastPts = -1;
if (!enabled) { stage(QStringLiteral("idle")); return true; } if (!enabled) { stage(QStringLiteral("idle")); return true; }
stage(QStringLiteral("start")); stage(QStringLiteral("start"));
pending = std::make_shared<Pending>(); pending = std::make_shared<Pending>();

11
tools/chatgui/src/call_video_engine.cpp

@ -1,7 +1,6 @@
#include "call_video_engine.h" #include "call_video_engine.h"
#include "call_video_encoder.h" #include "call_video_encoder.h"
#include "call_video_decoder.h" #include "call_video_decoder.h"
#include "call_video_cadence.h"
#include "call_camera_capture.h" #include "call_camera_capture.h"
#include "../../../src/call/call_video_port.h" #include "../../../src/call/call_video_port.h"
#include "../../../src/call/call_video_playout.h" #include "../../../src/call/call_video_playout.h"
@ -167,8 +166,7 @@ void CallVideoEngine::transmit() {
bool forceKey = true; bool forceKey = true;
int64_t reportAt = nowUs(), encodeMax = 0, inputAgeMax = 0; int64_t reportAt = nowUs(), encodeMax = 0, inputAgeMax = 0;
uint64_t captureBase = 0; uint64_t captureBase = 0;
unsigned encodedCount = 0, keyCount = 0, sendBusy = 0, paced = 0, expired = 0; unsigned encodedCount = 0, keyCount = 0, sendBusy = 0, expired = 0;
CallVideoCadence cadence;
uint64_t wakeSerial = 0; uint64_t wakeSerial = 0;
auto idle = [this, &wakeSerial] { auto idle = [this, &wakeSerial] {
std::unique_lock<std::mutex> guard(m->lock); std::unique_lock<std::mutex> guard(m->lock);
@ -188,12 +186,12 @@ void CallVideoEngine::transmit() {
int64_t now = nowUs(); int64_t now = nowUs();
if (now - reportAt >= 1000000) { if (now - reportAt >= 1000000) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG,
"video capture flow call=%llu interval_us=%lld captured=%llu encoded=%u keys=%u send_busy=%u paced=%u " "video capture flow call=%llu interval_us=%lld captured=%llu encoded=%u keys=%u send_busy=%u "
"expired=%u encode_max_us=%lld input_age_max_us=%lld capture_age_us=%lld pts=%lld clock_correction_us=%lld", "expired=%u encode_max_us=%lld input_age_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), (unsigned long long)m->id, (long long)(now - reportAt), (unsigned long long)(captured - captureBase),
encodedCount, keyCount, sendBusy, paced, expired, (long long)encodeMax, (long long)inputAgeMax, encodedCount, keyCount, sendBusy, expired, (long long)encodeMax, (long long)inputAgeMax,
(long long)(pts >= 0 ? now - pts : 0), (long long)pts, (long long)clockCorrection); (long long)(pts >= 0 ? now - pts : 0), (long long)pts, (long long)clockCorrection);
reportAt = now; captureBase = captured; encodedCount = keyCount = sendBusy = paced = expired = 0; reportAt = now; captureBase = captured; encodedCount = keyCount = sendBusy = expired = 0;
encodeMax = inputAgeMax = 0; encodeMax = inputAgeMax = 0;
} }
if (revision != userRevision) { userRevision = revision; excluded = 0; } if (revision != userRevision) { userRevision = revision; excluded = 0; }
@ -236,7 +234,6 @@ void CallVideoEngine::transmit() {
call_video_port_applied(m->id, format.generation, request, 0); call_video_port_applied(m->id, format.generation, request, 0);
} }
#ifdef HAVE_STREAM_CAPTURE #ifdef HAVE_STREAM_CAPTURE
if (!cadence.accept(pts)) { lastPts = pts; ++paced; idle(); continue; }
if (image.isNull() || nowUs() - pts > CallCameraMaxFrameAgeUs) { if (image.isNull() || nowUs() - pts > CallCameraMaxFrameAgeUs) {
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX skips old capture age_us=%lld", (long long)(nowUs() - pts)); DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX skips old capture age_us=%lld", (long long)(nowUs() - pts));
lastPts = pts; ++expired; continue; lastPts = pts; ++expired; continue;

14
tools/chatgui/tests/test_call_camera_capture.cpp

@ -75,6 +75,7 @@ public:
bool start(const QByteArray& id, int w, int h) override { bool start(const QByteArray& id, int w, int h) override {
device = id; width = w; height = h; record("start:" + id); device = id; width = w; height = h; record("start:" + id);
if (id == "start-stall") QThread::sleep(60); if (id == "start-stall") QThread::sleep(60);
timer.setInterval(id == "fast" ? 5 : 33);
timer.start(); return true; timer.start(); return true;
} }
void stop() override { void stop() override {
@ -111,6 +112,19 @@ private slots:
path = directory.filePath(QString::number(QDateTime::currentMSecsSinceEpoch())); path = directory.filePath(QString::number(QDateTime::currentMSecsSinceEpoch()));
qputenv("UTUN_CAMERA_TEST_LOG", path.toUtf8()); qputenv("UTUN_CAMERA_TEST_LOG", path.toUtf8());
} }
void fastCameraKeepsThirtyFps() {
auto capture = std::make_unique<CallCameraCapture>();
unsigned frames = 0;
connect(capture.get(), &CallCameraCapture::frameReady, this, [&](const QImage&, qint64, qint64) { ++frames; });
QSignalSpy errors(capture.get(), &CallCameraCapture::failed);
capture->configure(true, "fast", 640, 480);
QTRY_VERIFY_WITH_TIMEOUT(frames >= 5, 3000);
const unsigned before = frames; QElapsedTimer timer; timer.start(); QTest::qWait(2000);
const double fps = (frames - before) * 1000.0 / timer.elapsed();
qInfo("fast camera converted output fps=%.2f frames=%u", fps, frames - before);
QVERIFY(fps >= 25.0); QVERIFY(fps <= 31.0); QCOMPARE(errors.size(), 0);
const qint64 child = pid(); capture.reset(); QTRY_VERIFY_WITH_TIMEOUT(!alive(child), 3000);
}
void latestFrameAndGeneration() { void latestFrameAndGeneration() {
auto capture = std::make_unique<CallCameraCapture>(); auto capture = std::make_unique<CallCameraCapture>();
unsigned frames = 0; qint64 maxAge = 0; QColor color; bool expectBlue = false, wrongGeneration = false; unsigned frames = 0; qint64 maxAge = 0; QColor color; bool expectBlue = false, wrongGeneration = false;

Loading…
Cancel
Save