diff options
Diffstat (limited to 'src')
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() |