Carry selected camera frame rate through isolated capture IPC

This commit is contained in:
evgeny committed 2026-10-20 00:02:25 +02:00
1 parent 74a4135cdc
commit ee1e28bff9
3 files changed
+27 -25

No files matched your search

+15 -12
View File
@@ -26,7 +26,7 @@ struct CallCameraCapture::Session : QObject {
quint64 generation = 0;
bool enabled = false, retiring = false, failureReported = false, restartRequested = false, exiting = false;
QByteArray device;
int width = 640, height = 480;
int width = 640, height = 480, fps = 30;
QString phase = QStringLiteral("launch");
qint64 progressAt = 0, frameAt = 0, stopAt = 0;
@@ -115,17 +115,20 @@ struct CallCameraCapture::Session : QObject {
socket->flush();
}
void sendConfiguration() {
send(packet(Configure, generation, [this](QDataStream& out) { out << enabled << device << qint32(width) << qint32(height); }));
send(packet(Configure, generation, [this](QDataStream& out) {
out << enabled << device << qint32(width) << qint32(height) << qint32(fps);
}));
}
void configure(bool active, const QByteArray& id, int w, int h) {
void configure(bool active, const QByteArray& id, int w, int h, int rate) {
if (exiting) return;
if (enabled == active && device == id && width == w && height == h && !failureReported) return;
enabled = active; device = id; width = w; height = h; ++generation;
if (!validSize(w, h) || rate < 1 || rate > 60) { fail(QStringLiteral("Invalid camera size/frame rate")); return; }
if (enabled == active && device == id && width == w && height == h && fps == rate && !failureReported) return;
enabled = active; device = id; width = w; height = h; fps = rate; ++generation;
progressAt = call_video_playout_now_us();
frameAt = progressAt;
stopAt = active ? 0 : progressAt + 1500000;
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera request generation=%llu enabled=%d size=%dx%d device=%s",
(unsigned long long)generation, active, w, h, id.constData());
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera request generation=%llu enabled=%d size=%dx%d output_fps=%d device=%s",
(unsigned long long)generation, active, w, h, rate, id.constData());
if (failureReported && active) restartRequested = true;
if (process.state() == QProcess::NotRunning) { restartRequested = false; launch(); }
if (socket) sendConfiguration();
@@ -151,9 +154,9 @@ struct CallCameraCapture::Session : QObject {
CallCameraDevice device; quint32 modes = 0; in >> device.name >> device.id >> modes;
if (in.status() != QDataStream::Ok || modes > 256) return false;
for (quint32 j = 0; j < modes; ++j) {
qint32 w = 0, h = 0; in >> w >> h;
if (!validSize(w, h)) return false;
device.modes.append({QSize(w, h), 30});
qint32 w = 0, h = 0, rate = 0; in >> w >> h >> rate;
if (!validSize(w, h) || rate < 1 || rate > 60) return false;
device.modes.append({QSize(w, h), rate});
}
devices.append(device);
}
@@ -190,6 +193,6 @@ struct CallCameraCapture::Session : QObject {
CallCameraCapture::CallCameraCapture(QObject* parent) : QObject(parent), m(new Session(this)) {}
CallCameraCapture::~CallCameraCapture() { m->shutdown(); }
void CallCameraCapture::configure(bool enabled, const QByteArray& device, int width, int height) {
m->configure(enabled, device, width, height);
void CallCameraCapture::configure(bool enabled, const QByteArray& device, int width, int height, int fps) {
m->configure(enabled, device, width, height, fps);
}
+2 -2
View File
@@ -8,7 +8,7 @@
struct CallCameraDevice {
QString name; // Отображаемое имя устройства.
QByteArray id; // Непрозрачный идентификатор Qt backend.
QList<StreamVideoMode> modes; // Аппаратные размеры до Full HD с частотой 30/29.97 fps.
QList<StreamVideoMode> modes; // Размеры до Full HD и частоты передачи до 60 fps, capture может быть быстрее.
};
using CallCameraDevices = QList<CallCameraDevice>;
constexpr qint64 CallCameraMaxFrameAgeUs = 250000;
@@ -19,7 +19,7 @@ class CallCameraCapture : public QObject {
public:
explicit CallCameraCapture(QObject* parent = nullptr);
~CallCameraCapture() override;
void configure(bool enabled, const QByteArray& device = {}, int width = 640, int height = 480);
void configure(bool enabled, const QByteArray& device = {}, int width = 640, int height = 480, int fps = 30);
signals:
void devicesChanged(const CallCameraDevices& devices);
void frameReady(const QImage& image, qint64 pts, qint64 correction);
+10 -11
View File
@@ -43,8 +43,7 @@ public:
if (validSize(size.width(), size.height()))
stream_video_add_camera_modes(entry.modes, size, format.minFrameRate(), format.maxFrameRate());
}
entry.modes.removeIf([](const StreamVideoMode& mode) { return mode.fps != 30; });
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera modes device=%s count=%lld limit=1920x1080 fps=30",
DEBUG_INFO(DEBUG_CATEGORY_CALL, "camera modes device=%s count=%lld limit=1920x1080 output_fps<=60",
device.id().constData(), (long long)entry.modes.size());
result.append(entry);
}
@@ -60,7 +59,7 @@ public:
QCameraFormat best;
for (const auto& format : selected.videoFormats()) {
if (!stream_video_camera_supports(format.resolution(), format.minFrameRate(), format.maxFrameRate(),
{QSize(width, height), 30})) continue;
{QSize(width, height), 1})) continue;
if (best.isNull() || format.maxFrameRate() < best.maxFrameRate()) best = format;
}
if (best.isNull()) {
@@ -104,7 +103,7 @@ class CameraWorker : public QObject {
CallVideoCadence cadence;
quint64 generation = 0, inFlight = 0;
bool sending = false;
int width = 640, height = 480;
int width = 640, height = 480, fps = 30;
qint64 lastPts = -1, reportAt = 0, convertMax = 0;
unsigned frames = 0, paced = 0;
@@ -124,7 +123,7 @@ class CameraWorker : public QObject {
out << quint32(devices.size());
for (const auto& device : devices) {
out << device.name << device.id << quint32(device.modes.size());
for (const auto& mode : device.modes) out << qint32(mode.size.width()) << qint32(mode.size.height());
for (const auto& mode : device.modes) out << qint32(mode.size.width()) << qint32(mode.size.height()) << qint32(mode.fps);
}
}));
stage(pending ? QStringLiteral("running") : QStringLiteral("idle"));
@@ -149,7 +148,7 @@ class CameraWorker : public QObject {
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;
}
// Ограничиваем поток до дорогой конвертации и IPC; preview и TX получают те же 30 fps.
// Ограничиваем поток до дорогой конвертации и IPC; preview и TX получают выбранную частоту.
if (!cadence.accept(pts)) { ++paced; return; }
stage(QStringLiteral("convert"));
QImage image = raw.toImage();
@@ -174,8 +173,8 @@ class CameraWorker : public QObject {
stage(QStringLiteral("release")); image = {}; raw = {}; stage(QStringLiteral("running"));
convertMax = std::max(convertMax, call_video_playout_now_us() - started);
if (started - reportAt >= 1000000) {
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "camera flow frames=%u paced=%u convert_max_us=%lld age_us=%lld clock_correction_us=%lld",
frames, paced, (long long)convertMax, (long long)(converted - pts), (long long)clock.correction);
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "camera flow output_fps=%d frames=%u paced=%u convert_max_us=%lld age_us=%lld clock_correction_us=%lld",
fps, frames, paced, (long long)convertMax, (long long)(converted - pts), (long long)clock.correction);
reportAt = started; convertMax = 0; frames = paced = 0;
}
}
@@ -191,9 +190,9 @@ class CameraWorker : public QObject {
else if (type == Ack) {
if (sending && revision == inFlight) { sending = false; if (pending) convert(pending, generation); }
} else if (type == Configure) {
bool enabled = false; QByteArray device; qint32 w = 0, h = 0; in >> enabled >> device >> w >> h;
if (in.status() != QDataStream::Ok || !validSize(w, h) || !pixels) return false;
stop(); generation = revision; width = w; height = h; clock = {}; cadence = {}; lastPts = -1;
bool enabled = false; QByteArray device; qint32 w = 0, h = 0, rate = 0; in >> enabled >> device >> w >> h >> rate;
if (in.status() != QDataStream::Ok || !validSize(w, h) || rate < 1 || rate > 60 || !pixels) return false;
stop(); generation = revision; width = w; height = h; fps = rate; clock = {}; cadence = CallVideoCadence(rate); lastPts = -1;
if (!enabled) { stage(QStringLiteral("idle")); return true; }
stage(QStringLiteral("start"));
pending = std::make_shared<Pending>();