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