| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qvideoframe.h" |
| |
| #include "qimagevideobuffer_p.h" |
| #include "qmemoryvideobuffer_p.h" |
| #include "qvideoframeconversionhelper_p.h" |
| |
| #include <qimage.h> |
| #include <qpair.h> |
| #include <qsize.h> |
| #include <qvariant.h> |
| #include <qvector.h> |
| #include <qmutex.h> |
| |
| #include <QDebug> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static void qRegisterVideoFrameMetaTypes() |
| { |
| qRegisterMetaType<QVideoFrame>(); |
| qRegisterMetaType<QVideoFrame::FieldType>(); |
| qRegisterMetaType<QVideoFrame::PixelFormat>(); |
| } |
| |
| Q_CONSTRUCTOR_FUNCTION(qRegisterVideoFrameMetaTypes) |
| |
| |
| class QVideoFramePrivate : public QSharedData |
| { |
| public: |
| QVideoFramePrivate() |
| : startTime(-1) |
| , endTime(-1) |
| , mappedBytes(0) |
| , planeCount(0) |
| , pixelFormat(QVideoFrame::Format_Invalid) |
| , fieldType(QVideoFrame::ProgressiveFrame) |
| , buffer(nullptr) |
| , 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) |
| , mappedBytes(0) |
| , planeCount(0) |
| , pixelFormat(format) |
| , fieldType(QVideoFrame::ProgressiveFrame) |
| , buffer(nullptr) |
| , mappedCount(0) |
| { |
| memset(data, 0, sizeof(data)); |
| memset(bytesPerLine, 0, sizeof(bytesPerLine)); |
| } |
| |
| ~QVideoFramePrivate() |
| { |
| if (buffer) |
| buffer->release(); |
| } |
| |
| QSize size; |
| qint64 startTime; |
| qint64 endTime; |
| uchar *data[4]; |
| int bytesPerLine[4]; |
| int mappedBytes; |
| int planeCount; |
| QVideoFrame::PixelFormat pixelFormat; |
| QVideoFrame::FieldType fieldType; |
| QAbstractVideoBuffer *buffer; |
| int mappedCount; |
| QMutex mapMutex; |
| QVariantMap metadata; |
| |
| private: |
| Q_DISABLE_COPY(QVideoFramePrivate) |
| }; |
| |
| /*! |
| \class QVideoFrame |
| \brief The QVideoFrame class represents a frame of video data. |
| \inmodule QtMultimedia |
| |
| \ingroup multimedia |
| \ingroup multimedia_video |
| |
| A QVideoFrame encapsulates the pixel data of a video frame, and information about the frame. |
| |
| Video frames can come from several places - decoded \l {QMediaPlayer}{media}, a |
| \l {QCamera}{camera}, or generated programmatically. The way pixels are described in these |
| frames can vary greatly, and some pixel formats offer greater compression opportunities at |
| the expense of ease of use. |
| |
| The pixel contents of a video frame can be mapped to memory using the map() function. While |
| mapped, the video data can accessed using the bits() function, which returns a pointer to a |
| buffer. The total size of this buffer is given by the mappedBytes() function, and the size of |
| each line is given by bytesPerLine(). The return value of the handle() function may also be |
| used to access frame data using the internal buffer's native APIs (for example - an OpenGL |
| texture handle). |
| |
| A video frame can also have timestamp information associated with it. These timestamps can be |
| used by an implementation of \l QAbstractVideoSurface to determine when to start and stop |
| displaying the frame, but not all surfaces might respect this setting. |
| |
| The video pixel data in a QVideoFrame is encapsulated in a QAbstractVideoBuffer. A QVideoFrame |
| may be constructed from any buffer type by subclassing the QAbstractVideoBuffer class. |
| |
| \note Since video frames can be expensive to copy, QVideoFrame is explicitly shared, so any |
| change made to a video frame will also apply to any copies. |
| */ |
| |
| /*! |
| \enum QVideoFrame::PixelFormat |
| |
| Enumerates video data types. |
| |
| \value Format_Invalid |
| The frame is invalid. |
| |
| \value Format_ARGB32 |
| The frame is stored using a 32-bit ARGB format (0xAARRGGBB). This is equivalent to |
| QImage::Format_ARGB32. |
| |
| \value Format_ARGB32_Premultiplied |
| The frame stored using a premultiplied 32-bit ARGB format (0xAARRGGBB). This is equivalent |
| to QImage::Format_ARGB32_Premultiplied. |
| |
| \value Format_RGB32 |
| The frame stored using a 32-bit RGB format (0xffRRGGBB). This is equivalent to |
| QImage::Format_RGB32 |
| |
| \value Format_RGB24 |
| The frame is stored using a 24-bit RGB format (8-8-8). This is equivalent to |
| QImage::Format_RGB888 |
| |
| \value Format_RGB565 |
| The frame is stored using a 16-bit RGB format (5-6-5). This is equivalent to |
| QImage::Format_RGB16. |
| |
| \value Format_RGB555 |
| The frame is stored using a 16-bit RGB format (5-5-5). This is equivalent to |
| QImage::Format_RGB555. |
| |
| \value Format_ARGB8565_Premultiplied |
| The frame is stored using a 24-bit premultiplied ARGB format (8-5-6-5). |
| |
| \value Format_BGRA32 |
| The frame is stored using a 32-bit BGRA format (0xBBGGRRAA). |
| |
| \value Format_BGRA32_Premultiplied |
| The frame is stored using a premultiplied 32bit BGRA format. |
| |
| \value Format_ABGR32 |
| The frame is stored using a 32-bit ABGR format (0xAABBGGRR). |
| |
| \value Format_BGR32 |
| The frame is stored using a 32-bit BGR format (0xBBGGRRff). |
| |
| \value Format_BGR24 |
| The frame is stored using a 24-bit BGR format (0xBBGGRR). |
| |
| \value Format_BGR565 |
| The frame is stored using a 16-bit BGR format (5-6-5). |
| |
| \value Format_BGR555 |
| The frame is stored using a 16-bit BGR format (5-5-5). |
| |
| \value Format_BGRA5658_Premultiplied |
| The frame is stored using a 24-bit premultiplied BGRA format (5-6-5-8). |
| |
| \value Format_AYUV444 |
| The frame is stored using a packed 32-bit AYUV format (0xAAYYUUVV). |
| |
| \value Format_AYUV444_Premultiplied |
| The frame is stored using a packed premultiplied 32-bit AYUV format (0xAAYYUUVV). |
| |
| \value Format_YUV444 |
| The frame is stored using a 24-bit packed YUV format (8-8-8). |
| |
| \value Format_YUV420P |
| The frame is stored using an 8-bit per component planar YUV format with the U and V planes |
| horizontally and vertically sub-sampled, i.e. the height and width of the U and V planes are |
| half that of the Y plane. |
| |
| \value Format_YUV422P |
| The frame is stored using an 8-bit per component planar YUV format with the U and V planes |
| horizontally sub-sampled, i.e. the width of the U and V planes are |
| half that of the Y plane, and height of U and V planes is the same as Y. |
| |
| \value Format_YV12 |
| The frame is stored using an 8-bit per component planar YVU format with the V and U planes |
| horizontally and vertically sub-sampled, i.e. the height and width of the V and U planes are |
| half that of the Y plane. |
| |
| \value Format_UYVY |
| The frame is stored using an 8-bit per component packed YUV format with the U and V planes |
| horizontally sub-sampled (U-Y-V-Y), i.e. two horizontally adjacent pixels are stored as a 32-bit |
| macropixel which has a Y value for each pixel and common U and V values. |
| |
| \value Format_YUYV |
| The frame is stored using an 8-bit per component packed YUV format with the U and V planes |
| horizontally sub-sampled (Y-U-Y-V), i.e. two horizontally adjacent pixels are stored as a 32-bit |
| macropixel which has a Y value for each pixel and common U and V values. |
| |
| \value Format_NV12 |
| The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y) |
| followed by a horizontally and vertically sub-sampled, packed UV plane (U-V). |
| |
| \value Format_NV21 |
| The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y) |
| followed by a horizontally and vertically sub-sampled, packed VU plane (V-U). |
| |
| \value Format_IMC1 |
| The frame is stored using an 8-bit per component planar YUV format with the U and V planes |
| horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except |
| that the bytes per line of the U and V planes are padded out to the same stride as the Y plane. |
| |
| \value Format_IMC2 |
| The frame is stored using an 8-bit per component planar YUV format with the U and V planes |
| horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except |
| that the lines of the U and V planes are interleaved, i.e. each line of U data is followed by a |
| line of V data creating a single line of the same stride as the Y data. |
| |
| \value Format_IMC3 |
| The frame is stored using an 8-bit per component planar YVU format with the V and U planes |
| horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that |
| the bytes per line of the V and U planes are padded out to the same stride as the Y plane. |
| |
| \value Format_IMC4 |
| The frame is stored using an 8-bit per component planar YVU format with the V and U planes |
| horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that |
| the lines of the V and U planes are interleaved, i.e. each line of V data is followed by a line |
| of U data creating a single line of the same stride as the Y data. |
| |
| \value Format_Y8 |
| The frame is stored using an 8-bit greyscale format. |
| |
| \value Format_Y16 |
| The frame is stored using a 16-bit linear greyscale format. Little endian. |
| |
| \value Format_Jpeg |
| The frame is stored in compressed Jpeg format. |
| |
| \value Format_CameraRaw |
| The frame is stored using a device specific camera raw format. |
| |
| \value Format_AdobeDng |
| The frame is stored using raw Adobe Digital Negative (DNG) format. |
| |
| \value Format_User |
| Start value for user defined pixel formats. |
| */ |
| |
| /*! |
| \enum QVideoFrame::FieldType |
| |
| Specifies the field an interlaced video frame belongs to. |
| |
| \value ProgressiveFrame The frame is not interlaced. |
| \value TopField The frame contains a top field. |
| \value BottomField The frame contains a bottom field. |
| \value InterlacedFrame The frame contains a merged top and bottom field. |
| */ |
| |
| /*! |
| Constructs a null video frame. |
| */ |
| QVideoFrame::QVideoFrame() |
| : d(new QVideoFramePrivate) |
| { |
| } |
| |
| /*! |
| Constructs a video frame from a \a buffer with the given pixel \a format and \a size in pixels. |
| |
| \note This doesn't increment the reference count of the video buffer. |
| */ |
| QVideoFrame::QVideoFrame( |
| QAbstractVideoBuffer *buffer, const QSize &size, PixelFormat format) |
| : d(new QVideoFramePrivate(size, format)) |
| { |
| d->buffer = buffer; |
| } |
| |
| /*! |
| Constructs a video frame of the given pixel \a format and \a size in pixels. |
| |
| The \a bytesPerLine (stride) is the length of each scan line in bytes, and \a bytes is the total |
| number of bytes that must be allocated for the frame. |
| */ |
| QVideoFrame::QVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format) |
| : d(new QVideoFramePrivate(size, format)) |
| { |
| if (bytes > 0) { |
| QByteArray data; |
| data.resize(bytes); |
| |
| // Check the memory was successfully allocated. |
| if (!data.isEmpty()) |
| d->buffer = new QMemoryVideoBuffer(data, bytesPerLine); |
| } |
| } |
| |
| /*! |
| Constructs a video frame from an \a image. |
| |
| \note This will construct an invalid video frame if there is no frame type equivalent to the |
| image format. |
| |
| \sa pixelFormatFromImageFormat() |
| */ |
| QVideoFrame::QVideoFrame(const QImage &image) |
| : d(new QVideoFramePrivate( |
| image.size(), pixelFormatFromImageFormat(image.format()))) |
| { |
| if (d->pixelFormat != Format_Invalid) |
| d->buffer = new QImageVideoBuffer(image); |
| } |
| |
| /*! |
| Constructs a shallow copy of \a other. Since QVideoFrame is |
| explicitly shared, these two instances will reflect the same frame. |
| |
| */ |
| QVideoFrame::QVideoFrame(const QVideoFrame &other) |
| : d(other.d) |
| { |
| } |
| |
| /*! |
| Assigns the contents of \a other to this video frame. Since QVideoFrame is |
| explicitly shared, these two instances will reflect the same frame. |
| |
| */ |
| QVideoFrame &QVideoFrame::operator =(const QVideoFrame &other) |
| { |
| d = other.d; |
| |
| return *this; |
| } |
| |
| /*! |
| \return \c true if this QVideoFrame and \a other reflect the same frame. |
| */ |
| bool QVideoFrame::operator==(const QVideoFrame &other) const |
| { |
| // Due to explicit sharing we just compare the QSharedData which in turn compares the pointers. |
| return d == other.d; |
| } |
| |
| /*! |
| \return \c true if this QVideoFrame and \a other do not reflect the same frame. |
| */ |
| bool QVideoFrame::operator!=(const QVideoFrame &other) const |
| { |
| return d != other.d; |
| } |
| |
| /*! |
| Destroys a video frame. |
| */ |
| QVideoFrame::~QVideoFrame() |
| { |
| } |
| |
| /*! |
| \return underlying video buffer or \c null if there is none. |
| \since 5.13 |
| */ |
| QAbstractVideoBuffer *QVideoFrame::buffer() const |
| { |
| return d->buffer; |
| } |
| |
| /*! |
| Identifies whether a video frame is valid. |
| |
| An invalid frame has no video buffer associated with it. |
| |
| Returns true if the frame is valid, and false if it is not. |
| */ |
| bool QVideoFrame::isValid() const |
| { |
| return d->buffer != nullptr; |
| } |
| |
| /*! |
| Returns the color format of a video frame. |
| */ |
| QVideoFrame::PixelFormat QVideoFrame::pixelFormat() const |
| { |
| return d->pixelFormat; |
| } |
| |
| /*! |
| Returns the type of a video frame's handle. |
| |
| */ |
| QAbstractVideoBuffer::HandleType QVideoFrame::handleType() const |
| { |
| return d->buffer ? d->buffer->handleType() : QAbstractVideoBuffer::NoHandle; |
| } |
| |
| /*! |
| Returns the dimensions of a video frame. |
| */ |
| QSize QVideoFrame::size() const |
| { |
| return d->size; |
| } |
| |
| /*! |
| Returns the width of a video frame. |
| */ |
| int QVideoFrame::width() const |
| { |
| return d->size.width(); |
| } |
| |
| /*! |
| Returns the height of a video frame. |
| */ |
| int QVideoFrame::height() const |
| { |
| return d->size.height(); |
| } |
| |
| /*! |
| Returns the field an interlaced video frame belongs to. |
| |
| If the video is not interlaced this will return WholeFrame. |
| */ |
| QVideoFrame::FieldType QVideoFrame::fieldType() const |
| { |
| return d->fieldType; |
| } |
| |
| /*! |
| Sets the \a field an interlaced video frame belongs to. |
| */ |
| void QVideoFrame::setFieldType(QVideoFrame::FieldType field) |
| { |
| d->fieldType = field; |
| } |
| |
| /*! |
| Identifies if a video frame's contents are currently mapped to system memory. |
| |
| This is a convenience function which checks that the \l {QAbstractVideoBuffer::MapMode}{MapMode} |
| of the frame is not equal to QAbstractVideoBuffer::NotMapped. |
| |
| Returns true if the contents of the video frame are mapped to system memory, and false |
| otherwise. |
| |
| \sa mapMode(), QAbstractVideoBuffer::MapMode |
| */ |
| |
| bool QVideoFrame::isMapped() const |
| { |
| return d->buffer != nullptr && d->buffer->mapMode() != QAbstractVideoBuffer::NotMapped; |
| } |
| |
| /*! |
| Identifies if the mapped contents of a video frame will be persisted when the frame is unmapped. |
| |
| This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode} |
| contains the QAbstractVideoBuffer::WriteOnly flag. |
| |
| Returns true if the video frame will be updated when unmapped, and false otherwise. |
| |
| \note The result of altering the data of a frame that is mapped in read-only mode is undefined. |
| Depending on the buffer implementation the changes may be persisted, or worse alter a shared |
| buffer. |
| |
| \sa mapMode(), QAbstractVideoBuffer::MapMode |
| */ |
| bool QVideoFrame::isWritable() const |
| { |
| return d->buffer != nullptr && (d->buffer->mapMode() & QAbstractVideoBuffer::WriteOnly); |
| } |
| |
| /*! |
| Identifies if the mapped contents of a video frame were read from the frame when it was mapped. |
| |
| This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode} |
| contains the QAbstractVideoBuffer::WriteOnly flag. |
| |
| Returns true if the contents of the mapped memory were read from the video frame, and false |
| otherwise. |
| |
| \sa mapMode(), QAbstractVideoBuffer::MapMode |
| */ |
| bool QVideoFrame::isReadable() const |
| { |
| return d->buffer != nullptr && (d->buffer->mapMode() & QAbstractVideoBuffer::ReadOnly); |
| } |
| |
| /*! |
| Returns the mode a video frame was mapped to system memory in. |
| |
| \sa map(), QAbstractVideoBuffer::MapMode |
| */ |
| QAbstractVideoBuffer::MapMode QVideoFrame::mapMode() const |
| { |
| return d->buffer != nullptr ? d->buffer->mapMode() : QAbstractVideoBuffer::NotMapped; |
| } |
| |
| /*! |
| Maps the contents of a video frame to system (CPU addressable) memory. |
| |
| In some cases the video frame data might be stored in video memory or otherwise inaccessible |
| memory, so it is necessary to map a frame before accessing the pixel data. This may involve |
| copying the contents around, so avoid mapping and unmapping unless required. |
| |
| The map \a mode indicates whether the contents of the mapped memory should be read from and/or |
| written to the frame. If the map mode includes the \c QAbstractVideoBuffer::ReadOnly flag the |
| mapped memory will be populated with the content of the video frame 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 frame when unmapped. |
| |
| While mapped the contents of a video frame can be accessed directly through the pointer returned |
| by the bits() function. |
| |
| 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 video frame contents. |
| |
| If the video frame has been mapped in read only mode, it is permissible to map it |
| multiple times in read only mode (and unmap it a corresponding number of times). In all |
| other cases it is necessary to unmap the frame first before mapping a second time. |
| |
| \note Writing to memory that is mapped as read-only is undefined, and may result in changes |
| to shared data or crashes. |
| |
| Returns true if the frame was mapped to memory in the given \a mode and false otherwise. |
| |
| \sa unmap(), mapMode(), bits() |
| */ |
| bool QVideoFrame::map(QAbstractVideoBuffer::MapMode mode) |
| { |
| QMutexLocker lock(&d->mapMutex); |
| |
| if (!d->buffer) |
| return false; |
| |
| if (mode == QAbstractVideoBuffer::NotMapped) |
| return false; |
| |
| if (d->mappedCount > 0) { |
| //it's allowed to map the video frame multiple times in read only mode |
| if (d->buffer->mapMode() == QAbstractVideoBuffer::ReadOnly |
| && mode == QAbstractVideoBuffer::ReadOnly) { |
| d->mappedCount++; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| Q_ASSERT(d->data[0] == nullptr); |
| Q_ASSERT(d->bytesPerLine[0] == 0); |
| Q_ASSERT(d->planeCount == 0); |
| Q_ASSERT(d->mappedBytes == 0); |
| |
| d->planeCount = d->buffer->mapPlanes(mode, &d->mappedBytes, d->bytesPerLine, d->data); |
| if (d->planeCount == 0) |
| return false; |
| |
| 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_ABGR32: |
| 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_YUV422P: |
| 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 uvHeight = d->pixelFormat == Format_YUV422P ? height : height / 2; |
| const int uvStride = (d->mappedBytes - (yStride * height)) / uvHeight / 2; |
| |
| // Three planes, the second and third vertically (and horizontally for other than Format_YUV422P formats) 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 * uvHeight); |
| 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; |
| } |
| default: |
| break; |
| } |
| |
| d->mappedCount++; |
| return true; |
| } |
| |
| /*! |
| Releases the memory mapped by the map() function. |
| |
| If the \l {QAbstractVideoBuffer::MapMode}{MapMode} included the QAbstractVideoBuffer::WriteOnly |
| flag this will persist the current content of the mapped memory to the video frame. |
| |
| unmap() should not be called if map() function failed. |
| |
| \sa map() |
| */ |
| void QVideoFrame::unmap() |
| { |
| QMutexLocker lock(&d->mapMutex); |
| |
| if (!d->buffer) |
| return; |
| |
| if (d->mappedCount == 0) { |
| qWarning() << "QVideoFrame::unmap() was called more times then QVideoFrame::map()"; |
| return; |
| } |
| |
| d->mappedCount--; |
| |
| if (d->mappedCount == 0) { |
| d->mappedBytes = 0; |
| d->planeCount = 0; |
| memset(d->bytesPerLine, 0, sizeof(d->bytesPerLine)); |
| memset(d->data, 0, sizeof(d->data)); |
| |
| d->buffer->unmap(); |
| } |
| } |
| |
| /*! |
| Returns the number of bytes in a scan line. |
| |
| \note For planar formats this is the bytes per line of the first plane only. The bytes per line of subsequent |
| planes should be calculated as per the frame \l{QVideoFrame::PixelFormat}{pixel format}. |
| |
| This value is only valid while the frame data is \l {map()}{mapped}. |
| |
| \sa bits(), map(), mappedBytes() |
| */ |
| int QVideoFrame::bytesPerLine() const |
| { |
| 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; |
| } |
| |
| /*! |
| Returns a pointer to the start of the frame data buffer. |
| |
| 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() |
| */ |
| uchar *QVideoFrame::bits() |
| { |
| 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] : nullptr; |
| } |
| |
| /*! |
| Returns a pointer to the start of the frame data buffer. |
| |
| 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() |
| */ |
| const uchar *QVideoFrame::bits() const |
| { |
| 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] : nullptr; |
| } |
| |
| /*! |
| Returns the number of bytes occupied by the mapped frame data. |
| |
| This value is only valid while the frame data is \l {map()}{mapped}. |
| |
| \sa map() |
| */ |
| int QVideoFrame::mappedBytes() const |
| { |
| 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. |
| |
| For an OpenGL texture this would be the texture ID. |
| |
| \sa QAbstractVideoBuffer::handle() |
| */ |
| QVariant QVideoFrame::handle() const |
| { |
| return d->buffer != nullptr ? d->buffer->handle() : QVariant(); |
| } |
| |
| /*! |
| Returns the presentation time (in microseconds) when the frame should be displayed. |
| |
| An invalid time is represented as -1. |
| |
| */ |
| qint64 QVideoFrame::startTime() const |
| { |
| return d->startTime; |
| } |
| |
| /*! |
| Sets the presentation \a time (in microseconds) when the frame should initially be displayed. |
| |
| An invalid time is represented as -1. |
| |
| */ |
| void QVideoFrame::setStartTime(qint64 time) |
| { |
| d->startTime = time; |
| } |
| |
| /*! |
| Returns the presentation time (in microseconds) when a frame should stop being displayed. |
| |
| An invalid time is represented as -1. |
| |
| */ |
| qint64 QVideoFrame::endTime() const |
| { |
| return d->endTime; |
| } |
| |
| /*! |
| Sets the presentation \a time (in microseconds) when a frame should stop being displayed. |
| |
| An invalid time is represented as -1. |
| |
| */ |
| void QVideoFrame::setEndTime(qint64 time) |
| { |
| d->endTime = time; |
| } |
| |
| /*! |
| Returns any extra metadata associated with this frame. |
| */ |
| QVariantMap QVideoFrame::availableMetaData() const |
| { |
| return d->metadata; |
| } |
| |
| /*! |
| Returns any metadata for this frame for the given \a key. |
| |
| This might include frame specific information from |
| a camera, or subtitles from a decoded video stream. |
| |
| See the documentation for the relevant video frame |
| producer for further information about available metadata. |
| */ |
| QVariant QVideoFrame::metaData(const QString &key) const |
| { |
| return d->metadata.value(key); |
| } |
| |
| /*! |
| Sets the metadata for the given \a key to \a value. |
| |
| If \a value is a null variant, any metadata for this key will be removed. |
| |
| The producer of the video frame might use this to associate |
| certain data with this frame, or for an intermediate processor |
| to add information for a consumer of this frame. |
| */ |
| void QVideoFrame::setMetaData(const QString &key, const QVariant &value) |
| { |
| if (!value.isNull()) |
| d->metadata.insert(key, value); |
| else |
| d->metadata.remove(key); |
| } |
| |
| /*! |
| Returns a video pixel format equivalent to an image \a format. If there is no equivalent |
| format QVideoFrame::InvalidType is returned instead. |
| |
| \note In general \l QImage does not handle YUV formats. |
| |
| */ |
| QVideoFrame::PixelFormat QVideoFrame::pixelFormatFromImageFormat(QImage::Format format) |
| { |
| switch (format) { |
| case QImage::Format_RGB32: |
| case QImage::Format_RGBX8888: |
| return Format_RGB32; |
| case QImage::Format_ARGB32: |
| case QImage::Format_RGBA8888: |
| return Format_ARGB32; |
| case QImage::Format_ARGB32_Premultiplied: |
| case QImage::Format_RGBA8888_Premultiplied: |
| return Format_ARGB32_Premultiplied; |
| case QImage::Format_RGB16: |
| return Format_RGB565; |
| case QImage::Format_ARGB8565_Premultiplied: |
| return Format_ARGB8565_Premultiplied; |
| case QImage::Format_RGB555: |
| return Format_RGB555; |
| case QImage::Format_RGB888: |
| return Format_RGB24; |
| case QImage::Format_Grayscale8: |
| return Format_Y8; |
| default: |
| return Format_Invalid; |
| } |
| } |
| |
| /*! |
| Returns an image format equivalent to a video frame pixel \a format. If there is no equivalent |
| format QImage::Format_Invalid is returned instead. |
| |
| \note In general \l QImage does not handle YUV formats. |
| |
| */ |
| QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format) |
| { |
| switch (format) { |
| case Format_Invalid: |
| return QImage::Format_Invalid; |
| case Format_ARGB32: |
| return QImage::Format_ARGB32; |
| case Format_ARGB32_Premultiplied: |
| return QImage::Format_ARGB32_Premultiplied; |
| case Format_RGB32: |
| return QImage::Format_RGB32; |
| case Format_RGB24: |
| return QImage::Format_RGB888; |
| case Format_RGB565: |
| return QImage::Format_RGB16; |
| case Format_RGB555: |
| return QImage::Format_RGB555; |
| case Format_ARGB8565_Premultiplied: |
| return QImage::Format_ARGB8565_Premultiplied; |
| case Format_BGRA32: |
| case Format_BGRA32_Premultiplied: |
| case Format_BGR32: |
| case Format_BGR24: |
| return QImage::Format_Invalid; |
| case Format_BGR565: |
| case Format_BGR555: |
| case Format_BGRA5658_Premultiplied: |
| case Format_AYUV444: |
| case Format_AYUV444_Premultiplied: |
| case Format_YUV444: |
| case Format_YUV420P: |
| case Format_YUV422P: |
| case Format_YV12: |
| case Format_UYVY: |
| case Format_YUYV: |
| case Format_NV12: |
| case Format_NV21: |
| case Format_IMC1: |
| case Format_IMC2: |
| case Format_IMC3: |
| case Format_IMC4: |
| case Format_Y16: |
| case Format_Jpeg: |
| case Format_CameraRaw: |
| case Format_AdobeDng: |
| return QImage::Format_Invalid; |
| case Format_Y8: |
| return QImage::Format_Grayscale8; |
| case Format_User: |
| default: |
| return QImage::Format_Invalid; |
| } |
| return QImage::Format_Invalid; |
| } |
| |
| |
| extern void QT_FASTCALL qt_convert_BGRA32_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_BGR24_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_BGR565_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_BGR555_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_AYUV444_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_YUV444_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_YUV420P_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_YV12_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_UYVY_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_YUYV_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_NV12_to_ARGB32(const QVideoFrame&, uchar*); |
| extern void QT_FASTCALL qt_convert_NV21_to_ARGB32(const QVideoFrame&, uchar*); |
| |
| static VideoFrameConvertFunc qConvertFuncs[QVideoFrame::NPixelFormats] = { |
| /* Format_Invalid */ nullptr, // Not needed |
| /* Format_ARGB32 */ nullptr, // Not needed |
| /* Format_ARGB32_Premultiplied */ nullptr, // Not needed |
| /* Format_RGB32 */ nullptr, // Not needed |
| /* Format_RGB24 */ nullptr, // Not needed |
| /* Format_RGB565 */ nullptr, // Not needed |
| /* Format_RGB555 */ nullptr, // Not needed |
| /* Format_ARGB8565_Premultiplied */ nullptr, // Not needed |
| /* Format_BGRA32 */ qt_convert_BGRA32_to_ARGB32, |
| /* Format_BGRA32_Premultiplied */ qt_convert_BGRA32_to_ARGB32, |
| /* Format_BGR32 */ qt_convert_BGRA32_to_ARGB32, |
| /* Format_BGR24 */ qt_convert_BGR24_to_ARGB32, |
| /* Format_BGR565 */ qt_convert_BGR565_to_ARGB32, |
| /* Format_BGR555 */ qt_convert_BGR555_to_ARGB32, |
| /* Format_BGRA5658_Premultiplied */ nullptr, |
| /* Format_AYUV444 */ qt_convert_AYUV444_to_ARGB32, |
| /* Format_AYUV444_Premultiplied */ nullptr, |
| /* Format_YUV444 */ qt_convert_YUV444_to_ARGB32, |
| /* Format_YUV420P */ qt_convert_YUV420P_to_ARGB32, |
| /* Format_YV12 */ qt_convert_YV12_to_ARGB32, |
| /* Format_UYVY */ qt_convert_UYVY_to_ARGB32, |
| /* Format_YUYV */ qt_convert_YUYV_to_ARGB32, |
| /* Format_NV12 */ qt_convert_NV12_to_ARGB32, |
| /* Format_NV21 */ qt_convert_NV21_to_ARGB32, |
| /* Format_IMC1 */ nullptr, |
| /* Format_IMC2 */ nullptr, |
| /* Format_IMC3 */ nullptr, |
| /* Format_IMC4 */ nullptr, |
| /* Format_Y8 */ nullptr, |
| /* Format_Y16 */ nullptr, |
| /* Format_Jpeg */ nullptr, // Not needed |
| /* Format_CameraRaw */ nullptr, |
| /* Format_AdobeDng */ nullptr, |
| /* Format_ABGR32 */ nullptr, // ### Qt 6: reorder |
| /* Format_YUV422P */ nullptr, |
| }; |
| |
| static void qInitConvertFuncsAsm() |
| { |
| #ifdef QT_COMPILER_SUPPORTS_SSE2 |
| extern void QT_FASTCALL qt_convert_BGRA32_to_ARGB32_sse2(const QVideoFrame&, uchar*); |
| if (qCpuHasFeature(SSE2)){ |
| qConvertFuncs[QVideoFrame::Format_BGRA32] = qt_convert_BGRA32_to_ARGB32_sse2; |
| qConvertFuncs[QVideoFrame::Format_BGRA32_Premultiplied] = qt_convert_BGRA32_to_ARGB32_sse2; |
| qConvertFuncs[QVideoFrame::Format_BGR32] = qt_convert_BGRA32_to_ARGB32_sse2; |
| } |
| #endif |
| #ifdef QT_COMPILER_SUPPORTS_SSSE3 |
| extern void QT_FASTCALL qt_convert_BGRA32_to_ARGB32_ssse3(const QVideoFrame&, uchar*); |
| if (qCpuHasFeature(SSSE3)){ |
| qConvertFuncs[QVideoFrame::Format_BGRA32] = qt_convert_BGRA32_to_ARGB32_ssse3; |
| qConvertFuncs[QVideoFrame::Format_BGRA32_Premultiplied] = qt_convert_BGRA32_to_ARGB32_ssse3; |
| qConvertFuncs[QVideoFrame::Format_BGR32] = qt_convert_BGRA32_to_ARGB32_ssse3; |
| } |
| #endif |
| #ifdef QT_COMPILER_SUPPORTS_AVX2 |
| extern void QT_FASTCALL qt_convert_BGRA32_to_ARGB32_avx2(const QVideoFrame&, uchar*); |
| if (qCpuHasFeature(AVX2)){ |
| qConvertFuncs[QVideoFrame::Format_BGRA32] = qt_convert_BGRA32_to_ARGB32_avx2; |
| qConvertFuncs[QVideoFrame::Format_BGRA32_Premultiplied] = qt_convert_BGRA32_to_ARGB32_avx2; |
| qConvertFuncs[QVideoFrame::Format_BGR32] = qt_convert_BGRA32_to_ARGB32_avx2; |
| } |
| #endif |
| } |
| |
| /*! |
| Based on the pixel format converts current video frame to image. |
| \since 5.15 |
| */ |
| QImage QVideoFrame::image() const |
| { |
| QVideoFrame frame = *this; |
| QImage result; |
| |
| if (!frame.isValid() || !frame.map(QAbstractVideoBuffer::ReadOnly)) |
| return result; |
| |
| // Formats supported by QImage don't need conversion |
| QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()); |
| if (imageFormat != QImage::Format_Invalid) { |
| result = QImage(frame.bits(), frame.width(), frame.height(), frame.bytesPerLine(), imageFormat).copy(); |
| } |
| |
| // Load from JPG |
| else if (frame.pixelFormat() == QVideoFrame::Format_Jpeg) { |
| result.loadFromData(frame.bits(), frame.mappedBytes(), "JPG"); |
| } |
| |
| // Need conversion |
| else { |
| static bool initAsmFuncsDone = false; |
| if (!initAsmFuncsDone) { |
| qInitConvertFuncsAsm(); |
| initAsmFuncsDone = true; |
| } |
| VideoFrameConvertFunc convert = qConvertFuncs[frame.pixelFormat()]; |
| if (!convert) { |
| qWarning() << Q_FUNC_INFO << ": unsupported pixel format" << frame.pixelFormat(); |
| } else { |
| result = QImage(frame.width(), frame.height(), QImage::Format_ARGB32); |
| convert(frame, result.bits()); |
| } |
| } |
| |
| frame.unmap(); |
| |
| return result; |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| QDebug operator<<(QDebug dbg, QVideoFrame::PixelFormat pf) |
| { |
| QDebugStateSaver saver(dbg); |
| dbg.nospace(); |
| switch (pf) { |
| case QVideoFrame::Format_Invalid: |
| return dbg << "Format_Invalid"; |
| case QVideoFrame::Format_ARGB32: |
| return dbg << "Format_ARGB32"; |
| case QVideoFrame::Format_ARGB32_Premultiplied: |
| return dbg << "Format_ARGB32_Premultiplied"; |
| case QVideoFrame::Format_RGB32: |
| return dbg << "Format_RGB32"; |
| case QVideoFrame::Format_RGB24: |
| return dbg << "Format_RGB24"; |
| case QVideoFrame::Format_RGB565: |
| return dbg << "Format_RGB565"; |
| case QVideoFrame::Format_RGB555: |
| return dbg << "Format_RGB555"; |
| case QVideoFrame::Format_ARGB8565_Premultiplied: |
| return dbg << "Format_ARGB8565_Premultiplied"; |
| case QVideoFrame::Format_BGRA32: |
| return dbg << "Format_BGRA32"; |
| case QVideoFrame::Format_BGRA32_Premultiplied: |
| return dbg << "Format_BGRA32_Premultiplied"; |
| case QVideoFrame::Format_ABGR32: |
| return dbg << "Format_ABGR32"; |
| case QVideoFrame::Format_BGR32: |
| return dbg << "Format_BGR32"; |
| case QVideoFrame::Format_BGR24: |
| return dbg << "Format_BGR24"; |
| case QVideoFrame::Format_BGR565: |
| return dbg << "Format_BGR565"; |
| case QVideoFrame::Format_BGR555: |
| return dbg << "Format_BGR555"; |
| case QVideoFrame::Format_BGRA5658_Premultiplied: |
| return dbg << "Format_BGRA5658_Premultiplied"; |
| case QVideoFrame::Format_AYUV444: |
| return dbg << "Format_AYUV444"; |
| case QVideoFrame::Format_AYUV444_Premultiplied: |
| return dbg << "Format_AYUV444_Premultiplied"; |
| case QVideoFrame::Format_YUV444: |
| return dbg << "Format_YUV444"; |
| case QVideoFrame::Format_YUV420P: |
| return dbg << "Format_YUV420P"; |
| case QVideoFrame::Format_YUV422P: |
| return dbg << "Format_YUV422P"; |
| case QVideoFrame::Format_YV12: |
| return dbg << "Format_YV12"; |
| case QVideoFrame::Format_UYVY: |
| return dbg << "Format_UYVY"; |
| case QVideoFrame::Format_YUYV: |
| return dbg << "Format_YUYV"; |
| case QVideoFrame::Format_NV12: |
| return dbg << "Format_NV12"; |
| case QVideoFrame::Format_NV21: |
| return dbg << "Format_NV21"; |
| case QVideoFrame::Format_IMC1: |
| return dbg << "Format_IMC1"; |
| case QVideoFrame::Format_IMC2: |
| return dbg << "Format_IMC2"; |
| case QVideoFrame::Format_IMC3: |
| return dbg << "Format_IMC3"; |
| case QVideoFrame::Format_IMC4: |
| return dbg << "Format_IMC4"; |
| case QVideoFrame::Format_Y8: |
| return dbg << "Format_Y8"; |
| case QVideoFrame::Format_Y16: |
| return dbg << "Format_Y16"; |
| case QVideoFrame::Format_Jpeg: |
| return dbg << "Format_Jpeg"; |
| case QVideoFrame::Format_AdobeDng: |
| return dbg << "Format_AdobeDng"; |
| case QVideoFrame::Format_CameraRaw: |
| return dbg << "Format_CameraRaw"; |
| |
| default: |
| return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(pf)).toLatin1().constData(); |
| } |
| } |
| |
| QDebug operator<<(QDebug dbg, QVideoFrame::FieldType f) |
| { |
| QDebugStateSaver saver(dbg); |
| dbg.nospace(); |
| switch (f) { |
| case QVideoFrame::TopField: |
| return dbg << "TopField"; |
| case QVideoFrame::BottomField: |
| return dbg << "BottomField"; |
| case QVideoFrame::InterlacedFrame: |
| return dbg << "InterlacedFrame"; |
| default: |
| return dbg << "ProgressiveFrame"; |
| } |
| } |
| |
| static QString qFormatTimeStamps(qint64 start, qint64 end) |
| { |
| // Early out for invalid. |
| if (start < 0) |
| return QLatin1String("[no timestamp]"); |
| |
| bool onlyOne = (start == end); |
| |
| // [hh:]mm:ss.ms |
| const int s_millis = start % 1000000; |
| start /= 1000000; |
| const int s_seconds = start % 60; |
| start /= 60; |
| const int s_minutes = start % 60; |
| start /= 60; |
| |
| if (onlyOne) { |
| if (start > 0) |
| return QString::fromLatin1("@%1:%2:%3.%4") |
| .arg(start, 1, 10, QLatin1Char('0')) |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')); |
| else |
| return QString::fromLatin1("@%1:%2.%3") |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')); |
| } else if (end == -1) { |
| // Similar to start-start, except it means keep displaying it? |
| if (start > 0) |
| return QString::fromLatin1("%1:%2:%3.%4 - forever") |
| .arg(start, 1, 10, QLatin1Char('0')) |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')); |
| else |
| return QString::fromLatin1("%1:%2.%3 - forever") |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')); |
| } else { |
| const int e_millis = end % 1000000; |
| end /= 1000000; |
| const int e_seconds = end % 60; |
| end /= 60; |
| const int e_minutes = end % 60; |
| end /= 60; |
| |
| if (start > 0 || end > 0) |
| return QString::fromLatin1("%1:%2:%3.%4 - %5:%6:%7.%8") |
| .arg(start, 1, 10, QLatin1Char('0')) |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')) |
| .arg(end, 1, 10, QLatin1Char('0')) |
| .arg(e_minutes, 2, 10, QLatin1Char('0')) |
| .arg(e_seconds, 2, 10, QLatin1Char('0')) |
| .arg(e_millis, 2, 10, QLatin1Char('0')); |
| else |
| return QString::fromLatin1("%1:%2.%3 - %4:%5.%6") |
| .arg(s_minutes, 2, 10, QLatin1Char('0')) |
| .arg(s_seconds, 2, 10, QLatin1Char('0')) |
| .arg(s_millis, 2, 10, QLatin1Char('0')) |
| .arg(e_minutes, 2, 10, QLatin1Char('0')) |
| .arg(e_seconds, 2, 10, QLatin1Char('0')) |
| .arg(e_millis, 2, 10, QLatin1Char('0')); |
| } |
| } |
| |
| QDebug operator<<(QDebug dbg, const QVideoFrame& f) |
| { |
| QDebugStateSaver saver(dbg); |
| dbg.nospace(); |
| dbg << "QVideoFrame(" << f.size() << ", " |
| << f.pixelFormat() << ", " |
| << f.handleType() << ", " |
| << f.mapMode() << ", " |
| << qFormatTimeStamps(f.startTime(), f.endTime()).toLatin1().constData(); |
| if (f.availableMetaData().count()) |
| dbg << ", metaData: " << f.availableMetaData(); |
| dbg << ')'; |
| return dbg; |
| } |
| #endif |
| |
| QT_END_NAMESPACE |
| |