summaryrefslogtreecommitdiffstats
path: root/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/android/src/mediacapture/qandroidcamerasession.cpp')
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.cpp287
1 files changed, 222 insertions, 65 deletions
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
index df9f0367b..0b082b02b 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
@@ -1,31 +1,38 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL21$
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -61,7 +68,6 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
, m_savedState(-1)
, m_status(QCamera::UnloadedStatus)
, m_previewStarted(false)
- , m_imageSettingsDirty(true)
, m_captureDestination(QCameraImageCapture::CaptureToFile)
, m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
, m_lastImageCaptureId(0)
@@ -94,7 +100,7 @@ void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
emit captureModeChanged(m_captureMode);
if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
- adjustViewfinderSize(m_imageSettings.resolution());
+ applyViewfinderSettings(m_actualImageSettings.resolution());
}
bool QAndroidCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
@@ -217,7 +223,8 @@ void QAndroidCameraSession::close()
m_readyForCapture = false;
m_currentImageCaptureId = -1;
m_currentImageCaptureFileName.clear();
- m_imageSettingsDirty = true;
+ m_actualImageSettings = m_requestedImageSettings;
+ m_actualViewfinderSettings = m_requestedViewfinderSettings;
m_camera->release();
delete m_camera;
@@ -245,48 +252,119 @@ void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
}
}
-void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool restartPreview)
+void QAndroidCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ if (m_requestedViewfinderSettings == settings)
+ return;
+
+ m_requestedViewfinderSettings = m_actualViewfinderSettings = settings;
+
+ if (m_readyForCapture)
+ applyViewfinderSettings();
+}
+
+void QAndroidCameraSession::applyViewfinderSettings(const QSize &captureSize, bool restartPreview)
{
if (!m_camera)
return;
- QSize currentViewfinderResolution = m_camera->previewSize();
- QSize adjustedViewfinderResolution;
+ const QSize currentViewfinderResolution = m_camera->previewSize();
+ const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
+ const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
- if (m_captureMode.testFlag(QCamera::CaptureVideo) && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
+ // -- adjust resolution
+ QSize adjustedViewfinderResolution;
+ const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
+ if (m_captureMode.testFlag(QCamera::CaptureVideo)
+ && validCaptureSize
+ && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
// According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
// the preview size cannot be different from the capture size
adjustedViewfinderResolution = captureSize;
} else {
- // search for viewfinder resolution with the same aspect ratio
- const qreal aspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
- QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
- qreal minAspectDiff = 1;
- QSize closestResolution;
- for (int i = previewSizes.count() - 1; i >= 0; --i) {
- const QSize &size = previewSizes.at(i);
- const qreal sizeAspect = qreal(size.width()) / size.height();
- if (qFuzzyCompare(aspectRatio, sizeAspect)) {
- adjustedViewfinderResolution = size;
- break;
- } else if (minAspectDiff > qAbs(sizeAspect - aspectRatio)) {
- closestResolution = size;
- minAspectDiff = qAbs(sizeAspect - aspectRatio);
+ qreal captureAspectRatio = 0;
+ if (validCaptureSize)
+ captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
+
+ const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
+
+ const QSize vfRes = m_requestedViewfinderSettings.resolution();
+ if (vfRes.width() > 0 && vfRes.height() > 0
+ && (!validCaptureSize || qAbs(captureAspectRatio - (qreal(vfRes.width()) / vfRes.height())) < 0.01)
+ && previewSizes.contains(vfRes)) {
+ adjustedViewfinderResolution = vfRes;
+ } else if (validCaptureSize) {
+ // search for viewfinder resolution with the same aspect ratio
+ qreal minAspectDiff = 1;
+ QSize closestResolution;
+ for (int i = previewSizes.count() - 1; i >= 0; --i) {
+ const QSize &size = previewSizes.at(i);
+ const qreal sizeAspect = qreal(size.width()) / size.height();
+ if (qFuzzyCompare(captureAspectRatio, sizeAspect)) {
+ adjustedViewfinderResolution = size;
+ break;
+ } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) {
+ closestResolution = size;
+ minAspectDiff = qAbs(sizeAspect - captureAspectRatio);
+ }
}
+ if (!adjustedViewfinderResolution.isValid()) {
+ qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
+ if (closestResolution.isValid()) {
+ adjustedViewfinderResolution = closestResolution;
+ qWarning("Using closest viewfinder resolution.");
+ } else {
+ return;
+ }
+ }
+ } else {
+ adjustedViewfinderResolution = previewSizes.last();
}
+ }
+ m_actualViewfinderSettings.setResolution(adjustedViewfinderResolution);
- if (!adjustedViewfinderResolution.isValid()) {
- qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
- if (closestResolution.isValid()) {
- adjustedViewfinderResolution = closestResolution;
- qWarning("Using closest viewfinder resolution.");
- } else {
- return;
+ // -- adjust pixel format
+
+ AndroidCamera::ImageFormat adjustedPreviewFormat = AndroidCamera::NV21;
+ if (m_requestedViewfinderSettings.pixelFormat() != QVideoFrame::Format_Invalid) {
+ const AndroidCamera::ImageFormat f = AndroidImageFormatFromQtPixelFormat(m_requestedViewfinderSettings.pixelFormat());
+ if (f == AndroidCamera::UnknownImageFormat || !m_camera->getSupportedPreviewFormats().contains(f))
+ qWarning("Unsupported viewfinder pixel format");
+ else
+ adjustedPreviewFormat = f;
+ }
+ m_actualViewfinderSettings.setPixelFormat(QtPixelFormatFromAndroidImageFormat(adjustedPreviewFormat));
+
+ // -- adjust FPS
+
+ AndroidCamera::FpsRange adjustedFps = currentFpsRange;
+ const AndroidCamera::FpsRange requestedFpsRange = AndroidCamera::FpsRange::makeFromQReal(m_requestedViewfinderSettings.minimumFrameRate(),
+ m_requestedViewfinderSettings.maximumFrameRate());
+ if (requestedFpsRange.min > 0 || requestedFpsRange.max > 0) {
+ int minDist = INT_MAX;
+ const QList<AndroidCamera::FpsRange> supportedFpsRanges = m_camera->getSupportedPreviewFpsRange();
+ auto it = supportedFpsRanges.rbegin(), end = supportedFpsRanges.rend();
+ for (; it != end; ++it) {
+ int dist = (requestedFpsRange.min > 0 ? qAbs(requestedFpsRange.min - it->min) : 0)
+ + (requestedFpsRange.max > 0 ? qAbs(requestedFpsRange.max - it->max) : 0);
+ if (dist < minDist) {
+ minDist = dist;
+ adjustedFps = *it;
+ if (minDist == 0)
+ break; // exact match
}
}
}
+ m_actualViewfinderSettings.setMinimumFrameRate(adjustedFps.getMinReal());
+ m_actualViewfinderSettings.setMaximumFrameRate(adjustedFps.getMaxReal());
+
+ // -- Set values on camera
+
+ if (currentViewfinderResolution != adjustedViewfinderResolution
+ || currentPreviewFormat != adjustedPreviewFormat
+ || currentFpsRange.min != adjustedFps.min
+ || currentFpsRange.max != adjustedFps.max) {
- if (currentViewfinderResolution != adjustedViewfinderResolution) {
if (m_videoOutput)
m_videoOutput->setVideoSize(adjustedViewfinderResolution);
@@ -295,6 +373,8 @@ void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool
m_camera->stopPreview();
m_camera->setPreviewSize(adjustedViewfinderResolution);
+ m_camera->setPreviewFormat(adjustedPreviewFormat);
+ m_camera->setPreviewFpsRange(adjustedFps);
// restart preview
if (m_previewStarted && restartPreview)
@@ -302,6 +382,36 @@ void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool
}
}
+QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
+{
+ return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
+}
+
+QList<QVideoFrame::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
+{
+ QList<QVideoFrame::PixelFormat> formats;
+
+ if (!m_camera)
+ return formats;
+
+ const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
+
+ formats.reserve(nativeFormats.size());
+
+ for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
+ QVideoFrame::PixelFormat format = QtPixelFormatFromAndroidImageFormat(nativeFormat);
+ if (format != QVideoFrame::Format_Invalid)
+ formats.append(format);
+ }
+
+ return formats;
+}
+
+QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
+{
+ return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
+}
+
bool QAndroidCameraSession::startPreview()
{
if (!m_camera)
@@ -328,7 +438,8 @@ bool QAndroidCameraSession::startPreview()
emit statusChanged(m_status);
applyImageSettings();
- adjustViewfinderSize(m_imageSettings.resolution());
+ applyViewfinderSettings(m_captureMode.testFlag(QCamera::CaptureStillImage) ? m_actualImageSettings.resolution()
+ : QSize());
AndroidMultimediaUtils::enableOrientationListener(true);
@@ -362,19 +473,15 @@ void QAndroidCameraSession::stopPreview()
void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
{
- if (m_imageSettings == settings)
+ if (m_requestedImageSettings == settings)
return;
- m_imageSettings = settings;
- if (m_imageSettings.codec().isEmpty())
- m_imageSettings.setCodec(QLatin1String("jpeg"));
-
- m_imageSettingsDirty = true;
+ m_requestedImageSettings = m_actualImageSettings = settings;
applyImageSettings();
if (m_readyForCapture && m_captureMode.testFlag(QCamera::CaptureStillImage))
- adjustViewfinderSize(m_imageSettings.resolution());
+ applyViewfinderSettings(m_actualImageSettings.resolution());
}
int QAndroidCameraSession::currentCameraRotation() const
@@ -431,15 +538,32 @@ void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
void QAndroidCameraSession::applyImageSettings()
{
- if (!m_camera || !m_imageSettingsDirty)
+ if (!m_camera)
return;
- const QSize requestedResolution = m_imageSettings.resolution();
- const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
+ if (m_actualImageSettings.codec().isEmpty())
+ m_actualImageSettings.setCodec(QLatin1String("jpeg"));
+ const QSize requestedResolution = m_requestedImageSettings.resolution();
+ const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
if (!requestedResolution.isValid()) {
- // if no resolution is set, use the highest supported one
- m_imageSettings.setResolution(supportedResolutions.last());
+ // if the viewfinder resolution is explicitly set, pick the highest available capture
+ // resolution with the same aspect ratio
+ if (m_requestedViewfinderSettings.resolution().isValid()) {
+ const QSize vfResolution = m_actualViewfinderSettings.resolution();
+ const qreal vfAspectRatio = qreal(vfResolution.width()) / vfResolution.height();
+
+ auto it = supportedResolutions.rbegin(), end = supportedResolutions.rend();
+ for (; it != end; ++it) {
+ if (qAbs(vfAspectRatio - (qreal(it->width()) / it->height())) < 0.01) {
+ m_actualImageSettings.setResolution(*it);
+ break;
+ }
+ }
+ } else {
+ // otherwise, use the highest supported one
+ m_actualImageSettings.setResolution(supportedResolutions.last());
+ }
} else if (!supportedResolutions.contains(requestedResolution)) {
// if the requested resolution is not supported, find the closest one
int reqPixelCount = requestedResolution.width() * requestedResolution.height();
@@ -449,11 +573,12 @@ void QAndroidCameraSession::applyImageSettings()
supportedPixelCounts.append(s.width() * s.height());
}
int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
- m_imageSettings.setResolution(supportedResolutions.at(closestIndex));
+ m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
}
+ m_camera->setPictureSize(m_actualImageSettings.resolution());
int jpegQuality = 100;
- switch (m_imageSettings.quality()) {
+ switch (m_requestedImageSettings.quality()) {
case QMultimedia::VeryLowQuality:
jpegQuality = 20;
break;
@@ -470,11 +595,7 @@ void QAndroidCameraSession::applyImageSettings()
jpegQuality = 100;
break;
}
-
- m_camera->setPictureSize(m_imageSettings.resolution());
m_camera->setJpegQuality(jpegQuality);
-
- m_imageSettingsDirty = false;
}
bool QAndroidCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
@@ -536,7 +657,7 @@ int QAndroidCameraSession::capture(const QString &fileName)
m_currentImageCaptureFileName = fileName;
applyImageSettings();
- adjustViewfinderSize(m_imageSettings.resolution());
+ applyViewfinderSettings(m_actualImageSettings.resolution());
// adjust picture rotation depending on the device orientation
m_camera->setRotation(currentCameraRotation());
@@ -599,7 +720,7 @@ void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
{
m_videoProbesMutex.lock();
- foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes)
+ for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes))
probe->newFrameProbed(frame);
if (m_previewCallback)
@@ -615,7 +736,7 @@ void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data)
QtConcurrent::run(this, &QAndroidCameraSession::processCapturedImage,
m_currentImageCaptureId,
data,
- m_imageSettings.resolution(),
+ m_actualImageSettings.resolution(),
m_captureDestination,
m_currentImageCaptureFileName);
}
@@ -707,6 +828,42 @@ void QAndroidCameraSession::processCapturedImage(int id,
}
}
+QVideoFrame::PixelFormat QAndroidCameraSession::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
+{
+ switch (format) {
+ case AndroidCamera::RGB565:
+ return QVideoFrame::Format_RGB565;
+ case AndroidCamera::NV21:
+ return QVideoFrame::Format_NV21;
+ case AndroidCamera::YUY2:
+ return QVideoFrame::Format_YUYV;
+ case AndroidCamera::JPEG:
+ return QVideoFrame::Format_Jpeg;
+ case AndroidCamera::YV12:
+ return QVideoFrame::Format_YV12;
+ default:
+ return QVideoFrame::Format_Invalid;
+ }
+}
+
+AndroidCamera::ImageFormat QAndroidCameraSession::AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrame::Format_RGB565:
+ return AndroidCamera::RGB565;
+ case QVideoFrame::Format_NV21:
+ return AndroidCamera::NV21;
+ case QVideoFrame::Format_YUYV:
+ return AndroidCamera::YUY2;
+ case QVideoFrame::Format_Jpeg:
+ return AndroidCamera::JPEG;
+ case QVideoFrame::Format_YV12:
+ return AndroidCamera::YV12;
+ default:
+ return AndroidCamera::UnknownImageFormat;
+ }
+}
+
void QAndroidCameraSession::onVideoOutputReady(bool ready)
{
if (ready && m_state == QCamera::ActiveState)