File indexing completed on 2025-01-19 03:57:04

0001 /*********************************************************
0002  * Copyright (C) 2020, Val Doroshchuk <valbok@gmail.com> *
0003  *                                                       *
0004  * This file is part of QtAVPlayer.                      *
0005  * Free Qt Media Player based on FFmpeg.                 *
0006  *********************************************************/
0007 
0008 #include "qavhwdevice_d3d11_p.h"
0009 #include "qavvideobuffer_gpu_p.h"
0010 #include <d3d11.h>
0011 
0012 #ifdef QT_AVPLAYER_MULTIMEDIA
0013 
0014 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
0015 #include <private/qrhi_p.h>
0016 #include <private/qrhid3d11_p.h>
0017 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 2)
0018 #include <private/qcomptr_p.h>
0019 #else
0020 #include <private/qwindowsiupointer_p.h>
0021 template <class T>
0022 using ComPtr = QWindowsIUPointer<T>;
0023 #endif
0024 #include <system_error>
0025 #endif
0026 
0027 #endif // QT_AVPLAYER_MULTIMEDIA
0028 
0029 extern "C" {
0030 #include <libavcodec/avcodec.h>
0031 #include <libavcodec/d3d11va.h>
0032 #include <libavutil/hwcontext_d3d11va.h>
0033 }
0034 
0035 QT_BEGIN_NAMESPACE
0036 
0037 void QAVHWDevice_D3D11::init(AVCodecContext *avctx)
0038 {
0039 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
0040     int ret = avcodec_get_hw_frames_parameters(avctx,
0041                                                avctx->hw_device_ctx,
0042                                                AV_PIX_FMT_D3D11,
0043                                                &avctx->hw_frames_ctx);
0044 
0045     if (ret < 0) {
0046         qWarning() << "Failed to allocate HW frames context:" << ret;
0047         return;
0048     }
0049 
0050     auto frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
0051     auto hwctx = (AVD3D11VAFramesContext *)frames_ctx->hwctx;
0052     hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
0053     hwctx->BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
0054     ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
0055     if (ret < 0) {
0056         qWarning() << "Failed to initialize HW frames context:" << ret;
0057         av_buffer_unref(&avctx->hw_frames_ctx);
0058     }
0059 #else
0060     Q_UNUSED(avctx);
0061 #endif
0062 }
0063 
0064 AVPixelFormat QAVHWDevice_D3D11::format() const
0065 {
0066     return AV_PIX_FMT_D3D11;
0067 }
0068 
0069 AVHWDeviceType QAVHWDevice_D3D11::type() const
0070 {
0071     return AV_HWDEVICE_TYPE_D3D11VA;
0072 }
0073 
0074 #ifdef QT_AVPLAYER_MULTIMEDIA
0075 
0076 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
0077 
0078 template <class T>
0079 static T **address(ComPtr<T> &ptr)
0080 {
0081 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 2)
0082     return ptr.GetAddressOf();
0083 #else
0084     return ptr.address();
0085 #endif
0086 }
0087 
0088 template <class T>
0089 static T *get(const ComPtr<T> &ptr)
0090 {
0091 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 2)
0092     return ptr.Get();
0093 #else
0094     return ptr.get();
0095 #endif
0096 }
0097 
0098 static ComPtr<ID3D11Texture2D> shareTexture(ID3D11Device *dev, ID3D11Texture2D *tex)
0099 {
0100     ComPtr<IDXGIResource> dxgiResource;
0101     HRESULT hr = tex->QueryInterface(__uuidof(IDXGIResource), reinterpret_cast<void **>(address(dxgiResource)));
0102     if (FAILED(hr)) {
0103         qWarning() << "Failed to obtain resource handle from FFmpeg texture:" << hr << std::system_category().message(hr);
0104         return {};
0105     }
0106 
0107     HANDLE shared = nullptr;
0108     hr = dxgiResource->GetSharedHandle(&shared);
0109     if (FAILED(hr)) {
0110         qWarning() << "Failed to obtain shared handle for FFmpeg texture:" << hr << std::system_category().message(hr);
0111         return {};
0112     }
0113 
0114     ComPtr<ID3D11Texture2D> sharedTex;
0115     hr = dev->OpenSharedResource(shared, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(address(sharedTex)));
0116     if (FAILED(hr))
0117         qWarning() << "Failed to share FFmpeg texture:" << hr << std::system_category().message(hr);
0118     return sharedTex;
0119 }
0120 
0121 static ComPtr<ID3D11Texture2D> copyTexture(ID3D11Device *dev, ID3D11Texture2D *from, int index)
0122 {
0123     D3D11_TEXTURE2D_DESC fromDesc = {};
0124     from->GetDesc(&fromDesc);
0125 
0126     D3D11_TEXTURE2D_DESC toDesc = {};
0127     toDesc.Width = fromDesc.Width;
0128     toDesc.Height = fromDesc.Height;
0129     toDesc.Format = fromDesc.Format;
0130     toDesc.ArraySize = 1;
0131     toDesc.MipLevels = 1;
0132     toDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
0133     toDesc.MiscFlags = 0;
0134     toDesc.SampleDesc = { 1, 0 };
0135 
0136     ComPtr<ID3D11Texture2D> copy;
0137     HRESULT hr = dev->CreateTexture2D(&toDesc, nullptr, address(copy));
0138     if (FAILED(hr)) {
0139         qWarning() << "Failed to create texture:" << hr << std::system_category().message(hr);
0140         return {};
0141     }
0142 
0143     ComPtr<ID3D11DeviceContext> ctx;
0144     dev->GetImmediateContext(address(ctx));
0145     ctx->CopySubresourceRegion(get(copy), 0, 0, 0, 0, from, index, nullptr);
0146     return copy;
0147 }
0148 
0149 class VideoBuffer_D3D11: public QAVVideoBuffer_GPU
0150 {
0151 public:
0152     VideoBuffer_D3D11(const QAVVideoFrame &frame)
0153         : QAVVideoBuffer_GPU(frame)
0154     {
0155     }
0156 
0157     QAVVideoFrame::HandleType handleType() const override
0158     {
0159         return QAVVideoFrame::D3D11Texture2DHandle;
0160     }
0161 
0162     QVariant handle(QRhi *rhi) const override
0163     {
0164         if (!rhi || rhi->backend() != QRhi::D3D11)
0165             return {};
0166 
0167         if (!m_texture) {
0168             if (frame().format() != AV_PIX_FMT_NV12) {
0169                 qWarning() << "Only NV12 is supported";
0170                 return {};
0171             }
0172             auto av_frame = frame().frame();
0173             auto texture = (ID3D11Texture2D *)(uintptr_t)av_frame->data[0];
0174             auto texture_index = (intptr_t)av_frame->data[1];
0175             if (!texture) {
0176                 qWarning() << "No texture in the frame" << frame().pts();
0177                 return {};
0178             }
0179             auto nh = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
0180             if (!nh) {
0181                 qWarning() << "No QRhiD3D11NativeHandles";
0182                 return {};
0183             }
0184 
0185             auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
0186             if (!dev) {
0187                 qWarning() << "No ID3D11Device device";
0188                 return {};
0189             }
0190             auto shared = shareTexture(dev, texture);
0191             if (shared)
0192                 const_cast<VideoBuffer_D3D11*>(this)->m_texture = copyTexture(dev, get(shared), texture_index);
0193         }
0194 
0195         QList<quint64> textures = {quint64(get(m_texture)), quint64(get(m_texture))};
0196         return QVariant::fromValue(textures);
0197     }
0198 
0199     ComPtr<ID3D11Texture2D> m_texture;
0200 };
0201 
0202 QAVVideoBuffer *QAVHWDevice_D3D11::videoBuffer(const QAVVideoFrame &frame) const
0203 {
0204     return new VideoBuffer_D3D11(frame);
0205 }
0206 
0207 #else // QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
0208 
0209 QAVVideoBuffer *QAVHWDevice_D3D11::videoBuffer(const QAVVideoFrame &frame) const
0210 {
0211     return new QAVVideoBuffer_GPU(frame);
0212 }
0213 
0214 #endif
0215 
0216 #else // QT_AVPLAYER_MULTIMEDIA
0217 
0218 QAVVideoBuffer *QAVHWDevice_D3D11::videoBuffer(const QAVVideoFrame &frame) const
0219 {
0220     return new QAVVideoBuffer_GPU(frame);
0221 }
0222 
0223 #endif // QT_AVPLAYER_MULTIMEDIA
0224 
0225 QT_END_NAMESPACE