summaryrefslogtreecommitdiffstats
path: root/src/plugins/avfoundation/camera
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/avfoundation/camera')
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.mm11
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h4
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm6
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.h20
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.mm84
5 files changed, 79 insertions, 46 deletions
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm
index fd473b37b..20156f06f 100644
--- a/src/plugins/avfoundation/camera/avfcameraservice.mm
+++ b/src/plugins/avfoundation/camera/avfcameraservice.mm
@@ -203,18 +203,15 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
void AVFCameraService::releaseControl(QMediaControl *control)
{
- if (m_videoOutput == control) {
- m_session->setVideoOutput(0);
- delete m_videoOutput;
- m_videoOutput = 0;
- }
AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control);
if (videoProbe) {
m_session->removeProbe(videoProbe);
delete videoProbe;
- return;
+ } else if (m_videoOutput == control) {
+ m_session->setVideoOutput(0);
+ delete m_videoOutput;
+ m_videoOutput = 0;
}
-
}
AVFMediaRecorderControl *AVFCameraService::recorderControl() const
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 1be847b7c..13a8a35c5 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 4ede4a514..993e28319 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -220,7 +220,7 @@ void AVFCameraSession::updateCameraDevices()
// the screen when held in portrait ==> 270 degrees clockwise angle
// - Front-facing cameras have the top side of the sensor aligned with the left side of
// the screen when held in portrait ==> 270 degrees clockwise angle
- // On Mac OS, the position will always be unspecified and the sensor orientation unknown.
+ // On OS X, the position will always be unspecified and the sensor orientation unknown.
switch (device.position) {
case AVCaptureDevicePositionBack:
info.position = QCamera::BackFace;
@@ -346,6 +346,7 @@ void AVFCameraSession::attachVideoInputDevice()
[m_captureSession removeInput:m_videoInput];
[m_videoInput release];
m_videoInput = 0;
+ m_activeCameraInfo = AVFCameraInfo();
}
AVCaptureDevice *videoDevice = m_service->videoDeviceControl()->createCaptureDevice();
@@ -359,6 +360,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 {
@@ -432,6 +434,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..dd5e8e8bb 100644
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
+++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
@@ -36,19 +36,23 @@
#import <AVFoundation/AVFoundation.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qsemaphore.h>
#include <QtMultimedia/qcameraimagecapturecontrol.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 +68,11 @@ public:
private Q_SLOTS:
void updateCaptureConnection();
void updateReadyStatus();
+ void onNewViewfinderFrame(const QVideoFrame &frame);
private:
+ void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, int rotation);
+
AVFCameraSession *m_session;
AVFCameraControl *m_cameraControl;
bool m_ready;
@@ -73,8 +80,13 @@ private:
AVCaptureStillImageOutput *m_stillImageOutput;
AVCaptureConnection *m_videoConnection;
AVFStorageLocation m_storageLocation;
+
+ 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..edaaf8ce3 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,66 @@ 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,
+ 0 /* rotation */);
+}
+
+void AVFImageCaptureControl::makeCapturePreview(CaptureRequest request,
+ const QVideoFrame &frame,
+ int rotation)
+{
+ QTransform transform;
+ transform.rotate(rotation);
+
+ Q_EMIT imageCaptured(request.captureId, qt_imageFromVideoFrame(frame).transformed(transform));
+
+ request.previewReady->release();
}
void AVFImageCaptureControl::cancelCapture()