AVFCameraViewfinderSettings - add NV12 format
authorTimur Pocheptsov <Timur.Pocheptsov@digia.com>
Tue, 17 Mar 2015 08:48:54 +0000 (09:48 +0100)
committerTimur Pocheptsov <Timur.Pocheptsov@digia.com>
Fri, 20 Mar 2015 13:23:00 +0000 (13:23 +0000)
Add QVideoFrame::Format_NV12 (AVFoundation has kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
and kCVPixelFormatType_420YpCbCr8BiPlanarFullRange). Report it (set it) only if
it's supported by renderer's surface. Add bi-planar format support into CVPixelBufferVideoBuffer.

Change-Id: Ibc1c2be056bddf5cf3b595570fc40c626ee3ccf5
Reviewed-by: Yoann Lopes <yoann.lopes@theqtcompany.com>
src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm

index 87bfeb8..cf13635 100644 (file)
@@ -32,6 +32,7 @@
 ****************************************************************************/
 
 #include "avfcameraviewfindersettingscontrol.h"
+#include "private/qabstractvideobuffer_p.h"
 #include "avfcamerarenderercontrol.h"
 #include "avfcamerasession.h"
 #include "avfcameraservice.h"
 
 #include <QtMultimedia/qabstractvideosurface.h>
 #include <QtMultimedia/qabstractvideobuffer.h>
+
 #include <QtMultimedia/qvideosurfaceformat.h>
 
 QT_USE_NAMESPACE
 
-class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer
+class CVPixelBufferVideoBuffer : public QAbstractPlanarVideoBuffer
 {
+    friend class CVPixelBufferVideoBufferPrivate;
 public:
     CVPixelBufferVideoBuffer(CVPixelBufferRef buffer)
-        : QAbstractVideoBuffer(NoHandle)
+        : QAbstractPlanarVideoBuffer(NoHandle)
         , m_buffer(buffer)
         , m_mode(NotMapped)
     {
@@ -61,6 +64,42 @@ public:
 
     MapMode mapMode() const { return m_mode; }
 
+    int map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4])
+    {
+        // We only support RGBA or NV12 (or Apple's version of NV12),
+        // they are either 0 planes or 2.
+        const size_t nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
+        Q_ASSERT(nPlanes <= 2);
+
+        if (!nPlanes) {
+            data[0] = map(mode, numBytes, bytesPerLine);
+            return data[0] ? 1 : 0;
+        }
+
+        // For a bi-planar format we have to set the parameters correctly:
+        if (mode != QAbstractVideoBuffer::NotMapped && m_mode == QAbstractVideoBuffer::NotMapped) {
+            CVPixelBufferLockBaseAddress(m_buffer, 0);
+
+            if (numBytes)
+                *numBytes = CVPixelBufferGetDataSize(m_buffer);
+
+            if (bytesPerLine) {
+                // At the moment we handle only bi-planar format.
+                bytesPerLine[0] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 0);
+                bytesPerLine[1] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 1);
+            }
+
+            if (data) {
+                data[0] = (uchar *)CVPixelBufferGetBaseAddressOfPlane(m_buffer, 0);
+                data[1] = (uchar *)CVPixelBufferGetBaseAddressOfPlane(m_buffer, 1);
+            }
+
+            m_mode = mode;
+        }
+
+        return nPlanes;
+    }
+
     uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
     {
         if (mode != NotMapped && m_mode == NotMapped) {
@@ -73,7 +112,6 @@ public:
                 *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer);
 
             m_mode = mode;
-
             return (uchar*)CVPixelBufferGetBaseAddress(m_buffer);
         } else {
             return 0;
@@ -93,6 +131,7 @@ private:
     MapMode m_mode;
 };
 
+
 @interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
 {
 @private
index fccc938..cf2f512 100644 (file)
@@ -74,6 +74,7 @@ private:
     void setFramerate(qreal minFPS, qreal maxFPS, bool useActive);
     void setPixelFormat(QVideoFrame::PixelFormat newFormat);
     AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const;
+    QVector<QVideoFrame::PixelFormat> viewfinderPixelFormats() const;
     bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const;
     void applySettings();
     QCameraViewfinderSettings requestedSettings() const;
index 250aae9..60df1e2 100644 (file)
@@ -38,6 +38,8 @@
 #include "avfcameraservice.h"
 #include "avfcameradebug.h"
 
+#include <QtMultimedia/qabstractvideosurface.h>
+
 #include <QtCore/qvariant.h>
 #include <QtCore/qsysinfo.h>
 #include <QtCore/qvector.h>
@@ -52,28 +54,6 @@ QT_BEGIN_NAMESPACE
 
 namespace {
 
-QVector<QVideoFrame::PixelFormat> qt_viewfinder_pixel_formats(AVCaptureVideoDataOutput *videoOutput)
-{
-    Q_ASSERT(videoOutput);
-
-    QVector<QVideoFrame::PixelFormat> qtFormats;
-
-    NSArray *pixelFormats = [videoOutput availableVideoCVPixelFormatTypes];
-    for (NSObject *obj in pixelFormats) {
-        if (![obj isKindOfClass:[NSNumber class]])
-            continue;
-
-        NSNumber *formatAsNSNumber = static_cast<NSNumber *>(obj);
-        // It's actually FourCharCode (== UInt32):
-        const QVideoFrame::PixelFormat qtFormat(AVFCameraViewfinderSettingsControl2::
-                                                QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue]));
-        if (qtFormat != QVideoFrame::Format_Invalid)
-            qtFormats << qtFormat;
-    }
-
-    return qtFormats;
-}
-
 bool qt_framerates_sane(const QCameraViewfinderSettings &settings)
 {
     const qreal minFPS = settings.minimumFrameRate();
@@ -269,7 +249,8 @@ QList<QCameraViewfinderSettings> AVFCameraViewfinderSettingsControl2::supportedV
 
     QVector<AVFPSRange> framerates;
 
-    QVector<QVideoFrame::PixelFormat> pixelFormats(qt_viewfinder_pixel_formats(m_videoOutput));
+    QVector<QVideoFrame::PixelFormat> pixelFormats(viewfinderPixelFormats());
+
     if (!pixelFormats.size())
         pixelFormats << QVideoFrame::Format_Invalid; // The default value.
 #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
@@ -397,6 +378,9 @@ QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromC
         return QVideoFrame::Format_RGB24;
     case kCVPixelFormatType_24BGR:
         return QVideoFrame::Format_BGR24;
+    case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+    case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
+        return QVideoFrame::Format_NV12;
     default:
         return QVideoFrame::Format_Invalid;
     }
@@ -414,6 +398,9 @@ bool AVFCameraViewfinderSettingsControl2::CVPixelFormatFromQtFormat(QVideoFrame:
     case QVideoFrame::Format_BGRA32:
         conv = kCVPixelFormatType_32ARGB;
         break;
+    case QVideoFrame::Format_NV12:
+        conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
+        break;
     // These two formats below are not supported
     // by QSGVideoNodeFactory_RGB, so for now I have to
     // disable them.
@@ -467,7 +454,37 @@ AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(
     return nil;
 }
 
-bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat, unsigned &avfFormat)const
+QVector<QVideoFrame::PixelFormat> AVFCameraViewfinderSettingsControl2::viewfinderPixelFormats() const
+{
+    Q_ASSERT(m_videoOutput);
+
+    QVector<QVideoFrame::PixelFormat> qtFormats;
+    QList<QVideoFrame::PixelFormat> filter;
+
+    NSArray *pixelFormats = [m_videoOutput availableVideoCVPixelFormatTypes];
+    const QAbstractVideoSurface *surface = m_service->videoOutput() ? m_service->videoOutput()->surface() : 0;
+
+    if (surface)
+        filter = surface->supportedPixelFormats();
+
+    for (NSObject *obj in pixelFormats) {
+        if (![obj isKindOfClass:[NSNumber class]])
+            continue;
+
+        NSNumber *formatAsNSNumber = static_cast<NSNumber *>(obj);
+        // It's actually FourCharCode (== UInt32):
+        const QVideoFrame::PixelFormat qtFormat(QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue]));
+        if (qtFormat != QVideoFrame::Format_Invalid && (!surface || filter.contains(qtFormat))
+            && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range.
+            qtFormats << qtFormat;
+        }
+    }
+
+    return qtFormats;
+}
+
+bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat,
+                                                                        unsigned &avfFormat)const
 {
     Q_ASSERT(m_videoOutput);
 
@@ -479,17 +496,25 @@ bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFr
     if (!formats || !formats.count)
         return false;
 
+    if (m_service->videoOutput() && m_service->videoOutput()->surface()) {
+        const QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
+        if (!surface->supportedPixelFormats().contains(qtFormat))
+            return false;
+    }
+
+    bool found = false;
     for (NSObject *obj in formats) {
         if (![obj isKindOfClass:[NSNumber class]])
             continue;
+
         NSNumber *nsNum = static_cast<NSNumber *>(obj);
         if ([nsNum unsignedIntValue] == conv) {
             avfFormat = conv;
-            return true;
+            found = true;
         }
     }
 
-    return false;
+    return found;
 }
 
 void AVFCameraViewfinderSettingsControl2::applySettings()