AVFCameraViewfinderSettings - add NV12 format

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>
This commit is contained in:
Timur Pocheptsov
2015-03-17 09:48:54 +01:00
parent 10354f4127
commit 09afe9377d
3 changed files with 94 additions and 29 deletions

View File

@@ -32,6 +32,7 @@
****************************************************************************/
#include "avfcameraviewfindersettingscontrol.h"
#include "private/qabstractvideobuffer_p.h"
#include "avfcamerarenderercontrol.h"
#include "avfcamerasession.h"
#include "avfcameraservice.h"
@@ -39,15 +40,17 @@
#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

View 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;

View 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()