Browse Source

Keep UVC camera frame rate stable during video calls

master
evgeny 4 days ago
parent
commit
f84b24806e
  1. 63
      tools/chatgui/src/call_camera_rate.h
  2. 7
      tools/chatgui/src/call_video_engine.cpp

63
tools/chatgui/src/call_camera_rate.h

@ -0,0 +1,63 @@
#pragma once
#include <QByteArray>
#include "debug_config.h"
#ifdef Q_OS_LINUX
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <cerrno>
#include <cstring>
#endif
// Qt задаёт 30 fps, но UVC auto-exposure может разрешить камере снижать их до 16–17.
// Меняем только этот флаг на время захвата; сама автоэкспозиция остаётся включённой.
class CallCameraRate {
#ifdef Q_OS_LINUX
int fd = -1;
#endif
public:
~CallCameraRate() { restore(); }
void restore() {
#ifdef Q_OS_LINUX
if (fd < 0) return;
v4l2_control control{}; control.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY; control.value = 1;
if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0)
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call camera dynamic fps restore failed: %s", std::strerror(errno));
else DEBUG_INFO(DEBUG_CATEGORY_CALL, "call camera dynamic fps restored=1");
::close(fd); fd = -1;
#endif
}
void activate(const QByteArray& device) {
restore();
#ifdef Q_OS_LINUX
if (!device.startsWith("/dev/video")) {
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call camera fps policy unavailable device=%s", device.constData()); return;
}
int camera = ::open(device.constData(), O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (camera < 0) {
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call camera fps control open failed device=%s: %s", device.constData(), std::strerror(errno));
return;
}
v4l2_control control{}; control.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY;
if (ioctl(camera, VIDIOC_G_CTRL, &control) < 0) {
if (errno == EINVAL) DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call camera dynamic fps control unsupported device=%s", device.constData());
else DEBUG_WARN(DEBUG_CATEGORY_CALL, "call camera fps control read failed: %s", std::strerror(errno));
} else if (!control.value) {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call camera dynamic fps already disabled device=%s", device.constData());
} else {
control.value = 0;
if (ioctl(camera, VIDIOC_S_CTRL, &control) < 0)
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call camera dynamic fps disable failed: %s", std::strerror(errno));
else {
fd = camera;
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call camera dynamic fps disabled device=%s; auto exposure retained", device.constData());
return;
}
}
::close(camera);
#else
Q_UNUSED(device);
#endif
}
};

7
tools/chatgui/src/call_video_engine.cpp

@ -2,6 +2,7 @@
#include "call_video_encoder.h"
#include "call_video_decoder.h"
#include "call_video_cadence.h"
#include "call_camera_rate.h"
#include "../../../src/call/call_video_port.h"
#include "../../../src/call/call_audio.h"
#include "debug_config.h"
@ -48,6 +49,7 @@ struct CallVideoEngine::Impl {
std::unique_ptr<QCamera> camera;
std::unique_ptr<QMediaCaptureSession> capture;
std::unique_ptr<QVideoSink> sink;
CallCameraRate cameraRate;
QVideoFrame raw;
#endif
};
@ -105,6 +107,7 @@ void CallVideoEngine::closeCamera() {
#ifdef HAVE_STREAM_CAPTURE
if (m->sink) disconnect(m->sink.get(), nullptr, this, nullptr);
if (m->camera) { disconnect(m->camera.get(), nullptr, this, nullptr); m->camera->stop(); }
m->cameraRate.restore();
m->capture.reset(); m->camera.reset(); m->sink.reset();
{ std::lock_guard<std::mutex> guard(m->lock); m->raw = {}; m->pts = m->backend = m->anchor = -1; }
#endif
@ -141,6 +144,10 @@ void CallVideoEngine::setVideo(bool enabled, const QByteArray& cameraId, int wid
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call camera selected device=%s size=%dx%d pixel=%d fps=%.3f..%.3f",
selected.id().constData(), selectedFormat.resolution().width(), selectedFormat.resolution().height(),
selectedFormat.pixelFormat(), selectedFormat.minFrameRate(), selectedFormat.maxFrameRate());
connect(m->camera.get(), &QCamera::activeChanged, this, [this, device = selected.id()](bool active) {
if (active) m->cameraRate.activate(device);
else m->cameraRate.restore();
});
connect(m->sink.get(), &QVideoSink::videoFrameChanged, this, [this, revision](const QVideoFrame& frame) {
if (!frame.isValid() || !m->running) return;
std::lock_guard<std::mutex> guard(m->lock);

Loading…
Cancel
Save