Select camera capture rate and verify 30/60 fps pacing
This commit is contained in:
1 parent
ee1e28bff9
commit
a852a9738b
3 files changed
+36
-15
No files matched your search
@@ -10,7 +10,7 @@ class CallCameraSource : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual CallCameraDevices devices() = 0;
|
||||
virtual bool start(const QByteArray& device, int width, int height) = 0;
|
||||
virtual bool start(const QByteArray& device, int width, int height, int fps) = 0;
|
||||
virtual void stop() = 0;
|
||||
signals:
|
||||
void devicesChanged();
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool start(const QByteArray& device, int width, int height) override {
|
||||
bool start(const QByteArray& device, int width, int height, int fps) override {
|
||||
QCameraDevice selected;
|
||||
for (const auto& item : QMediaDevices::videoInputs()) if (item.id() == device) selected = item;
|
||||
if (selected.isNull()) { emit error(QStringLiteral("Camera is unavailable")); return false; }
|
||||
@@ -59,18 +59,18 @@ public:
|
||||
QCameraFormat best;
|
||||
for (const auto& format : selected.videoFormats()) {
|
||||
if (!stream_video_camera_supports(format.resolution(), format.minFrameRate(), format.maxFrameRate(),
|
||||
{QSize(width, height), 1})) continue;
|
||||
{QSize(width, height), fps})) continue;
|
||||
if (best.isNull() || format.maxFrameRate() < best.maxFrameRate()) best = format;
|
||||
}
|
||||
if (best.isNull()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "camera mode unavailable device=%s size=%dx%d fps=30", device.constData(), width, height);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "camera mode unavailable device=%s size=%dx%d output_fps=%d", device.constData(), width, height, fps);
|
||||
emit error(QStringLiteral("Selected camera mode is unavailable")); return false;
|
||||
}
|
||||
camera->setCameraFormat(best);
|
||||
const auto format = camera->cameraFormat();
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera selected device=%s size=%dx%d pixel=%d fps=%.3f..%.3f",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera selected device=%s size=%dx%d pixel=%d capture_fps=%.3f..%.3f output_fps=%d",
|
||||
device.constData(), format.resolution().width(), format.resolution().height(), format.pixelFormat(),
|
||||
format.minFrameRate(), format.maxFrameRate());
|
||||
format.minFrameRate(), format.maxFrameRate(), fps);
|
||||
connect(camera.get(), &QCamera::activeChanged, this, [this, device](bool active) {
|
||||
if (active) rate.activate(device); else rate.restore();
|
||||
});
|
||||
@@ -211,7 +211,7 @@ class CameraWorker : public QObject {
|
||||
post = !slot->posted; slot->posted = true; }
|
||||
if (post) QMetaObject::invokeMethod(this, [this, slot, revision] { convert(slot, revision); }, Qt::QueuedConnection);
|
||||
}, Qt::DirectConnection);
|
||||
if (source->start(device, width, height)) stage(QStringLiteral("running"));
|
||||
if (source->start(device, width, height, fps)) stage(QStringLiteral("running"));
|
||||
} else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "../src/call_camera_source.h"
|
||||
#include "../src/call_camera_buffer.h"
|
||||
#include "../src/call_video_cadence.h"
|
||||
#include "../../../src/call/call_video_playout.h"
|
||||
#include "debug_config.h"
|
||||
#include <QtTest>
|
||||
@@ -73,12 +74,13 @@ public:
|
||||
});
|
||||
}
|
||||
CallCameraDevices devices() override {
|
||||
return {{QStringLiteral("Test camera"), "red", {{QSize(1920, 1080), 30}, {QSize(1280, 720), 30}, {QSize(640, 480), 30}}}};
|
||||
return {{QStringLiteral("Test camera"), "red", {{QSize(1920, 1080), 30}, {QSize(1280, 720), 30},
|
||||
{QSize(640, 480), 30}, {QSize(640, 480), 60}}}};
|
||||
}
|
||||
bool start(const QByteArray& id, int w, int h) override {
|
||||
bool start(const QByteArray& id, int w, int h, int fps) override {
|
||||
device = id; width = w; height = h; record("start:" + id);
|
||||
if (id == "start-stall") QThread::sleep(60);
|
||||
timer.setInterval(id == "fast" ? 5 : 33);
|
||||
timer.setInterval(id == "fast" ? 3 : 1000 / fps);
|
||||
timer.start(); return true;
|
||||
}
|
||||
void stop() override {
|
||||
@@ -115,21 +117,40 @@ private slots:
|
||||
path = directory.filePath(QString::number(QDateTime::currentMSecsSinceEpoch()));
|
||||
qputenv("UTUN_CAMERA_TEST_LOG", path.toUtf8());
|
||||
}
|
||||
void fastCameraKeepsThirtyFps() {
|
||||
void captureCadence() {
|
||||
for (int input : {50, 100, 330}) for (int output : {30, 50, 60}) {
|
||||
if (output > input) continue;
|
||||
CallVideoCadence cadence(output); unsigned accepted = 0;
|
||||
for (int i = 0; i < input * 3; ++i) if (cadence.accept(1000000 + i * 1000000ll / input)) ++accepted;
|
||||
qInfo("capture cadence input=%d output=%d accepted=%u/3s", input, output, accepted);
|
||||
QVERIFY(std::abs(int(accepted) - output * 3) <= 1);
|
||||
QVERIFY(!cadence.accept(1000000));
|
||||
QVERIFY(cadence.accept(5000000)); QVERIFY(!cadence.accept(5000001));
|
||||
}
|
||||
CallVideoCadence jitter(30); unsigned accepted = 0;
|
||||
for (int i = 0; i < 90; ++i) if (jitter.accept(1000000 + i * 1000000ll / 30 + (i % 2 ? 700 : -700))) ++accepted;
|
||||
QCOMPARE(accepted, 90u);
|
||||
}
|
||||
void fastCameraKeepsSelectedFps_data() {
|
||||
QTest::addColumn<int>("fps"); QTest::newRow("30") << 30; QTest::newRow("60") << 60;
|
||||
}
|
||||
void fastCameraKeepsSelectedFps() {
|
||||
QFETCH(int, fps);
|
||||
auto capture = std::make_unique<CallCameraCapture>();
|
||||
CallCameraDevices devices;
|
||||
connect(capture.get(), &CallCameraCapture::devicesChanged, this, [&](const CallCameraDevices& value) { devices = value; });
|
||||
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);
|
||||
capture->configure(true, "fast", 640, 480, fps);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(frames >= 5, 3000);
|
||||
QCOMPARE(devices.size(), 1); QCOMPARE(devices.front().id, QByteArray("red"));
|
||||
QVERIFY(devices.front().modes.contains({QSize(1920, 1080), 30}));
|
||||
QVERIFY(devices.front().modes.contains({QSize(640, 480), 60}));
|
||||
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 double actual = (frames - before) * 1000.0 / timer.elapsed();
|
||||
qInfo("fast camera converted target=%d output_fps=%.2f frames=%u", fps, actual, frames - before);
|
||||
QVERIFY(actual >= fps * 0.85); QVERIFY(actual <= fps + 1); QCOMPARE(errors.size(), 0);
|
||||
const qint64 child = pid(); capture.reset(); QTRY_VERIFY_WITH_TIMEOUT(!alive(child), 3000);
|
||||
}
|
||||
void latestFrameAndGeneration_data() {
|
||||
|
||||
Reference in new issue
Block a user