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{ u"No %1 decoder found"_s.arg(videoCodecPolicy == Hw ? "HW"
79 : "SW") };
80
81 qCDebug(qLcPlaybackEngineCodec)
82 << "found decoder" << decoder->name() << "for id" << decoder->id();
83
84 AVCodecContextUPtr context(avcodec_alloc_context3(decoder->get()));
85 if (!context)
86 return q23::unexpected{ u"Failed to allocate a FFmpeg codec context"_s };
87
88 // Use HW decoding even if the codec level doesn't match the reported capabilities
89 // of the hardware. FFmpeg documentation recommendeds setting this flag by default.
90 context->hwaccel_flags |= AV_HWACCEL_FLAG_IGNORE_LEVEL;
91
92 static const bool allowProfileMismatch = static_cast<bool>(
93 qEnvironmentVariableIntValue("QT_FFMPEG_HW_ALLOW_PROFILE_MISMATCH"));
94 if (allowProfileMismatch) {
95 // Use HW decoding even if the codec profile doesn't match the reported capabilities
96 // of the hardware.
97 context->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH;
98 }
99
100 if (hwAccel)
101 context->hw_device_ctx = av_buffer_ref(hwAccel->hwDeviceContextAsBuffer());
102
103 if (context->codec_type != AVMEDIA_TYPE_AUDIO && context->codec_type != AVMEDIA_TYPE_VIDEO
104 && context->codec_type != AVMEDIA_TYPE_SUBTITLE) {
105 return q23::unexpected{ u"Unknown codec type"_s };
106 }
107
108 int ret = avcodec_parameters_to_context(context.get(), stream->codecpar);
109 if (ret < 0)
110 return q23::unexpected{
111 QStringLiteral("Failed to set FFmpeg codec parameters: %1").arg(err2str(ret))
112 };
113
114 // Required so that decoders can rescale packet-side timestamps (e.g. when
115 // trimming AAC/Opus priming/padding samples signalled via skip_samples).
116 context->pkt_timebase = stream->time_base;
117
118 // ### This still gives errors about wrong HW formats (as we accept all of them)
119 // But it would be good to get so we can filter out pixel format we don't support natively
120 context->get_format = QFFmpeg::getFormat;
121
122 /* Init the decoder, with reference counting and threading */
123 AVDictionaryHolder opts;
124 if (options.playbackIntent() == QPlaybackOptions::PlaybackIntent::LowLatencyStreaming)
125 av_dict_set(opts, "flags", "low_delay", 0);
126
127 av_dict_set(opts, "refcounted_frames", "1", 0);
128 av_dict_set(opts, "threads", "auto", 0);
129 applyExperimentalCodecOptions(*decoder, opts);
130
131 ret = avcodec_open2(context.get(), decoder->get(), opts);
132
133 if (ret < 0)
134 return q23::unexpected{
135 QStringLiteral("Failed to open FFmpeg codec context: %1").arg(err2str(ret))
136 };
137
138 return CodecContext(new Data(std::move(context), stream, formatContext, std::move(hwAccel)));
139}
140
142
143} // namespace QFFmpeg
AVRational pixelAspectRatio(AVFrame *frame) const
The QPlaybackOptions class enables low-level control of media playback options.
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)