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