Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegconverter.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtMultimedia/qvideoframeformat.h>
6#include <QtMultimedia/qvideoframe.h>
7#include <QtCore/qloggingcategory.h>
8#include <private/qvideotexturehelper_p.h>
9
10extern "C" {
11#include <libswscale/swscale.h>
12}
13
15
16namespace {
17
18Q_LOGGING_CATEGORY(lc, "qt.multimedia.ffmpeg.converter");
19
20
21// Converts to FFmpeg pixel format. This function differs from
22// QFFmpegVideoBuffer::toAVPixelFormat which only covers the subset
23// of pixel formats required for encoding. Here we need to cover more
24// pixel formats to be able to generate test images for decoding/display
26{
27 switch (pixelFormat) {
28 default:
30 return AV_PIX_FMT_NONE;
33 return AV_PIX_FMT_NONE; // TODO: Fixme (No corresponding FFmpeg format available)
39 return AV_PIX_FMT_YUV420P;
41 return AV_PIX_FMT_BGRA;
43 return AV_PIX_FMT_ARGB;
46 return AV_PIX_FMT_0RGB;
48 return AV_PIX_FMT_BGRA;
51 return AV_PIX_FMT_BGR0;
53 return AV_PIX_FMT_ABGR;
55 return AV_PIX_FMT_0BGR;
57 return AV_PIX_FMT_RGBA;
59 return AV_PIX_FMT_RGB0;
61 return AV_PIX_FMT_YUV422P;
63 return AV_PIX_FMT_YUV420P;
65 return AV_PIX_FMT_YUV420P10;
67 return AV_PIX_FMT_UYVY422;
69 return AV_PIX_FMT_YUYV422;
71 return AV_PIX_FMT_NV12;
73 return AV_PIX_FMT_NV21;
75 return AV_PIX_FMT_GRAY8;
77 return AV_PIX_FMT_GRAY16;
79 return AV_PIX_FMT_P010;
81 return AV_PIX_FMT_P016;
83 return AV_PIX_FMT_MEDIACODEC;
84 }
85}
86
88{
89 static constexpr int arraySize = 4; // Array size required by sws_scale
90 std::array<uchar *, arraySize> bits;
91 std::array<int, arraySize> stride;
92};
93
95{
96 switch (dst.pixelFormat()) {
99 return { { dst.bits(0), dst.bits(2), dst.bits(1), nullptr },
100 { dst.bytesPerLine(0), dst.bytesPerLine(2), dst.bytesPerLine(1), 0 } };
101
103 return { { dst.bits(0), dst.bits(1) + dst.bytesPerLine(1) / 2, dst.bits(1), nullptr },
104 { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(1), 0 } };
105
107 return { { dst.bits(0), dst.bits(1), dst.bits(1) + dst.bytesPerLine(1) / 2, nullptr },
108 { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(1), 0 } };
109 default:
110 return { { dst.bits(0), dst.bits(1), dst.bits(2), nullptr },
111 { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(2), 0 } };
112 }
113}
114
116{
118 int colorRange; // 0 - mpeg/video, 1 - jpeg/full
119};
120
121// Qt heuristics for determining color space requires checking
122// both frame color space and range. This function mimics logic
123// used elsewhere in Qt Multimedia.
126{
127 const int avRange = colorRange == QVideoFrameFormat::ColorRange_Video ? 0 : 1;
128
129 switch (colorSpace) {
131 if (colorRange == QVideoFrameFormat::ColorRange_Full)
132 return { SWS_CS_ITU709, 1 }; // TODO: FIXME - Not exact match
133 return { SWS_CS_ITU601, 0 };
135 return { SWS_CS_ITU709, avRange };
137 return { SWS_CS_ITU601, 1 }; // TODO: Why do ITU601 and Adobe RGB match well?
139 return { SWS_CS_BT2020, avRange };
141 default:
142 return { SWS_CS_DEFAULT, avRange };
143 }
144}
145
146using SwsContextUPtr = std::unique_ptr<SwsContext, decltype(&sws_freeContext)>;
148
149// clang-format off
150
152 const QSize &dstSize, PixelFormat dstPixFmt)
153{
154 SwsContext* context = sws_getContext(
155 srcSize.width(), srcSize.height(), toAVPixelFormat(srcPixFmt),
156 dstSize.width(), dstSize.height(), toAVPixelFormat(dstPixFmt),
157 SWS_BILINEAR, nullptr, nullptr, nullptr);
158
159 return { context, &sws_freeContext };
160}
161
163 const QVideoFrameFormat &srcFormat,
164 const QVideoFrameFormat &dstFormat)
165{
166 const SwsColorSpace src = toSwsColorSpace(srcFormat.colorRange(), srcFormat.colorSpace());
167 const SwsColorSpace dst = toSwsColorSpace(dstFormat.colorRange(), dstFormat.colorSpace());
168
169 constexpr int brightness = 0;
170 constexpr int contrast = 0;
171 constexpr int saturation = 0;
172 const int status = sws_setColorspaceDetails(context,
173 sws_getCoefficients(src.colorSpace), src.colorRange,
174 sws_getCoefficients(dst.colorSpace), dst.colorRange,
175 brightness, contrast, saturation);
176
177 return status == 0;
178}
179
181{
183 return false;
184
185 QScopeGuard unmapSrc{[&] {
186 src.unmap();
187 }};
188
190 return false;
191
192 QScopeGuard unmapDst{[&] {
193 dst.unmap();
194 }};
195
196 const SwsFrameData srcData = getSwsData(src);
197 const SwsFrameData dstData = getSwsData(dst);
198
199 constexpr int firstSrcSliceRow = 0;
200 const int scaledHeight = sws_scale(context,
201 srcData.bits.data(), srcData.stride.data(),
202 firstSrcSliceRow, srcHeight,
203 dstData.bits.data(), dstData.stride.data());
204
205 if (scaledHeight != srcHeight)
206 return false;
207
208 return true;
209}
210
211// Ensure even size if using planar format with chroma subsampling
213{
214 const auto* srcDesc = QVideoTextureHelper::textureDescription(srcFmt);
215 const auto* dstDesc = QVideoTextureHelper::textureDescription(dstFmt);
216
217 QSize output = size;
218 for (const auto desc : { srcDesc, dstDesc }) {
219 for (int i = 0; i < desc->nplanes; ++i) {
220 // TODO: Assumes that max subsampling is 2
221 if (desc->sizeScale[i].x != 1)
222 output.setWidth(output.width() & ~1); // Make even
223
224 if (desc->sizeScale[i].y != 1)
225 output.setHeight(output.height() & ~1); // Make even
226 }
227 }
228
229 return output;
230}
231
232} // namespace
233
234// Converts a video frame to the dstFormat video frame format.
236{
237 if (src.size() != dstFormat.frameSize()) {
238 qCCritical(lc) << "Resizing is not supported";
239 return {};
240 }
241
242 // Adjust size to even width/height if we have chroma subsampling
243 const QSize size = adjustSize(src.size(), src.pixelFormat(), dstFormat.pixelFormat());
244 if (size != src.size())
245 qCWarning(lc) << "Input truncated to even width/height";
246
247 const SwsContextUPtr conv = createConverter(
248 size, src.pixelFormat(), size, dstFormat.pixelFormat());
249
250 if (!conv) {
251 qCCritical(lc) << "Failed to create SW converter";
252 return {};
253 }
254
255 if (!setColorSpaceDetails(conv.get(), src.surfaceFormat(), dstFormat)) {
256 qCCritical(lc) << "Failed to set color space details";
257 return {};
258 }
259
260 QVideoFrame dst{ dstFormat };
261
262 if (!convert(conv.get(), src, size.height(), dst)) {
263 qCCritical(lc) << "Frame conversion failed";
264 return {};
265 }
266
267 return dst;
268}
269
270// clang-format on
271
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
ColorSpace
Enumerates the color space of video frames.
PixelFormat
Enumerates video data types.
ColorRange
Describes the color range used by the video data.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
Combined button and popup list for selecting options.
AVPixelFormat toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat)
SwsColorSpace toSwsColorSpace(QVideoFrameFormat::ColorRange colorRange, QVideoFrameFormat::ColorSpace colorSpace)
bool setColorSpaceDetails(SwsContext *context, const QVideoFrameFormat &srcFormat, const QVideoFrameFormat &dstFormat)
SwsFrameData getSwsData(QVideoFrame &dst)
QSize adjustSize(const QSize &size, PixelFormat srcFmt, PixelFormat dstFmt)
std::unique_ptr< SwsContext, decltype(&sws_freeContext)> SwsContextUPtr
SwsContextUPtr createConverter(const QSize &srcSize, PixelFormat srcPixFmt, const QSize &dstSize, PixelFormat dstPixFmt)
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
static void * context
QVideoFrame convertFrame(QVideoFrame &src, const QVideoFrameFormat &dstFormat)
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCWarning(category,...)
GLenum GLint GLint GLint GLint GLuint GLenum GLint GLint GLint GLint GLsizei GLsizei srcHeight
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum src
GLenum GLenum dst
static constexpr To convert(const std::array< Mapping, N > &mapping, From Mapping::*from, To Mapping::*to, From value, To defaultValue)
QT_BEGIN_NAMESPACE typedef uchar * output
std::array< uchar *, arraySize > bits
std::array< int, arraySize > stride