Support per-plane strides and data offsets in QVideoFrame.
Since just adding a new virtual isn't binary compatible add a new derivative type with a virtual member and connect it up through a virtual in the private class. [ChangeLog] Support for per-plane strides and data offsets in QVideoFrame. Task-number: QTBUG-38345 Change-Id: I1974c2b0b454d130e17971ce549031259d61f9cd Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
This commit is contained in:
committed by
Yoann Lopes
parent
ab379c3da2
commit
1a3ae99441
@@ -56,6 +56,15 @@ static void qRegisterAbstractVideoBufferMetaTypes()
|
|||||||
|
|
||||||
Q_CONSTRUCTOR_FUNCTION(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
|
\class QAbstractVideoBuffer
|
||||||
@@ -130,6 +139,7 @@ QAbstractVideoBuffer::QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, Hand
|
|||||||
: d_ptr(&dd)
|
: d_ptr(&dd)
|
||||||
, m_type(type)
|
, m_type(type)
|
||||||
{
|
{
|
||||||
|
d_ptr->q_ptr = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -199,6 +209,44 @@ QAbstractVideoBuffer::HandleType QAbstractVideoBuffer::handleType() const
|
|||||||
\sa unmap(), mapMode()
|
\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()
|
\fn QAbstractVideoBuffer::unmap()
|
||||||
|
|
||||||
@@ -222,6 +270,90 @@ QVariant QAbstractVideoBuffer::handle() const
|
|||||||
return QVariant();
|
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
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
QDebug operator<<(QDebug dbg, QAbstractVideoBuffer::HandleType type)
|
QDebug operator<<(QDebug dbg, QAbstractVideoBuffer::HandleType type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ public:
|
|||||||
virtual MapMode mapMode() const = 0;
|
virtual MapMode mapMode() const = 0;
|
||||||
|
|
||||||
virtual uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) = 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 void unmap() = 0;
|
||||||
|
|
||||||
virtual QVariant handle() const;
|
virtual QVariant handle() const;
|
||||||
@@ -100,6 +101,23 @@ private:
|
|||||||
Q_DISABLE_COPY(QAbstractVideoBuffer)
|
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
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::HandleType);
|
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::HandleType);
|
||||||
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::MapMode);
|
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QAbstractVideoBuffer::MapMode);
|
||||||
|
|||||||
@@ -66,10 +66,31 @@ class QAbstractVideoBufferPrivate
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QAbstractVideoBufferPrivate()
|
QAbstractVideoBufferPrivate()
|
||||||
|
: q_ptr(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~QAbstractVideoBufferPrivate()
|
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
|
QT_END_NAMESPACE
|
||||||
|
|||||||
@@ -71,28 +71,30 @@ public:
|
|||||||
QVideoFramePrivate()
|
QVideoFramePrivate()
|
||||||
: startTime(-1)
|
: startTime(-1)
|
||||||
, endTime(-1)
|
, endTime(-1)
|
||||||
, data(0)
|
|
||||||
, mappedBytes(0)
|
, mappedBytes(0)
|
||||||
, bytesPerLine(0)
|
, planeCount(0)
|
||||||
, pixelFormat(QVideoFrame::Format_Invalid)
|
, pixelFormat(QVideoFrame::Format_Invalid)
|
||||||
, fieldType(QVideoFrame::ProgressiveFrame)
|
, fieldType(QVideoFrame::ProgressiveFrame)
|
||||||
, buffer(0)
|
, buffer(0)
|
||||||
, mappedCount(0)
|
, mappedCount(0)
|
||||||
{
|
{
|
||||||
|
memset(data, 0, sizeof(data));
|
||||||
|
memset(bytesPerLine, 0, sizeof(bytesPerLine));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVideoFramePrivate(const QSize &size, QVideoFrame::PixelFormat format)
|
QVideoFramePrivate(const QSize &size, QVideoFrame::PixelFormat format)
|
||||||
: size(size)
|
: size(size)
|
||||||
, startTime(-1)
|
, startTime(-1)
|
||||||
, endTime(-1)
|
, endTime(-1)
|
||||||
, data(0)
|
|
||||||
, mappedBytes(0)
|
, mappedBytes(0)
|
||||||
, bytesPerLine(0)
|
, planeCount(0)
|
||||||
, pixelFormat(format)
|
, pixelFormat(format)
|
||||||
, fieldType(QVideoFrame::ProgressiveFrame)
|
, fieldType(QVideoFrame::ProgressiveFrame)
|
||||||
, buffer(0)
|
, buffer(0)
|
||||||
, mappedCount(0)
|
, mappedCount(0)
|
||||||
{
|
{
|
||||||
|
memset(data, 0, sizeof(data));
|
||||||
|
memset(bytesPerLine, 0, sizeof(bytesPerLine));
|
||||||
}
|
}
|
||||||
|
|
||||||
~QVideoFramePrivate()
|
~QVideoFramePrivate()
|
||||||
@@ -104,9 +106,10 @@ public:
|
|||||||
QSize size;
|
QSize size;
|
||||||
qint64 startTime;
|
qint64 startTime;
|
||||||
qint64 endTime;
|
qint64 endTime;
|
||||||
uchar *data;
|
uchar *data[4];
|
||||||
|
int bytesPerLine[4];
|
||||||
int mappedBytes;
|
int mappedBytes;
|
||||||
int bytesPerLine;
|
int planeCount;
|
||||||
QVideoFrame::PixelFormat pixelFormat;
|
QVideoFrame::PixelFormat pixelFormat;
|
||||||
QVideoFrame::FieldType fieldType;
|
QVideoFrame::FieldType fieldType;
|
||||||
QAbstractVideoBuffer *buffer;
|
QAbstractVideoBuffer *buffer;
|
||||||
@@ -564,18 +567,88 @@ bool QVideoFrame::map(QAbstractVideoBuffer::MapMode mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(d->data == 0);
|
Q_ASSERT(d->data[0] == 0);
|
||||||
Q_ASSERT(d->bytesPerLine == 0);
|
Q_ASSERT(d->bytesPerLine[0] == 0);
|
||||||
|
Q_ASSERT(d->planeCount == 0);
|
||||||
Q_ASSERT(d->mappedBytes == 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) {
|
if (d->planeCount > 1) {
|
||||||
d->mappedCount++;
|
// If the plane count is derive the additional planes for planar formats.
|
||||||
return true;
|
} 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) {
|
if (d->mappedCount == 0) {
|
||||||
d->mappedBytes = 0;
|
d->mappedBytes = 0;
|
||||||
d->bytesPerLine = 0;
|
d->planeCount = 0;
|
||||||
d->data = 0;
|
memset(d->bytesPerLine, 0, sizeof(d->bytesPerLine));
|
||||||
|
memset(d->data, 0, sizeof(d->data));
|
||||||
|
|
||||||
d->buffer->unmap();
|
d->buffer->unmap();
|
||||||
}
|
}
|
||||||
@@ -623,7 +697,21 @@ void QVideoFrame::unmap()
|
|||||||
*/
|
*/
|
||||||
int QVideoFrame::bytesPerLine() const
|
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()
|
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
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -669,6 +790,20 @@ int QVideoFrame::mappedBytes() const
|
|||||||
return d->mappedBytes;
|
return d->mappedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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.
|
Returns a type specific handle to a video frame's buffer.
|
||||||
|
|
||||||
@@ -852,9 +987,6 @@ QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format)
|
|||||||
return QImage::Format_Invalid;
|
return QImage::Format_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
QDebug operator<<(QDebug dbg, QVideoFrame::PixelFormat pf)
|
QDebug operator<<(QDebug dbg, QVideoFrame::PixelFormat pf)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,10 +139,14 @@ public:
|
|||||||
void unmap();
|
void unmap();
|
||||||
|
|
||||||
int bytesPerLine() const;
|
int bytesPerLine() const;
|
||||||
|
int bytesPerLine(int plane) const;
|
||||||
|
|
||||||
uchar *bits();
|
uchar *bits();
|
||||||
|
uchar *bits(int plane);
|
||||||
const uchar *bits() const;
|
const uchar *bits() const;
|
||||||
|
const uchar *bits(int plane) const;
|
||||||
int mappedBytes() const;
|
int mappedBytes() const;
|
||||||
|
int planeCount() const;
|
||||||
|
|
||||||
QVariant handle() const;
|
QVariant handle() const;
|
||||||
|
|
||||||
|
|||||||
@@ -257,33 +257,23 @@ void QSGVideoMaterial_YUV420::bind()
|
|||||||
m_textureSize = m_frame.size();
|
m_textureSize = m_frame.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const uchar *bits = m_frame.bits();
|
const int y = 0;
|
||||||
int yStride = m_frame.bytesPerLine();
|
const int u = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 1 : 2;
|
||||||
// The UV stride is usually half the Y stride and is 32-bit aligned.
|
const int v = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 2 : 1;
|
||||||
// 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_yWidth = qreal(fw) / m_frame.bytesPerLine(y);
|
||||||
m_uvWidth = qreal(fw) / (2 * uvStride);
|
m_uvWidth = qreal(fw) / (2 * m_frame.bytesPerLine(u));
|
||||||
|
|
||||||
if (m_frame.pixelFormat() == QVideoFrame::Format_YV12)
|
|
||||||
qSwap(offsetU, offsetV);
|
|
||||||
|
|
||||||
GLint previousAlignment;
|
GLint previousAlignment;
|
||||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousAlignment);
|
glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousAlignment);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
functions->glActiveTexture(GL_TEXTURE1);
|
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);
|
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
|
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);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, previousAlignment);
|
||||||
|
|
||||||
@@ -350,7 +340,6 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
|
|||||||
mat->m_opacity = state.opacity();
|
mat->m_opacity = state.opacity();
|
||||||
program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
|
program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.isMatrixDirty())
|
if (state.isMatrixDirty())
|
||||||
program()->setUniformValue(m_id_matrix, state.combinedMatrix());
|
program()->setUniformValue(m_id_matrix, state.combinedMatrix());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ private slots:
|
|||||||
void map();
|
void map();
|
||||||
void mapImage_data();
|
void mapImage_data();
|
||||||
void mapImage();
|
void mapImage();
|
||||||
|
void mapPlanes_data();
|
||||||
|
void mapPlanes();
|
||||||
void imageDetach();
|
void imageDetach();
|
||||||
void formatConversion_data();
|
void formatConversion_data();
|
||||||
void formatConversion();
|
void formatConversion();
|
||||||
@@ -115,6 +117,35 @@ public:
|
|||||||
void unmap() {}
|
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()
|
tst_QVideoFrame::tst_QVideoFrame()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -727,6 +758,97 @@ void tst_QVideoFrame::mapImage()
|
|||||||
QCOMPARE(frame.mapMode(), QAbstractVideoBuffer::NotMapped);
|
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()
|
void tst_QVideoFrame::imageDetach()
|
||||||
{
|
{
|
||||||
const uint red = qRgb(255, 0, 0);
|
const uint red = qRgb(255, 0, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user