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
qffmpegvideoencoderutils.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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 "private/qmultimediautils_p.h"
6
7extern "C" {
8#include <libavutil/pixdesc.h>
9}
10
12
13namespace QFFmpeg {
14
15static AVScore calculateTargetSwFormatScore(const AVPixFmtDescriptor *sourceSwFormatDesc,
16 AVPixelFormat fmt)
17{
18 // determine the format used by the encoder.
19 // We prefer YUV422 based formats such as NV12 or P010. Selection trues to find the best
20 // matching format for the encoder depending on the bit depth of the source format
21
22 const auto *desc = av_pix_fmt_desc_get(fmt);
23 if (!desc)
24 return NotSuitableAVScore;
25
26 const int sourceDepth = sourceSwFormatDesc ? sourceSwFormatDesc->comp[0].depth : 0;
27
28 if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
29 // we really don't want HW accelerated formats here
30 return NotSuitableAVScore;
31
32 auto score = DefaultAVScore;
33
34 if (desc == sourceSwFormatDesc)
35 // prefer exact matches
36 score += 10;
37 if (desc->comp[0].depth == sourceDepth)
38 score += 100;
39 else if (desc->comp[0].depth < sourceDepth)
40 score -= 100 + (sourceDepth - desc->comp[0].depth);
41 if (desc->log2_chroma_h == 1)
42 score += 1;
43 if (desc->log2_chroma_w == 1)
44 score += 1;
45 if (desc->flags & AV_PIX_FMT_FLAG_BE)
46 score -= 10;
47 if (desc->flags & AV_PIX_FMT_FLAG_PAL)
48 // we don't want paletted formats
49 score -= 10000;
50 if (desc->flags & AV_PIX_FMT_FLAG_RGB)
51 // we don't want RGB formats
52 score -= 1000;
53
54 // qCDebug(qLcVideoFrameEncoder)
55 // << "checking format" << fmt << Qt::hex << desc->flags << desc->comp[0].depth
56 // << desc->log2_chroma_h << desc->log2_chroma_w << "score:" << score;
57
58 return score;
59}
60
61static auto targetSwFormatScoreCalculator(AVPixelFormat sourceFormat)
62{
63 const auto sourceSwFormatDesc = av_pix_fmt_desc_get(sourceFormat);
64 return [=](AVPixelFormat fmt) { return calculateTargetSwFormatScore(sourceSwFormatDesc, fmt); };
65}
66
67static bool isHwFormatAcceptedByCodec(AVPixelFormat pixFormat)
68{
69 switch (pixFormat) {
70 case AV_PIX_FMT_MEDIACODEC:
71 // Mediacodec doesn't accept AV_PIX_FMT_MEDIACODEC (QTBUG-116836)
72 return false;
73 default:
74 return true;
75 }
76}
77
78AVPixelFormat findTargetSWFormat(AVPixelFormat sourceSWFormat, const AVCodec *codec,
79 const HWAccel &accel)
80{
81 auto scoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
82
83 const auto constraints = accel.constraints();
84 if (constraints && constraints->valid_sw_formats)
85 return findBestAVFormat(constraints->valid_sw_formats, scoreCalculator).first;
86
87 // Some codecs, e.g. mediacodec, don't expose constraints, let's find the format in
88 // codec->pix_fmts
89 if (codec->pix_fmts)
90 return findBestAVFormat(codec->pix_fmts, scoreCalculator).first;
91
92 return AV_PIX_FMT_NONE;
93}
94
95AVPixelFormat findTargetFormat(AVPixelFormat sourceFormat, AVPixelFormat sourceSWFormat,
96 const AVCodec *codec, const HWAccel *accel)
97{
98 Q_UNUSED(sourceFormat);
99
100 if (accel) {
101 const auto hwFormat = accel->hwFormat();
102
103 // TODO: handle codec->capabilities & AV_CODEC_CAP_HARDWARE here
104 if (!isHwFormatAcceptedByCodec(hwFormat))
105 return findTargetSWFormat(sourceSWFormat, codec, *accel);
106
107 const auto constraints = accel->constraints();
108 if (constraints && hasAVFormat(constraints->valid_hw_formats, hwFormat))
109 return hwFormat;
110
111 // Some codecs, don't expose constraints, let's find the format in codec->pix_fmts
112 if (hasAVFormat(codec->pix_fmts, hwFormat))
113 return hwFormat;
114 }
115
116 if (!codec->pix_fmts) {
117 qWarning() << "Codec pix formats are undefined, it's likely to behave incorrectly";
118
119 return sourceSWFormat;
120 }
121
122 auto swScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
123 return findBestAVFormat(codec->pix_fmts, swScoreCalculator).first;
124}
125
126std::pair<const AVCodec *, std::unique_ptr<HWAccel>> findHwEncoder(AVCodecID codecID,
127 const QSize &resolution)
128{
129 auto matchesSizeConstraints = [&resolution](const HWAccel &accel) {
130 const auto constraints = accel.constraints();
131 if (!constraints)
132 return true;
133
134 return resolution.width() >= constraints->min_width
135 && resolution.height() >= constraints->min_height
136 && resolution.width() <= constraints->max_width
137 && resolution.height() <= constraints->max_height;
138 };
139
140 // 1st - attempt to find hw accelerated encoder
141 auto result = HWAccel::findEncoderWithHwAccel(codecID, matchesSizeConstraints);
142 Q_ASSERT(!!result.first == !!result.second);
143
144 return result;
145}
146
147const AVCodec *findSwEncoder(AVCodecID codecID, AVPixelFormat sourceSWFormat)
148{
149 auto formatScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
150
151 return findAVEncoder(codecID, [&formatScoreCalculator](const AVCodec *codec) {
152 if (!codec->pix_fmts)
153 // codecs without pix_fmts are suspicious
154 return MinAVScore;
155
156 return findBestAVFormat(codec->pix_fmts, formatScoreCalculator).second;
157 });
158}
159
160AVRational adjustFrameRate(const AVRational *supportedRates, qreal requestedRate)
161{
162 auto calcScore = [requestedRate](const AVRational &rate) {
163 // relative comparison
164 return qMin(requestedRate * rate.den, qreal(rate.num))
165 / qMax(requestedRate * rate.den, qreal(rate.num));
166 };
167
168 const auto result = findBestAVValue(supportedRates, calcScore).first;
169 if (result.num && result.den)
170 return result;
171
172 const auto [num, den] = qRealToFraction(requestedRate);
173 return { num, den };
174}
175
176AVRational adjustFrameTimeBase(const AVRational *supportedRates, AVRational frameRate)
177{
178 // TODO: user-specified frame rate might be required.
179 if (supportedRates) {
180 auto hasFrameRate = [&]() {
181 for (auto rate = supportedRates; rate->num && rate->den; ++rate)
182 if (rate->den == frameRate.den && rate->num == frameRate.num)
183 return true;
184
185 return false;
186 };
187
188 Q_ASSERT(hasFrameRate());
189
190 return { frameRate.den, frameRate.num };
191 }
192
193 constexpr int TimeScaleFactor = 1000; // Allows not to follow fixed rate
194 return { frameRate.den, frameRate.num * TimeScaleFactor };
195}
196
197QSize adjustVideoResolution(const AVCodec *codec, QSize requestedResolution)
198{
199#ifdef Q_OS_WINDOWS
200 // TODO: investigate, there might be more encoders not supporting odd resolution
201 if (strcmp(codec->name, "h264_mf") == 0) {
202 auto makeEven = [](int size) { return size & ~1; };
203 return QSize(makeEven(requestedResolution.width()), makeEven(requestedResolution.height()));
204 }
205#else
207#endif
208 return requestedResolution;
209}
210
211} // namespace QFFmpeg
212
static std::pair< const AVCodec *, std::unique_ptr< HWAccel > > findEncoderWithHwAccel(AVCodecID id, const std::function< bool(const HWAccel &)> &hwAccelPredicate=nullptr)
const AVHWFramesConstraints * constraints() const
AVPixelFormat hwFormat() const
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
const AVCodec * findAVEncoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
Definition qffmpeg.cpp:433
std::pair< Format, AVScore > findBestAVFormat(const Format *fmts, const CalculateScore &calculateScore)
Definition qffmpeg_p.h:210
AVPixelFormat findTargetFormat(AVPixelFormat sourceFormat, AVPixelFormat sourceSWFormat, const AVCodec *codec, const HWAccel *accel)
QSize adjustVideoResolution(const AVCodec *codec, QSize requestedResolution)
std::pair< const AVCodec *, std::unique_ptr< HWAccel > > findHwEncoder(AVCodecID codecID, const QSize &resolution)
static bool isHwFormatAcceptedByCodec(AVPixelFormat pixFormat)
int AVScore
Definition qffmpeg_p.h:158
constexpr AVScore DefaultAVScore
Definition qffmpeg_p.h:160
bool hasAVFormat(const Format *fmts, Format format)
Definition qffmpeg_p.h:178
const AVCodec * findSwEncoder(AVCodecID codecID, AVPixelFormat sourceSWFormat)
constexpr AVScore NotSuitableAVScore
Definition qffmpeg_p.h:161
static AVScore calculateTargetSwFormatScore(const AVPixFmtDescriptor *sourceSwFormatDesc, AVPixelFormat fmt)
static auto targetSwFormatScoreCalculator(AVPixelFormat sourceFormat)
AVRational adjustFrameRate(const AVRational *supportedRates, qreal requestedRate)
adjustFrameRate get a rational frame rate be requested qreal rate. If the codec supports fixed frame ...
AVPixelFormat findTargetSWFormat(AVPixelFormat sourceSWFormat, const AVCodec *codec, const HWAccel &accel)
AVRational adjustFrameTimeBase(const AVRational *supportedRates, AVRational frameRate)
adjustFrameTimeBase gets adjusted timebase by a list of supported frame rates and an already adjusted...
constexpr AVScore MinAVScore
Definition qffmpeg_p.h:162
auto findBestAVValue(const Value *values, const CalculateScore &calculateScore, Value invalidValue={})
Definition qffmpeg_p.h:193
Combined button and popup list for selecting options.
QMediaFormat::AudioCodec codec
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
QT_BEGIN_NAMESPACE Fraction qRealToFraction(qreal value)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLenum * rate
GLuint64EXT * result
[6]
GLuint num
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
QVideoFrameFormat::PixelFormat fmt