Revision control

++ // Copyright (C) 2024 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegtextureconverter_p.h>
++
++ #include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
++
++ #include <rhi/qrhi.h>
++
++ #include <q20type_traits.h>
++
++ #if QT_CONFIG(vaapi)
++ # include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_vaapi_p.h>
++ #endif
++
++ #ifdef Q_OS_DARWIN
++ # include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_videotoolbox_p.h>
++ #endif
++
++ #ifdef Q_OS_WINDOWS
++ # include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_d3d11_p.h>
++ #endif
++
++ #ifdef Q_OS_ANDROID
++ # include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_mediacodec_p.h>
++ #endif
++
++ #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
++ # include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_cuda_p.h>
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace QFFmpeg;
++
++ namespace {
++
++ template <typename Converter>
++ using ConverterTypeIdentity = q20::type_identity<Converter>;
++
++ template <typename ConverterTypeHandler>
++ void applyConverterTypeByPixelFormat(AVFrame &hwFrame, QRhi &rhi, ConverterTypeHandler &&handler)
++ {
++ if (!TextureConverter::hwTextureConversionEnabled())
++ return;
++
++ switch (AVPixelFormat(hwFrame.format)) {
++ #if QT_CONFIG(vaapi)
++ case AV_PIX_FMT_VAAPI:
++ handler(ConverterTypeIdentity<VAAPITextureConverter>{});
++ break;
++ #endif
++ #ifdef Q_OS_DARWIN
++ case AV_PIX_FMT_VIDEOTOOLBOX:
++ handler(ConverterTypeIdentity<VideoToolBoxTextureConverter>{});
++ break;
++ #endif
++ #ifdef Q_OS_WINDOWS
++ case AV_PIX_FMT_D3D11:
++ if (D3D11TextureConverter::isSupported(hwFrame, rhi))
++ handler(ConverterTypeIdentity<D3D11TextureConverter>{});
++ break;
++ #endif
++ #ifdef Q_OS_ANDROID
++ case AV_PIX_FMT_MEDIACODEC:
++ handler(ConverterTypeIdentity<MediaCodecTextureConverter>{});
++ break;
++ #endif
++ #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
++ case AV_PIX_FMT_CUDA:
++ handler(ConverterTypeIdentity<CUDATextureConverterFactory>{});
++ break;
++ #endif
++ default:
++ Q_UNUSED(handler)
++ Q_UNUSED(rhi)
++ break;
++ }
++ }
++
++ } // namespace
++
++ TextureConverterBackend::~TextureConverterBackend() = default;
++
++ TextureConverter::TextureConverter(QRhi &rhi) : m_rhi(rhi) { }
++
++ bool TextureConverter::init(AVFrame &hwFrame)
++ {
++ Q_ASSERT(hwFrame.hw_frames_ctx);
++ if (AVPixelFormat(hwFrame.format) != m_format)
++ updateBackend(hwFrame);
++ return !isNull();
++ }
++
++ QVideoFrameTexturesUPtr TextureConverter::createTextures(AVFrame &hwFrame,
++ QVideoFrameTexturesUPtr &oldTextures)
++ {
++ if (isNull())
++ return nullptr;
++
++ Q_ASSERT(hwFrame.format == m_format);
++ return m_backend->createTextures(&hwFrame, oldTextures);
++ }
++
++ QVideoFrameTexturesHandlesUPtr
++ TextureConverter::createTextureHandles(AVFrame &hwFrame, QVideoFrameTexturesHandlesUPtr oldHandles)
++ {
++ if (isNull())
++ return nullptr;
++
++ Q_ASSERT(hwFrame.format == m_format);
++ return m_backend->createTextureHandles(&hwFrame, std::move(oldHandles));
++ }
++
++ void TextureConverter::updateBackend(AVFrame &hwFrame)
++ {
++ m_backend = nullptr;
++ m_format = AVPixelFormat(hwFrame.format); // should be saved even if m_backend is not created
++
++ applyConverterTypeByPixelFormat(hwFrame, m_rhi, [this](auto converterTypeIdentity) {
++ using ConverterType = typename decltype(converterTypeIdentity)::type;
++ m_backend = ConverterType::create(&m_rhi);
++ });
++ }
++
++ bool TextureConverter::hwTextureConversionEnabled()
++ {
++ // HW texture conversions are not stable in specific cases, dependent on the hardware and OS.
++ // We need the env var for testing with no texture conversion on the user's side.
++ static const int disableHwConversion =
++ qEnvironmentVariableIntValue("QT_DISABLE_HW_TEXTURES_CONVERSION");
++
++ return !disableHwConversion;
++ }
++
++ void TextureConverter::applyDecoderPreset(const AVPixelFormat format, AVCodecContext &codecContext)
++ {
++ if (!hwTextureConversionEnabled())
++ return;
++
++ Q_ASSERT(codecContext.codec && Codec(codecContext.codec).isDecoder());
++
++ #ifdef Q_OS_WINDOWS
++ if (format == AV_PIX_FMT_D3D11)
++ D3D11TextureConverter::SetupDecoderTextures(&codecContext);
++ #elif defined Q_OS_ANDROID
++ if (format == AV_PIX_FMT_MEDIACODEC)
++ MediaCodecTextureConverter::setupDecoderSurface(&codecContext);
++ #else
++ Q_UNUSED(codecContext);
++ Q_UNUSED(format);
++ #endif
++ }
++
++ bool TextureConverter::isBackendAvailable(AVFrame &hwFrame, QRhi &rhi)
++ {
++ bool result = false;
++ applyConverterTypeByPixelFormat(hwFrame, rhi, [&result](auto) {
++ result = true;
++ });
++ return result;
++ }
++
++ QT_END_NAMESPACE