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
qandroidcamerasession.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
6
12#include <qvideosink.h>
13#include <QtConcurrent/qtconcurrentrun.h>
14#include <qfile.h>
15#include <qguiapplication.h>
16#include <qscreen.h>
17#include <qdebug.h>
18#include <qvideoframe.h>
19#include <private/qplatformimagecapture_p.h>
20#include <private/qplatformvideosink_p.h>
21#include <private/qmemoryvideobuffer_p.h>
22#include <private/qcameradevice_p.h>
23#include <private/qmediastoragelocation_p.h>
24#include <private/qvideoframe_p.h>
25#include <QImageWriter>
26
27QT_BEGIN_NAMESPACE
28
29Q_GLOBAL_STATIC(QList<QCameraDevice>, g_availableCameras)
30
31QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
32 : QObject(parent)
33 , m_selectedCamera(0)
34 , m_camera(0)
35 , m_videoOutput(0)
36 , m_savedState(-1)
37 , m_previewStarted(false)
38 , m_readyForCapture(false)
39 , m_currentImageCaptureId(-1)
40 , m_previewCallback(0)
41 , m_keepActive(false)
42{
43 if (qApp) {
44 connect(qApp, &QGuiApplication::applicationStateChanged,
45 this, &QAndroidCameraSession::onApplicationStateChanged);
46
47 auto screen = qApp->primaryScreen();
48 if (screen) {
49 connect(screen, &QScreen::orientationChanged, this,
50 &QAndroidCameraSession::updateOrientation);
51 enableRotation();
52 }
53 }
54}
55
56QAndroidCameraSession::~QAndroidCameraSession()
57{
58 if (m_sink)
59 disconnect(m_retryPreviewConnection);
60 close();
61}
62
63//void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
64//{
65// if (m_captureMode == mode || !isCaptureModeSupported(mode))
66// return;
67
68// m_captureMode = mode;
69// emit captureModeChanged(m_captureMode);
70
71// if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
72// applyResolution(m_actualImageSettings.resolution());
73//}
74
75void QAndroidCameraSession::setActive(bool active)
76{
77 if (m_active == active)
78 return;
79
80 // If the application is inactive, the camera shouldn't be started. Save the desired state
81 // instead and it will be set when the application becomes active.
82 if (active && qApp->applicationState() == Qt::ApplicationInactive) {
83 m_isStateSaved = true;
84 m_savedState = active;
85 return;
86 }
87
88 m_isStateSaved = false;
89 m_active = active;
90 setActiveHelper(m_active);
91 emit activeChanged(m_active);
92}
93
94void QAndroidCameraSession::setActiveHelper(bool active)
95{
96 if (!active) {
97 stopPreview();
98 close();
99 } else {
100 if (!m_camera && !open()) {
101 emit error(QCamera::CameraError, QStringLiteral("Failed to open camera"));
102 return;
103 }
104 startPreview();
105 }
106}
107
108void QAndroidCameraSession::updateAvailableCameras()
109{
110 g_availableCameras->clear();
111
112 const int numCameras = AndroidCamera::getNumberOfCameras();
113 for (int i = 0; i < numCameras; ++i) {
114 QCameraDevicePrivate *info = new QCameraDevicePrivate;
115 AndroidCamera::getCameraInfo(i, info);
116
117 if (!info->id.isEmpty()) {
118 // Add supported picture and video sizes to the camera info
119 AndroidCamera *camera = AndroidCamera::open(i);
120
121 if (camera) {
122 info->videoFormats = camera->getSupportedFormats();
123 info->photoResolutions = camera->getSupportedPictureSizes();
124 }
125
126 delete camera;
127 g_availableCameras->append(info->create());
128 }
129 }
130}
131
132const QList<QCameraDevice> &QAndroidCameraSession::availableCameras()
133{
134 if (g_availableCameras->isEmpty())
135 updateAvailableCameras();
136
137 return *g_availableCameras;
138}
139
140bool QAndroidCameraSession::open()
141{
142 close();
143
144 m_camera = AndroidCamera::open(m_selectedCamera);
145
146 if (m_camera) {
147 connect(m_camera, &AndroidCamera::pictureExposed,
148 this, &QAndroidCameraSession::onCameraPictureExposed);
149 connect(m_camera, &AndroidCamera::lastPreviewFrameFetched,
150 this, &QAndroidCameraSession::onLastPreviewFrameFetched,
151 Qt::DirectConnection);
152 connect(m_camera, &AndroidCamera::newPreviewFrame,
153 this, &QAndroidCameraSession::onNewPreviewFrame,
154 Qt::DirectConnection);
155 connect(m_camera, &AndroidCamera::pictureCaptured,
156 this, &QAndroidCameraSession::onCameraPictureCaptured);
157 connect(m_camera, &AndroidCamera::previewStarted,
158 this, &QAndroidCameraSession::onCameraPreviewStarted);
159 connect(m_camera, &AndroidCamera::previewStopped,
160 this, &QAndroidCameraSession::onCameraPreviewStopped);
161 connect(m_camera, &AndroidCamera::previewFailedToStart,
162 this, &QAndroidCameraSession::onCameraPreviewFailedToStart);
163 connect(m_camera, &AndroidCamera::takePictureFailed,
164 this, &QAndroidCameraSession::onCameraTakePictureFailed);
165
166 if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
167 m_camera->setPreviewFormat(AndroidCamera::NV21);
168
169 m_camera->notifyNewFrames(m_previewCallback);
170
171 emit opened();
172 setActive(true);
173 }
174
175 return m_camera != 0;
176}
177
178void QAndroidCameraSession::close()
179{
180 if (!m_camera)
181 return;
182
183 stopPreview();
184
185 m_readyForCapture = false;
186 m_currentImageCaptureId = -1;
187 m_currentImageCaptureFileName.clear();
188 m_actualImageSettings = m_requestedImageSettings;
189
190 m_camera->release();
191 delete m_camera;
192 m_camera = 0;
193
194 setActive(false);
195}
196
197void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
198{
199 if (m_videoOutput) {
200 m_videoOutput->stop();
201 m_videoOutput->reset();
202 }
203
204 if (output) {
205 m_videoOutput = output;
206 if (m_videoOutput->isReady()) {
207 onVideoOutputReady(true);
208 } else {
209 connect(m_videoOutput, &QAndroidVideoOutput::readyChanged,
210 this, &QAndroidCameraSession::onVideoOutputReady);
211 }
212 } else {
213 m_videoOutput = 0;
214 }
215}
216
217void QAndroidCameraSession::setCameraFormat(const QCameraFormat &format)
218{
219 m_requestedFpsRange.min = format.minFrameRate();
220 m_requestedFpsRange.max = format.maxFrameRate();
221 m_requestedPixelFromat = AndroidCamera::AndroidImageFormatFromQtPixelFormat(format.pixelFormat());
222
223 m_requestedImageSettings.setResolution(format.resolution());
224 m_actualImageSettings.setResolution(format.resolution());
225 if (m_readyForCapture)
226 applyResolution(m_actualImageSettings.resolution());
227}
228
229void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool restartPreview)
230{
231 if (!m_camera)
232 return;
233
234 const QSize currentViewfinderResolution = m_camera->previewSize();
235 const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
236 const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
237
238 // -- adjust resolution
239 QSize adjustedViewfinderResolution;
240 const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
241
242 const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
243 if (validCaptureSize
244 && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
245 // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
246 // the preview size cannot be different from the capture size
247 adjustedViewfinderResolution = captureSize;
248 } else {
249 qreal captureAspectRatio = 0;
250 if (validCaptureSize)
251 captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
252
253 if (validCaptureSize) {
254 // search for viewfinder resolution with the same aspect ratio
255 qreal minAspectDiff = 1;
256 QSize closestResolution;
257 for (int i = previewSizes.count() - 1; i >= 0; --i) {
258 const QSize &size = previewSizes.at(i);
259 const qreal sizeAspect = qreal(size.width()) / size.height();
260 if (qFuzzyCompare(captureAspectRatio, sizeAspect)) {
261 adjustedViewfinderResolution = size;
262 break;
263 } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) {
264 closestResolution = size;
265 minAspectDiff = qAbs(sizeAspect - captureAspectRatio);
266 }
267 }
268 if (!adjustedViewfinderResolution.isValid()) {
269 qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
270 if (closestResolution.isValid()) {
271 adjustedViewfinderResolution = closestResolution;
272 qWarning("Using closest viewfinder resolution.");
273 } else {
274 return;
275 }
276 }
277 } else {
278 adjustedViewfinderResolution = previewSizes.last();
279 }
280 }
281
282 // -- adjust pixel format
283
284 AndroidCamera::ImageFormat adjustedPreviewFormat = m_requestedPixelFromat;
285 if (adjustedPreviewFormat == AndroidCamera::UnknownImageFormat)
286 adjustedPreviewFormat = AndroidCamera::NV21;
287
288 // -- adjust FPS
289
290 AndroidCamera::FpsRange adjustedFps = m_requestedFpsRange;
291 if (adjustedFps.min == 0 || adjustedFps.max == 0)
292 adjustedFps = currentFpsRange;
293
294 // -- Set values on camera
295
296 // fix the resolution of output based on the orientation
297 QSize cameraOutputResolution = adjustedViewfinderResolution;
298 QSize videoOutputResolution = adjustedViewfinderResolution;
299 QSize currentVideoOutputResolution = m_videoOutput ? m_videoOutput->getVideoSize() : QSize(0, 0);
300 const int rotation = currentCameraRotation();
301 // only transpose if it's valid for the preview
302 if (rotation == 90 || rotation == 270) {
303 videoOutputResolution.transpose();
304 if (previewSizes.contains(cameraOutputResolution.transposed()))
305 cameraOutputResolution.transpose();
306 }
307
308 if (currentViewfinderResolution != cameraOutputResolution
309 || (m_videoOutput && currentVideoOutputResolution != videoOutputResolution)
310 || currentPreviewFormat != adjustedPreviewFormat || currentFpsRange.min != adjustedFps.min
311 || currentFpsRange.max != adjustedFps.max) {
312 if (m_videoOutput) {
313 m_videoOutput->setVideoSize(videoOutputResolution);
314 }
315
316 // if preview is started, we have to stop it first before changing its size
317 if (m_previewStarted && restartPreview)
318 m_camera->stopPreview();
319
320 m_camera->setPreviewSize(cameraOutputResolution);
321 m_camera->setPreviewFormat(adjustedPreviewFormat);
322 m_camera->setPreviewFpsRange(adjustedFps);
323
324 // restart preview
325 if (m_previewStarted && restartPreview)
326 m_camera->startPreview();
327 }
328}
329
330QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
331{
332 return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
333}
334
335QList<QVideoFrameFormat::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
336{
337 QList<QVideoFrameFormat::PixelFormat> formats;
338
339 if (!m_camera)
340 return formats;
341
342 const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
343
344 formats.reserve(nativeFormats.size());
345
346 for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
347 QVideoFrameFormat::PixelFormat format = AndroidCamera::QtPixelFormatFromAndroidImageFormat(nativeFormat);
348 if (format != QVideoFrameFormat::Format_Invalid)
349 formats.append(format);
350 }
351
352 return formats;
353}
354
355QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
356{
357 return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
358}
359
360
361bool QAndroidCameraSession::startPreview()
362{
363 if (!m_camera || !m_videoOutput)
364 return false;
365
366 if (m_previewStarted)
367 return true;
368
369 if (!m_videoOutput->isReady())
370 return true; // delay starting until the video output is ready
371
372 Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder());
373
374 if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
375 || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder())))
376 return false;
377
378 applyResolution(m_actualImageSettings.resolution());
379
380 AndroidMultimediaUtils::enableOrientationListener(true);
381
382 updateOrientation();
383 m_camera->startPreview();
384 m_previewStarted = true;
385 m_videoOutput->start();
386
387 return true;
388}
389
390QSize QAndroidCameraSession::getDefaultResolution() const
391{
392 const bool hasHighQualityProfile = AndroidCamcorderProfile::hasProfile(
393 m_camera->cameraId(),
394 AndroidCamcorderProfile::Quality(AndroidCamcorderProfile::QUALITY_HIGH));
395
396 if (hasHighQualityProfile) {
397 const AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(
398 m_camera->cameraId(),
399 AndroidCamcorderProfile::Quality(AndroidCamcorderProfile::QUALITY_HIGH));
400
401 return QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
402 camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
403 }
404 return QSize();
405}
406
407void QAndroidCameraSession::stopPreview()
408{
409 if (!m_camera || !m_previewStarted)
410 return;
411
412 AndroidMultimediaUtils::enableOrientationListener(false);
413
414 m_camera->stopPreview();
415 m_camera->setPreviewSize(QSize());
416 m_camera->setPreviewTexture(0);
417 m_camera->setPreviewDisplay(0);
418
419 if (m_videoOutput) {
420 m_videoOutput->stop();
421 }
422 m_previewStarted = false;
423}
424
425void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
426{
427 if (m_requestedImageSettings == settings)
428 return;
429
430 m_requestedImageSettings = m_actualImageSettings = settings;
431
432 applyImageSettings();
433
434 if (m_readyForCapture)
435 applyResolution(m_actualImageSettings.resolution());
436}
437
438void QAndroidCameraSession::enableRotation()
439{
440 m_rotationEnabled = true;
441}
442
443void QAndroidCameraSession::disableRotation()
444{
445 m_rotationEnabled = false;
446}
447
448void QAndroidCameraSession::updateOrientation()
449{
450 if (!m_camera || !m_rotationEnabled)
451 return;
452
453 m_camera->setDisplayOrientation(currentCameraRotation());
454 applyResolution(m_actualImageSettings.resolution());
455}
456
457
458int QAndroidCameraSession::currentCameraRotation() const
459{
460 if (!m_camera)
461 return 0;
462
463 auto screen = QGuiApplication::primaryScreen();
464 auto screenOrientation = screen->orientation();
465 if (screenOrientation == Qt::PrimaryOrientation)
466 screenOrientation = screen->primaryOrientation();
467
468 int deviceOrientation = 0;
469 switch (screenOrientation) {
470 case Qt::PrimaryOrientation:
471 case Qt::PortraitOrientation:
472 break;
473 case Qt::LandscapeOrientation:
474 deviceOrientation = 90;
475 break;
476 case Qt::InvertedPortraitOrientation:
477 deviceOrientation = 180;
478 break;
479 case Qt::InvertedLandscapeOrientation:
480 deviceOrientation = 270;
481 break;
482 }
483
484 int nativeCameraOrientation = m_camera->getNativeOrientation();
485
486 int rotation;
487 // subtract natural camera orientation and physical device orientation
488 if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) {
489 rotation = (nativeCameraOrientation + deviceOrientation) % 360;
490 rotation = (360 - rotation) % 360; // compensate the mirror
491 } else { // back-facing camera
492 rotation = (nativeCameraOrientation - deviceOrientation + 360) % 360;
493 }
494 return rotation;
495}
496
497void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format)
498{
499 if (format == AndroidCamera::UnknownImageFormat)
500 return;
501
502 m_camera->setPreviewFormat(format);
503}
504
505void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
506{
507 m_videoFrameCallbackMutex.lock();
508 m_previewCallback = callback;
509 if (m_camera)
510 m_camera->notifyNewFrames(m_previewCallback);
511 m_videoFrameCallbackMutex.unlock();
512}
513
514void QAndroidCameraSession::applyImageSettings()
515{
516 if (!m_camera)
517 return;
518
519 // only supported format right now.
520 m_actualImageSettings.setFormat(QImageCapture::JPEG);
521
522 const QSize requestedResolution = m_requestedImageSettings.resolution();
523 const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
524 if (!requestedResolution.isValid()) {
525 m_actualImageSettings.setResolution(getDefaultResolution());
526 } else if (!supportedResolutions.contains(requestedResolution)) {
527 // if the requested resolution is not supported, find the closest one
528 int reqPixelCount = requestedResolution.width() * requestedResolution.height();
529 QList<int> supportedPixelCounts;
530 for (int i = 0; i < supportedResolutions.size(); ++i) {
531 const QSize &s = supportedResolutions.at(i);
532 supportedPixelCounts.append(s.width() * s.height());
533 }
534 int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
535 m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
536 }
537 m_camera->setPictureSize(m_actualImageSettings.resolution());
538
539 int jpegQuality = 100;
540 switch (m_requestedImageSettings.quality()) {
541 case QImageCapture::VeryLowQuality:
542 jpegQuality = 20;
543 break;
544 case QImageCapture::LowQuality:
545 jpegQuality = 40;
546 break;
547 case QImageCapture::NormalQuality:
548 jpegQuality = 60;
549 break;
550 case QImageCapture::HighQuality:
551 jpegQuality = 80;
552 break;
553 case QImageCapture::VeryHighQuality:
554 jpegQuality = 100;
555 break;
556 }
557 m_camera->setJpegQuality(jpegQuality);
558}
559
560bool QAndroidCameraSession::isReadyForCapture() const
561{
562 return isActive() && m_readyForCapture;
563}
564
565void QAndroidCameraSession::setReadyForCapture(bool ready)
566{
567 if (m_readyForCapture == ready)
568 return;
569
570 m_readyForCapture = ready;
571 emit readyForCaptureChanged(ready);
572}
573
574int QAndroidCameraSession::captureImage()
575{
576 const int newImageCaptureId = m_currentImageCaptureId + 1;
577
578 if (!isReadyForCapture()) {
579 emit imageCaptureError(newImageCaptureId, QImageCapture::NotReadyError,
580 QPlatformImageCapture::msgCameraNotReady());
581 return newImageCaptureId;
582 }
583
584 setReadyForCapture(false);
585
586 m_currentImageCaptureId = newImageCaptureId;
587
588 applyResolution(m_actualImageSettings.resolution());
589 m_camera->takePicture();
590
591 return m_currentImageCaptureId;
592}
593
594int QAndroidCameraSession::capture(const QString &fileName)
595{
596 m_currentImageCaptureFileName = fileName;
597 m_imageCaptureToBuffer = false;
598 return captureImage();
599}
600
601int QAndroidCameraSession::captureToBuffer()
602{
603 m_currentImageCaptureFileName.clear();
604 m_imageCaptureToBuffer = true;
605 return captureImage();
606}
607
608void QAndroidCameraSession::onCameraTakePictureFailed()
609{
610 emit imageCaptureError(m_currentImageCaptureId, QImageCapture::ResourceError,
611 tr("Failed to capture image"));
612
613 // Preview needs to be restarted and the preview call back must be setup again
614 m_camera->startPreview();
615}
616
617void QAndroidCameraSession::onCameraPictureExposed()
618{
619 if (!m_camera)
620 return;
621
622 emit imageExposed(m_currentImageCaptureId);
623 m_camera->fetchLastPreviewFrame();
624}
625
626void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
627{
628 if (!m_camera)
629 return;
630
631 updateOrientation();
632
633 (void)QtConcurrent::run(&QAndroidCameraSession::processPreviewImage, this,
634 m_currentImageCaptureId, frame, currentCameraRotation());
635}
636
637void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
638{
639 // Preview display of front-facing cameras is flipped horizontally, but the frame data
640 // we get here is not. Flip it ourselves if the camera is front-facing to match what the user
641 // sees on the viewfinder.
642 QTransform transform;
643 transform.rotate(rotation);
644
645 if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
646 transform.scale(-1, 1);
647
648 emit imageCaptured(id, frame.toImage().transformed(transform));
649}
650
651void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
652{
653 if (!m_camera)
654 return;
655
656 m_videoFrameCallbackMutex.lock();
657
658 if (m_previewCallback)
659 m_previewCallback->onFrameAvailable(frame);
660
661 m_videoFrameCallbackMutex.unlock();
662}
663
664void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &bytes,
665 QVideoFrameFormat::PixelFormat format, QSize size,int bytesPerLine)
666{
667 if (m_imageCaptureToBuffer) {
668 processCapturedImageToBuffer(m_currentImageCaptureId, bytes, format, size, bytesPerLine);
669 } else {
670 // Loading and saving the captured image can be slow, do it in a separate thread
671 (void)QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
672 m_currentImageCaptureId, bytes, m_currentImageCaptureFileName);
673 }
674
675 // Preview needs to be restarted after taking a picture
676 if (m_camera)
677 m_camera->startPreview();
678}
679
680void QAndroidCameraSession::onCameraPreviewStarted()
681{
682 setReadyForCapture(true);
683}
684
685void QAndroidCameraSession::onCameraPreviewFailedToStart()
686{
687 if (isActive()) {
688 Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start."));
689
690 AndroidMultimediaUtils::enableOrientationListener(false);
691 m_camera->setPreviewSize(QSize());
692 m_camera->setPreviewTexture(0);
693 if (m_videoOutput) {
694 m_videoOutput->stop();
695 m_videoOutput->reset();
696 }
697 m_previewStarted = false;
698
699 setActive(false);
700 setReadyForCapture(false);
701 }
702}
703
704void QAndroidCameraSession::onCameraPreviewStopped()
705{
706 if (!m_previewStarted)
707 setActive(false);
708 setReadyForCapture(false);
709}
710
711void QAndroidCameraSession::processCapturedImage(int id, const QByteArray &bytes, const QString &fileName)
712{
713 const QString actualFileName = QMediaStorageLocation::generateFileName(
714 fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
715 QFile writer(actualFileName);
716 if (!writer.open(QIODeviceBase::WriteOnly)) {
717 const QString errorMessage = tr("File is not available: %1").arg(writer.errorString());
718 emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
719 return;
720 }
721
722 if (writer.write(bytes) < 0) {
723 const QString errorMessage = tr("Could not save to file: %1").arg(writer.errorString());
724 emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
725 return;
726 }
727
728 writer.close();
729 if (fileName.isEmpty() || QFileInfo(fileName).isRelative())
730 AndroidMultimediaUtils::registerMediaFile(actualFileName);
731
732 emit imageSaved(id, actualFileName);
733}
734
735void QAndroidCameraSession::processCapturedImageToBuffer(int id, const QByteArray &bytes,
736 QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine)
737{
738 QVideoFrame frame = QVideoFramePrivate::createFrame(
739 std::make_unique<QMemoryVideoBuffer>(bytes, bytesPerLine),
740 QVideoFrameFormat(size, format));
741 emit imageAvailable(id, frame);
742}
743
744void QAndroidCameraSession::onVideoOutputReady(bool ready)
745{
746 if (ready && m_active)
747 startPreview();
748}
749
750void QAndroidCameraSession::onApplicationStateChanged()
751{
752
753 switch (QGuiApplication::applicationState()) {
754 case Qt::ApplicationInactive:
755 if (!m_keepActive && m_active) {
756 m_savedState = m_active;
757 setActive(false);
758 m_isStateSaved = true;
759 }
760 break;
761 case Qt::ApplicationActive:
762 if (m_isStateSaved) {
763 setActive(m_savedState);
764 m_isStateSaved = false;
765 }
766 break;
767 default:
768 break;
769 }
770}
771
772void QAndroidCameraSession::setKeepAlive(bool keepAlive)
773{
774 m_keepActive = keepAlive;
775}
776
777void QAndroidCameraSession::setVideoSink(QVideoSink *sink)
778{
779 if (m_sink == sink)
780 return;
781
782 if (m_sink)
783 disconnect(m_retryPreviewConnection);
784
785 m_sink = sink;
786
787 if (m_sink)
788 m_retryPreviewConnection =
789 connect(m_sink->platformVideoSink(), &QPlatformVideoSink::rhiChanged, this, [&]()
790 {
791 if (m_active) {
792 setActive(false);
793 setActive(true);
794 }
795 }, Qt::DirectConnection);
796 if (m_sink) {
797 delete m_textureOutput;
798 m_textureOutput = nullptr;
799
800 m_textureOutput = new QAndroidTextureVideoOutput(m_sink, this);
801 }
802
803 setVideoOutput(m_textureOutput);
804}
805
806QT_END_NAMESPACE
807
808#include "moc_qandroidcamerasession_p.cpp"
#define qApp