diff --git a/tools/chatgui/src/call_camera_rate.h b/tools/chatgui/src/call_camera_rate.h new file mode 100644 index 00000000..0c9b4981 --- /dev/null +++ b/tools/chatgui/src/call_camera_rate.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include "debug_config.h" +#ifdef Q_OS_LINUX +#include +#include +#include +#include +#include +#include +#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 + } +}; diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 469d379e..6c6d30f6 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/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 camera; std::unique_ptr capture; std::unique_ptr 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 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 guard(m->lock);