Carry selected camera frame rate through isolated capture IPC
This commit is contained in:
1 parent
74a4135cdc
commit
ee1e28bff9
3 files changed
+27
-25
No files matched your search
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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>();
|
||||
|
||||
Reference in new issue
Block a user