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