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