summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h4
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm4
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.h22
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.mm90
4 files changed, 82 insertions, 38 deletions
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 2b322cfaf..838234522 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ b/src/plugins/avfoundation/camera/avfcamerasession.h
@@ -70,6 +70,7 @@ public:
static int defaultCameraIndex();
static const QList<AVFCameraInfo> &availableCameraDevices();
static AVFCameraInfo cameraDeviceInfo(const QByteArray &device);
+ AVFCameraInfo activeCameraInfo() const { return m_activeCameraInfo; }
void setVideoOutput(AVFCameraRendererControl *output);
AVCaptureSession *captureSession() const { return m_captureSession; }
@@ -93,10 +94,12 @@ public Q_SLOTS:
void processSessionStopped();
void onCameraFrameFetched(const QVideoFrame &frame);
+
Q_SIGNALS:
void readyToConfigureConnections();
void stateChanged(QCamera::State newState);
void activeChanged(bool);
+ void newViewfinderFrame(const QVideoFrame &frame);
void error(int error, const QString &errorString);
private:
@@ -107,6 +110,7 @@ private:
static int m_defaultCameraIndex;
static QList<AVFCameraInfo> m_cameraDevices;
+ AVFCameraInfo m_activeCameraInfo;
AVFCameraService *m_service;
AVFCameraRendererControl *m_videoOutput;
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 83f8c0ed3..66dc43d81 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -333,6 +333,7 @@ void AVFCameraSession::attachVideoInputDevice()
[m_captureSession removeInput:m_videoInput];
[m_videoInput release];
m_videoInput = 0;
+ m_activeCameraInfo = AVFCameraInfo();
}
AVCaptureDevice *videoDevice = m_service->videoDeviceControl()->createCaptureDevice();
@@ -346,6 +347,7 @@ void AVFCameraSession::attachVideoInputDevice()
qWarning() << "Failed to create video device input";
} else {
if ([m_captureSession canAddInput:m_videoInput]) {
+ m_activeCameraInfo = m_cameraDevices.at(m_service->videoDeviceControl()->selectedDevice());
[m_videoInput retain];
[m_captureSession addInput:m_videoInput];
} else {
@@ -414,6 +416,8 @@ FourCharCode AVFCameraSession::defaultCodec()
void AVFCameraSession::onCameraFrameFetched(const QVideoFrame &frame)
{
+ Q_EMIT newViewfinderFrame(frame);
+
m_videoProbesMutex.lock();
QSet<AVFMediaVideoProbeControl *>::const_iterator i = m_videoProbes.constBegin();
while (i != m_videoProbes.constEnd()) {
diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
index 993725653..1fee8164c 100644
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
+++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
@@ -36,19 +36,24 @@
#import <AVFoundation/AVFoundation.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qsemaphore.h>
#include <QtMultimedia/qcameraimagecapturecontrol.h>
+#include <private/qvideooutputorientationhandler_p.h>
+#include "avfcamerasession.h"
#include "avfstoragelocation.h"
QT_BEGIN_NAMESPACE
-class AVFCameraSession;
-class AVFCameraService;
-class AVFCameraControl;
-
class AVFImageCaptureControl : public QCameraImageCaptureControl
{
Q_OBJECT
public:
+ struct CaptureRequest {
+ int captureId;
+ QSemaphore *previewReady;
+ };
+
AVFImageCaptureControl(AVFCameraService *service, QObject *parent = 0);
~AVFImageCaptureControl();
@@ -64,8 +69,11 @@ public:
private Q_SLOTS:
void updateCaptureConnection();
void updateReadyStatus();
+ void onNewViewfinderFrame(const QVideoFrame &frame);
private:
+ void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, AVFCameraInfo cameraInfo, int screenOrientation);
+
AVFCameraSession *m_session;
AVFCameraControl *m_cameraControl;
bool m_ready;
@@ -73,8 +81,14 @@ private:
AVCaptureStillImageOutput *m_stillImageOutput;
AVCaptureConnection *m_videoConnection;
AVFStorageLocation m_storageLocation;
+ QVideoOutputOrientationHandler m_orientationHandler;
+
+ QMutex m_requestsMutex;
+ QQueue<CaptureRequest> m_captureRequests;
};
+Q_DECLARE_TYPEINFO(AVFImageCaptureControl::CaptureRequest, Q_PRIMITIVE_TYPE);
+
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
index c28ccef8e..4703f8727 100644
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
+++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
@@ -33,14 +33,15 @@
#include "avfcameradebug.h"
#include "avfimagecapturecontrol.h"
-#include "avfcamerasession.h"
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
#include <QtCore/qurl.h>
#include <QtCore/qfile.h>
#include <QtCore/qbuffer.h>
+#include <QtConcurrent/qtconcurrentrun.h>
#include <QtGui/qimagereader.h>
+#include <private/qvideoframe_p.h>
QT_USE_NAMESPACE
@@ -65,6 +66,10 @@ AVFImageCaptureControl::AVFImageCaptureControl(AVFCameraService *service, QObjec
connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateReadyStatus()));
connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(updateCaptureConnection()));
+
+ connect(m_session, &AVFCameraSession::newViewfinderFrame,
+ this, &AVFImageCaptureControl::onNewViewfinderFrame,
+ Qt::DirectConnection);
}
AVFImageCaptureControl::~AVFImageCaptureControl()
@@ -106,9 +111,18 @@ int AVFImageCaptureControl::capture(const QString &fileName)
qDebugCamera() << "Capture image to" << actualFileName;
- int captureId = m_lastCaptureId;
+ CaptureRequest request = { m_lastCaptureId, new QSemaphore };
+ m_requestsMutex.lock();
+ m_captureRequests.enqueue(request);
+ m_requestsMutex.unlock();
+
[m_stillImageOutput captureStillImageAsynchronouslyFromConnection:m_videoConnection
completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
+
+ // Wait for the preview to be generated before saving the JPEG
+ request.previewReady->acquire();
+ delete request.previewReady;
+
if (error) {
QStringList messageParts;
messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
@@ -119,64 +133,72 @@ int AVFImageCaptureControl::capture(const QString &fileName)
qDebugCamera() << "Image capture failed:" << errorMessage;
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, captureId),
+ Q_ARG(int, request.captureId),
Q_ARG(int, QCameraImageCapture::ResourceError),
Q_ARG(QString, errorMessage));
} else {
- qDebugCamera() << "Image captured:" << actualFileName;
- //we can't find the exact time the image is exposed,
- //but image capture is very fast on desktop, so emit it here
- QMetaObject::invokeMethod(this, "imageExposed", Qt::QueuedConnection,
- Q_ARG(int, captureId));
+ qDebugCamera() << "Image capture completed:" << actualFileName;
NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]);
- //Generate snap preview as downscalled image
- {
- QBuffer buffer(&jpgData);
- QImageReader imageReader(&buffer);
- QSize imgSize = imageReader.size();
- int downScaleSteps = 0;
- while (imgSize.width() > 800 && downScaleSteps < 8) {
- imgSize.rwidth() /= 2;
- imgSize.rheight() /= 2;
- downScaleSteps++;
- }
-
- imageReader.setScaledSize(imgSize);
- QImage snapPreview = imageReader.read();
-
- QMetaObject::invokeMethod(this, "imageCaptured", Qt::QueuedConnection,
- Q_ARG(int, captureId),
- Q_ARG(QImage, snapPreview));
- }
-
- qDebugCamera() << "Image captured" << actualFileName;
-
QFile f(actualFileName);
if (f.open(QFile::WriteOnly)) {
if (f.write(jpgData) != -1) {
QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection,
- Q_ARG(int, captureId),
+ Q_ARG(int, request.captureId),
Q_ARG(QString, actualFileName));
} else {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, captureId),
+ Q_ARG(int, request.captureId),
Q_ARG(int, QCameraImageCapture::OutOfSpaceError),
Q_ARG(QString, f.errorString()));
}
} else {
QString errorMessage = tr("Could not open destination file:\n%1").arg(actualFileName);
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, captureId),
+ Q_ARG(int, request.captureId),
Q_ARG(int, QCameraImageCapture::ResourceError),
Q_ARG(QString, errorMessage));
}
}
}];
- return captureId;
+ return request.captureId;
+}
+
+void AVFImageCaptureControl::onNewViewfinderFrame(const QVideoFrame &frame)
+{
+ QMutexLocker locker(&m_requestsMutex);
+
+ if (m_captureRequests.isEmpty())
+ return;
+
+ CaptureRequest request = m_captureRequests.dequeue();
+ Q_EMIT imageExposed(request.captureId);
+
+ QtConcurrent::run(this, &AVFImageCaptureControl::makeCapturePreview,
+ request,
+ frame,
+ m_session->activeCameraInfo(),
+ m_orientationHandler.currentOrientation());
+}
+
+void AVFImageCaptureControl::makeCapturePreview(CaptureRequest request,
+ const QVideoFrame &frame,
+ AVFCameraInfo cameraInfo,
+ int screenOrientation)
+{
+ QTransform transform;
+ screenOrientation = 360 - screenOrientation;
+ if (cameraInfo.position == QCamera::FrontFace)
+ transform.rotate((screenOrientation + cameraInfo.orientation) % 360);
+ else
+ transform.rotate((screenOrientation + (360 - cameraInfo.orientation)) % 360);
+
+ Q_EMIT imageCaptured(request.captureId, qt_imageFromVideoFrame(frame).transformed(transform));
+
+ request.previewReady->release();
}
void AVFImageCaptureControl::cancelCapture()