Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
6#include <QtFFmpegMediaPluginImpl/private/qffmpeg_p.h>
7
8#include <QtMultimedia/private/qmultimediautils_p.h>
9#include <QtCore/qglobalstatic.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qoperatingsystemversion.h>
12#include <QtCore/qreadwritelock.h>
13#include <QtCore/private/qminimalflatset_p.h>
14#include <QtFFmpegMediaPluginImpl/private/qffmpegrecordingengineutils_p.h>
15
16#include <map>
17#include <mutex>
18#include <shared_mutex>
19
20extern "C" {
21#include <libavutil/hwcontext.h>
22#include <libavutil/pixdesc.h>
23}
24
26
27Q_STATIC_LOGGING_CATEGORY(qLcVideoEncoderUtils, "qt.multimedia.ffmpeg.videoencoderutils");
28
29namespace QFFmpeg {
30
31using namespace Qt::Literals;
32
33namespace {
34
35bool is16BitFormat(const AVPixFmtDescriptor *desc)
36{
37 return desc->comp[0].depth == 16;
38}
39
40bool is10BitFormat(const AVPixFmtDescriptor *desc)
41{
42 return desc->comp[0].depth == 10;
43}
44
45bool is8BitFormat(const AVPixFmtDescriptor *desc)
46{
47 return desc->comp[0].depth == 8;
48}
49
50bool is444Format(const AVPixFmtDescriptor *desc)
51{
52 return desc->log2_chroma_h == 0 && desc->log2_chroma_w == 0;
53}
54
55bool is422Format(const AVPixFmtDescriptor *desc)
56{
57 return desc->log2_chroma_h == 1 && desc->log2_chroma_w == 0;
58}
59
60bool is420Format(const AVPixFmtDescriptor *desc)
61{
62 return desc->log2_chroma_h == 1 && desc->log2_chroma_w == 1;
63}
64
65bool isGreyFormat(const AVPixFmtDescriptor *desc)
66{
67 return desc->nb_components == 1;
68}
69
70AVScore scoreTargetSwFormat(const AVPixFmtDescriptor *sourceSwFormatDesc, AVPixelFormat fmt)
71{
72 constexpr int AlphaMismatchPenalty = 50;
73
74 // determine the format used by the encoder.
75 // We prefer YUV420 based formats such as NV12 or P010. Selection trues to find the best
76 // matching format for the encoder depending on the bit depth of the source format
77
78 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
79 if (!desc)
80 return NotSuitableAVScore;
81
82 if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
83 // we really don't want HW accelerated formats here
84 return NotSuitableAVScore;
85
86 AVScore score = DefaultAVScore;
87
88 if (desc == sourceSwFormatDesc)
89 // prefer exact matches
90 score += 10;
91
92 const int sourceBpp = av_get_bits_per_pixel(sourceSwFormatDesc);
93 int bpp = av_get_bits_per_pixel(desc);
94
95 const bool sourceHasAlpha = sourceSwFormatDesc->flags & AV_PIX_FMT_FLAG_ALPHA;
96 if ((desc->flags & AV_PIX_FMT_FLAG_ALPHA) && !sourceHasAlpha) {
97 // The source has no real alpha to preserve -- don't credit the target's alpha bits
98 // toward a bpp match, and penalize picking an alpha target over an equally-good
99 // non-alpha one.
100 bpp -= desc->comp[desc->nb_components - 1].depth; // alpha is always the last component
101 score -= AlphaMismatchPenalty;
102 }
103
104 // we want formats with the same bpp
105 if (bpp == sourceBpp)
106 score += 100;
107 else if (bpp < sourceBpp)
108 score -= 100 + (sourceBpp - bpp);
109
110 // pessimize 10 and 16 bit formats if the source format is 8 bit
111 if (is8BitFormat(sourceSwFormatDesc)) {
112 if (is10BitFormat(desc))
113 score -= 100;
114 else if (is16BitFormat(desc))
115 score -= 200;
116 }
117
118 // Add a slight preference for 4:2:0 formats.
119 if (is420Format(desc))
120 score += 2;
121 else if (is422Format(desc))
122 score += 1;
123 else if (is444Format(desc))
124 score -= 1;
125
126 if constexpr (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Android) {
127 // Add a slight preference for NV12 on Android
128 // as it's supported better than other 4:2:0 formats
129 if (fmt == AV_PIX_FMT_NV12)
130 score += 1;
131 }
132
133 if (isGreyFormat(desc) && !isGreyFormat(sourceSwFormatDesc)) // we don't want greyscale formats
134 return AVScore::NotSuitableAVScore;
135
136 if (desc->flags & AV_PIX_FMT_FLAG_BE) // we don't want big endian formats
137 score -= 10;
138 if (desc->flags & AV_PIX_FMT_FLAG_PAL) // we don't want paletted formats
139 score -= 10000;
140 if (desc->flags & AV_PIX_FMT_FLAG_RGB) // we don't want RGB formats
141 score -= 1000;
142
143 return score;
144}
145
146auto targetSwFormatScoreCalculator(AVPixelFormat sourceFormat)
147{
148 const auto sourceSwFormatDesc = av_pix_fmt_desc_get(sourceFormat);
149 return [=](AVPixelFormat fmt) {
150 return scoreTargetSwFormat(sourceSwFormatDesc, fmt);
151 };
152}
153
154bool isHwFormatAcceptedByCodec(AVPixelFormat pixFormat)
155{
156 switch (pixFormat) {
157 case AV_PIX_FMT_MEDIACODEC:
158 // Mediacodec doesn't accept AV_PIX_FMT_MEDIACODEC (QTBUG-116836)
159 return false;
160 default:
161 return true;
162 }
163}
164
165// The sw formats commonly accepted by mpeg2, mpeg4, h264, h265, vp8, vp9 and av1 encoders.
166// Probing is only a fallback for codecs that tell us nothing, so the list is deliberately short;
167// scoreTargetSwFormat() takes care of ranking whatever we find.
168static constexpr AVPixelFormat probeCandidates[] = {
169 AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12, AV_PIX_FMT_NV21, AV_PIX_FMT_YUVJ420P,
170 AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_P010LE,
171 AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_GRAY8,
172};
173
174class ProbedPixelFormatCache
175{
176public:
177 std::vector<AVPixelFormat> pixelFormats(const Codec &codec, QSize resolution,
178 const HWAccel *accel);
179
180private:
181 std::optional<std::vector<AVPixelFormat>> lookup(const AVCodec *codec, QSize resolution,
182 AVPixelFormat hwFormat);
183
184 struct ProbeRecord
185 {
186 QSize resolution;
187 AVPixelFormat hwFormat = AV_PIX_FMT_NONE;
188 AVPixelFormat format = AV_PIX_FMT_NONE;
189 bool supported = false;
190 };
191
192 QReadWriteLock m_lock;
193 std::multimap<const AVCodec *, ProbeRecord> m_cache;
194};
195
196std::vector<AVPixelFormat>
197ProbedPixelFormatCache::pixelFormats(const Codec &codec, QSize resolution, const HWAccel *accel)
198{
199 const AVPixelFormat hwFormat = accel ? accel->hwFormat() : AV_PIX_FMT_NONE;
200
201 {
202 std::shared_lock locker(m_lock);
203 if (std::optional cached = lookup(codec.get(), resolution, hwFormat))
204 return *cached;
205 }
206
207 std::unique_lock locker(m_lock);
208 if (std::optional cached = lookup(codec.get(), resolution, hwFormat))
209 return *cached;
210
211 const std::vector<AVPixelFormat> supported = probePixelFormats(codec, resolution, accel);
212 qCDebug(qLcVideoEncoderUtils) << "Probed pixel formats for" << codec.name() << "at"
213 << resolution << "hwFormat" << hwFormat << ":" << supported;
214
215 using namespace QtMultimediaPrivate;
216 for (AVPixelFormat format : probeCandidates)
217 m_cache.emplace(codec.get(),
218 ProbeRecord{
219 resolution,
220 hwFormat,
221 format,
222 ranges::contains(supported, format),
223 });
224
225 return supported;
226}
227
228std::optional<std::vector<AVPixelFormat>>
229ProbedPixelFormatCache::lookup(const AVCodec *codec, QSize resolution, AVPixelFormat hwFormat)
230{
231 using namespace QtMultimediaPrivate;
232 namespace ranges = QtMultimediaPrivate::ranges;
233
234 auto range_pair = m_cache.equal_range(codec);
235 ranges::subrange<std::multimap<const AVCodec *, ProbeRecord>::iterator> range{
236 range_pair.first,
237 range_pair.second,
238 };
239
240 auto matches = [&](const auto &entry) {
241 return entry.second.resolution == resolution && entry.second.hwFormat == hwFormat;
242 };
243
244 if (!ranges::any_of(range, matches))
245 return std::nullopt;
246
247 return views::filter(range, [&](const auto &entry) {
248 return matches(entry) && entry.second.supported;
249 }) | views::transform([](const auto &entry) {
250 return entry.second.format;
251 }) | ranges::to<std::vector>();
252}
253
254Q_GLOBAL_STATIC(ProbedPixelFormatCache, probedPixelFormatCache)
255
256bool isProbingDisabled()
257{
258 static const bool disabled =
259 qEnvironmentVariableIntValue("QT_FFMPEG_DISABLE_ENCODER_PROBE") != 0;
260 return disabled;
261}
262
263AVBufferUPtr createProbeFramesContext(const HWAccel &accel, AVPixelFormat swFormat,
264 QSize resolution)
265{
266 AVBufferUPtr framesContext{ av_hwframe_ctx_alloc(accel.hwDeviceContextAsBuffer()) };
267 if (!framesContext)
268 return nullptr;
269
270 auto *context = reinterpret_cast<AVHWFramesContext *>(framesContext->data);
271 context->format = accel.hwFormat();
272 context->sw_format = swFormat;
273 context->width = resolution.width();
274 context->height = resolution.height();
275
276 if (av_hwframe_ctx_init(framesContext.get()) < 0)
277 return nullptr;
278
279 return framesContext;
280}
281
282// Some hw encoders (e.g. nvenc, qsv) advertise the sw formats they can consume directly in
283// codec.pixelFormats(), on top of their own hw pixel format. Others (e.g. vaapi) only ever
284// declare their own hw pixel format there and rely entirely on the hw accel's frame
285// constraints (valid_sw_formats) to describe the sw formats they accept.
286bool codecDeclaresSwFormats(const Codec &codec, QSize resolution, const HWAccel *accel)
287{
288 const std::vector<AVPixelFormat> formats = encoderPixelFormats(codec, resolution, accel);
289 return QtMultimediaPrivate::ranges::any_of(formats, [](AVPixelFormat fmt) {
290 return !isHwPixelFormat(fmt);
291 });
292}
293
294inline constexpr auto filterSuitablePixelFormats =
295 QtMultimediaPrivate::views::filter([](const ScoredPixelFormat &scoredFmt) {
296 return scoredFmt.score > AVScore::NotSuitableAVScore;
297});
298
299} // namespace
300
301std::vector<AVPixelFormat> probePixelFormats(const Codec &codec, QSize resolution,
302 const HWAccel *accel)
303{
304 std::vector<AVPixelFormat> result;
305
306 for (AVPixelFormat format : probeCandidates) {
307 AVCodecContextUPtr context{ avcodec_alloc_context3(codec.get()) };
308 if (!context) {
309 qCWarning(qLcVideoEncoderUtils)
310 << "Could not allocate codec context to probe" << codec.name();
311 break;
312 }
313
314 QSpan<const AVRational> supportedFrameRates = codec.frameRates();
315 const AVRational frameRate =
316 supportedFrameRates.empty() ? AVRational{ 25, 1 } : supportedFrameRates.front();
317
318 context->width = resolution.width();
319 context->height = resolution.height();
320 context->time_base = { frameRate.den, frameRate.num };
321 context->framerate = frameRate;
322
323 AVBufferUPtr framesContext;
324 if (accel) {
325 framesContext = createProbeFramesContext(*accel, format, resolution);
326 if (!framesContext)
327 continue;
328 context->hw_frames_ctx = av_buffer_ref(framesContext.get());
329 context->pix_fmt = accel->hwFormat();
330 } else {
331 context->pix_fmt = format;
332 }
333
334 AVDictionaryHolder opts;
335 applyExperimentalCodecOptions(codec, opts);
336
337 if (avcodec_open2(context.get(), codec.get(), opts) >= 0)
338 result.push_back(format);
339 }
340
341 return result;
342}
343
344std::vector<AVPixelFormat> encoderPixelFormats(const Codec &codec, QSize resolution,
345 const HWAccel *accel)
346{
347 // An empty result from FFmpeg means 'unknown', not 'nothing is supported', so fall back to
348 // asking the encoder itself rather than guessing.
349 const QSpan<const AVPixelFormat> declared = codec.pixelFormats();
350 if (!declared.empty() || isProbingDisabled())
351 return std::vector<AVPixelFormat>(declared.begin(), declared.end());
352
353 return probedPixelFormatCache->pixelFormats(codec, resolution, accel);
354}
355
356AVScore scoreTargetSwFormat(AVPixelFormat source, AVPixelFormat target)
357{
358 const auto *desc = av_pix_fmt_desc_get(source);
359 return desc ? scoreTargetSwFormat(desc, target) : AVScore::NotSuitableAVScore;
360}
361
362std::optional<AVPixelFormat> findTargetSWFormat(AVPixelFormat sourceSWFormat, const Codec &codec,
363 const HWAccel &accel, QSize resolution)
364{
365 using namespace QtMultimediaPrivate;
366
367 auto scoreTargetSwFormat = targetSwFormatScoreCalculator(sourceSWFormat);
368
369 const auto constraints = accel.constraints();
370 if (constraints && constraints->valid_sw_formats) {
371
372 const auto validSWFormatsForHWAccel =
373 makeSpan(constraints->valid_sw_formats) | ranges::to<QMinimalFlatSet>();
374
375 auto bestPixelFormat = [&]() -> std::optional<AVPixelFormat> {
376 if (codecDeclaresSwFormats(codec, resolution, &accel)) {
377 // If the codec declares sw formats, we can find the best one among the intersection
378 // of the codec's sw formats and the valid sw formats for the hw accel.
379 const std::vector<AVPixelFormat> codecPixelFormats =
380 encoderPixelFormats(codec, resolution, &accel);
381 auto formats = views::filter(codecPixelFormats, [&](AVPixelFormat fmt) {
382 return validSWFormatsForHWAccel.contains(fmt);
383 });
384
385 return findBestAVValue(formats, scoreTargetSwFormat);
386 } else {
387 return findBestAVValue(validSWFormatsForHWAccel, scoreTargetSwFormat);
388 }
389 }();
390
391 if (bestPixelFormat)
392 return bestPixelFormat;
393 }
394
395 // Some codecs, e.g. mediacodec, don't expose constraints, let's find the format in
396 // codec->pix_fmts (avcodec_get_supported_config with AV_CODEC_CONFIG_PIX_FORMAT since n7.1),
397 // or, if the codec declares nothing at all, in the probed formats.
398 const std::vector<AVPixelFormat> codecPixelFormats =
399 encoderPixelFormats(codec, resolution, &accel);
400 return findBestAVValue(codecPixelFormats, scoreTargetSwFormat);
401}
402
404 const Codec &codec,
405 const HWAccel *accel, QSize resolution)
406{
407 if constexpr (false) {
408 qDebug() << "findAndScoreTargetSWFormats" << codec.name() << codec.id();
409 }
410
411 using namespace QtMultimediaPrivate;
412
413 auto scoreTargetSwFormat = targetSwFormatScoreCalculator(sourceSWFormat);
414
415 auto score = views::transform([&](AVPixelFormat arg) {
416 return ScoredPixelFormat{ arg, scoreTargetSwFormat(arg) };
417 });
418
419 std::vector<ScoredPixelFormat> scoredPixelFormats = [&] {
420 const AVHWFramesConstraints *constraints = accel ? accel->constraints() : nullptr;
421 if (constraints && constraints->valid_sw_formats) {
422 const auto validSWFormatsForHWAccel =
423 makeSpan(constraints->valid_sw_formats) | ranges::to<QMinimalFlatSet>();
424
425 if (codecDeclaresSwFormats(codec, resolution, accel)) {
426 // If the codec declares sw formats, we can find the best one among the intersection
427 // of the codec's sw formats and the valid sw formats for the hw accel.
428 const std::vector<AVPixelFormat> codecPixelFormats =
429 encoderPixelFormats(codec, resolution, accel);
430 auto validCodecPixelFormats =
431 views::filter(codecPixelFormats, [&](AVPixelFormat fmt) {
432 return validSWFormatsForHWAccel.contains(fmt);
433 });
434 return validCodecPixelFormats | score | filterSuitablePixelFormats
435 | ranges::to<std::vector>();
436 } else {
437 // The codec only declares its own hw pixel format (e.g. vaapi); the sw formats it
438 // actually accepts come solely from the hw accel's frame constraints.
439 return validSWFormatsForHWAccel | score | filterSuitablePixelFormats
440 | ranges::to<std::vector>();
441 }
442 } else {
443 // Some codecs, e.g. mediacodec, don't expose constraints, and pure software
444 // codecs (accel == nullptr) never have constraints at all; let's find the
445 // format in codec->pix_fmts (avcodec_get_supported_config with
446 // AV_CODEC_CONFIG_PIX_FORMAT since n7.1), or, if the codec declares nothing at
447 // all, in the probed formats.
448
449 const std::vector<AVPixelFormat> codecPixelFormats =
450 encoderPixelFormats(codec, resolution, accel);
451 return codecPixelFormats | score | filterSuitablePixelFormats
452 | ranges::to<std::vector>();
453 }
454 }();
455
456 ranges::sort(scoredPixelFormats, [](const ScoredPixelFormat &a, const ScoredPixelFormat &b) {
457 return a.score > b.score;
458 });
459
460 return scoredPixelFormats;
461}
462
463std::optional<AVPixelFormat> findTargetFormat(AVPixelFormat sourceSWFormat, const Codec &codec,
464 const HWAccel *accel, QSize resolution)
465{
466 using namespace QtMultimediaPrivate;
467
468 if (accel) {
469 const auto hwFormat = accel->hwFormat();
470
471 // TODO: handle codec->capabilities & AV_CODEC_CAP_HARDWARE here
472 if (!isHwFormatAcceptedByCodec(hwFormat))
473 return findTargetSWFormat(sourceSWFormat, codec, *accel, resolution);
474
475 const auto constraints = accel->constraints();
476 if (constraints && ranges::contains(makeSpan(constraints->valid_hw_formats), hwFormat))
477 return hwFormat;
478
479 // Some codecs, don't expose constraints,
480 // let's find the format in codec->pix_fmts (avcodec_get_supported_config with
481 // AV_CODEC_CONFIG_PIX_FORMAT since n7.1) and hw_config
482 if (isAVFormatSupported(codec, hwFormat))
483 return hwFormat;
484 }
485
486 const auto pixelFormats = encoderPixelFormats(codec, resolution, nullptr);
487 if (pixelFormats.empty()) {
488 qCWarning(qLcVideoEncoderUtils)
489 << "Codec pix formats are undefined and probing" << codec.name()
490 << "found none either, it's likely to behave incorrectly";
491
492 return sourceSWFormat;
493 }
494
495 auto swScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
496 return findBestAVValue(pixelFormats, swScoreCalculator);
497}
498
499AVScore findSWFormatScores(const Codec &codec, AVPixelFormat sourceSWFormat, QSize resolution)
500{
501 const auto pixelFormats = encoderPixelFormats(codec, resolution, nullptr);
502 if (pixelFormats.empty())
503 // codecs that neither declare pixel formats nor accept any of the probed ones
504 // are suspicious
505 return MinAVScore;
506
507 auto formatScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
508 std::optional bestFormatWithScore =
509 findBestAVValueWithScore(pixelFormats, formatScoreCalculator);
510 if (bestFormatWithScore)
511 return bestFormatWithScore->score;
512 else
513 return MinAVScore;
514}
515
516AVRational adjustFrameRate(QSpan<const AVRational> supportedRates, qreal settingsRate,
517 qreal sourceRate)
518{
519 qreal preferredRate = 0.;
520 if (settingsRate > 0)
521 preferredRate = settingsRate;
522 else if (sourceRate > 0)
523 preferredRate = sourceRate;
524 else if (supportedRates.empty())
525 preferredRate = 0.;
526 else
527 preferredRate = qreal(DefaultVideoFrameRate);
528
529 auto calcScore = [preferredRate](const AVRational &rate) {
530 // relative comparison
531 return qMin(preferredRate * rate.den, qreal(rate.num))
532 / qMax(preferredRate * rate.den, qreal(rate.num));
533 };
534
535 const auto result = findBestAVValue(supportedRates, calcScore);
536 if (result && result->num && result->den)
537 return *result;
538
539 const auto [num, den] = qRealToFraction(preferredRate);
540 return { num, den };
541}
542
543AVRational adjustFrameTimeBase(QSpan<const AVRational> supportedRates, AVRational frameRate,
544 bool isFixedRate)
545{
546 // TODO: user-specified frame rate might be required.
547 if (!supportedRates.empty()) {
548 auto hasFrameRate = [&]() {
549 for (AVRational rate : supportedRates)
550 if (rate.den == frameRate.den && rate.num == frameRate.num)
551 return true;
552
553 return false;
554 };
555
556 Q_ASSERT(hasFrameRate());
557
558 return { frameRate.den, frameRate.num };
559 }
560
561 if (isFixedRate)
562 return { frameRate.den, frameRate.num };
563
564 constexpr int TimeScaleFactor = 1000; // Allows not to follow fixed rate
565 return { frameRate.den, frameRate.num * TimeScaleFactor };
566}
567
568QSize adjustVideoResolution(const Codec &codec, QSize requestedResolution)
569{
570 if constexpr (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows) {
571 // TODO: investigate, there might be more encoders not supporting odd resolution
572 if (codec.name() == "h264_mf"_L1) {
573 auto makeEven = [](int size) { return size & ~1; };
574 return QSize(makeEven(requestedResolution.width()), makeEven(requestedResolution.height()));
575 }
576 }
577 return requestedResolution;
578}
579
580SwsFlags getScaleConversionType(const QSize &sourceSize, const QSize &targetSize)
581{
582 SwsFlags conversionType = SWS_FAST_BILINEAR;
583
584 if constexpr (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Android) {
585 // On Android, use SWS_BICUBIC for upscaling if least one dimension is upscaled
586 // to avoid a crash caused by ff_hcscale_fast_c with SWS_FAST_BILINEAR.
587 if (targetSize.width() > sourceSize.width() || targetSize.height() > sourceSize.height())
588 conversionType = SWS_BICUBIC;
589 }
590
591 return conversionType;
592}
593
594} // namespace QFFmpeg
595
596QT_END_NAMESPACE
std::vector< ScoredPixelFormat > findAndScoreTargetSWFormats(AVPixelFormat sourceSWFormat, const Codec &codec, const HWAccel *accel, QSize resolution)
SwsFlags getScaleConversionType(const QSize &sourceSize, const QSize &targetSize)
QSize adjustVideoResolution(const Codec &codec, QSize requestedResolution)
std::optional< AVPixelFormat > findTargetFormat(AVPixelFormat sourceSWFormat, const Codec &codec, const HWAccel *accel, QSize resolution)
std::vector< AVPixelFormat > encoderPixelFormats(const Codec &codec, QSize resolution, const HWAccel *accel)
AVRational adjustFrameRate(QSpan< const AVRational > supportedRates, qreal settingsRate, qreal sourceRate)
adjustFrameRate resolves the effective frame rate to negotiate with a codec.
AVScore scoreTargetSwFormat(AVPixelFormat source, AVPixelFormat target)
std::optional< AVPixelFormat > findTargetSWFormat(AVPixelFormat sourceSWFormat, const Codec &codec, const HWAccel &accel, QSize resolution)
AVRational adjustFrameTimeBase(QSpan< const AVRational > supportedRates, AVRational frameRate, bool isFixedRate)
adjustFrameTimeBase gets adjusted timebase by a list of supported frame rates and an already adjusted...
AVScore findSWFormatScores(const Codec &codec, AVPixelFormat sourceSWFormat, QSize resolution)
std::vector< AVPixelFormat > probePixelFormats(const Codec &codec, QSize resolution, const HWAccel *accel)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)