AVFoundation: mirror viewfinder frames of front-facing cameras.
authorYoann Lopes <yoann.lopes@digia.com>
Fri, 7 Feb 2014 13:23:32 +0000 (14:23 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 14 Feb 2014 14:05:13 +0000 (15:05 +0100)
Change-Id: I95920aa459ff0931819cb6f8278ab296db542601
Reviewed-by: Andy Nichols <andy.nichols@digia.com>
src/plugins/avfoundation/camera/avfcamerasession.h
src/plugins/avfoundation/camera/avfcamerasession.mm
src/plugins/avfoundation/camera/avfvideorenderercontrol.h
src/plugins/avfoundation/camera/avfvideorenderercontrol.mm

index 48681aa..2630a35 100644 (file)
@@ -64,6 +64,7 @@ public:
 
     void setVideoOutput(AVFVideoRendererControl *output);
     AVCaptureSession *captureSession() const { return m_captureSession; }
+    AVCaptureDevice *videoCaptureDevice() const;
 
     QCamera::State state() const;
     QCamera::State requestedState() const { return m_state; }
index 5f1385a..93c2bac 100644 (file)
@@ -155,7 +155,15 @@ void AVFCameraSession::setVideoOutput(AVFVideoRendererControl *output)
 {
     m_videoOutput = output;
     if (output)
-        output->configureAVCaptureSession(m_captureSession);
+        output->configureAVCaptureSession(this);
+}
+
+AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const
+{
+    if (m_videoInput)
+        return m_videoInput.device;
+
+    return 0;
 }
 
 QCamera::State AVFCameraSession::state() const
index 57978c0..c080451 100644 (file)
@@ -66,7 +66,7 @@ public:
     QAbstractVideoSurface *surface() const;
     void setSurface(QAbstractVideoSurface *surface);
 
-    void configureAVCaptureSession(AVCaptureSession *captureSession);
+    void configureAVCaptureSession(AVFCameraSession *cameraSession);
     void syncHandleViewfinderFrame(const QVideoFrame &frame);
 
 Q_SIGNALS:
@@ -74,13 +74,16 @@ Q_SIGNALS:
 
 private Q_SLOTS:
     void handleViewfinderFrame();
+    void updateCaptureConnection();
 
 private:
     QAbstractVideoSurface *m_surface;
     AVFCaptureFramesDelegate *m_viewfinderFramesDelegate;
-    AVCaptureSession *m_captureSession;
+    AVFCameraSession *m_cameraSession;
     AVCaptureVideoDataOutput *m_videoDataOutput;
 
+    bool m_needsHorizontalMirroring;
+
     QVideoFrame m_lastViewfinderFrame;
     QMutex m_vfMutex;
 };
index 6efa3cb..1a20544 100644 (file)
@@ -146,13 +146,14 @@ private:
 AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
    : QVideoRendererControl(parent)
    , m_surface(0)
+   , m_needsHorizontalMirroring(false)
 {
     m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
 }
 
 AVFVideoRendererControl::~AVFVideoRendererControl()
 {
-    [m_captureSession removeOutput:m_videoDataOutput];
+    [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
     [m_viewfinderFramesDelegate release];
 }
 
@@ -169,9 +170,13 @@ void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
     }
 }
 
-void AVFVideoRendererControl::configureAVCaptureSession(AVCaptureSession *captureSession)
+void AVFVideoRendererControl::configureAVCaptureSession(AVFCameraSession *cameraSession)
 {
-    m_captureSession = captureSession;
+    m_cameraSession = cameraSession;
+    connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
+            this, SLOT(updateCaptureConnection()));
+
+    m_needsHorizontalMirroring = false;
 
     m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
 
@@ -188,7 +193,23 @@ void AVFVideoRendererControl::configureAVCaptureSession(AVCaptureSession *captur
             [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
             forKey:(id)kCVPixelBufferPixelFormatTypeKey];
 
-    [m_captureSession addOutput:m_videoDataOutput];
+    [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
+}
+
+void AVFVideoRendererControl::updateCaptureConnection()
+{
+    AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+    if (connection == nil || !m_cameraSession->videoCaptureDevice())
+        return;
+
+    // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
+    // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
+    if (connection.isVideoMirroringSupported)
+        connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+
+    // If the connection does't support mirroring, we'll have to do it ourselves
+    m_needsHorizontalMirroring = !connection.isVideoMirrored
+            && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
 }
 
 //can be called from non main thread
@@ -203,6 +224,22 @@ void AVFVideoRendererControl::syncHandleViewfinderFrame(const QVideoFrame &frame
     }
 
     m_lastViewfinderFrame = frame;
+
+    if (m_needsHorizontalMirroring) {
+        m_lastViewfinderFrame.map(QAbstractVideoBuffer::ReadOnly);
+
+        // no deep copy
+        QImage image(m_lastViewfinderFrame.bits(),
+                     m_lastViewfinderFrame.size().width(),
+                     m_lastViewfinderFrame.size().height(),
+                     m_lastViewfinderFrame.bytesPerLine(),
+                     QImage::Format_RGB32);
+
+        QImage mirrored = image.mirrored(true, false);
+
+        m_lastViewfinderFrame.unmap();
+        m_lastViewfinderFrame = QVideoFrame(mirrored);
+    }
 }
 
 void AVFVideoRendererControl::handleViewfinderFrame()