Revision control
Copy as Markdown
// Copyright (C) 2016 Research In Motion
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#undef QT_NO_CONTEXTLESS_CONNECT // Remove after porting connect() calls
#include "qqnxplatformcamera_p.h"
#include "qqnxcameraframebuffer_p.h"
#include "qqnxmediacapturesession_p.h"
#include "qqnxvideosink_p.h"
#include <qcameradevice.h>
#include <qmediadevices.h>
#include <private/qmediastoragelocation_p.h>
#include <private/qvideoframe_p.h>
#include <camera/camera_api.h>
#include <camera/camera_3a.h>
#include <algorithm>
#include <array>
#include <dlfcn.h>
struct FocusModeMapping
{
QCamera::FocusMode qt;
camera_focusmode_t qnx;
};
constexpr std::array<FocusModeMapping, 6> focusModes {{
{ QCamera::FocusModeAuto, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
{ QCamera::FocusModeAutoFar, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
{ QCamera::FocusModeInfinity, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
{ QCamera::FocusModeAutoNear, CAMERA_FOCUSMODE_CONTINUOUS_MACRO },
{ QCamera::FocusModeHyperfocal, CAMERA_FOCUSMODE_EDOF },
{ QCamera::FocusModeManual, CAMERA_FOCUSMODE_MANUAL },
}};
template <typename Mapping, typename From, typename To, size_t N>
static constexpr To convert(const std::array<Mapping, N> &mapping,
From Mapping::* from, To Mapping::* to, From value, To defaultValue)
{
for (const Mapping &m : mapping) {
const auto fromValue = m.*from;
const auto toValue = m.*to;
if (value == fromValue)
return toValue;
}
return defaultValue;
}
static constexpr camera_focusmode_t qnxFocusMode(QCamera::FocusMode mode)
{
return convert(focusModes, &FocusModeMapping::qt,
&FocusModeMapping::qnx, mode, CAMERA_FOCUSMODE_CONTINUOUS_AUTO);
}
static constexpr QCamera::FocusMode qtFocusMode(camera_focusmode_t mode)
{
return convert(focusModes, &FocusModeMapping::qnx,
&FocusModeMapping::qt, mode, QCamera::FocusModeAuto);
}
QT_BEGIN_NAMESPACE
QQnxPlatformCamera::QQnxPlatformCamera(QCamera *parent)
: QPlatformCamera(parent)
{
if (parent)
setCamera(parent->cameraDevice());
else
setCamera(QMediaDevices::defaultVideoInput());
}
QQnxPlatformCamera::~QQnxPlatformCamera()
{
stop();
}
bool QQnxPlatformCamera::isActive() const
{
return m_qnxCamera && m_qnxCamera->isActive();
}
void QQnxPlatformCamera::setActive(bool active)
{
if (active)
start();
else
stop();
}
void QQnxPlatformCamera::start()
{
if (!m_qnxCamera || isActive())
return;
if (m_session)
m_videoSink = m_session->videoSink();
m_qnxCamera->start();
Q_EMIT activeChanged(true);
}
void QQnxPlatformCamera::stop()
{
if (!m_qnxCamera)
return;
m_qnxCamera->stop();
m_videoSink = nullptr;
Q_EMIT activeChanged(false);
}
void QQnxPlatformCamera::setCamera(const QCameraDevice &camera)
{
if (m_cameraDevice == camera)
return;
const auto cameraUnit = static_cast<camera_unit_t>(camera.id().toUInt());
m_qnxCamera = std::make_unique<QQnxCamera>(cameraUnit);
connect(m_qnxCamera.get(), &QQnxCamera::focusModeChanged,
this, [this](camera_focusmode_t mode) { Q_EMIT focusModeChanged(qtFocusMode(mode)); });
connect(m_qnxCamera.get(), &QQnxCamera::customFocusPointChanged,
this, &QQnxPlatformCamera::customFocusPointChanged);
connect(m_qnxCamera.get(), &QQnxCamera::frameAvailable,
this, &QQnxPlatformCamera::onFrameAvailable, Qt::QueuedConnection);
m_cameraDevice = camera;
updateCameraFeatures();
}
bool QQnxPlatformCamera::setCameraFormat(const QCameraFormat &format)
{
const QSize resolution = format.resolution();
if (resolution.isEmpty()) {
qWarning("QQnxPlatformCamera: invalid resolution requested");
return false;
}
return m_qnxCamera->setCameraFormat(resolution.width(),
resolution.height(), format.maxFrameRate());
}
void QQnxPlatformCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
{
if (m_session == session)
return;
m_session = static_cast<QQnxMediaCaptureSession *>(session);
}
bool QQnxPlatformCamera::isFocusModeSupported(QCamera::FocusMode mode) const
{
if (!m_qnxCamera)
return false;
return m_qnxCamera->supportedFocusModes().contains(::qnxFocusMode(mode));
}
void QQnxPlatformCamera::setFocusMode(QCamera::FocusMode mode)
{
if (!m_qnxCamera)
return;
m_qnxCamera->setFocusMode(::qnxFocusMode(mode));
}
void QQnxPlatformCamera::setCustomFocusPoint(const QPointF &point)
{
if (!m_qnxCamera)
return;
m_qnxCamera->setCustomFocusPoint(point);
}
void QQnxPlatformCamera::setFocusDistance(float distance)
{
if (!m_qnxCamera)
return;
const int maxDistance = m_qnxCamera->maxFocusStep();
if (maxDistance < 0)
return;
const int qnxDistance = maxDistance * std::min(distance, 1.0f);
m_qnxCamera->setManualFocusStep(qnxDistance);
}
void QQnxPlatformCamera::zoomTo(float factor, float)
{
if (!m_qnxCamera)
return;
const uint32_t minZoom = m_qnxCamera->minimumZoomLevel();
const uint32_t maxZoom = m_qnxCamera->maximumZoomLevel();
if (maxZoom <= minZoom)
return;
// QNX has an integer based API. Interpolate between the levels according to the factor we get
const float max = maxZoomFactor();
const float min = minZoomFactor();
if (max <= min)
return;
factor = qBound(min, factor, max) - min;
const uint32_t zoom = minZoom
+ static_cast<uint32_t>(qRound(factor*(maxZoom - minZoom)/(max - min)));
if (m_qnxCamera->setZoomFactor(zoom))
zoomFactorChanged(factor);
}
void QQnxPlatformCamera::setExposureCompensation(float ev)
{
if (!m_qnxCamera)
return;
m_qnxCamera->setEvOffset(ev);
}
int QQnxPlatformCamera::isoSensitivity() const
{
if (!m_qnxCamera)
return 0;
return m_qnxCamera->manualIsoSensitivity();
}
void QQnxPlatformCamera::setManualIsoSensitivity(int value)
{
if (!m_qnxCamera)
return;
const uint32_t isoValue = std::max(0, value);
m_qnxCamera->setManualIsoSensitivity(isoValue);
}
void QQnxPlatformCamera::setManualExposureTime(float seconds)
{
if (!m_qnxCamera)
return;
m_qnxCamera->setManualExposureTime(seconds);
}
float QQnxPlatformCamera::exposureTime() const
{
if (!m_qnxCamera)
return 0.0;
return static_cast<float>(m_qnxCamera->manualExposureTime());
}
bool QQnxPlatformCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
{
if (m_maxColorTemperature != 0)
return true;
return mode == QCamera::WhiteBalanceAuto;
}
void QQnxPlatformCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
{
if (!m_qnxCamera)
return;
if (mode == QCamera::WhiteBalanceAuto) {
m_qnxCamera->setWhiteBalanceMode(CAMERA_WHITEBALANCEMODE_AUTO);
} else {
m_qnxCamera->setWhiteBalanceMode(CAMERA_WHITEBALANCEMODE_MANUAL);
setColorTemperature(colorTemperatureForWhiteBalance(mode));
}
}
void QQnxPlatformCamera::setColorTemperature(int temperature)
{
if (!m_qnxCamera)
return;
const auto normalizedTemp = std::clamp<uint32_t>(std::max(0, temperature),
m_minColorTemperature, m_maxColorTemperature);
if (m_qnxCamera->hasContinuousWhiteBalanceValues()) {
m_qnxCamera->setManualWhiteBalance(normalizedTemp);
} else {
uint32_t delta = std::numeric_limits<uint32_t>::max();
uint32_t closestTemp = 0;
for (uint32_t value : m_qnxCamera->supportedWhiteBalanceValues()) {
const auto &[min, max] = std::minmax(value, normalizedTemp);
const uint32_t currentDelta = max - min;
if (currentDelta < delta) {
closestTemp = value;
delta = currentDelta;
}
}
m_qnxCamera->setManualWhiteBalance(closestTemp);
}
}
bool QQnxPlatformCamera::startVideoRecording()
{
if (!m_qnxCamera) {
qWarning("QQnxPlatformCamera: cannot start video recording - no no camera assigned");
return false;
}
if (!isVideoEncodingSupported()) {
qWarning("QQnxPlatformCamera: cannot start video recording - not supported");
return false;
}
if (!m_qnxCamera->isActive()) {
qWarning("QQnxPlatformCamera: cannot start video recording - camera not started");
return false;
}
const QString container = m_encoderSettings.preferredSuffix();
const QString location = QMediaStorageLocation::generateFileName(m_outputUrl.toLocalFile(),
QStandardPaths::MoviesLocation, container);
#if 0
{
static void *libScreen = nullptr;
if (!libScreen)
libScreen = dlopen("/usr/lib/libscreen.so.1", RTLD_GLOBAL);
}
#endif
qDebug() << "Recording to" << location;
return m_qnxCamera->startVideoRecording(location);
}
void QQnxPlatformCamera::requestVideoFrame(VideoFrameCallback cb)
{
m_videoFrameRequests.emplace_back(std::move(cb));
}
bool QQnxPlatformCamera::isVideoEncodingSupported() const
{
return m_qnxCamera && m_qnxCamera->hasFeature(CAMERA_FEATURE_VIDEO);
}
void QQnxPlatformCamera::setOutputUrl(const QUrl &url)
{
m_outputUrl = url;
}
void QQnxPlatformCamera::setMediaEncoderSettings(const QMediaEncoderSettings &settings)
{
m_encoderSettings = settings;
}
void QQnxPlatformCamera::updateCameraFeatures()
{
if (!m_qnxCamera)
return;
QCamera::Features features = {};
if (m_qnxCamera->hasFeature(CAMERA_FEATURE_REGIONFOCUS))
features |= QCamera::Feature::CustomFocusPoint;
supportedFeaturesChanged(features);
minimumZoomFactorChanged(m_qnxCamera->minimumZoomLevel());
maximumZoomFactorChanged(m_qnxCamera->maximumZoomLevel());
const QList<uint32_t> wbValues = m_qnxCamera->supportedWhiteBalanceValues();
if (wbValues.isEmpty()) {
m_minColorTemperature = m_maxColorTemperature = 0;
} else {
const auto &[minTemp, maxTemp] = std::minmax_element(wbValues.begin(), wbValues.end());
m_minColorTemperature = *minTemp;
m_maxColorTemperature = *maxTemp;
}
}
void QQnxPlatformCamera::onFrameAvailable()
{
if (!m_videoSink)
return;
std::unique_ptr<QQnxCameraFrameBuffer> currentFrameBuffer = m_qnxCamera->takeCurrentFrame();
if (!currentFrameBuffer)
return;
QVideoFrameFormat format(currentFrameBuffer->size(), currentFrameBuffer->pixelFormat());
const QVideoFrame actualFrame =
QVideoFramePrivate::createFrame(std::move(currentFrameBuffer), std::move(format));
m_videoSink->setVideoFrame(actualFrame);
if (!m_videoFrameRequests.empty()) {
VideoFrameCallback cb = std::move(m_videoFrameRequests.front());
m_videoFrameRequests.pop_front();
cb(actualFrame);
}
}
QT_END_NAMESPACE
#include "moc_qqnxplatformcamera_p.cpp"