13#include <QtConcurrent/qtconcurrentrun.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 <QImageWriter>
36 , m_previewStarted(
false)
37 , m_readyForCapture(
false)
38 , m_currentImageCaptureId(-1)
39 , m_previewCallback(0)
44 this, &QAndroidCameraSession::onApplicationStateChanged);
49 &QAndroidCameraSession::updateOrientation);
76 if (m_active == active)
82 m_isStateSaved =
true;
83 m_savedState = active;
87 m_isStateSaved =
false;
89 setActiveHelper(m_active);
93void QAndroidCameraSession::setActiveHelper(
bool active)
99 if (!m_camera && !open()) {
107void QAndroidCameraSession::updateAvailableCameras()
109 g_availableCameras->clear();
112 for (
int i = 0;
i < numCameras; ++
i) {
116 if (!
info->id.isEmpty()) {
126 g_availableCameras->append(
info->create());
133 if (g_availableCameras->isEmpty())
134 updateAvailableCameras();
136 return *g_availableCameras;
139bool QAndroidCameraSession::open()
147 this, &QAndroidCameraSession::onCameraPictureExposed);
149 this, &QAndroidCameraSession::onLastPreviewFrameFetched,
152 this, &QAndroidCameraSession::onNewPreviewFrame,
155 this, &QAndroidCameraSession::onCameraPictureCaptured);
157 this, &QAndroidCameraSession::onCameraPreviewStarted);
159 this, &QAndroidCameraSession::onCameraPreviewStopped);
161 this, &QAndroidCameraSession::onCameraPreviewFailedToStart);
163 this, &QAndroidCameraSession::onCameraTakePictureFailed);
174 return m_camera != 0;
177void QAndroidCameraSession::close()
184 m_readyForCapture =
false;
185 m_currentImageCaptureId = -1;
186 m_currentImageCaptureFileName.
clear();
187 m_actualImageSettings = m_requestedImageSettings;
199 m_videoOutput->
stop();
200 m_videoOutput->
reset();
205 if (m_videoOutput->
isReady()) {
206 onVideoOutputReady(
true);
209 this, &QAndroidCameraSession::onVideoOutputReady);
218 m_requestedFpsRange.
min =
format.minFrameRate();
219 m_requestedFpsRange.
max =
format.maxFrameRate();
224 if (m_readyForCapture)
238 QSize adjustedViewfinderResolution;
241 const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
246 adjustedViewfinderResolution = captureSize;
248 qreal captureAspectRatio = 0;
249 if (validCaptureSize)
250 captureAspectRatio =
qreal(captureSize.width()) /
qreal(captureSize.height());
252 if (validCaptureSize) {
254 qreal minAspectDiff = 1;
255 QSize closestResolution;
256 for (
int i = previewSizes.count() - 1;
i >= 0; --
i) {
260 adjustedViewfinderResolution =
size;
262 }
else if (minAspectDiff >
qAbs(sizeAspect - captureAspectRatio)) {
263 closestResolution =
size;
264 minAspectDiff =
qAbs(sizeAspect - captureAspectRatio);
267 if (!adjustedViewfinderResolution.isValid()) {
268 qWarning(
"Cannot find a viewfinder resolution matching the capture aspect ratio.");
269 if (closestResolution.isValid()) {
270 adjustedViewfinderResolution = closestResolution;
271 qWarning(
"Using closest viewfinder resolution.");
277 adjustedViewfinderResolution = previewSizes.last();
290 if (adjustedFps.min == 0 || adjustedFps.max == 0)
291 adjustedFps = currentFpsRange;
296 QSize cameraOutputResolution = adjustedViewfinderResolution;
297 QSize videoOutputResolution = adjustedViewfinderResolution;
301 if (rotation == 90 || rotation == 270) {
302 videoOutputResolution.transpose();
303 if (previewSizes.contains(cameraOutputResolution.transposed()))
304 cameraOutputResolution.transpose();
307 if (currentViewfinderResolution != cameraOutputResolution
308 || (m_videoOutput && currentVideoOutputResolution != videoOutputResolution)
309 || currentPreviewFormat != adjustedPreviewFormat || currentFpsRange.min != adjustedFps.min
310 || currentFpsRange.max != adjustedFps.max) {
316 if (m_previewStarted && restartPreview)
324 if (m_previewStarted && restartPreview)
336 QList<QVideoFrameFormat::PixelFormat>
formats;
343 formats.reserve(nativeFormats.size());
360bool QAndroidCameraSession::startPreview()
362 if (!m_camera || !m_videoOutput)
365 if (m_previewStarted)
383 m_previewStarted =
true;
384 m_videoOutput->
start();
389QSize QAndroidCameraSession::getDefaultResolution()
const
395 if (hasHighQualityProfile) {
406void QAndroidCameraSession::stopPreview()
408 if (!m_camera || !m_previewStarted)
419 m_videoOutput->
stop();
421 m_previewStarted =
false;
426 if (m_requestedImageSettings ==
settings)
429 m_requestedImageSettings = m_actualImageSettings =
settings;
431 applyImageSettings();
433 if (m_readyForCapture)
439 m_rotationEnabled =
true;
444 m_rotationEnabled =
false;
447void QAndroidCameraSession::updateOrientation()
449 if (!m_camera || !m_rotationEnabled)
467 int deviceOrientation = 0;
468 switch (screenOrientation) {
473 deviceOrientation = 90;
476 deviceOrientation = 180;
479 deviceOrientation = 270;
488 rotation = (nativeCameraOrientation + deviceOrientation) % 360;
489 rotation = (360 - rotation) % 360;
491 rotation = (nativeCameraOrientation - deviceOrientation + 360) % 360;
506 m_videoFrameCallbackMutex.
lock();
507 m_previewCallback = callback;
510 m_videoFrameCallbackMutex.
unlock();
513void QAndroidCameraSession::applyImageSettings()
521 const QSize requestedResolution = m_requestedImageSettings.
resolution();
523 if (!requestedResolution.isValid()) {
525 }
else if (!supportedResolutions.contains(requestedResolution)) {
527 int reqPixelCount = requestedResolution.width() * requestedResolution.height();
528 QList<int> supportedPixelCounts;
529 for (
int i = 0;
i < supportedResolutions.size(); ++
i) {
530 const QSize &
s = supportedResolutions.at(
i);
531 supportedPixelCounts.append(
s.width() *
s.height());
534 m_actualImageSettings.
setResolution(supportedResolutions.at(closestIndex));
538 int jpegQuality = 100;
539 switch (m_requestedImageSettings.
quality()) {
561 return isActive() && m_readyForCapture;
566 if (m_readyForCapture == ready)
569 m_readyForCapture = ready;
573int QAndroidCameraSession::captureImage()
575 const int newImageCaptureId = m_currentImageCaptureId + 1;
580 return newImageCaptureId;
585 m_currentImageCaptureId = newImageCaptureId;
590 return m_currentImageCaptureId;
595 m_currentImageCaptureFileName =
fileName;
596 m_imageCaptureToBuffer =
false;
597 return captureImage();
602 m_currentImageCaptureFileName.
clear();
603 m_imageCaptureToBuffer =
true;
604 return captureImage();
607void QAndroidCameraSession::onCameraTakePictureFailed()
610 tr(
"Failed to capture image"));
616void QAndroidCameraSession::onCameraPictureExposed()
625void QAndroidCameraSession::onLastPreviewFrameFetched(
const QVideoFrame &
frame)
636void QAndroidCameraSession::processPreviewImage(
int id,
const QVideoFrame &
frame,
int rotation)
655 m_videoFrameCallbackMutex.
lock();
657 if (m_previewCallback)
660 m_videoFrameCallbackMutex.
unlock();
663void QAndroidCameraSession::onCameraPictureCaptured(
const QByteArray &bytes,
666 if (m_imageCaptureToBuffer) {
667 processCapturedImageToBuffer(m_currentImageCaptureId, bytes,
format,
size, bytesPerLine);
671 m_currentImageCaptureId, bytes, m_currentImageCaptureFileName);
679void QAndroidCameraSession::onCameraPreviewStarted()
684void QAndroidCameraSession::onCameraPreviewFailedToStart()
693 m_videoOutput->
stop();
694 m_videoOutput->
reset();
696 m_previewStarted =
false;
703void QAndroidCameraSession::onCameraPreviewStopped()
705 if (!m_previewStarted)
714 QFile writer(actualFileName);
721 if (writer.write(bytes) < 0) {
734void QAndroidCameraSession::processCapturedImageToBuffer(
int id,
const QByteArray &bytes,
741void QAndroidCameraSession::onVideoOutputReady(
bool ready)
743 if (ready && m_active)
747void QAndroidCameraSession::onApplicationStateChanged()
752 if (!m_keepActive && m_active) {
753 m_savedState = m_active;
755 m_isStateSaved =
true;
759 if (m_isStateSaved) {
761 m_isStateSaved =
false;
771 m_keepActive = keepAlive;
785 m_retryPreviewConnection =
794 delete m_textureOutput;
795 m_textureOutput =
nullptr;
800 setVideoOutput(m_textureOutput);
805#include "moc_qandroidcamerasession_p.cpp"
static bool hasProfile(jint cameraId, Quality quality)
static AndroidCamcorderProfile get(jint cameraId, Quality quality)
ImageFormat getPreviewFormat()
QSize getPreferredPreviewSizeForVideo()
void setPictureSize(const QSize &size)
static AndroidCamera::ImageFormat AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat)
QList< QCameraFormat > getSupportedFormats()
bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder)
QList< FpsRange > getSupportedPreviewFpsRange()
void newPreviewFrame(const QVideoFrame &frame)
static int getNumberOfCameras()
void previewFailedToStart()
static QVideoFrameFormat::PixelFormat QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat)
void lastPreviewFrameFetched(const QVideoFrame &frame)
bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
FpsRange getPreviewFpsRange()
void fetchLastPreviewFrame()
void setPreviewFpsRange(FpsRange)
void pictureCaptured(const QByteArray &frame, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine)
void setJpegQuality(int quality)
QList< QSize > getSupportedPictureSizes()
static void getCameraInfo(int id, QCameraDevicePrivate *info)
QList< QSize > getSupportedPreviewSizes()
static AndroidCamera * open(int cameraId)
QList< ImageFormat > getSupportedPreviewFormats()
void setDisplayOrientation(int degrees)
QSize previewSize() const
int getNativeOrientation()
void notifyNewFrames(bool notify)
void setPreviewSize(const QSize &size)
void setPreviewFormat(ImageFormat fmt)
void setVideoSink(QVideoSink *surface)
int currentCameraRotation() const
void setImageSettings(const QImageEncoderSettings &settings)
void setActive(bool active)
void imageCaptured(int id, const QImage &preview)
QList< AndroidCamera::FpsRange > getSupportedPreviewFpsRange() const
void setCameraFormat(const QCameraFormat &format)
int capture(const QString &fileName)
void readyForCaptureChanged(bool)
void imageCaptureError(int id, int error, const QString &errorString)
void setVideoOutput(QAndroidVideoOutput *output)
bool isReadyForCapture() const
void imageAvailable(int id, const QVideoFrame &buffer)
void applyResolution(const QSize &captureSize=QSize(), bool restartPreview=true)
AndroidCamera * camera() const
QList< QVideoFrameFormat::PixelFormat > getSupportedPixelFormats() const
QList< QSize > getSupportedPreviewSizes() const
void setKeepAlive(bool keepAlive)
void imageSaved(int id, const QString &fileName)
static const QList< QCameraDevice > & availableCameras()
void setPreviewFormat(AndroidCamera::ImageFormat format)
void imageExposed(int id)
void setReadyForCapture(bool ready)
void setPreviewCallback(PreviewCallback *callback)
virtual AndroidSurfaceHolder * surfaceHolder()
virtual void setVideoSize(const QSize &)
virtual QSize getVideoSize() const
virtual AndroidSurfaceTexture * surfaceTexture()
static Qt::ApplicationState applicationState()
QScreen * primaryScreen
the primary (or default) screen of the application.
void applicationStateChanged(Qt::ApplicationState state)
QImageCapture::Quality quality() const
void setFormat(QImageCapture::FileFormat f)
void setResolution(const QSize &s)
The QMemoryVideoBuffer class provides a system memory allocated video data buffer.
void unlock() noexcept
Unlocks the mutex.
void lock() noexcept
Locks the mutex.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Qt::ScreenOrientation primaryOrientation
the primary screen orientation
Qt::ScreenOrientation orientation
the screen orientation
void orientationChanged(Qt::ScreenOrientation orientation)
This signal is emitted when the orientation of the screen changes with orientation as an argument.
constexpr int height() const noexcept
Returns the height.
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
\macro QT_RESTRICTED_CAST_FROM_ASCII
void clear()
Clears the contents of the string and makes it null.
The QVideoFrame class represents a frame of video data.
The QVideoSink class represents a generic sink for video data.
QPlatformVideoSink * platformVideoSink() const
Combined button and popup list for selecting options.
QTCONCURRENT_RUN_NODISCARD auto run(QThreadPool *pool, Function &&f, Args &&...args)
@ InvertedLandscapeOrientation
@ InvertedPortraitOrientation
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
constexpr T qAbs(const T &t)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum format
GLuint GLenum GLenum transform
GLsizei GLenum GLboolean sink
static QT_BEGIN_NAMESPACE bool isRelative(const QString &path)
QLatin1StringView QLatin1String
#define QStringLiteral(str)
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
QT_BEGIN_NAMESPACE typedef uchar * output
QSettings settings("MySoft", "Star Runner")
[0]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
myObject disconnect()
[26]
virtual void onFrameAvailable(const QVideoFrame &frame)=0