| 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 <QtMultimedia/qplaybackoptions.h> |
| 8 | #include <QtCore/qloggingcategory.h> |
| 9 | |
| 10 | QT_BEGIN_NAMESPACE |
| 11 | |
| 12 | using namespace Qt::StringLiterals; |
| 13 | |
| 14 | Q_STATIC_LOGGING_CATEGORY(qLcPlaybackEngineCodec, "qt.multimedia.playbackengine.codec" ); |
| 15 | |
| 16 | namespace QFFmpeg { |
| 17 | |
| 18 | CodecContext::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(format: formatContext, stream, frame: nullptr); |
| 28 | } |
| 29 | |
| 30 | q23::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, playbackOptions: options, videoCodecPolicy: Hw); |
| 39 | if (hwCodec) |
| 40 | return hwCodec; |
| 41 | |
| 42 | qCInfo(qLcPlaybackEngineCodec) << hwCodec.error(); |
| 43 | } |
| 44 | |
| 45 | auto context = create(stream, formatContext, playbackOptions: options, videoCodecPolicy: Sw); |
| 46 | if (!context) |
| 47 | qCWarning(qLcPlaybackEngineCodec) << context.error(); |
| 48 | |
| 49 | return context; |
| 50 | } |
| 51 | |
| 52 | AVRational CodecContext::pixelAspectRatio(AVFrame *frame) const |
| 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 | |
| 59 | q23::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(args&: decoder, args&: hwAccel) = HWAccel::findDecoderWithHwAccel(id: stream->codecpar->codec_id); |
| 74 | else |
| 75 | decoder = QFFmpeg::findAVDecoder(codecId: stream->codecpar->codec_id); |
| 76 | |
| 77 | if (!decoder) |
| 78 | return q23::unexpected{ |
| 79 | QString(u"No %1 decoder found" ).arg(a: 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(codec: 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(varName: "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(buf: 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(codec: context.get(), par: stream->codecpar); |
| 110 | if (ret < 0) |
| 111 | return q23::unexpected{ |
| 112 | QStringLiteral("Failed to set FFmpeg codec parameters: %1" ).arg(a: err2str(errnum: 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(pm: opts, key: "flags" , value: "low_delay" , flags: 0); |
| 123 | |
| 124 | av_dict_set(pm: opts, key: "refcounted_frames" , value: "1" , flags: 0); |
| 125 | av_dict_set(pm: opts, key: "threads" , value: "auto" , flags: 0); |
| 126 | applyExperimentalCodecOptions(codec: *decoder, opts); |
| 127 | |
| 128 | ret = avcodec_open2(avctx: context.get(), codec: decoder->get(), options: opts); |
| 129 | |
| 130 | if (ret < 0) |
| 131 | return q23::unexpected{ |
| 132 | QStringLiteral("Failed to open FFmpeg codec context: %1" ).arg(a: err2str(errnum: ret)) |
| 133 | }; |
| 134 | |
| 135 | return CodecContext(new Data(std::move(context), stream, formatContext, std::move(hwAccel))); |
| 136 | } |
| 137 | |
| 138 | QT_END_NAMESPACE |
| 139 | |
| 140 | } // namespace QFFmpeg |
| 141 | |