Skip to content

Commit 62d9b69

Browse files
rusloyolopes
authored andcommitted
Implement QCameraViewfinderSettingsControl2 interface for Android
Add class QAndroidViewfinderSettingsControl2 which implements QCameraViewfinderSettingsControl2 interface. This make next QCamera methods working on Android: * QCamera::setViewfinderSettings * QCamera::supportedViewfinderFrameRateRanges * QCamera::supportedViewfinderPixelFormats * QCamera::supportedViewfinderResolutions * QCamera::supportedViewfinderSettings * QCamera::viewfinderSettings Originally from: * headupinclouds/gatherer#109 Change-Id: Ic43e434289a626691f01ed9832654fa9b0105c7d Reviewed-by: Ruslan Baratov <[email protected]> Reviewed-by: Christian Stromme <[email protected]>
1 parent f2b9fb7 commit 62d9b69

File tree

10 files changed

+519
-45
lines changed

10 files changed

+519
-45
lines changed

src/plugins/android/src/mediacapture/mediacapture.pri

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SOURCES += \
1414
$$PWD/qandroidcameracapturebufferformatcontrol.cpp \
1515
$$PWD/qandroidcameraflashcontrol.cpp \
1616
$$PWD/qandroidcamerafocuscontrol.cpp \
17+
$$PWD/qandroidviewfindersettingscontrol.cpp \
1718
$$PWD/qandroidcameralockscontrol.cpp \
1819
$$PWD/qandroidcapturesession.cpp \
1920
$$PWD/qandroidmediarecordercontrol.cpp \
@@ -39,6 +40,7 @@ HEADERS += \
3940
$$PWD/qandroidcameracapturebufferformatcontrol.h \
4041
$$PWD/qandroidcameraflashcontrol.h \
4142
$$PWD/qandroidcamerafocuscontrol.h \
43+
$$PWD/qandroidviewfindersettingscontrol.h \
4244
$$PWD/qandroidcameralockscontrol.h \
4345
$$PWD/qandroidcapturesession.h \
4446
$$PWD/qandroidmediarecordercontrol.h \

src/plugins/android/src/mediacapture/qandroidcamerasession.cpp

Lines changed: 191 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/****************************************************************************
22
**
33
** Copyright (C) 2016 The Qt Company Ltd.
4+
** Copyright (C) 2016 Ruslan Baratov
45
** Contact: https://www.qt.io/licensing/
56
**
67
** This file is part of the Qt Toolkit.
@@ -67,7 +68,6 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
6768
, m_savedState(-1)
6869
, m_status(QCamera::UnloadedStatus)
6970
, m_previewStarted(false)
70-
, m_imageSettingsDirty(true)
7171
, m_captureDestination(QCameraImageCapture::CaptureToFile)
7272
, m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
7373
, m_lastImageCaptureId(0)
@@ -100,7 +100,7 @@ void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
100100
emit captureModeChanged(m_captureMode);
101101

102102
if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
103-
adjustViewfinderSize(m_imageSettings.resolution());
103+
applyViewfinderSettings(m_actualImageSettings.resolution());
104104
}
105105

106106
bool QAndroidCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
@@ -223,7 +223,8 @@ void QAndroidCameraSession::close()
223223
m_readyForCapture = false;
224224
m_currentImageCaptureId = -1;
225225
m_currentImageCaptureFileName.clear();
226-
m_imageSettingsDirty = true;
226+
m_actualImageSettings = m_requestedImageSettings;
227+
m_actualViewfinderSettings = m_requestedViewfinderSettings;
227228

228229
m_camera->release();
229230
delete m_camera;
@@ -251,37 +252,108 @@ void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
251252
}
252253
}
253254

254-
void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool restartPreview)
255+
void QAndroidCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
256+
{
257+
if (m_requestedViewfinderSettings == settings)
258+
return;
259+
260+
m_requestedViewfinderSettings = m_actualViewfinderSettings = settings;
261+
262+
if (m_readyForCapture)
263+
applyViewfinderSettings();
264+
}
265+
266+
void QAndroidCameraSession::applyViewfinderSettings(const QSize &captureSize, bool restartPreview)
255267
{
256268
if (!m_camera)
257269
return;
258270

259-
QSize currentViewfinderResolution = m_camera->previewSize();
260-
QSize adjustedViewfinderResolution;
271+
const QSize currentViewfinderResolution = m_camera->previewSize();
272+
const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
273+
const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
261274

262-
if (m_captureMode.testFlag(QCamera::CaptureVideo) && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
275+
// -- adjust resolution
276+
QSize adjustedViewfinderResolution;
277+
const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
278+
if (m_captureMode.testFlag(QCamera::CaptureVideo)
279+
&& validCaptureSize
280+
&& m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
263281
// According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
264282
// the preview size cannot be different from the capture size
265283
adjustedViewfinderResolution = captureSize;
266284
} else {
267-
// search for viewfinder resolution with the same aspect ratio
268-
const qreal aspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
269-
QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
270-
for (int i = previewSizes.count() - 1; i >= 0; --i) {
271-
const QSize &size = previewSizes.at(i);
272-
if (qAbs(aspectRatio - (qreal(size.width()) / size.height())) < 0.01) {
273-
adjustedViewfinderResolution = size;
274-
break;
285+
qreal captureAspectRatio = 0;
286+
if (validCaptureSize)
287+
captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
288+
289+
const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
290+
291+
const QSize vfRes = m_requestedViewfinderSettings.resolution();
292+
if (vfRes.width() > 0 && vfRes.height() > 0
293+
&& (!validCaptureSize || qAbs(captureAspectRatio - (qreal(vfRes.width()) / vfRes.height())) < 0.01)
294+
&& previewSizes.contains(vfRes)) {
295+
adjustedViewfinderResolution = vfRes;
296+
} else if (validCaptureSize) {
297+
// search for viewfinder resolution with the same aspect ratio
298+
for (int i = previewSizes.count() - 1; i >= 0; --i) {
299+
const QSize &size = previewSizes.at(i);
300+
if (qAbs(captureAspectRatio - (qreal(size.width()) / size.height())) < 0.01) {
301+
adjustedViewfinderResolution = size;
302+
break;
303+
}
304+
}
305+
if (!adjustedViewfinderResolution.isValid()) {
306+
qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
307+
return;
275308
}
309+
} else {
310+
adjustedViewfinderResolution = previewSizes.last();
276311
}
312+
}
313+
m_actualViewfinderSettings.setResolution(adjustedViewfinderResolution);
277314

278-
if (!adjustedViewfinderResolution.isValid()) {
279-
qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
280-
return;
315+
// -- adjust pixel format
316+
317+
AndroidCamera::ImageFormat adjustedPreviewFormat = AndroidCamera::NV21;
318+
if (m_requestedViewfinderSettings.pixelFormat() != QVideoFrame::Format_Invalid) {
319+
const AndroidCamera::ImageFormat f = AndroidImageFormatFromQtPixelFormat(m_requestedViewfinderSettings.pixelFormat());
320+
if (f == AndroidCamera::UnknownImageFormat || !m_camera->getSupportedPreviewFormats().contains(f))
321+
qWarning("Unsupported viewfinder pixel format");
322+
else
323+
adjustedPreviewFormat = f;
324+
}
325+
m_actualViewfinderSettings.setPixelFormat(QtPixelFormatFromAndroidImageFormat(adjustedPreviewFormat));
326+
327+
// -- adjust FPS
328+
329+
AndroidCamera::FpsRange adjustedFps = currentFpsRange;
330+
const AndroidCamera::FpsRange requestedFpsRange = AndroidCamera::FpsRange::makeFromQReal(m_requestedViewfinderSettings.minimumFrameRate(),
331+
m_requestedViewfinderSettings.maximumFrameRate());
332+
if (requestedFpsRange.min > 0 || requestedFpsRange.max > 0) {
333+
int minDist = INT_MAX;
334+
const QList<AndroidCamera::FpsRange> supportedFpsRanges = m_camera->getSupportedPreviewFpsRange();
335+
auto it = supportedFpsRanges.rbegin(), end = supportedFpsRanges.rend();
336+
for (; it != end; ++it) {
337+
int dist = (requestedFpsRange.min > 0 ? qAbs(requestedFpsRange.min - it->min) : 0)
338+
+ (requestedFpsRange.max > 0 ? qAbs(requestedFpsRange.max - it->max) : 0);
339+
if (dist < minDist) {
340+
minDist = dist;
341+
adjustedFps = *it;
342+
if (minDist == 0)
343+
break; // exact match
344+
}
281345
}
282346
}
347+
m_actualViewfinderSettings.setMinimumFrameRate(adjustedFps.getMinReal());
348+
m_actualViewfinderSettings.setMaximumFrameRate(adjustedFps.getMaxReal());
349+
350+
// -- Set values on camera
351+
352+
if (currentViewfinderResolution != adjustedViewfinderResolution
353+
|| currentPreviewFormat != adjustedPreviewFormat
354+
|| currentFpsRange.min != adjustedFps.min
355+
|| currentFpsRange.max != adjustedFps.max) {
283356

284-
if (currentViewfinderResolution != adjustedViewfinderResolution) {
285357
if (m_videoOutput)
286358
m_videoOutput->setVideoSize(adjustedViewfinderResolution);
287359

@@ -290,13 +362,45 @@ void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool
290362
m_camera->stopPreview();
291363

292364
m_camera->setPreviewSize(adjustedViewfinderResolution);
365+
m_camera->setPreviewFormat(adjustedPreviewFormat);
366+
m_camera->setPreviewFpsRange(adjustedFps);
293367

294368
// restart preview
295369
if (m_previewStarted && restartPreview)
296370
m_camera->startPreview();
297371
}
298372
}
299373

374+
QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
375+
{
376+
return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
377+
}
378+
379+
QList<QVideoFrame::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
380+
{
381+
QList<QVideoFrame::PixelFormat> formats;
382+
383+
if (!m_camera)
384+
return formats;
385+
386+
const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
387+
388+
formats.reserve(nativeFormats.size());
389+
390+
for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
391+
QVideoFrame::PixelFormat format = QtPixelFormatFromAndroidImageFormat(nativeFormat);
392+
if (format != QVideoFrame::Format_Invalid)
393+
formats.append(format);
394+
}
395+
396+
return formats;
397+
}
398+
399+
QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
400+
{
401+
return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
402+
}
403+
300404
bool QAndroidCameraSession::startPreview()
301405
{
302406
if (!m_camera)
@@ -323,7 +427,8 @@ bool QAndroidCameraSession::startPreview()
323427
emit statusChanged(m_status);
324428

325429
applyImageSettings();
326-
adjustViewfinderSize(m_imageSettings.resolution());
430+
applyViewfinderSettings(m_captureMode.testFlag(QCamera::CaptureStillImage) ? m_actualImageSettings.resolution()
431+
: QSize());
327432

328433
AndroidMultimediaUtils::enableOrientationListener(true);
329434

@@ -357,19 +462,15 @@ void QAndroidCameraSession::stopPreview()
357462

358463
void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
359464
{
360-
if (m_imageSettings == settings)
465+
if (m_requestedImageSettings == settings)
361466
return;
362467

363-
m_imageSettings = settings;
364-
if (m_imageSettings.codec().isEmpty())
365-
m_imageSettings.setCodec(QLatin1String("jpeg"));
366-
367-
m_imageSettingsDirty = true;
468+
m_requestedImageSettings = m_actualImageSettings = settings;
368469

369470
applyImageSettings();
370471

371472
if (m_readyForCapture && m_captureMode.testFlag(QCamera::CaptureStillImage))
372-
adjustViewfinderSize(m_imageSettings.resolution());
473+
applyViewfinderSettings(m_actualImageSettings.resolution());
373474
}
374475

375476
int QAndroidCameraSession::currentCameraRotation() const
@@ -426,15 +527,32 @@ void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
426527

427528
void QAndroidCameraSession::applyImageSettings()
428529
{
429-
if (!m_camera || !m_imageSettingsDirty)
530+
if (!m_camera)
430531
return;
431532

432-
const QSize requestedResolution = m_imageSettings.resolution();
433-
const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
533+
if (m_actualImageSettings.codec().isEmpty())
534+
m_actualImageSettings.setCodec(QLatin1String("jpeg"));
434535

536+
const QSize requestedResolution = m_requestedImageSettings.resolution();
537+
const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
435538
if (!requestedResolution.isValid()) {
436-
// if no resolution is set, use the highest supported one
437-
m_imageSettings.setResolution(supportedResolutions.last());
539+
// if the viewfinder resolution is explicitly set, pick the highest available capture
540+
// resolution with the same aspect ratio
541+
if (m_requestedViewfinderSettings.resolution().isValid()) {
542+
const QSize vfResolution = m_actualViewfinderSettings.resolution();
543+
const qreal vfAspectRatio = qreal(vfResolution.width()) / vfResolution.height();
544+
545+
auto it = supportedResolutions.rbegin(), end = supportedResolutions.rend();
546+
for (; it != end; ++it) {
547+
if (qAbs(vfAspectRatio - (qreal(it->width()) / it->height())) < 0.01) {
548+
m_actualImageSettings.setResolution(*it);
549+
break;
550+
}
551+
}
552+
} else {
553+
// otherwise, use the highest supported one
554+
m_actualImageSettings.setResolution(supportedResolutions.last());
555+
}
438556
} else if (!supportedResolutions.contains(requestedResolution)) {
439557
// if the requested resolution is not supported, find the closest one
440558
int reqPixelCount = requestedResolution.width() * requestedResolution.height();
@@ -444,11 +562,12 @@ void QAndroidCameraSession::applyImageSettings()
444562
supportedPixelCounts.append(s.width() * s.height());
445563
}
446564
int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
447-
m_imageSettings.setResolution(supportedResolutions.at(closestIndex));
565+
m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
448566
}
567+
m_camera->setPictureSize(m_actualImageSettings.resolution());
449568

450569
int jpegQuality = 100;
451-
switch (m_imageSettings.quality()) {
570+
switch (m_requestedImageSettings.quality()) {
452571
case QMultimedia::VeryLowQuality:
453572
jpegQuality = 20;
454573
break;
@@ -465,11 +584,7 @@ void QAndroidCameraSession::applyImageSettings()
465584
jpegQuality = 100;
466585
break;
467586
}
468-
469-
m_camera->setPictureSize(m_imageSettings.resolution());
470587
m_camera->setJpegQuality(jpegQuality);
471-
472-
m_imageSettingsDirty = false;
473588
}
474589

475590
bool QAndroidCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
@@ -531,7 +646,7 @@ int QAndroidCameraSession::capture(const QString &fileName)
531646
m_currentImageCaptureFileName = fileName;
532647

533648
applyImageSettings();
534-
adjustViewfinderSize(m_imageSettings.resolution());
649+
applyViewfinderSettings(m_actualImageSettings.resolution());
535650

536651
// adjust picture rotation depending on the device orientation
537652
m_camera->setRotation(currentCameraRotation());
@@ -610,7 +725,7 @@ void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data)
610725
QtConcurrent::run(this, &QAndroidCameraSession::processCapturedImage,
611726
m_currentImageCaptureId,
612727
data,
613-
m_imageSettings.resolution(),
728+
m_actualImageSettings.resolution(),
614729
m_captureDestination,
615730
m_currentImageCaptureFileName);
616731
}
@@ -702,6 +817,42 @@ void QAndroidCameraSession::processCapturedImage(int id,
702817
}
703818
}
704819

820+
QVideoFrame::PixelFormat QAndroidCameraSession::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
821+
{
822+
switch (format) {
823+
case AndroidCamera::RGB565:
824+
return QVideoFrame::Format_RGB565;
825+
case AndroidCamera::NV21:
826+
return QVideoFrame::Format_NV21;
827+
case AndroidCamera::YUY2:
828+
return QVideoFrame::Format_YUYV;
829+
case AndroidCamera::JPEG:
830+
return QVideoFrame::Format_Jpeg;
831+
case AndroidCamera::YV12:
832+
return QVideoFrame::Format_YV12;
833+
default:
834+
return QVideoFrame::Format_Invalid;
835+
}
836+
}
837+
838+
AndroidCamera::ImageFormat QAndroidCameraSession::AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat format)
839+
{
840+
switch (format) {
841+
case QVideoFrame::Format_RGB565:
842+
return AndroidCamera::RGB565;
843+
case QVideoFrame::Format_NV21:
844+
return AndroidCamera::NV21;
845+
case QVideoFrame::Format_YUYV:
846+
return AndroidCamera::YUY2;
847+
case QVideoFrame::Format_Jpeg:
848+
return AndroidCamera::JPEG;
849+
case QVideoFrame::Format_YV12:
850+
return AndroidCamera::YV12;
851+
default:
852+
return AndroidCamera::UnknownImageFormat;
853+
}
854+
}
855+
705856
void QAndroidCameraSession::onVideoOutputReady(bool ready)
706857
{
707858
if (ready && m_state == QCamera::ActiveState)

0 commit comments

Comments
 (0)