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
qffmpegvideoframeencoder.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
9
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/private/qexpected_p.h>
12
13extern "C" {
14#include "libavutil/display.h"
15#include "libavutil/pixdesc.h"
16}
17
19
20Q_STATIC_LOGGING_CATEGORY(qLcVideoFrameEncoder, "qt.multimedia.ffmpeg.videoencoder");
21
22namespace QFFmpeg {
23
24namespace {
25
26AVCodecID avCodecID(const QMediaEncoderSettings &settings)
27{
28 const QMediaFormat::VideoCodec qVideoCodec = settings.videoCodec();
29 return QFFmpegMediaFormatInfo::codecIdForVideoCodec(qVideoCodec);
30}
31
32} // namespace
33
34VideoFrameEncoderUPtr VideoFrameEncoder::create(const QMediaEncoderSettings &encoderSettings,
35 const SourceParams &sourceParams,
36 AVFormatContext *formatContext)
37{
38 Q_ASSERT(isSwPixelFormat(sourceParams.swFormat));
39 Q_ASSERT(isHwPixelFormat(sourceParams.format) || sourceParams.swFormat == sourceParams.format);
40
41 AVStream *stream = createStream(sourceParams, formatContext);
42
43 if (!stream)
44 return nullptr;
45
46 CreationResult result;
47
48 auto findAndOpenAVEncoder = [&](const auto &scoresGetter, const auto &creator) {
49 auto createWithTargetFormatFallback = [&](const Codec &codec) {
50 result = creator(codec, AVPixelFormatSet{});
51#ifdef Q_OS_ANDROID
52 // On Android some encoders fail to open encoders with 4:2:0 formats unless it's NV12.
53 // Let's fallback to another format.
54 if (!result.encoder) {
55 const auto targetFormatDesc = av_pix_fmt_desc_get(result.targetFormat);
56 const bool is420TargetFormat = targetFormatDesc
57 && targetFormatDesc->log2_chroma_h == 1
58 && targetFormatDesc->log2_chroma_w == 1;
59 if (is420TargetFormat && result.targetFormat != AV_PIX_FMT_NV12)
60 result = creator(codec, AVPixelFormatSet{ result.targetFormat });
61 }
62#endif
63 return result.encoder != nullptr;
64 };
65 return QFFmpeg::findAndOpenAVEncoder(avCodecID(encoderSettings), scoresGetter,
66 createWithTargetFormatFallback);
67 };
68
69 {
70 const auto &deviceTypes = HWAccel::encodingDeviceTypes();
71
72 auto findDeviceType = [&](const Codec &codec) {
73 std::optional<AVPixelFormat> pixelFormat = findAVPixelFormat(codec, &isHwPixelFormat);
74 if (!pixelFormat)
75 return deviceTypes.end();
76
77 return std::find_if(deviceTypes.begin(), deviceTypes.end(),
78 [pixelFormat](AVHWDeviceType deviceType) {
79 return pixelFormatForHwDevice(deviceType) == pixelFormat;
80 });
81 };
82
83 findAndOpenAVEncoder(
84 [&](const Codec &codec) {
85 const auto found = findDeviceType(codec);
86 if (found == deviceTypes.end())
87 return NotSuitableAVScore;
88
89 return DefaultAVScore - static_cast<AVScore>(found - deviceTypes.begin());
90 },
91 [&](const Codec &codec, const AVPixelFormatSet &prohibitedTargetFormats) {
92 HWAccelUPtr hwAccel = HWAccel::create(*findDeviceType(codec));
93 if (!hwAccel)
94 return CreationResult{};
95 if (!hwAccel->matchesSizeContraints(encoderSettings.videoResolution()))
96 return CreationResult{};
97 return create(stream, codec, std::move(hwAccel), sourceParams, encoderSettings,
98 prohibitedTargetFormats);
99 });
100 }
101
102 if (!result.encoder) {
103 findAndOpenAVEncoder(
104 [&](const Codec &codec) {
105 return findSWFormatScores(codec, sourceParams.swFormat);
106 },
107 [&](const Codec &codec, const AVPixelFormatSet &prohibitedTargetFormats) {
108 return create(stream, codec, nullptr, sourceParams, encoderSettings,
109 prohibitedTargetFormats);
110 });
111 }
112
113 if (auto &encoder = result.encoder)
114 qCDebug(qLcVideoFrameEncoder)
115 << "found" << (encoder->m_accel ? "hw" : "sw") << "encoder"
116 << encoder->m_codec.name() << "for id" << encoder->m_codec.id();
117 else
118 qCWarning(qLcVideoFrameEncoder) << "No valid video codecs found";
119
120 return std::move(result.encoder);
121}
122
123VideoFrameEncoder::VideoFrameEncoder(AVStream *stream, const Codec &codec, HWAccelUPtr hwAccel,
124 const SourceParams &sourceParams,
125 const QMediaEncoderSettings &encoderSettings)
126 : m_settings(encoderSettings),
127 m_stream(stream),
128 m_codec(codec),
129 m_accel(std::move(hwAccel)),
130 m_sourceSize(sourceParams.size),
131 m_sourceFormat(sourceParams.format),
132 m_sourceSWFormat(sourceParams.swFormat)
133{
134}
135
136AVStream *VideoFrameEncoder::createStream(const SourceParams &sourceParams,
137 AVFormatContext *formatContext)
138{
139 AVStream *stream = avformat_new_stream(formatContext, nullptr);
140
141 if (!stream)
142 return stream;
143
144 stream->id = formatContext->nb_streams - 1;
145 stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
146
147 stream->codecpar->color_trc = sourceParams.colorTransfer;
148 stream->codecpar->color_space = sourceParams.colorSpace;
149 stream->codecpar->color_range = sourceParams.colorRange;
150
151 if (sourceParams.transform.rotation != QtVideo::Rotation::None || sourceParams.transform.mirroredHorizontallyAfterRotation) {
152 constexpr auto displayMatrixSize = sizeof(int32_t) * 9;
153 AVPacketSideData sideData = { reinterpret_cast<uint8_t *>(av_malloc(displayMatrixSize)),
154 displayMatrixSize, AV_PKT_DATA_DISPLAYMATRIX };
155 int32_t *matrix = reinterpret_cast<int32_t *>(sideData.data);
156 av_display_rotation_set(matrix, static_cast<double>(sourceParams.transform.rotation));
157 if (sourceParams.transform.mirroredHorizontallyAfterRotation)
158 av_display_matrix_flip(matrix, sourceParams.transform.mirroredHorizontallyAfterRotation, false);
159
160 addStreamSideData(stream, sideData);
161 }
162
163 return stream;
164}
165
166VideoFrameEncoder::CreationResult
167VideoFrameEncoder::create(AVStream *stream, const Codec &codec, HWAccelUPtr hwAccel,
168 const SourceParams &sourceParams,
169 const QMediaEncoderSettings &encoderSettings,
170 const AVPixelFormatSet &prohibitedTargetFormats)
171{
172 VideoFrameEncoderUPtr frameEncoder(new VideoFrameEncoder(stream, codec, std::move(hwAccel),
173 sourceParams, encoderSettings));
174 frameEncoder->initTargetSize();
175
176 frameEncoder->initCodecFrameRate();
177
178 if (!frameEncoder->initTargetFormats(prohibitedTargetFormats))
179 return {};
180
181 frameEncoder->initStream();
182
183 const AVPixelFormat targetFormat = frameEncoder->m_targetFormat;
184
185 if (!frameEncoder->initCodecContext())
186 return { nullptr, targetFormat };
187
188 if (!frameEncoder->open())
189 return { nullptr, targetFormat };
190
191 frameEncoder->updateConversions();
192
193 return { std::move(frameEncoder), targetFormat };
194}
195
196void VideoFrameEncoder::initTargetSize()
197{
198 m_targetSize = adjustVideoResolution(m_codec, m_settings.videoResolution());
199
200#ifdef Q_OS_WINDOWS
201 // TODO: investigate, there might be more encoders not supporting odd resolution
202 if (m_codec.name() == u"h264_mf") {
203 auto makeEven = [](int size) { return size & ~1; };
204 const QSize fixedSize(makeEven(m_targetSize.width()), makeEven(m_targetSize.height()));
205 if (fixedSize != m_targetSize) {
206 qCDebug(qLcVideoFrameEncoder) << "Fix odd video resolution for codec" << m_codec.name()
207 << ":" << m_targetSize << "->" << fixedSize;
208 m_targetSize = fixedSize;
209 }
210 }
211#endif
212}
213
214void VideoFrameEncoder::initCodecFrameRate()
215{
216 const auto frameRates = m_codec.frameRates();
217 if (qLcVideoFrameEncoder().isEnabled(QtDebugMsg))
218 for (AVRational rate : frameRates)
219 qCDebug(qLcVideoFrameEncoder) << "supported frame rate:" << rate;
220
221 m_codecFrameRate = adjustFrameRate(frameRates, m_settings.videoFrameRate());
222 qCDebug(qLcVideoFrameEncoder) << "Adjusted frame rate:" << m_codecFrameRate;
223}
224
225bool VideoFrameEncoder::initTargetFormats(const AVPixelFormatSet &prohibitedTargetFormats)
226{
227 const auto format = findTargetFormat(m_sourceFormat, m_sourceSWFormat, m_codec, m_accel.get(),
228 prohibitedTargetFormats);
229
230 if (!format) {
231 qWarning() << "Could not find target format for codecId" << m_codec.id();
232 return false;
233 }
234
235 m_targetFormat = *format;
236
237 if (isHwPixelFormat(m_targetFormat)) {
238 Q_ASSERT(m_accel);
239
240 // don't pass prohibitedTargetFormats here as m_targetSWFormat is the format,
241 // from which we load a hardware texture, and the format doesn't impact on encoding.
242 const auto swFormat = findTargetSWFormat(m_sourceSWFormat, m_codec, *m_accel);
243 if (!swFormat) {
244 qWarning() << "Cannot find software target format. sourceSWFormat:" << m_sourceSWFormat
245 << "targetFormat:" << m_targetFormat;
246 return false;
247 }
248
249 m_targetSWFormat = *swFormat;
250
251 m_accel->createFramesContext(m_targetSWFormat, m_targetSize);
252 if (!m_accel->hwFramesContextAsBuffer())
253 return false;
254 } else {
255 m_targetSWFormat = m_targetFormat;
256 }
257
258 return true;
259}
260
262
263void VideoFrameEncoder::initStream()
264{
265 m_stream->codecpar->codec_id = m_codec.id();
266
267 // Apples HEVC decoders don't like the hev1 tag ffmpeg uses by default, use hvc1 as the more
268 // commonly accepted tag
269 if (m_codec.id() == AV_CODEC_ID_HEVC)
270 m_stream->codecpar->codec_tag = MKTAG('h', 'v', 'c', '1');
271 else
272 m_stream->codecpar->codec_tag = 0;
273
274 // ### Fix hardcoded values
275 m_stream->codecpar->format = m_targetFormat;
276 m_stream->codecpar->width = m_targetSize.width();
277 m_stream->codecpar->height = m_targetSize.height();
278 m_stream->codecpar->sample_aspect_ratio = AVRational{ 1, 1 };
279#if QT_CODEC_PARAMETERS_HAVE_FRAMERATE
280 m_stream->codecpar->framerate = m_codecFrameRate;
281#endif
282
283 const auto frameRates = m_codec.frameRates();
284 m_stream->time_base = adjustFrameTimeBase(frameRates, m_codecFrameRate);
285}
286
287bool VideoFrameEncoder::initCodecContext()
288{
289 Q_ASSERT(m_stream->codecpar->codec_id);
290
291 m_codecContext.reset(avcodec_alloc_context3(m_codec.get()));
292 if (!m_codecContext) {
293 qWarning() << "Could not allocate codec context";
294 return false;
295 }
296
297 // copies format, size, color params, framerate
298 avcodec_parameters_to_context(m_codecContext.get(), m_stream->codecpar);
299#if !QT_CODEC_PARAMETERS_HAVE_FRAMERATE
300 m_codecContext->framerate = m_codecFrameRate;
301#endif
302 m_codecContext->time_base = m_stream->time_base;
303 qCDebug(qLcVideoFrameEncoder) << "codecContext time base" << m_codecContext->time_base.num
304 << m_codecContext->time_base.den;
305
306 if (m_accel) {
307 auto deviceContext = m_accel->hwDeviceContextAsBuffer();
308 Q_ASSERT(deviceContext);
309 m_codecContext->hw_device_ctx = av_buffer_ref(deviceContext);
310
311 if (auto framesContext = m_accel->hwFramesContextAsBuffer())
312 m_codecContext->hw_frames_ctx = av_buffer_ref(framesContext);
313 }
314
315 avcodec_parameters_from_context(m_stream->codecpar, m_codecContext.get());
316
317 return true;
318}
319
320bool VideoFrameEncoder::open()
321{
322 Q_ASSERT(m_codecContext);
323
324 AVDictionaryHolder opts;
325 applyVideoEncoderOptions(m_settings, QByteArray{ m_codec.name() }, m_codecContext.get(), opts);
326 applyExperimentalCodecOptions(m_codec, opts);
327
328 qCDebug(qLcVideoFrameEncoder) << "Opening encoder" << m_codec.name() << "with" << opts;
329
330 const int res = avcodec_open2(m_codecContext.get(), m_codec.get(), opts);
331 if (res < 0) {
332 qCWarning(qLcVideoFrameEncoder)
333 << "Couldn't open video encoder" << m_codec.name() << "; result:" << AVError(res);
334 return false;
335 }
336 qCDebug(qLcVideoFrameEncoder) << "video codec opened" << res << "time base"
337 << m_codecContext->time_base;
338 return true;
339}
340
342{
343 return m_codecFrameRate.den ? qreal(m_codecFrameRate.num) / m_codecFrameRate.den : 0.;
344}
345
347{
348 qint64 div = 1'000'000 * m_stream->time_base.num;
349 return div != 0 ? (us * m_stream->time_base.den + div / 2) / div : 0;
350}
351
353{
354 return m_stream->time_base;
355}
356
357namespace {
358struct FrameConverter
359{
360 FrameConverter(AVFrameUPtr inputFrame) : m_inputFrame{ std::move(inputFrame) } { }
361
362 int downloadFromHw()
363 {
364 AVFrameUPtr cpuFrame = makeAVFrame();
365
366 int err = av_hwframe_transfer_data(cpuFrame.get(), currentFrame(), 0);
367 if (err < 0) {
368 qCDebug(qLcVideoFrameEncoder)
369 << "Error transferring frame data to surface." << AVError(err);
370 return err;
371 }
372
373 setFrame(std::move(cpuFrame));
374 return 0;
375 }
376
377 void convert(SwsContext *scaleContext, AVPixelFormat format, const QSize &size)
378 {
379 AVFrameUPtr scaledFrame = makeAVFrame();
380
381 scaledFrame->format = format;
382 scaledFrame->width = size.width();
383 scaledFrame->height = size.height();
384
385 av_frame_get_buffer(scaledFrame.get(), 0);
386
387 const AVFrame *srcFrame = currentFrame();
388
389 const auto scaledHeight =
390 sws_scale(scaleContext, srcFrame->data, srcFrame->linesize, 0, srcFrame->height,
391 scaledFrame->data, scaledFrame->linesize);
392
393 if (scaledHeight != scaledFrame->height)
394 qCWarning(qLcVideoFrameEncoder)
395 << "Scaled height" << scaledHeight << "!=" << scaledFrame->height;
396
397 setFrame(std::move(scaledFrame));
398 }
399
400 int uploadToHw(HWAccel *accel)
401 {
402 auto *hwFramesContext = accel->hwFramesContextAsBuffer();
403 Q_ASSERT(hwFramesContext);
404 AVFrameUPtr hwFrame = makeAVFrame();
405 if (!hwFrame)
406 return AVERROR(ENOMEM);
407
408 int err = av_hwframe_get_buffer(hwFramesContext, hwFrame.get(), 0);
409 if (err < 0) {
410 qCDebug(qLcVideoFrameEncoder) << "Error getting HW buffer" << AVError(err);
411 return err;
412 } else {
413 qCDebug(qLcVideoFrameEncoder) << "got HW buffer";
414 }
415 if (!hwFrame->hw_frames_ctx) {
416 qCDebug(qLcVideoFrameEncoder) << "no hw frames context";
417 return AVERROR(ENOMEM);
418 }
419 err = av_hwframe_transfer_data(hwFrame.get(), currentFrame(), 0);
420 if (err < 0) {
421 qCDebug(qLcVideoFrameEncoder)
422 << "Error transferring frame data to surface." << AVError(err);
423 return err;
424 }
425
426 setFrame(std::move(hwFrame));
427
428 return 0;
429 }
430
431 q23::expected<AVFrameUPtr, int> takeResultFrame()
432 {
433 // Ensure that object is reset to empty state
434 AVFrameUPtr converted = std::move(m_convertedFrame);
435 AVFrameUPtr input = std::move(m_inputFrame);
436
437 if (!converted)
438 return input;
439
440 // Copy metadata except size and format from input frame
441 const int status = av_frame_copy_props(converted.get(), input.get());
442 if (status != 0)
443 return q23::unexpected{ status };
444
445 return converted;
446 }
447
448private:
449 void setFrame(AVFrameUPtr frame) { m_convertedFrame = std::move(frame); }
450
451 AVFrame *currentFrame() const
452 {
453 if (m_convertedFrame)
454 return m_convertedFrame.get();
455 return m_inputFrame.get();
456 }
457
458 AVFrameUPtr m_inputFrame;
459 AVFrameUPtr m_convertedFrame;
460};
461} // namespace
462
463int VideoFrameEncoder::sendFrame(AVFrameUPtr inputFrame)
464{
465 if (!m_codecContext) {
466 qWarning() << "codec context is not initialized!";
467 return AVERROR(EINVAL);
468 }
469
470 if (!inputFrame)
471 return avcodec_send_frame(m_codecContext.get(), nullptr); // Flush
472
473 if (!updateSourceFormatAndSize(inputFrame.get()))
474 return AVERROR(EINVAL);
475
476 FrameConverter converter{ std::move(inputFrame) };
477
478 if (m_downloadFromHW) {
479 const int status = converter.downloadFromHw();
480 if (status != 0)
481 return status;
482 }
483
484 if (m_scaleContext)
485 converter.convert(m_scaleContext.get(), m_targetSWFormat, m_targetSize);
486
487 if (m_uploadToHW) {
488 const int status = converter.uploadToHw(m_accel.get());
489 if (status != 0)
490 return status;
491 }
492
493 const q23::expected<AVFrameUPtr, int> resultFrame = converter.takeResultFrame();
494 if (!resultFrame)
495 return resultFrame.error();
496
497 AVRational timeBase{};
498 int64_t pts{};
499 getAVFrameTime(*resultFrame.value(), pts, timeBase);
500 qCDebug(qLcVideoFrameEncoder) << "sending frame" << pts << "*" << timeBase;
501
502 return avcodec_send_frame(m_codecContext.get(), resultFrame.value().get());
503}
504
505qint64 VideoFrameEncoder::estimateDuration(const AVPacket &packet, bool isFirstPacket)
506{
507 qint64 duration = 0; // In stream units, multiply by time_base to get seconds
508
509 if (isFirstPacket) {
510 // First packet - Estimate duration from frame rate. Duration must
511 // be set for single-frame videos, otherwise they won't open in
512 // media player.
513 const AVRational frameDuration = av_inv_q(m_codecContext->framerate);
514 duration = av_rescale_q(1, frameDuration, m_stream->time_base);
515 } else {
516 // Duration is calculated from actual packet times. TODO: Handle discontinuities
517 duration = packet.pts - m_lastPacketTime;
518 }
519
520 return duration;
521}
522
524{
525 if (!m_codecContext)
526 return nullptr;
527
528 auto getPacket = [&]() {
529 AVPacketUPtr packet(av_packet_alloc());
530 const int ret = avcodec_receive_packet(m_codecContext.get(), packet.get());
531 if (ret < 0) {
532 if (ret != AVERROR(EOF) && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
533 qCDebug(qLcVideoFrameEncoder) << "Error receiving packet" << ret << AVError(ret);
534 return AVPacketUPtr{};
535 }
536 auto ts = timeStampMs(packet->pts, m_stream->time_base);
537
538 qCDebug(qLcVideoFrameEncoder)
539 << "got a packet" << packet->pts << packet->dts << (ts ? *ts : 0);
540
541 packet->stream_index = m_stream->id;
542
543 if (packet->duration == 0) {
544 const bool firstFrame = m_lastPacketTime == AV_NOPTS_VALUE;
545 packet->duration = estimateDuration(*packet, firstFrame);
546 }
547
548 m_lastPacketTime = packet->pts;
549
550 return packet;
551 };
552
553 auto fixPacketDts = [&](AVPacket &packet) {
554 // Workaround for some ffmpeg codecs bugs (e.g. nvenc)
555 // Ideally, packet->pts < packet->dts is not expected
556
557 if (packet.dts == AV_NOPTS_VALUE)
558 return true;
559
560 packet.dts -= m_packetDtsOffset;
561
562 if (packet.pts != AV_NOPTS_VALUE && packet.pts < packet.dts) {
563 m_packetDtsOffset += packet.dts - packet.pts;
564 packet.dts = packet.pts;
565
566 if (m_prevPacketDts != AV_NOPTS_VALUE && packet.dts < m_prevPacketDts) {
567 qCWarning(qLcVideoFrameEncoder)
568 << "Skip packet; failed to fix dts:" << packet.dts << m_prevPacketDts;
569 return false;
570 }
571 }
572
573 m_prevPacketDts = packet.dts;
574
575 return true;
576 };
577
578 while (auto packet = getPacket()) {
579 if (fixPacketDts(*packet))
580 return packet;
581 }
582
583 return nullptr;
584}
585
586bool VideoFrameEncoder::updateSourceFormatAndSize(const AVFrame *frame)
587{
588 Q_ASSERT(frame);
589
590 const QSize frameSize(frame->width, frame->height);
591 const AVPixelFormat frameFormat = static_cast<AVPixelFormat>(frame->format);
592
593 if (frameSize == m_sourceSize && frameFormat == m_sourceFormat)
594 return true;
595
596 auto applySourceFormatAndSize = [&](AVPixelFormat swFormat) {
597 m_sourceSize = frameSize;
598 m_sourceFormat = frameFormat;
599 m_sourceSWFormat = swFormat;
600 updateConversions();
601 return true;
602 };
603
604 if (frameFormat == m_sourceFormat)
605 return applySourceFormatAndSize(m_sourceSWFormat);
606
607 if (frameFormat == AV_PIX_FMT_NONE) {
608 qWarning() << "Got a frame with invalid pixel format";
609 return false;
610 }
611
612 if (isSwPixelFormat(frameFormat))
613 return applySourceFormatAndSize(frameFormat);
614
615 auto framesCtx = reinterpret_cast<const AVHWFramesContext *>(frame->hw_frames_ctx->data);
616 if (!framesCtx || framesCtx->sw_format == AV_PIX_FMT_NONE) {
617 qWarning() << "Cannot update conversions as hw frame has invalid framesCtx" << framesCtx;
618 return false;
619 }
620
621 return applySourceFormatAndSize(framesCtx->sw_format);
622}
623
624void VideoFrameEncoder::updateConversions()
625{
626 const bool needToScale = m_sourceSize != m_targetSize;
627 const bool zeroCopy = m_sourceFormat == m_targetFormat && !needToScale;
628
629 m_scaleContext.reset();
630
631 if (zeroCopy) {
632 m_downloadFromHW = false;
633 m_uploadToHW = false;
634
635 qCDebug(qLcVideoFrameEncoder) << "zero copy encoding, format" << m_targetFormat;
636 // no need to initialize any converters
637 return;
638 }
639
640 m_downloadFromHW = m_sourceFormat != m_sourceSWFormat;
641 m_uploadToHW = m_targetFormat != m_targetSWFormat;
642
643 if (m_sourceSWFormat != m_targetSWFormat || needToScale) {
644 qCDebug(qLcVideoFrameEncoder)
645 << "video source and encoder use different formats:" << m_sourceSWFormat
646 << m_targetSWFormat << "or sizes:" << m_sourceSize << m_targetSize;
647
648 const SwsFlags conversionType = getScaleConversionType(m_sourceSize, m_targetSize);
649
650 m_scaleContext = createSwsContext(m_sourceSize, m_sourceSWFormat, m_targetSize,
651 m_targetSWFormat, conversionType);
652 }
653
654 qCDebug(qLcVideoFrameEncoder) << "VideoFrameEncoder conversions initialized:"
655 << "sourceFormat:" << m_sourceFormat
656 << (isHwPixelFormat(m_sourceFormat) ? "(hw)" : "(sw)")
657 << "targetFormat:" << m_targetFormat
658 << (isHwPixelFormat(m_targetFormat) ? "(hw)" : "(sw)")
659 << "sourceSWFormat:" << m_sourceSWFormat
660 << "targetSWFormat:" << m_targetSWFormat
661 << "scaleContext:" << m_scaleContext.get();
662}
663
664} // namespace QFFmpeg
665
666QT_END_NAMESPACE
const AVRational & getTimeBase() const
int sendFrame(AVFrameUPtr inputFrame)
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")