Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
androidcamera.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Ruslan Baratov
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
10
11#include <private/qvideoframe_p.h>
12
13#include <qhash.h>
14#include <qstringlist.h>
15#include <qdebug.h>
16#include <QtCore/qthread.h>
17#include <QtCore/qreadwritelock.h>
18#include <QtCore/qmutex.h>
19#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
20#include <QtMultimedia/private/qmultimedia_ranges_p.h>
21#include <QtCore/qcoreapplication.h>
22
23#include <mutex>
24
25QT_BEGIN_NAMESPACE
26namespace ranges = QtMultimediaPrivate::ranges;
27
28Q_STATIC_LOGGING_CATEGORY(lcAndroidCamera, "qt.multimedia.android.camera");
29
30static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener";
31
33Q_GLOBAL_STATIC(CameraMap, cameras)
34Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
35
36static QRect areaToRect(jobject areaObj)
37{
38 QJniObject area(areaObj);
39 QJniObject rect = area.getObjectField("rect", "Landroid/graphics/Rect;");
40
41 return QRect(rect.getField<jint>("left"),
42 rect.getField<jint>("top"),
43 rect.callMethod<jint>("width"),
44 rect.callMethod<jint>("height"));
45}
46
47static QJniObject rectToArea(const QRect &rect)
48{
49 QJniObject jrect("android/graphics/Rect",
50 "(IIII)V",
51 rect.left(), rect.top(), rect.right(), rect.bottom());
52
53 QJniObject area("android/hardware/Camera$Area",
54 "(Landroid/graphics/Rect;I)V",
55 jrect.object(), 500);
56
57 return area;
58}
59
60// native method for QtCameraLisener.java
61static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success)
62{
63 QReadLocker locker(rwLock);
64 const auto it = cameras->constFind(id);
65 if (Q_UNLIKELY(it == cameras->cend()))
66 return;
67
68 Q_EMIT (*it)->autoFocusComplete(success);
69}
70
71static void notifyPictureExposed(JNIEnv* , jobject, int id)
72{
73 QReadLocker locker(rwLock);
74 const auto it = cameras->constFind(id);
75 if (Q_UNLIKELY(it == cameras->cend()))
76 return;
77
78 Q_EMIT (*it)->pictureExposed();
79}
80
81static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
82{
83 QReadLocker locker(rwLock);
84 const auto it = cameras->constFind(id);
85 if (Q_UNLIKELY(it == cameras->cend())) {
86 qCWarning(lcAndroidCamera) << "Could not obtain camera!";
87 return;
88 }
89
90 AndroidCamera *camera = (*it);
91
92 const int arrayLength = env->GetArrayLength(data);
93 QByteArray bytes(arrayLength, Qt::Uninitialized);
94 env->GetByteArrayRegion(data, 0, arrayLength, reinterpret_cast<jbyte *>(bytes.data()));
95
96 auto parameters = camera->getParametersObject();
97
98 QJniObject size =
99 parameters.callObjectMethod("getPictureSize", "()Landroid/hardware/Camera$Size;");
100
101 if (!size.isValid()) {
102 qCWarning(lcAndroidCamera) << "Picture Size is not valid!";
103 return;
104 }
105
106 QSize pictureSize(size.getField<jint>("width"), size.getField<jint>("height"));
107
108 auto format = AndroidCamera::ImageFormat(parameters.callMethod<jint>("getPictureFormat"));
109
110 if (format == AndroidCamera::ImageFormat::UnknownImageFormat) {
111 qCWarning(lcAndroidCamera) << "Android Camera Image Format is UnknownImageFormat!";
112 return;
113 }
114
115 int bytesPerLine = 0;
116
117 switch (format) {
118 case AndroidCamera::ImageFormat::YV12:
119 bytesPerLine = (pictureSize.width() + 15) & ~15;
120 break;
121 case AndroidCamera::ImageFormat::NV21:
122 bytesPerLine = pictureSize.width();
123 break;
124 case AndroidCamera::ImageFormat::RGB565:
125 case AndroidCamera::ImageFormat::YUY2:
126 bytesPerLine = pictureSize.width() * 2;
127 break;
128 default:
129 bytesPerLine = -1;
130 }
131
132 auto pictureFormat = qt_pixelFormatFromAndroidImageFormat(format);
133
134 emit camera->pictureCaptured(bytes, pictureFormat, pictureSize, bytesPerLine);
135}
136
137static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
138 int width, int height, int format, int bpl)
139{
140 QReadLocker locker(rwLock);
141 const auto it = cameras->constFind(id);
142 if (Q_UNLIKELY(it == cameras->cend()))
143 return;
144
145 const int arrayLength = env->GetArrayLength(data);
146 if (arrayLength == 0)
147 return;
148
149 QByteArray bytes(arrayLength, Qt::Uninitialized);
150 env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
151
152 QVideoFrameFormat frameFormat(
153 QSize(width, height),
154 qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
155
156 QVideoFrame frame = QVideoFramePrivate::createFrame(
157 std::make_unique<QMemoryVideoBuffer>(std::move(bytes), bpl), std::move(frameFormat));
158
159 Q_EMIT (*it)->newPreviewFrame(frame);
160}
161
162static void notifyFrameAvailable(JNIEnv *, jobject, int id)
163{
164 QReadLocker locker(rwLock);
165 const auto it = cameras->constFind(id);
166 if (Q_UNLIKELY(it == cameras->cend()))
167 return;
168
169 (*it)->fetchLastPreviewFrame();
170}
171
172class AndroidCameraPrivate : public QObject
173{
174 Q_OBJECT
175public:
176 AndroidCameraPrivate();
177 ~AndroidCameraPrivate();
178
179 Q_INVOKABLE bool init(int cameraId);
180
181 Q_INVOKABLE void release();
182 Q_INVOKABLE bool lock();
183 Q_INVOKABLE bool unlock();
184 Q_INVOKABLE bool reconnect();
185
186 Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
187 Q_INVOKABLE int getNativeOrientation();
188
189 Q_INVOKABLE QSize getPreferredPreviewSizeForVideo();
190 Q_INVOKABLE QList<QSize> getSupportedPreviewSizes();
191 static QList<QSize> getSupportedPreviewSizes(QJniObject &parameters);
192
193 Q_INVOKABLE QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange();
194
195 Q_INVOKABLE AndroidCamera::FpsRange getPreviewFpsRange();
196 static AndroidCamera::FpsRange getPreviewFpsRange(QJniObject &parameters);
197 Q_INVOKABLE void setPreviewFpsRange(int min, int max);
198
199 Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat();
200 Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt);
201 Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats();
202 static QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(QJniObject &parameters);
203
204 Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
205 Q_INVOKABLE QSize getPreviewSize();
206 Q_INVOKABLE void updatePreviewSize();
207 Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
208 Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder);
209 Q_INVOKABLE void setDisplayOrientation(int degrees);
210
211 Q_INVOKABLE bool isZoomSupported();
212 Q_INVOKABLE int getMaxZoom();
213 Q_INVOKABLE QList<int> getZoomRatios();
214 Q_INVOKABLE int getZoom();
215 Q_INVOKABLE void setZoom(int value);
216
217 Q_INVOKABLE QString getFlashMode();
218 Q_INVOKABLE void setFlashMode(const QString &value);
219
220 Q_INVOKABLE QString getFocusMode();
221 Q_INVOKABLE void setFocusMode(const QString &value);
222
223 Q_INVOKABLE int getMaxNumFocusAreas();
224 Q_INVOKABLE QList<QRect> getFocusAreas();
225 Q_INVOKABLE void setFocusAreas(const QList<QRect> &areas);
226
227 Q_INVOKABLE void autoFocus();
228 Q_INVOKABLE void cancelAutoFocus();
229
230 Q_INVOKABLE bool isAutoExposureLockSupported();
231 Q_INVOKABLE bool getAutoExposureLock();
232 Q_INVOKABLE void setAutoExposureLock(bool toggle);
233
234 Q_INVOKABLE bool isAutoWhiteBalanceLockSupported();
235 Q_INVOKABLE bool getAutoWhiteBalanceLock();
236 Q_INVOKABLE void setAutoWhiteBalanceLock(bool toggle);
237
238 Q_INVOKABLE int getExposureCompensation();
239 Q_INVOKABLE void setExposureCompensation(int value);
240 Q_INVOKABLE float getExposureCompensationStep();
241 Q_INVOKABLE int getMinExposureCompensation();
242 Q_INVOKABLE int getMaxExposureCompensation();
243
244 Q_INVOKABLE QString getSceneMode();
245 Q_INVOKABLE void setSceneMode(const QString &value);
246
247 Q_INVOKABLE QString getWhiteBalance();
248 Q_INVOKABLE void setWhiteBalance(const QString &value);
249
250 Q_INVOKABLE void updateRotation();
251
252 Q_INVOKABLE QList<QSize> getSupportedPictureSizes();
253 Q_INVOKABLE QList<QSize> getSupportedVideoSizes();
254 Q_INVOKABLE void setPictureSize(const QSize &size);
255 Q_INVOKABLE void setJpegQuality(int quality);
256
257 Q_INVOKABLE void startPreview();
258 Q_INVOKABLE void stopPreview();
259
260 Q_INVOKABLE void takePicture();
261
262 Q_INVOKABLE void setupPreviewFrameCallback();
263 Q_INVOKABLE void notifyNewFrames(bool notify);
264 Q_INVOKABLE void fetchLastPreviewFrame();
265
266 Q_INVOKABLE void applyParameters();
267
268 Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName);
269
270 int m_cameraId;
271 QRecursiveMutex m_parametersMutex;
272 QSize m_previewSize;
273 int m_rotation;
274 QJniObject m_info;
275 QJniObject m_parameters;
276 QJniObject m_camera;
277 QJniObject m_cameraListener;
278
279Q_SIGNALS:
280 void previewSizeChanged();
281 void previewStarted();
282 void previewFailedToStart();
283 void previewStopped();
284
285 void autoFocusStarted();
286
287 void whiteBalanceChanged();
288
289 void takePictureFailed();
290
291 void lastPreviewFrameFetched(const QVideoFrame &frame);
292};
293
294AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
295 : QObject(),
296 d_ptr(d),
297 m_worker(worker)
298
299{
300 connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged);
301 connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted);
302 connect(d, &AndroidCameraPrivate::previewFailedToStart, this, &AndroidCamera::previewFailedToStart);
303 connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
304 connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
305 connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
306 connect(d, &AndroidCameraPrivate::takePictureFailed, this, &AndroidCamera::takePictureFailed);
307 connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched);
308}
309
311{
312 Q_D(AndroidCamera);
313 if (d->m_camera.isValid()) {
314 release();
315 QWriteLocker locker(rwLock);
316 cameras->remove(cameraId());
317 }
318
319 m_worker->exit();
320 m_worker->wait(5000);
321}
322
324{
326 return nullptr;
327
328 AndroidCameraPrivate *d = new AndroidCameraPrivate();
329 QThread *worker = new QThread;
330 worker->start();
331 d->moveToThread(worker);
332 connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
333 bool ok = true;
334 QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
335 if (!ok) {
336 worker->quit();
337 worker->wait(5000);
338 delete worker;
339 return 0;
340 }
341
342 AndroidCamera *q = new AndroidCamera(d, worker);
343 QWriteLocker locker(rwLock);
344 cameras->insert(cameraId, q);
345
346 return q;
347}
348
350{
351 Q_D(const AndroidCamera);
352 return d->m_cameraId;
353}
354
356{
357 Q_D(AndroidCamera);
358 bool ok = true;
359 QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
360 return ok;
361}
362
364{
365 Q_D(AndroidCamera);
366 bool ok = true;
367 QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
368 return ok;
369}
370
372{
373 Q_D(AndroidCamera);
374 bool ok = true;
375 QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
376 return ok;
377}
378
380{
381 Q_D(AndroidCamera);
382 QMetaObject::invokeMethod(d, "release", Qt::BlockingQueuedConnection);
383}
384
386{
387 Q_D(AndroidCamera);
388 return d->getFacing();
389}
390
392{
393 Q_D(AndroidCamera);
394 return d->getNativeOrientation();
395}
396
398{
399 Q_D(AndroidCamera);
400 return d->getPreferredPreviewSizeForVideo();
401}
402
404{
405 Q_D(AndroidCamera);
406 return d->getSupportedPreviewSizes();
407}
408
410{
411 Q_D(AndroidCamera);
412 return d->getSupportedPreviewFpsRange();
413}
414
416{
417 Q_D(AndroidCamera);
418 return d->getPreviewFpsRange();
419}
420
422{
423 Q_D(AndroidCamera);
424 QMetaObject::invokeMethod(d, "setPreviewFpsRange", Q_ARG(int, range.min), Q_ARG(int, range.max));
425}
426
428{
429 Q_D(AndroidCamera);
430 return d->getPreviewFormat();
431}
432
433void AndroidCamera::setPreviewFormat(ImageFormat fmt)
434{
435 Q_D(AndroidCamera);
436 QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt));
437}
438
440{
441 Q_D(AndroidCamera);
442 return d->getSupportedPreviewFormats();
443}
444
446{
447 Q_D(const AndroidCamera);
448 return d->m_previewSize;
449}
450
452{
453 Q_D(AndroidCamera);
454 return d->getPreviewSize();
455}
456
457void AndroidCamera::setPreviewSize(const QSize &size)
458{
459 Q_D(AndroidCamera);
460 d->m_parametersMutex.lock();
461 bool areParametersValid = d->m_parameters.isValid();
462 d->m_parametersMutex.unlock();
463 if (!areParametersValid)
464 return;
465
466 d->m_previewSize = size;
467 QMetaObject::invokeMethod(d, "updatePreviewSize");
468}
469
471{
472 Q_D(AndroidCamera);
473 bool ok = true;
474 QMetaObject::invokeMethod(d,
475 "setPreviewTexture",
476 Qt::BlockingQueuedConnection,
477 Q_RETURN_ARG(bool, ok),
478 Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
479 return ok;
480}
481
483{
484 Q_D(AndroidCamera);
485 bool ok = true;
486 QMetaObject::invokeMethod(d,
487 "setPreviewDisplay",
488 Qt::BlockingQueuedConnection,
489 Q_RETURN_ARG(bool, ok),
490 Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0));
491 return ok;
492}
493
495{
496 Q_D(AndroidCamera);
497 QMetaObject::invokeMethod(d, "setDisplayOrientation", Qt::QueuedConnection, Q_ARG(int, degrees));
498}
499
501{
502 Q_D(AndroidCamera);
503 return d->isZoomSupported();
504}
505
507{
508 Q_D(AndroidCamera);
509 return d->getMaxZoom();
510}
511
513{
514 Q_D(AndroidCamera);
515 return d->getZoomRatios();
516}
517
519{
520 Q_D(AndroidCamera);
521 return d->getZoom();
522}
523
524void AndroidCamera::setZoom(int value)
525{
526 Q_D(AndroidCamera);
527 QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value));
528}
529
531{
532 Q_D(AndroidCamera);
533 return d->callParametersStringListMethod("getSupportedFlashModes");
534}
535
537{
538 Q_D(AndroidCamera);
539 return d->getFlashMode();
540}
541
542void AndroidCamera::setFlashMode(const QString &value)
543{
544 Q_D(AndroidCamera);
545 QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value));
546}
547
549{
550 Q_D(AndroidCamera);
551 return d->callParametersStringListMethod("getSupportedFocusModes");
552}
553
555{
556 Q_D(AndroidCamera);
557 return d->getFocusMode();
558}
559
560void AndroidCamera::setFocusMode(const QString &value)
561{
562 Q_D(AndroidCamera);
563 QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value));
564}
565
567{
568 Q_D(AndroidCamera);
569 return d->getMaxNumFocusAreas();
570}
571
573{
574 Q_D(AndroidCamera);
575 return d->getFocusAreas();
576}
577
578void AndroidCamera::setFocusAreas(const QList<QRect> &areas)
579{
580 Q_D(AndroidCamera);
581 QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList<QRect>, areas));
582}
583
585{
586 Q_D(AndroidCamera);
587 QMetaObject::invokeMethod(d, "autoFocus");
588}
589
591{
592 Q_D(AndroidCamera);
593 QMetaObject::invokeMethod(d, "cancelAutoFocus", Qt::QueuedConnection);
594}
595
597{
598 Q_D(AndroidCamera);
599 return d->isAutoExposureLockSupported();
600}
601
603{
604 Q_D(AndroidCamera);
605 return d->getAutoExposureLock();
606}
607
609{
610 Q_D(AndroidCamera);
611 QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle));
612}
613
615{
616 Q_D(AndroidCamera);
617 return d->isAutoWhiteBalanceLockSupported();
618}
619
621{
622 Q_D(AndroidCamera);
623 return d->getAutoWhiteBalanceLock();
624}
625
627{
628 Q_D(AndroidCamera);
629 QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle));
630}
631
633{
634 Q_D(AndroidCamera);
635 return d->getExposureCompensation();
636}
637
639{
640 Q_D(AndroidCamera);
641 QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value));
642}
643
645{
646 Q_D(AndroidCamera);
647 return d->getExposureCompensationStep();
648}
649
651{
652 Q_D(AndroidCamera);
653 return d->getMinExposureCompensation();
654}
655
657{
658 Q_D(AndroidCamera);
659 return d->getMaxExposureCompensation();
660}
661
663{
664 Q_D(AndroidCamera);
665 return d->callParametersStringListMethod("getSupportedSceneModes");
666}
667
669{
670 Q_D(AndroidCamera);
671 return d->getSceneMode();
672}
673
674void AndroidCamera::setSceneMode(const QString &value)
675{
676 Q_D(AndroidCamera);
677 QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value));
678}
679
681{
682 Q_D(AndroidCamera);
683 return d->callParametersStringListMethod("getSupportedWhiteBalance");
684}
685
687{
688 Q_D(AndroidCamera);
689 return d->getWhiteBalance();
690}
691
692void AndroidCamera::setWhiteBalance(const QString &value)
693{
694 Q_D(AndroidCamera);
695 QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value));
696}
697
698void AndroidCamera::setRotation(int rotation)
699{
700 Q_D(AndroidCamera);
701 //We need to do it here and not in worker class because we cache rotation
702 d->m_parametersMutex.lock();
703 bool areParametersValid = d->m_parameters.isValid();
704 d->m_parametersMutex.unlock();
705 if (!areParametersValid)
706 return;
707
708 d->m_rotation = rotation;
709 QMetaObject::invokeMethod(d, "updateRotation");
710}
711
713{
714 Q_D(const AndroidCamera);
715 return d->m_rotation;
716}
717
719{
720 Q_D(AndroidCamera);
721 return d->getSupportedPictureSizes();
722}
723
725{
726 Q_D(AndroidCamera);
727 return d->getSupportedVideoSizes();
728}
729
730void AndroidCamera::setPictureSize(const QSize &size)
731{
732 Q_D(AndroidCamera);
733 QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size));
734}
735
737{
738 Q_D(AndroidCamera);
739 QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality));
740}
741
743{
744 Q_D(AndroidCamera);
745 QMetaObject::invokeMethod(d, "takePicture", Qt::BlockingQueuedConnection);
746}
747
749{
750 Q_D(AndroidCamera);
751 QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
752}
753
755{
756 Q_D(AndroidCamera);
757 QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify));
758}
759
761{
762 Q_D(AndroidCamera);
763 QMetaObject::invokeMethod(d, "fetchLastPreviewFrame");
764}
765
767{
768 Q_D(AndroidCamera);
769 return d->m_camera;
770}
771
773{
775 return 0;
776
777 return QJniObject::callStaticMethod<jint>("android/hardware/Camera",
778 "getNumberOfCameras");
779}
780
781void AndroidCamera::getCameraInfo(int id, QCameraDevicePrivate *info)
782{
783 Q_ASSERT(info);
784
785 QJniObject cameraInfo("android/hardware/Camera$CameraInfo");
786 QJniObject::callStaticMethod<void>("android/hardware/Camera",
787 "getCameraInfo",
788 "(ILandroid/hardware/Camera$CameraInfo;)V",
789 id, cameraInfo.object());
790
791 AndroidCamera::CameraFacing facing = AndroidCamera::CameraFacing(cameraInfo.getField<jint>("facing"));
792 // The orientation provided by Android is counter-clockwise, we need it clockwise
793 info->orientation = (360 - cameraInfo.getField<jint>("orientation")) % 360;
794
795 switch (facing) {
796 case AndroidCamera::CameraFacingBack:
797 info->id = QByteArray("back");
798 info->description = QStringLiteral("Rear-facing camera");
799 info->position = QCameraDevice::BackFace;
800 info->isDefault = true;
801 break;
802 case AndroidCamera::CameraFacingFront:
803 info->id = QByteArray("front");
804 info->description = QStringLiteral("Front-facing camera");
805 info->position = QCameraDevice::FrontFace;
806 break;
807 default:
808 break;
809 }
810 // Add a number to allow correct access to cameras on systems with two
811 // (and more) front/back cameras
812 if (id > 1) {
813 info->id.append(QByteArray::number(id));
814 info->description.append(QStringLiteral(" %1").arg(id));
815 }
816}
817
818QVideoFrameFormat::PixelFormat AndroidCamera::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
819{
820 switch (format) {
821 case AndroidCamera::NV21:
822 return QVideoFrameFormat::Format_NV21;
823 case AndroidCamera::YUY2:
824 return QVideoFrameFormat::Format_YUYV;
825 case AndroidCamera::JPEG:
826 return QVideoFrameFormat::Format_Jpeg;
827 case AndroidCamera::YV12:
828 return QVideoFrameFormat::Format_YV12;
829 default:
830 return QVideoFrameFormat::Format_Invalid;
831 }
832}
833
834AndroidCamera::ImageFormat AndroidCamera::AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat format)
835{
836 switch (format) {
837 case QVideoFrameFormat::Format_NV21:
838 return AndroidCamera::NV21;
839 case QVideoFrameFormat::Format_YUYV:
840 return AndroidCamera::YUY2;
841 case QVideoFrameFormat::Format_Jpeg:
842 return AndroidCamera::JPEG;
843 case QVideoFrameFormat::Format_YV12:
844 return AndroidCamera::YV12;
845 default:
846 return AndroidCamera::UnknownImageFormat;
847 }
848}
849
851{
852 QList<QCameraFormat> formats;
853 AndroidCamera::FpsRange range = getPreviewFpsRange();
854 for (const auto &previewSize : getSupportedVideoSizes()) {
855 for (const auto &previewFormat : getSupportedPreviewFormats()) {
856 QCameraFormatPrivate * format = new QCameraFormatPrivate();
857 format->pixelFormat = QtPixelFormatFromAndroidImageFormat(previewFormat);
858 format->resolution = previewSize;
859 format->minFrameRate = range.min;
860 format->maxFrameRate = range.max;
861 formats.append(format->create());
862 }
863 }
864
865 return formats;
866}
867
869{
870 Q_D(AndroidCamera);
871 QMetaObject::invokeMethod(d, "startPreview");
872}
873
875{
876 Q_D(AndroidCamera);
877 QMetaObject::invokeMethod(d, "stopPreview");
878}
879
881{
882 Q_D(AndroidCamera);
883 QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
884}
885
887{
888 Q_D(AndroidCamera);
889 return d->m_parameters;
890}
891
892AndroidCameraPrivate::AndroidCameraPrivate()
893 : QObject()
894{
895}
896
897AndroidCameraPrivate::~AndroidCameraPrivate()
898{
899}
900
902
903bool AndroidCameraPrivate::init(int cameraId)
904{
905 m_cameraId = cameraId;
906 QJniEnvironment env;
907
908 const bool opened = s_activeCameras & (1 << cameraId);
909 if (opened)
910 return false;
911
912 m_camera = QJniObject::callStaticObjectMethod("android/hardware/Camera",
913 "open",
914 "(I)Landroid/hardware/Camera;",
915 cameraId);
916 if (!m_camera.isValid())
917 return false;
918
919 m_cameraListener = QJniObject(QtCameraListenerClassName, "(I)V", m_cameraId);
920 m_info = QJniObject("android/hardware/Camera$CameraInfo");
921 m_camera.callStaticMethod<void>("android/hardware/Camera",
922 "getCameraInfo",
923 "(ILandroid/hardware/Camera$CameraInfo;)V",
924 cameraId,
925 m_info.object());
926
927 QJniObject params = m_camera.callObjectMethod("getParameters",
928 "()Landroid/hardware/Camera$Parameters;");
929 m_parameters = QJniObject(params);
930 s_activeCameras |= 1 << cameraId;
931
932 return true;
933}
934
935void AndroidCameraPrivate::release()
936{
937 m_previewSize = QSize();
938 m_parametersMutex.lock();
939 m_parameters = QJniObject();
940 m_parametersMutex.unlock();
941 if (m_camera.isValid()) {
942 m_camera.callMethod<void>("release");
943 s_activeCameras &= ~(1 << m_cameraId);
944 }
945}
946
947bool AndroidCameraPrivate::lock()
948{
949 QJniEnvironment env;
950 auto methodId = env->GetMethodID(m_camera.objectClass(), "lock", "()V");
951 env->CallVoidMethod(m_camera.object(), methodId);
952
953 if (env.checkAndClearExceptions())
954 return false;
955 return true;
956}
957
958bool AndroidCameraPrivate::unlock()
959{
960 QJniEnvironment env;
961 auto methodId = env->GetMethodID(m_camera.objectClass(), "unlock", "()V");
962 env->CallVoidMethod(m_camera.object(), methodId);
963
964 if (env.checkAndClearExceptions())
965 return false;
966 return true;
967}
968
969bool AndroidCameraPrivate::reconnect()
970{
971 QJniEnvironment env;
972 auto methodId = env->GetMethodID(m_camera.objectClass(), "reconnect", "()V");
973 env->CallVoidMethod(m_camera.object(), methodId);
974
975 if (env.checkAndClearExceptions())
976 return false;
977 return true;
978}
979
980AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
981{
982 return AndroidCamera::CameraFacing(m_info.getField<jint>("facing"));
983}
984
985int AndroidCameraPrivate::getNativeOrientation()
986{
987 return m_info.getField<jint>("orientation");
988}
989
990QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo()
991{
992 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
993
994 if (!m_parameters.isValid())
995 return QSize();
996
997 QJniObject size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo",
998 "()Landroid/hardware/Camera$Size;");
999
1000 if (!size.isValid())
1001 return QSize();
1002
1003 return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
1004}
1005
1006QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes()
1007{
1008 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1009 return getSupportedPreviewSizes(m_parameters);
1010}
1011
1012QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes(QJniObject &parameters)
1013{
1014 QList<QSize> list;
1015
1016 if (parameters.isValid()) {
1017 QJniObject sizeList = parameters.callObjectMethod("getSupportedPreviewSizes",
1018 "()Ljava/util/List;");
1019 int count = sizeList.callMethod<jint>("size");
1020 for (int i = 0; i < count; ++i) {
1021 QJniObject size = sizeList.callObjectMethod("get",
1022 "(I)Ljava/lang/Object;",
1023 i);
1024 list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
1025 }
1026
1027 ranges::sort(list, qt_sizeLessThan);
1028 }
1029
1030 return list;
1031}
1032
1033QList<AndroidCamera::FpsRange> AndroidCameraPrivate::getSupportedPreviewFpsRange()
1034{
1035 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1036
1037 QJniEnvironment env;
1038
1039 QList<AndroidCamera::FpsRange> rangeList;
1040
1041 if (m_parameters.isValid()) {
1042 QJniObject rangeListNative = m_parameters.callObjectMethod("getSupportedPreviewFpsRange",
1043 "()Ljava/util/List;");
1044 int count = rangeListNative.callMethod<jint>("size");
1045
1046 rangeList.reserve(count);
1047
1048 for (int i = 0; i < count; ++i) {
1049 QJniObject range = rangeListNative.callObjectMethod("get",
1050 "(I)Ljava/lang/Object;",
1051 i);
1052
1053 jintArray jRange = static_cast<jintArray>(range.object());
1054 jint* rangeArray = env->GetIntArrayElements(jRange, 0);
1055
1056 AndroidCamera::FpsRange fpsRange;
1057
1058 fpsRange.min = rangeArray[0];
1059 fpsRange.max = rangeArray[1];
1060
1061 env->ReleaseIntArrayElements(jRange, rangeArray, 0);
1062
1063 rangeList << fpsRange;
1064 }
1065 }
1066
1067 return rangeList;
1068}
1069
1070AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange()
1071{
1072 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1073 return getPreviewFpsRange(m_parameters);
1074}
1075
1076AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange(QJniObject &parameters)
1077{
1078 QJniEnvironment env;
1079
1080 AndroidCamera::FpsRange range;
1081
1082 if (!parameters.isValid())
1083 return range;
1084
1085 jintArray jRangeArray = env->NewIntArray(2);
1086 parameters.callMethod<void>("getPreviewFpsRange", "([I)V", jRangeArray);
1087
1088 jint* jRangeElements = env->GetIntArrayElements(jRangeArray, 0);
1089
1090 // Android Camera API returns values scaled by 1000, so divide here to report
1091 // normal values for Qt
1092 range.min = jRangeElements[0] / 1000;
1093 range.max = jRangeElements[1] / 1000;
1094
1095 env->ReleaseIntArrayElements(jRangeArray, jRangeElements, 0);
1096 env->DeleteLocalRef(jRangeArray);
1097
1098 return range;
1099}
1100
1101void AndroidCameraPrivate::setPreviewFpsRange(int min, int max)
1102{
1103 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1104
1105 if (!m_parameters.isValid())
1106 return;
1107
1108 // Android Camera API returns values scaled by 1000, so multiply here to
1109 // give Android API the scale is expects
1110 m_parameters.callMethod<void>("setPreviewFpsRange", "(II)V", min * 1000, max * 1000);
1111}
1112
1113AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat()
1114{
1115 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1116
1117 if (!m_parameters.isValid())
1118 return AndroidCamera::UnknownImageFormat;
1119
1120 return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat"));
1121}
1122
1123void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt)
1124{
1125 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1126
1127 if (!m_parameters.isValid())
1128 return;
1129
1130 m_parameters.callMethod<void>("setPreviewFormat", "(I)V", jint(fmt));
1131 applyParameters();
1132}
1133
1134QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats()
1135{
1136 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1137 return getSupportedPreviewFormats(m_parameters);
1138}
1139
1140QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats(QJniObject &parameters)
1141{
1142 QList<AndroidCamera::ImageFormat> list;
1143
1144 if (parameters.isValid()) {
1145 QJniObject formatList = parameters.callObjectMethod("getSupportedPreviewFormats",
1146 "()Ljava/util/List;");
1147 int count = formatList.callMethod<jint>("size");
1148 for (int i = 0; i < count; ++i) {
1149 QJniObject format = formatList.callObjectMethod("get",
1150 "(I)Ljava/lang/Object;",
1151 i);
1152 list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue")));
1153 }
1154 }
1155
1156 return list;
1157}
1158
1159QSize AndroidCameraPrivate::getPreviewSize()
1160{
1161 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1162
1163 if (!m_parameters.isValid())
1164 return QSize();
1165
1166 QJniObject size = m_parameters.callObjectMethod("getPreviewSize",
1167 "()Landroid/hardware/Camera$Size;");
1168
1169 if (!size.isValid())
1170 return QSize();
1171
1172 return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
1173}
1174
1175void AndroidCameraPrivate::updatePreviewSize()
1176{
1177 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1178
1179 if (m_previewSize.isValid()) {
1180 m_parameters.callMethod<void>("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height());
1181 applyParameters();
1182 }
1183
1184 emit previewSizeChanged();
1185}
1186
1187bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
1188{
1189 QJniEnvironment env;
1190 auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewTexture",
1191 "(Landroid/graphics/SurfaceTexture;)V");
1192 env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceTexture));
1193
1194 if (env.checkAndClearExceptions())
1195 return false;
1196 return true;
1197}
1198
1199bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
1200{
1201 QJniEnvironment env;
1202 auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewDisplay",
1203 "(Landroid/view/SurfaceHolder;)V");
1204 env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceHolder));
1205
1206 if (env.checkAndClearExceptions())
1207 return false;
1208 return true;
1209}
1210
1211void AndroidCameraPrivate::setDisplayOrientation(int degrees)
1212{
1213 m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
1214 m_cameraListener.callMethod<void>("setPhotoRotation", "(I)V", degrees);
1215}
1216
1217bool AndroidCameraPrivate::isZoomSupported()
1218{
1219 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1220
1221 if (!m_parameters.isValid())
1222 return false;
1223
1224 return m_parameters.callMethod<jboolean>("isZoomSupported");
1225}
1226
1227int AndroidCameraPrivate::getMaxZoom()
1228{
1229 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1230
1231 if (!m_parameters.isValid())
1232 return 0;
1233
1234 return m_parameters.callMethod<jint>("getMaxZoom");
1235}
1236
1237QList<int> AndroidCameraPrivate::getZoomRatios()
1238{
1239 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1240
1241 QList<int> ratios;
1242
1243 if (m_parameters.isValid()) {
1244 QJniObject ratioList = m_parameters.callObjectMethod("getZoomRatios",
1245 "()Ljava/util/List;");
1246 int count = ratioList.callMethod<jint>("size");
1247 for (int i = 0; i < count; ++i) {
1248 QJniObject zoomRatio = ratioList.callObjectMethod("get",
1249 "(I)Ljava/lang/Object;",
1250 i);
1251
1252 ratios.append(zoomRatio.callMethod<jint>("intValue"));
1253 }
1254 }
1255
1256 return ratios;
1257}
1258
1259int AndroidCameraPrivate::getZoom()
1260{
1261 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1262
1263 if (!m_parameters.isValid())
1264 return 0;
1265
1266 return m_parameters.callMethod<jint>("getZoom");
1267}
1268
1269void AndroidCameraPrivate::setZoom(int value)
1270{
1271 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1272
1273 if (!m_parameters.isValid())
1274 return;
1275
1276 m_parameters.callMethod<void>("setZoom", "(I)V", value);
1277 applyParameters();
1278}
1279
1280QString AndroidCameraPrivate::getFlashMode()
1281{
1282 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1283
1284 QString value;
1285
1286 if (m_parameters.isValid()) {
1287 QJniObject flashMode = m_parameters.callObjectMethod("getFlashMode",
1288 "()Ljava/lang/String;");
1289 if (flashMode.isValid())
1290 value = flashMode.toString();
1291 }
1292
1293 return value;
1294}
1295
1296void AndroidCameraPrivate::setFlashMode(const QString &value)
1297{
1298 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1299
1300 if (!m_parameters.isValid())
1301 return;
1302
1303 m_parameters.callMethod<void>("setFlashMode",
1304 "(Ljava/lang/String;)V",
1305 QJniObject::fromString(value).object());
1306 applyParameters();
1307}
1308
1309QString AndroidCameraPrivate::getFocusMode()
1310{
1311 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1312
1313 QString value;
1314
1315 if (m_parameters.isValid()) {
1316 QJniObject focusMode = m_parameters.callObjectMethod("getFocusMode",
1317 "()Ljava/lang/String;");
1318 if (focusMode.isValid())
1319 value = focusMode.toString();
1320 }
1321
1322 return value;
1323}
1324
1325void AndroidCameraPrivate::setFocusMode(const QString &value)
1326{
1327 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1328
1329 if (!m_parameters.isValid())
1330 return;
1331
1332 m_parameters.callMethod<void>("setFocusMode",
1333 "(Ljava/lang/String;)V",
1334 QJniObject::fromString(value).object());
1335 applyParameters();
1336}
1337
1338int AndroidCameraPrivate::getMaxNumFocusAreas()
1339{
1340 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1341
1342 if (!m_parameters.isValid())
1343 return 0;
1344
1345 return m_parameters.callMethod<jint>("getMaxNumFocusAreas");
1346}
1347
1348QList<QRect> AndroidCameraPrivate::getFocusAreas()
1349{
1350 QList<QRect> areas;
1351 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1352
1353 if (m_parameters.isValid()) {
1354 QJniObject list = m_parameters.callObjectMethod("getFocusAreas",
1355 "()Ljava/util/List;");
1356
1357 if (list.isValid()) {
1358 int count = list.callMethod<jint>("size");
1359 for (int i = 0; i < count; ++i) {
1360 QJniObject area = list.callObjectMethod("get",
1361 "(I)Ljava/lang/Object;",
1362 i);
1363
1364 areas.append(areaToRect(area.object()));
1365 }
1366 }
1367 }
1368
1369 return areas;
1370}
1371
1372void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
1373{
1374 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1375
1376 if (!m_parameters.isValid() || areas.isEmpty())
1377 return;
1378
1379 QJniObject list;
1380
1381 if (!areas.isEmpty()) {
1382 QJniEnvironment env;
1383 QJniObject arrayList("java/util/ArrayList", "(I)V", areas.size());
1384 for (int i = 0; i < areas.size(); ++i) {
1385 arrayList.callMethod<jboolean>("add",
1386 "(Ljava/lang/Object;)Z",
1387 rectToArea(areas.at(i)).object());
1388 }
1389 list = arrayList;
1390 }
1391
1392 m_parameters.callMethod<void>("setFocusAreas", "(Ljava/util/List;)V", list.object());
1393
1394 applyParameters();
1395}
1396
1397void AndroidCameraPrivate::autoFocus()
1398{
1399 QJniEnvironment env;
1400 auto methodId = env->GetMethodID(m_camera.objectClass(), "autoFocus",
1401 "(Landroid/hardware/Camera$AutoFocusCallback;)V");
1402 env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object());
1403
1404 if (!env.checkAndClearExceptions())
1405 emit autoFocusStarted();
1406}
1407
1408void AndroidCameraPrivate::cancelAutoFocus()
1409{
1410 QJniEnvironment env;
1411 m_camera.callMethod<void>("cancelAutoFocus");
1412}
1413
1414bool AndroidCameraPrivate::isAutoExposureLockSupported()
1415{
1416 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1417
1418 if (!m_parameters.isValid())
1419 return false;
1420
1421 return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported");
1422}
1423
1424bool AndroidCameraPrivate::getAutoExposureLock()
1425{
1426 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1427
1428 if (!m_parameters.isValid())
1429 return false;
1430
1431 return m_parameters.callMethod<jboolean>("getAutoExposureLock");
1432}
1433
1434void AndroidCameraPrivate::setAutoExposureLock(bool toggle)
1435{
1436 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1437
1438 if (!m_parameters.isValid())
1439 return;
1440
1441 m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle);
1442 applyParameters();
1443}
1444
1445bool AndroidCameraPrivate::isAutoWhiteBalanceLockSupported()
1446{
1447 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1448
1449 if (!m_parameters.isValid())
1450 return false;
1451
1452 return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported");
1453}
1454
1455bool AndroidCameraPrivate::getAutoWhiteBalanceLock()
1456{
1457 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1458
1459 if (!m_parameters.isValid())
1460 return false;
1461
1462 return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock");
1463}
1464
1465void AndroidCameraPrivate::setAutoWhiteBalanceLock(bool toggle)
1466{
1467 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1468
1469 if (!m_parameters.isValid())
1470 return;
1471
1472 m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle);
1473 applyParameters();
1474}
1475
1476int AndroidCameraPrivate::getExposureCompensation()
1477{
1478 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1479
1480 if (!m_parameters.isValid())
1481 return 0;
1482
1483 return m_parameters.callMethod<jint>("getExposureCompensation");
1484}
1485
1486void AndroidCameraPrivate::setExposureCompensation(int value)
1487{
1488 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1489
1490 if (!m_parameters.isValid())
1491 return;
1492
1493 m_parameters.callMethod<void>("setExposureCompensation", "(I)V", value);
1494 applyParameters();
1495}
1496
1497float AndroidCameraPrivate::getExposureCompensationStep()
1498{
1499 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1500
1501 if (!m_parameters.isValid())
1502 return 0;
1503
1504 return m_parameters.callMethod<jfloat>("getExposureCompensationStep");
1505}
1506
1507int AndroidCameraPrivate::getMinExposureCompensation()
1508{
1509 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1510
1511 if (!m_parameters.isValid())
1512 return 0;
1513
1514 return m_parameters.callMethod<jint>("getMinExposureCompensation");
1515}
1516
1517int AndroidCameraPrivate::getMaxExposureCompensation()
1518{
1519 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1520
1521 if (!m_parameters.isValid())
1522 return 0;
1523
1524 return m_parameters.callMethod<jint>("getMaxExposureCompensation");
1525}
1526
1527QString AndroidCameraPrivate::getSceneMode()
1528{
1529 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1530
1531 QString value;
1532
1533 if (m_parameters.isValid()) {
1534 QJniObject sceneMode = m_parameters.callObjectMethod("getSceneMode",
1535 "()Ljava/lang/String;");
1536 if (sceneMode.isValid())
1537 value = sceneMode.toString();
1538 }
1539
1540 return value;
1541}
1542
1543void AndroidCameraPrivate::setSceneMode(const QString &value)
1544{
1545 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1546
1547 if (!m_parameters.isValid())
1548 return;
1549
1550 m_parameters.callMethod<void>("setSceneMode",
1551 "(Ljava/lang/String;)V",
1552 QJniObject::fromString(value).object());
1553 applyParameters();
1554}
1555
1556QString AndroidCameraPrivate::getWhiteBalance()
1557{
1558 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1559
1560 QString value;
1561
1562 if (m_parameters.isValid()) {
1563 QJniObject wb = m_parameters.callObjectMethod("getWhiteBalance",
1564 "()Ljava/lang/String;");
1565 if (wb.isValid())
1566 value = wb.toString();
1567 }
1568
1569 return value;
1570}
1571
1572void AndroidCameraPrivate::setWhiteBalance(const QString &value)
1573{
1574 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1575
1576 if (!m_parameters.isValid())
1577 return;
1578
1579 m_parameters.callMethod<void>("setWhiteBalance",
1580 "(Ljava/lang/String;)V",
1581 QJniObject::fromString(value).object());
1582 applyParameters();
1583
1584 emit whiteBalanceChanged();
1585}
1586
1587void AndroidCameraPrivate::updateRotation()
1588{
1589 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1590
1591 m_parameters.callMethod<void>("setRotation", "(I)V", m_rotation);
1592 applyParameters();
1593}
1594
1595QList<QSize> AndroidCameraPrivate::getSupportedPictureSizes()
1596{
1597 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1598
1599 QList<QSize> list;
1600
1601 if (m_parameters.isValid()) {
1602 QJniObject sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes",
1603 "()Ljava/util/List;");
1604 int count = sizeList.callMethod<jint>("size");
1605 for (int i = 0; i < count; ++i) {
1606 QJniObject size = sizeList.callObjectMethod("get",
1607 "(I)Ljava/lang/Object;",
1608 i);
1609 list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
1610 }
1611
1612 ranges::sort(list, qt_sizeLessThan);
1613 }
1614
1615 return list;
1616}
1617
1618QList<QSize> AndroidCameraPrivate::getSupportedVideoSizes()
1619{
1620 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1621 QList<QSize> list;
1622
1623 if (m_parameters.isValid()) {
1624 QJniObject sizeList = m_parameters.callObjectMethod("getSupportedVideoSizes",
1625 "()Ljava/util/List;");
1626 if (!sizeList.isValid())
1627 return list;
1628
1629 int count = sizeList.callMethod<jint>("size");
1630 for (int i = 0; i < count; ++i) {
1631 const QJniObject size = sizeList.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
1632 if (size.isValid())
1633 list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
1634 }
1635 ranges::sort(list, qt_sizeLessThan);
1636 }
1637
1638 return list;
1639}
1640
1641void AndroidCameraPrivate::setPictureSize(const QSize &size)
1642{
1643 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1644
1645 if (!m_parameters.isValid())
1646 return;
1647
1648 m_parameters.callMethod<void>("setPictureSize", "(II)V", size.width(), size.height());
1649 applyParameters();
1650}
1651
1652void AndroidCameraPrivate::setJpegQuality(int quality)
1653{
1654 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1655
1656 if (!m_parameters.isValid())
1657 return;
1658
1659 m_parameters.callMethod<void>("setJpegQuality", "(I)V", quality);
1660 applyParameters();
1661}
1662
1663void AndroidCameraPrivate::startPreview()
1664{
1665 setupPreviewFrameCallback();
1666
1667 QJniEnvironment env;
1668 auto methodId = env->GetMethodID(m_camera.objectClass(), "startPreview", "()V");
1669 env->CallVoidMethod(m_camera.object(), methodId);
1670
1671 if (env.checkAndClearExceptions())
1672 emit previewFailedToStart();
1673 else
1674 emit previewStarted();
1675}
1676
1677void AndroidCameraPrivate::stopPreview()
1678{
1679 // cancel any pending new frame notification
1680 m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false);
1681 m_camera.callMethod<void>("stopPreview");
1682 emit previewStopped();
1683}
1684
1685void AndroidCameraPrivate::takePicture()
1686{
1687 // We must clear the preview callback before calling takePicture(), otherwise the call will
1688 // block and the camera server will be frozen until the next device restart...
1689 // That problem only happens on some devices and on the emulator
1690 m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
1691
1692 QJniEnvironment env;
1693 auto methodId = env->GetMethodID(m_camera.objectClass(), "takePicture",
1694 "(Landroid/hardware/Camera$ShutterCallback;"
1695 "Landroid/hardware/Camera$PictureCallback;"
1696 "Landroid/hardware/Camera$PictureCallback;)V");
1697 env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object(),
1698 jobject(0), m_cameraListener.object());
1699
1700 if (env.checkAndClearExceptions())
1701 emit takePictureFailed();
1702}
1703
1704void AndroidCameraPrivate::setupPreviewFrameCallback()
1705{
1706 m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
1707}
1708
1709void AndroidCameraPrivate::notifyNewFrames(bool notify)
1710{
1711 m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify);
1712}
1713
1714void AndroidCameraPrivate::fetchLastPreviewFrame()
1715{
1716 QJniEnvironment env;
1717 QJniObject data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
1718
1719 if (!data.isValid()) {
1720 // If there's no buffer received yet, retry when the next one arrives
1721 m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true);
1722 return;
1723 }
1724
1725 const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
1726 if (arrayLength == 0)
1727 return;
1728
1729 QByteArray bytes(arrayLength, Qt::Uninitialized);
1730 env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
1731 0,
1732 arrayLength,
1733 reinterpret_cast<jbyte *>(bytes.data()));
1734
1735 const int width = m_cameraListener.callMethod<jint>("previewWidth");
1736 const int height = m_cameraListener.callMethod<jint>("previewHeight");
1737 const int format = m_cameraListener.callMethod<jint>("previewFormat");
1738 const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine");
1739
1740 QVideoFrameFormat frameFormat(
1741 QSize(width, height),
1742 qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
1743
1744 QVideoFrame frame = QVideoFramePrivate::createFrame(
1745 std::make_unique<QMemoryVideoBuffer>(std::move(bytes), bpl), std::move(frameFormat));
1746
1747 emit lastPreviewFrameFetched(frame);
1748}
1749
1750void AndroidCameraPrivate::applyParameters()
1751{
1752 QJniEnvironment env;
1753 m_camera.callMethod<void>("setParameters",
1754 "(Landroid/hardware/Camera$Parameters;)V",
1755 m_parameters.object());
1756}
1757
1758QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
1759{
1760 const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
1761
1762 QStringList stringList;
1763
1764 if (m_parameters.isValid()) {
1765 QJniObject list = m_parameters.callObjectMethod(methodName.constData(),
1766 "()Ljava/util/List;");
1767
1768 if (list.isValid()) {
1769 int count = list.callMethod<jint>("size");
1770 for (int i = 0; i < count; ++i) {
1771 QJniObject string = list.callObjectMethod("get",
1772 "(I)Ljava/lang/Object;",
1773 i);
1774 stringList.append(string.toString());
1775 }
1776 }
1777 }
1778
1779 return stringList;
1780}
1781
1783{
1784 static const JNINativeMethod methods[] = {
1785 {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
1786 {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
1787 {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
1788 {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame},
1789 {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable}
1790 };
1791
1792 const int size = std::size(methods);
1793 return QJniEnvironment().registerNativeMethods(QtCameraListenerClassName, methods, size);
1794}
1795
1796QT_END_NAMESPACE
1797
1798#include "androidcamera.moc"
1799#include "moc_androidcamera_p.cpp"
QHash< int, AndroidCamera * > CameraMap
static qint32 s_activeCameras
static const char QtCameraListenerClassName[]
void autoFocusStarted()
void setupPreviewFrameCallback()
QJniObject getCameraObject()
ImageFormat getPreviewFormat()
QSize getPreferredPreviewSizeForVideo()
void setPictureSize(const QSize &size)
void setFlashMode(const QString &value)
void previewStarted()
QSize actualPreviewSize()
void takePictureFailed()
int getExposureCompensation()
QString getWhiteBalance()
void setExposureCompensation(int value)
bool getAutoExposureLock()
float getExposureCompensationStep()
QList< QCameraFormat > getSupportedFormats()
void setFocusAreas(const QList< QRect > &areas)
void previewStopped()
bool getAutoWhiteBalanceLock()
QList< QRect > getFocusAreas()
void stopPreviewSynchronous()
QStringList getSupportedFocusModes()
bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder)
void setRotation(int rotation)
QList< FpsRange > getSupportedPreviewFpsRange()
bool isAutoWhiteBalanceLockSupported()
static int getNumberOfCameras()
QJniObject getParametersObject()
void previewFailedToStart()
void setWhiteBalance(const QString &value)
bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
void setAutoExposureLock(bool toggle)
FpsRange getPreviewFpsRange()
void fetchLastPreviewFrame()
void setPreviewFpsRange(FpsRange)
QString getFocusMode()
void setJpegQuality(int quality)
QStringList getSupportedSceneModes()
QList< QSize > getSupportedPictureSizes()
QList< QSize > getSupportedPreviewSizes()
QString getSceneMode()
int getRotation() const
static AndroidCamera * open(int cameraId)
void setSceneMode(const QString &value)
QList< QSize > getSupportedVideoSizes()
static bool registerNativeMethods()
void setFocusMode(const QString &value)
QString getFlashMode()
QList< ImageFormat > getSupportedPreviewFormats()
void setDisplayOrientation(int degrees)
QSize previewSize() const
QStringList getSupportedWhiteBalance()
int getMinExposureCompensation()
CameraFacing getFacing()
void notifyNewFrames(bool notify)
void whiteBalanceChanged()
void setPreviewSize(const QSize &size)
int getMaxExposureCompensation()
void setPreviewFormat(ImageFormat fmt)
int cameraId() const
QStringList getSupportedFlashModes()
QList< int > getZoomRatios()
void setAutoWhiteBalanceLock(bool toggle)
void setZoom(int value)
bool isAutoExposureLockSupported()
\inmodule QtCore
Definition qhash.h:843
bool qt_androidCheckCameraPermission()
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)