diff options
-rw-r--r-- | src/multimedia/video/qabstractvideobuffer.cpp | 132 | ||||
-rw-r--r-- | src/multimedia/video/qabstractvideobuffer.h | 18 | ||||
-rw-r--r-- | src/multimedia/video/qabstractvideobuffer_p.h | 21 | ||||
-rw-r--r-- | src/multimedia/video/qvideoframe.cpp | 174 | ||||
-rw-r--r-- | src/multimedia/video/qvideoframe.h | 4 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_i420.cpp | 29 | ||||
-rw-r--r-- | tests/auto/unit/qvideoframe/tst_qvideoframe.cpp | 122 |
7 files changed, 459 insertions, 41 deletions
diff --git a/src/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/video/qabstractvideobuffer.cpp index 79da6f9b7..5f29a01a1 100644 --- a/src/multimedia/video/qabstractvideobuffer.cpp +++ b/src/multimedia/video/qabstractvideobuffer.cpp @@ -56,6 +56,15 @@ static void qRegisterAbstractVideoBufferMetaTypes() Q_CONSTRUCTOR_FUNCTION(qRegisterAbstractVideoBufferMetaTypes) +int QAbstractVideoBufferPrivate::map( + QAbstractVideoBuffer::MapMode mode, + int *numBytes, + int bytesPerLine[4], + uchar *data[4]) +{ + data[0] = q_ptr->map(mode, numBytes, bytesPerLine); + return data[0] ? 1 : 0; +} /*! \class QAbstractVideoBuffer @@ -130,6 +139,7 @@ QAbstractVideoBuffer::QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, Hand : d_ptr(&dd) , m_type(type) { + d_ptr->q_ptr = this; } /*! @@ -199,6 +209,44 @@ QAbstractVideoBuffer::HandleType QAbstractVideoBuffer::handleType() const \sa unmap(), mapMode() */ + +/*! + Independently maps the planes of a video buffer to memory. + + The map \a mode indicates whether the contents of the mapped memory should be read from and/or + written to the buffer. If the map mode includes the \c QAbstractVideoBuffer::ReadOnly flag the + mapped memory will be populated with the content of the buffer when initially mapped. If the map + mode includes the \c QAbstractVideoBuffer::WriteOnly flag the content of the possibly modified + mapped memory will be written back to the buffer when unmapped. + + When access to the data is no longer needed be sure to call the unmap() function to release the + mapped memory and possibly update the buffer contents. + + Returns the number of planes in the mapped video data. For each plane the line stride of that + plane will be returned in \a bytesPerLine, and a pointer to the plane data will be returned in + \a data. The accumulative size of the mapped data is returned in \a numBytes. + + Not all buffer implementations will map more than the first plane, if this returns a single + plane for a planar format the additional planes will have to be calculated from the line stride + of the first plane and the frame height. Mapping a buffer with QVideoFrame will do this for + you. + + To implement this function create a derivative of QAbstractPlanarVideoBuffer and implement + its map function instance instead. + + \since 5.4 +*/ +int QAbstractVideoBuffer::mapPlanes(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) +{ + if (d_ptr) { + return d_ptr->map(mode, numBytes, bytesPerLine, data); + } else { + data[0] = map(mode, numBytes, bytesPerLine); + + return data[0] ? 1 : 0; + } +} + /*! \fn QAbstractVideoBuffer::unmap() @@ -222,6 +270,90 @@ QVariant QAbstractVideoBuffer::handle() const return QVariant(); } + +int QAbstractPlanarVideoBufferPrivate::map( + QAbstractVideoBuffer::MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) +{ + return q_func()->map(mode, numBytes, bytesPerLine, data); +} + +/*! + \class QAbstractPlanarVideoBuffer + \brief The QAbstractPlanarVideoBuffer class is an abstraction for planar video data. + \inmodule QtMultimedia + \ingroup QtMultimedia + \ingroup multimedia + \ingroup multimedia_video + + QAbstractPlanarVideoBuffer extends QAbstractVideoBuffer to support mapping + non-continuous planar video data. Implement this instead of QAbstractVideoBuffer when the + abstracted video data stores planes in separate buffers or includes padding between planes + which would interfere with calculating offsets from the bytes per line and frame height. + + \sa QAbstractVideoBuffer::mapPlanes() + \since 5.4 +*/ + +/*! + Constructs an abstract planar video buffer of the given \a type. +*/ +QAbstractPlanarVideoBuffer::QAbstractPlanarVideoBuffer(HandleType type) + : QAbstractVideoBuffer(*new QAbstractPlanarVideoBufferPrivate, type) +{ +} + +/*! + \internal +*/ +QAbstractPlanarVideoBuffer::QAbstractPlanarVideoBuffer( + QAbstractPlanarVideoBufferPrivate &dd, HandleType type) + : QAbstractVideoBuffer(dd, type) +{ +} +/*! + Destroys an abstract planar video buffer. +*/ +QAbstractPlanarVideoBuffer::~QAbstractPlanarVideoBuffer() +{ +} + +/*! + \internal +*/ +uchar *QAbstractPlanarVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + uchar *data[4]; + int strides[4]; + if (map(mode, numBytes, strides, data) > 0) { + if (bytesPerLine) + *bytesPerLine = strides[0]; + return data[0]; + } else { + return 0; + } +} + +/*! + \fn int QAbstractPlanarVideoBuffer::map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) + + Maps the contents of a video buffer to memory. + + The map \a mode indicates whether the contents of the mapped memory should be read from and/or + written to the buffer. If the map mode includes the \c QAbstractVideoBuffer::ReadOnly flag the + mapped memory will be populated with the content of the buffer when initially mapped. If the map + mode includes the \c QAbstractVideoBuffer::WriteOnly flag the content of the possibly modified + mapped memory will be written back to the buffer when unmapped. + + When access to the data is no longer needed be sure to call the unmap() function to release the + mapped memory and possibly update the buffer contents. + + Returns the number of planes in the mapped video data. For each plane the line stride of that + plane will be returned in \a bytesPerLine, and a pointer to the plane data will be returned in + \a data. The accumulative size of the mapped data is returned in \a numBytes. + + \sa QAbstractVideoBuffer::map(), QAbstractVideoBuffer::unmap(), QAbstractVideoBuffer::mapMode() +*/ + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QAbstractVideoBuffer::HandleType type) { diff --git a/src/multimedia/video/qabstractvideobuffer.h b/src/multimedia/video/qabstractvideobuffer.h index 1be90ff90..7f3edf569 100644 --- a/src/multimedia/video/qabstractvideobuffer.h +++ b/src/multimedia/video/qabstractvideobuffer.h @@ -85,6 +85,7 @@ public: virtual MapMode mapMode() const = 0; virtual uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) = 0; + int mapPlanes(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]); virtual void unmap() = 0; virtual QVariant handle() const; @@ -100,6 +101,23 @@ private: Q_DISABLE_COPY(QAbstractVideoBuffer) }; +class QAbstractPlanarVideoBufferPrivate; +class Q_MULTIMEDIA_EXPORT QAbstractPlanarVideoBuffer : public QAbstractVideoBuffer +{ +public: + QAbstractPlanarVideoBuffer(HandleType type); + virtual ~QAbstractPlanarVideoBuffer(); + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + virtual int map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) = 0; + +protected: + QAbstractPlanarVideoBuffer(QAbstractPlanarVideoBufferPrivate &dd, HandleType type); + +private: + Q_DISABLE_COPY(QAbstractPlanarVideoBuffer) +}; + #ifndef QT_NO_DEBUG_STREAM Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::HandleType); Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::MapMode); diff --git a/src/multimedia/video/qabstractvideobuffer_p.h b/src/multimedia/video/qabstractvideobuffer_p.h index 8841f75d1..dfb1df653 100644 --- a/src/multimedia/video/qabstractvideobuffer_p.h +++ b/src/multimedia/video/qabstractvideobuffer_p.h @@ -66,10 +66,31 @@ class QAbstractVideoBufferPrivate { public: QAbstractVideoBufferPrivate() + : q_ptr(0) {} virtual ~QAbstractVideoBufferPrivate() {} + + virtual int map( + QAbstractVideoBuffer::MapMode mode, + int *numBytes, + int bytesPerLine[4], + uchar *data[4]); + + QAbstractVideoBuffer *q_ptr; +}; + +class QAbstractPlanarVideoBufferPrivate : QAbstractVideoBufferPrivate +{ +public: + QAbstractPlanarVideoBufferPrivate() + {} + + int map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]); + +private: + Q_DECLARE_PUBLIC(QAbstractPlanarVideoBuffer) }; QT_END_NAMESPACE diff --git a/src/multimedia/video/qvideoframe.cpp b/src/multimedia/video/qvideoframe.cpp index 4a32bc557..7a9e3e1ba 100644 --- a/src/multimedia/video/qvideoframe.cpp +++ b/src/multimedia/video/qvideoframe.cpp @@ -71,28 +71,30 @@ public: QVideoFramePrivate() : startTime(-1) , endTime(-1) - , data(0) , mappedBytes(0) - , bytesPerLine(0) + , planeCount(0) , pixelFormat(QVideoFrame::Format_Invalid) , fieldType(QVideoFrame::ProgressiveFrame) , buffer(0) , mappedCount(0) { + memset(data, 0, sizeof(data)); + memset(bytesPerLine, 0, sizeof(bytesPerLine)); } QVideoFramePrivate(const QSize &size, QVideoFrame::PixelFormat format) : size(size) , startTime(-1) , endTime(-1) - , data(0) , mappedBytes(0) - , bytesPerLine(0) + , planeCount(0) , pixelFormat(format) , fieldType(QVideoFrame::ProgressiveFrame) , buffer(0) , mappedCount(0) { + memset(data, 0, sizeof(data)); + memset(bytesPerLine, 0, sizeof(bytesPerLine)); } ~QVideoFramePrivate() @@ -104,9 +106,10 @@ public: QSize size; qint64 startTime; qint64 endTime; - uchar *data; + uchar *data[4]; + int bytesPerLine[4]; int mappedBytes; - int bytesPerLine; + int planeCount; QVideoFrame::PixelFormat pixelFormat; QVideoFrame::FieldType fieldType; QAbstractVideoBuffer *buffer; @@ -564,18 +567,88 @@ bool QVideoFrame::map(QAbstractVideoBuffer::MapMode mode) } } - Q_ASSERT(d->data == 0); - Q_ASSERT(d->bytesPerLine == 0); + Q_ASSERT(d->data[0] == 0); + Q_ASSERT(d->bytesPerLine[0] == 0); + Q_ASSERT(d->planeCount == 0); Q_ASSERT(d->mappedBytes == 0); - d->data = d->buffer->map(mode, &d->mappedBytes, &d->bytesPerLine); + d->planeCount = d->buffer->mapPlanes(mode, &d->mappedBytes, d->bytesPerLine, d->data); + if (d->planeCount == 0) + return false; - if (d->data) { - d->mappedCount++; - return true; + if (d->planeCount > 1) { + // If the plane count is derive the additional planes for planar formats. + } else switch (d->pixelFormat) { + case Format_Invalid: + case Format_ARGB32: + case Format_ARGB32_Premultiplied: + case Format_RGB32: + case Format_RGB24: + case Format_RGB565: + case Format_RGB555: + case Format_ARGB8565_Premultiplied: + case Format_BGRA32: + case Format_BGRA32_Premultiplied: + case Format_BGR32: + case Format_BGR24: + case Format_BGR565: + case Format_BGR555: + case Format_BGRA5658_Premultiplied: + case Format_AYUV444: + case Format_AYUV444_Premultiplied: + case Format_YUV444: + case Format_UYVY: + case Format_YUYV: + case Format_Y8: + case Format_Y16: + case Format_Jpeg: + case Format_CameraRaw: + case Format_AdobeDng: + case Format_User: + // Single plane or opaque format. + break; + case Format_YUV420P: + case Format_YV12: { + // The UV stride is usually half the Y stride and is 32-bit aligned. + // However it's not always the case, at least on Windows where the + // UV planes are sometimes not aligned. + // We calculate the stride using the UV byte count to always + // have a correct stride. + const int height = d->size.height(); + const int yStride = d->bytesPerLine[0]; + const int uvStride = (d->mappedBytes - (yStride * height)) / height; + + // Three planes, the second and third vertically and horizontally subsampled. + d->planeCount = 3; + d->bytesPerLine[2] = d->bytesPerLine[1] = uvStride; + d->data[1] = d->data[0] + (yStride * height); + d->data[2] = d->data[1] + (uvStride * height / 2); + break; + } + case Format_NV12: + case Format_NV21: + case Format_IMC2: + case Format_IMC4: { + // Semi planar, Full resolution Y plane with interleaved subsampled U and V planes. + d->planeCount = 2; + d->bytesPerLine[1] = d->bytesPerLine[0]; + d->data[1] = d->data[0] + (d->bytesPerLine[0] * d->size.height()); + break; + } + case Format_IMC1: + case Format_IMC3: { + // Three planes, the second and third vertically and horizontally subsumpled, + // but with lines padded to the width of the first plane. + d->planeCount = 3; + d->bytesPerLine[2] = d->bytesPerLine[1] = d->bytesPerLine[0]; + d->data[1] = d->data[0] + (d->bytesPerLine[0] * d->size.height()); + d->data[2] = d->data[1] + (d->bytesPerLine[1] * d->size.height() / 2); + break; + } } - return false; + d->mappedCount++; + return true; } /*! @@ -604,8 +677,9 @@ void QVideoFrame::unmap() if (d->mappedCount == 0) { d->mappedBytes = 0; - d->bytesPerLine = 0; - d->data = 0; + d->planeCount = 0; + memset(d->bytesPerLine, 0, sizeof(d->bytesPerLine)); + memset(d->data, 0, sizeof(d->data)); d->buffer->unmap(); } @@ -623,7 +697,21 @@ void QVideoFrame::unmap() */ int QVideoFrame::bytesPerLine() const { - return d->bytesPerLine; + return d->bytesPerLine[0]; +} + +/*! + Returns the number of bytes in a scan line of a \a plane. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa bits(), map(), mappedBytes(), planeCount() + \since 5.4 +*/ + +int QVideoFrame::bytesPerLine(int plane) const +{ + return plane >= 0 && plane < d->planeCount ? d->bytesPerLine[plane] : 0; } /*! @@ -639,7 +727,24 @@ int QVideoFrame::bytesPerLine() const */ uchar *QVideoFrame::bits() { - return d->data; + return d->data[0]; +} + +/*! + Returns a pointer to the start of the frame data buffer for a \a plane. + + This value is only valid while the frame data is \l {map()}{mapped}. + + Changes made to data accessed via this pointer (when mapped with write access) + are only guaranteed to have been persisted when unmap() is called and when the + buffer has been mapped for writing. + + \sa map(), mappedBytes(), bytesPerLine(), planeCount() + \since 5.4 +*/ +uchar *QVideoFrame::bits(int plane) +{ + return plane >= 0 && plane < d->planeCount ? d->data[plane] : 0; } /*! @@ -654,7 +759,23 @@ uchar *QVideoFrame::bits() */ const uchar *QVideoFrame::bits() const { - return d->data; + return d->data[0]; +} + +/*! + Returns a pointer to the start of the frame data buffer for a \a plane. + + This value is only valid while the frame data is \l {map()}{mapped}. + + If the buffer was not mapped with read access, the contents of this + buffer will initially be uninitialized. + + \sa map(), mappedBytes(), bytesPerLine(), planeCount() + \since 5.4 +*/ +const uchar *QVideoFrame::bits(int plane) const +{ + return plane >= 0 && plane < d->planeCount ? d->data[plane] : 0; } /*! @@ -670,6 +791,20 @@ int QVideoFrame::mappedBytes() const } /*! + Returns the number of planes in the video frame. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa map() + \since 5.4 +*/ + +int QVideoFrame::planeCount() const +{ + return d->planeCount; +} + +/*! Returns a type specific handle to a video frame's buffer. For an OpenGL texture this would be the texture ID. @@ -852,9 +987,6 @@ QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format) return QImage::Format_Invalid; } - - - #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QVideoFrame::PixelFormat pf) { diff --git a/src/multimedia/video/qvideoframe.h b/src/multimedia/video/qvideoframe.h index 504a0d548..0ca13d886 100644 --- a/src/multimedia/video/qvideoframe.h +++ b/src/multimedia/video/qvideoframe.h @@ -139,10 +139,14 @@ public: void unmap(); int bytesPerLine() const; + int bytesPerLine(int plane) const; uchar *bits(); + uchar *bits(int plane); const uchar *bits() const; + const uchar *bits(int plane) const; int mappedBytes() const; + int planeCount() const; QVariant handle() const; diff --git a/src/qtmultimediaquicktools/qsgvideonode_i420.cpp b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp index 2d84f6ed1..2d904f8eb 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_i420.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp @@ -257,33 +257,23 @@ void QSGVideoMaterial_YUV420::bind() m_textureSize = m_frame.size(); } - const uchar *bits = m_frame.bits(); - int yStride = m_frame.bytesPerLine(); - // The UV stride is usually half the Y stride and is 32-bit aligned. - // However it's not always the case, at least on Windows where the - // UV planes are sometimes not aligned. - // We calculate the stride using the UV byte count to always - // have a correct stride. - int uvStride = (m_frame.mappedBytes() - yStride * fh) / fh; - int offsetU = yStride * fh; - int offsetV = yStride * fh + uvStride * fh / 2; - - m_yWidth = qreal(fw) / yStride; - m_uvWidth = qreal(fw) / (2 * uvStride); - - if (m_frame.pixelFormat() == QVideoFrame::Format_YV12) - qSwap(offsetU, offsetV); + const int y = 0; + const int u = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 1 : 2; + const int v = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 2 : 1; + + m_yWidth = qreal(fw) / m_frame.bytesPerLine(y); + m_uvWidth = qreal(fw) / (2 * m_frame.bytesPerLine(u)); GLint previousAlignment; glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousAlignment); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); functions->glActiveTexture(GL_TEXTURE1); - bindTexture(m_textureIds[1], uvStride, fh / 2, bits + offsetU); + bindTexture(m_textureIds[1], m_frame.bytesPerLine(u), fh / 2, m_frame.bits(u)); functions->glActiveTexture(GL_TEXTURE2); - bindTexture(m_textureIds[2], uvStride, fh / 2, bits + offsetV); + bindTexture(m_textureIds[2], m_frame.bytesPerLine(v), fh / 2, m_frame.bits(v)); functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit - bindTexture(m_textureIds[0], yStride, fh, bits); + bindTexture(m_textureIds[0], m_frame.bytesPerLine(y), fh, m_frame.bits(y)); glPixelStorei(GL_UNPACK_ALIGNMENT, previousAlignment); @@ -350,7 +340,6 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, mat->m_opacity = state.opacity(); program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); } - if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } diff --git a/tests/auto/unit/qvideoframe/tst_qvideoframe.cpp b/tests/auto/unit/qvideoframe/tst_qvideoframe.cpp index 403320e59..c9e11fff2 100644 --- a/tests/auto/unit/qvideoframe/tst_qvideoframe.cpp +++ b/tests/auto/unit/qvideoframe/tst_qvideoframe.cpp @@ -87,6 +87,8 @@ private slots: void map(); void mapImage_data(); void mapImage(); + void mapPlanes_data(); + void mapPlanes(); void imageDetach(); void formatConversion_data(); void formatConversion(); @@ -115,6 +117,35 @@ public: void unmap() {} }; +class QtTestPlanarVideoBuffer : public QAbstractPlanarVideoBuffer +{ +public: + QtTestPlanarVideoBuffer() + : QAbstractPlanarVideoBuffer(NoHandle), m_planeCount(0), m_mapMode(NotMapped) {} + explicit QtTestPlanarVideoBuffer(QAbstractVideoBuffer::HandleType type) + : QAbstractPlanarVideoBuffer(type), m_planeCount(0), m_mapMode(NotMapped) {} + + MapMode mapMode() const { return m_mapMode; } + + int map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) { + m_mapMode = mode; + if (numBytes) + *numBytes = m_numBytes; + for (int i = 0; i < m_planeCount; ++i) { + data[i] = m_data[i]; + bytesPerLine[i] = m_bytesPerLine[i]; + } + return m_planeCount; + } + void unmap() { m_mapMode = NotMapped; } + + uchar *m_data[4]; + int m_bytesPerLine[4]; + int m_planeCount; + int m_numBytes; + MapMode m_mapMode; +}; + tst_QVideoFrame::tst_QVideoFrame() { } @@ -727,6 +758,97 @@ void tst_QVideoFrame::mapImage() QCOMPARE(frame.mapMode(), QAbstractVideoBuffer::NotMapped); } +void tst_QVideoFrame::mapPlanes_data() +{ + QTest::addColumn<QVideoFrame>("frame"); + QTest::addColumn<QList<int> >("strides"); + QTest::addColumn<QList<int> >("offsets"); + + static uchar bufferData[1024]; + + QtTestPlanarVideoBuffer *planarBuffer = new QtTestPlanarVideoBuffer; + planarBuffer->m_data[0] = bufferData; + planarBuffer->m_data[1] = bufferData + 512; + planarBuffer->m_data[2] = bufferData + 765; + planarBuffer->m_bytesPerLine[0] = 64; + planarBuffer->m_bytesPerLine[1] = 36; + planarBuffer->m_bytesPerLine[2] = 36; + planarBuffer->m_planeCount = 3; + planarBuffer->m_numBytes = sizeof(bufferData); + + QTest::newRow("Planar") + << QVideoFrame(planarBuffer, QSize(64, 64), QVideoFrame::Format_YUV420P) + << (QList<int>() << 64 << 36 << 36) + << (QList<int>() << 512 << 765); + QTest::newRow("Format_YUV420P") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_YUV420P) + << (QList<int>() << 64 << 62 << 62) + << (QList<int>() << 4096 << 6080); + QTest::newRow("Format_YV12") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_YV12) + << (QList<int>() << 64 << 62 << 62) + << (QList<int>() << 4096 << 6080); + QTest::newRow("Format_NV12") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_NV12) + << (QList<int>() << 64 << 64) + << (QList<int>() << 4096); + QTest::newRow("Format_NV21") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_NV21) + << (QList<int>() << 64 << 64) + << (QList<int>() << 4096); + QTest::newRow("Format_IMC2") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_IMC2) + << (QList<int>() << 64 << 64) + << (QList<int>() << 4096); + QTest::newRow("Format_IMC4") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_IMC4) + << (QList<int>() << 64 << 64) + << (QList<int>() << 4096); + QTest::newRow("Format_IMC1") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_IMC1) + << (QList<int>() << 64 << 64 << 64) + << (QList<int>() << 4096 << 6144); + QTest::newRow("Format_IMC3") + << QVideoFrame(8096, QSize(60, 64), 64, QVideoFrame::Format_IMC3) + << (QList<int>() << 64 << 64 << 64) + << (QList<int>() << 4096 << 6144); + QTest::newRow("Format_ARGB32") + << QVideoFrame(8096, QSize(60, 64), 256, QVideoFrame::Format_ARGB32) + << (QList<int>() << 256) + << (QList<int>()); +} + +void tst_QVideoFrame::mapPlanes() +{ + QFETCH(QVideoFrame, frame); + QFETCH(QList<int>, strides); + QFETCH(QList<int>, offsets); + + QCOMPARE(strides.count(), offsets.count() + 1); + + QCOMPARE(frame.map(QAbstractVideoBuffer::ReadOnly), true); + QCOMPARE(frame.planeCount(), strides.count()); + + QVERIFY(strides.count() > 0); + QCOMPARE(frame.bytesPerLine(0), strides.at(0)); + QVERIFY(frame.bits(0)); + + if (strides.count() > 1) { + QCOMPARE(frame.bytesPerLine(1), strides.at(1)); + QCOMPARE(int(frame.bits(1) - frame.bits(0)), offsets.at(0)); + } + if (strides.count() > 2) { + QCOMPARE(frame.bytesPerLine(2), strides.at(2)); + QCOMPARE(int(frame.bits(2) - frame.bits(0)), offsets.at(1)); + } + if (strides.count() > 3) { + QCOMPARE(frame.bytesPerLine(3), strides.at(3)); + QCOMPARE(int(frame.bits(3) - frame.bits(0)), offsets.at(0)); + } + + frame.unmap(); +} + void tst_QVideoFrame::imageDetach() { const uint red = qRgb(255, 0, 0); |