1// Copyright (C) 2021 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
4#include "playbackengine/qffmpegcodeccontext_p.h"
5#include "qffmpegcodecstorage_p.h"
6
7#include <QtCore/qloggingcategory.h>
8
9QT_BEGIN_NAMESPACE
10
11using namespace Qt::StringLiterals;
12
13static Q_LOGGING_CATEGORY(qLcPlaybackEngineCodec, "qt.multimedia.playbackengine.codec");
14
15namespace QFFmpeg {
16
17CodecContext::Data::Data(AVCodecContextUPtr context, AVStream *avStream,
18 AVFormatContext *avFormatContext,
19 std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
20 : context(std::move(context)),
21 stream(avStream),
22 formatContext(avFormatContext),
23 hwAccel(std::move(hwAccel))
24{
25 if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
26 pixelAspectRatio = av_guess_sample_aspect_ratio(format: formatContext, stream, frame: nullptr);
27}
28
29QMaybe<CodecContext> CodecContext::create(AVStream *stream, AVFormatContext *formatContext)
30{
31 if (!stream)
32 return { u"Invalid stream"_s };
33
34 if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
35 auto hwCodec = create(stream, formatContext, videoCodecPolicy: Hw);
36 if (hwCodec)
37 return hwCodec;
38
39 qCInfo(qLcPlaybackEngineCodec) << hwCodec.error();
40 }
41
42 auto context = create(stream, formatContext, videoCodecPolicy: Sw);
43 if (!context)
44 qCWarning(qLcPlaybackEngineCodec) << context.error();
45
46 return context;
47}
48
49AVRational CodecContext::pixelAspectRatio(AVFrame *frame) const
50{
51 // does the same as av_guess_sample_aspect_ratio, but more efficient
52 return d->pixelAspectRatio.num && d->pixelAspectRatio.den ? d->pixelAspectRatio
53 : frame->sample_aspect_ratio;
54}
55
56QMaybe<CodecContext> CodecContext::create(AVStream *stream, AVFormatContext *formatContext,
57 VideoCodecCreationPolicy videoCodecPolicy)
58{
59 Q_ASSERT(stream);
60
61 if (videoCodecPolicy == Hw && stream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
62 Q_ASSERT(!"Codec::create has been called with Hw policy on a non-video stream");
63
64 std::optional<Codec> decoder;
65 std::unique_ptr<QFFmpeg::HWAccel> hwAccel;
66
67 if (videoCodecPolicy == Hw)
68 std::tie(args&: decoder, args&: hwAccel) = HWAccel::findDecoderWithHwAccel(id: stream->codecpar->codec_id);
69 else
70 decoder = QFFmpeg::findAVDecoder(codecId: stream->codecpar->codec_id);
71
72 if (!decoder)
73 return { QString(u"No %1 decoder found").arg(a: videoCodecPolicy == Hw ? u"HW" : u"SW") };
74
75 qCDebug(qLcPlaybackEngineCodec)
76 << "found decoder" << decoder->name() << "for id" << decoder->id();
77
78 AVCodecContextUPtr context(avcodec_alloc_context3(codec: decoder->get()));
79 if (!context)
80 return { u"Failed to allocate a FFmpeg codec context"_s };
81
82 // Use HW decoding even if the codec level doesn't match the reported capabilities
83 // of the hardware. FFmpeg documentation recommendeds setting this flag by default.
84 context->hwaccel_flags |= AV_HWACCEL_FLAG_IGNORE_LEVEL;
85
86 static const bool allowProfileMismatch = static_cast<bool>(
87 qEnvironmentVariableIntValue(varName: "QT_FFMPEG_HW_ALLOW_PROFILE_MISMATCH"));
88 if (allowProfileMismatch) {
89 // Use HW decoding even if the codec profile doesn't match the reported capabilities
90 // of the hardware.
91 context->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH;
92 }
93
94 if (hwAccel)
95 context->hw_device_ctx = av_buffer_ref(buf: hwAccel->hwDeviceContextAsBuffer());
96
97 if (context->codec_type != AVMEDIA_TYPE_AUDIO && context->codec_type != AVMEDIA_TYPE_VIDEO
98 && context->codec_type != AVMEDIA_TYPE_SUBTITLE) {
99 return { u"Unknown codec type"_s };
100 }
101
102 int ret = avcodec_parameters_to_context(codec: context.get(), par: stream->codecpar);
103 if (ret < 0)
104 return QStringLiteral("Failed to set FFmpeg codec parameters: %1").arg(a: err2str(errnum: ret));
105
106 // ### This still gives errors about wrong HW formats (as we accept all of them)
107 // But it would be good to get so we can filter out pixel format we don't support natively
108 context->get_format = QFFmpeg::getFormat;
109
110 /* Init the decoder, with reference counting and threading */
111 AVDictionaryHolder opts;
112 av_dict_set(pm: opts, key: "refcounted_frames", value: "1", flags: 0);
113 av_dict_set(pm: opts, key: "threads", value: "auto", flags: 0);
114 applyExperimentalCodecOptions(codec: *decoder, opts);
115
116 ret = avcodec_open2(avctx: context.get(), codec: decoder->get(), options: opts);
117
118 if (ret < 0)
119 return QStringLiteral("Failed to open FFmpeg codec context: %1").arg(a: err2str(errnum: ret));
120
121 return CodecContext(new Data(std::move(context), stream, formatContext, std::move(hwAccel)));
122}
123
124QT_END_NAMESPACE
125
126} // namespace QFFmpeg
127

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodeccontext.cpp