Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpeghwaccel_d3d11.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
6
7#include <qvideoframeformat.h>
9
10#include <private/qvideotexturehelper_p.h>
11#include <private/qcomptr_p.h>
12#include <private/quniquehandle_p.h>
13
14#include <rhi/qrhi.h>
15
16#include <qopenglfunctions.h>
17#include <qdebug.h>
18#include <qloggingcategory.h>
19
20#include <libavutil/hwcontext_d3d11va.h>
21#include <d3d11_1.h>
22#include <dxgi1_2.h>
23
25
26namespace {
27
28Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel");
29
30ComPtr<ID3D11Device1> GetD3DDevice(QRhi *rhi)
31{
32 const auto native = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
33 if (!native)
34 return {};
35
36 const ComPtr<ID3D11Device> rhiDevice = static_cast<ID3D11Device *>(native->dev);
37
38 ComPtr<ID3D11Device1> dev1;
39 if (rhiDevice.As(&dev1) != S_OK)
40 return nullptr;
41
42 return dev1;
43}
44
45} // namespace
46namespace QFFmpeg {
47
48bool TextureBridge::copyToSharedTex(ID3D11Device *dev, ID3D11DeviceContext *ctx,
49 const ComPtr<ID3D11Texture2D> &tex, UINT index,
50 const QSize &frameSize)
51{
52 if (!ensureSrcTex(dev, tex, frameSize))
53 return false;
54
55 // Flush to ensure that texture is fully updated before we share it.
56 ctx->Flush();
57
58 if (m_srcMutex->AcquireSync(m_srcKey, INFINITE) != S_OK)
59 return false;
60
61 const UINT width = static_cast<UINT>(frameSize.width());
62 const UINT height = static_cast<UINT>(frameSize.height());
63
64 // A crop box is needed because FFmpeg may have created textures
65 // that are bigger than the frame size to account for the decoder's
66 // surface alignment requirements.
67 const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
68 ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
69
70 m_srcMutex->ReleaseSync(m_destKey);
71 return true;
72}
73
74ComPtr<ID3D11Texture2D> TextureBridge::copyFromSharedTex(const ComPtr<ID3D11Device1> &dev,
75 const ComPtr<ID3D11DeviceContext> &ctx)
76{
77 if (!ensureDestTex(dev))
78 return {};
79
80 if (m_destMutex->AcquireSync(m_destKey, INFINITE) != S_OK)
81 return {};
82
83 ctx->CopySubresourceRegion(m_outputTex.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
84
85 m_destMutex->ReleaseSync(m_srcKey);
86
87 return m_outputTex;
88}
89
90bool TextureBridge::ensureDestTex(const ComPtr<ID3D11Device1> &dev)
91{
92 if (m_destDevice != dev) {
93 // Destination device changed. Recreate texture.
94 m_destTex = nullptr;
95 m_destDevice = dev;
96 }
97
98 if (m_destTex)
99 return true;
100
101 if (m_destDevice->OpenSharedResource1(m_sharedHandle.get(), IID_PPV_ARGS(&m_destTex)) != S_OK)
102 return false;
103
104 CD3D11_TEXTURE2D_DESC desc{};
105 m_destTex->GetDesc(&desc);
106
107 desc.MiscFlags = 0;
108 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
109
110 if (m_destDevice->CreateTexture2D(&desc, nullptr, m_outputTex.ReleaseAndGetAddressOf()) != S_OK)
111 return false;
112
113 if (m_destTex.As(&m_destMutex) != S_OK)
114 return false;
115
116 return true;
117}
118
119bool TextureBridge::ensureSrcTex(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize)
120{
121 if (!isSrcInitialized(dev, tex, frameSize))
122 return recreateSrc(dev, tex, frameSize);
123
124 return true;
125}
126
127bool TextureBridge::isSrcInitialized(const ID3D11Device *dev,
128 const ComPtr<ID3D11Texture2D> &tex,
129 const QSize &frameSize) const
130{
131 if (!m_srcTex)
132 return false;
133
134 // Check if device has changed
135 ComPtr<ID3D11Device> texDevice;
136 m_srcTex->GetDevice(texDevice.GetAddressOf());
137 if (dev != texDevice.Get())
138 return false;
139
140 // Check if shared texture has correct size and format
141 CD3D11_TEXTURE2D_DESC inputDesc{};
142 tex->GetDesc(&inputDesc);
143
144 CD3D11_TEXTURE2D_DESC currentDesc{};
145 m_srcTex->GetDesc(&currentDesc);
146
147 if (inputDesc.Format != currentDesc.Format)
148 return false;
149
150 const UINT width = static_cast<UINT>(frameSize.width());
151 const UINT height = static_cast<UINT>(frameSize.height());
152
153 if (currentDesc.Width != width || currentDesc.Height != height)
154 return false;
155
156 return true;
157}
158
159bool TextureBridge::recreateSrc(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize)
160{
161 m_sharedHandle.close();
162
163 CD3D11_TEXTURE2D_DESC desc{};
164 tex->GetDesc(&desc);
165
166 const UINT width = static_cast<UINT>(frameSize.width());
167 const UINT height = static_cast<UINT>(frameSize.height());
168
169 CD3D11_TEXTURE2D_DESC texDesc{ desc.Format, width, height };
170 texDesc.MipLevels = 1;
171 texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
172
173 if (dev->CreateTexture2D(&texDesc, nullptr, m_srcTex.ReleaseAndGetAddressOf()) != S_OK)
174 return false;
175
176 ComPtr<IDXGIResource1> res;
177 if (m_srcTex.As(&res) != S_OK)
178 return false;
179
180 const HRESULT hr =
181 res->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &m_sharedHandle);
182
183 if (hr != S_OK || !m_sharedHandle)
184 return false;
185
186 if (m_srcTex.As(&m_srcMutex) != S_OK || !m_srcMutex)
187 return false;
188
189 m_destTex = nullptr;
190 m_destMutex = nullptr;
191 return true;
192}
193
195{
196public:
197 D3D11TextureSet(QRhi *rhi, ComPtr<ID3D11Texture2D> &&tex)
198 : m_owner{ rhi }, m_tex(std::move(tex))
199 {
200 }
201
202 qint64 textureHandle(QRhi *rhi, int /*plane*/) override
203 {
204 if (rhi != m_owner)
205 return 0u;
206 return reinterpret_cast<qint64>(m_tex.Get());
207 }
208
209private:
210 QRhi *m_owner = nullptr;
211 ComPtr<ID3D11Texture2D> m_tex;
212};
213
214D3D11TextureConverter::D3D11TextureConverter(QRhi *rhi)
215 : TextureConverterBackend(rhi), m_rhiDevice{ GetD3DDevice(rhi) }
216{
217 if (!m_rhiDevice)
218 return;
219
220 m_rhiDevice->GetImmediateContext(m_rhiCtx.GetAddressOf());
221}
222
223TextureSet *D3D11TextureConverter::getTextures(AVFrame *frame)
224{
225 if (!m_rhiDevice)
226 return nullptr;
227
228 if (!frame || !frame->hw_frames_ctx || frame->format != AV_PIX_FMT_D3D11)
229 return nullptr;
230
231 const auto *fCtx = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
232 const auto *ctx = fCtx->device_ctx;
233
234 if (!ctx || ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
235 return nullptr;
236
237 const ComPtr<ID3D11Texture2D> ffmpegTex = reinterpret_cast<ID3D11Texture2D *>(frame->data[0]);
238 const int index = static_cast<int>(reinterpret_cast<intptr_t>(frame->data[1]));
239
240 if (rhi->backend() == QRhi::D3D11) {
241 {
242 const auto *avDeviceCtx = static_cast<AVD3D11VADeviceContext *>(ctx->hwctx);
243
244 if (!avDeviceCtx)
245 return nullptr;
246
247 // Lock the FFmpeg device context while we copy from FFmpeg's
248 // frame pool into a shared texture because the underlying ID3D11DeviceContext
249 // is not thread safe.
250 avDeviceCtx->lock(avDeviceCtx->lock_ctx);
251 QScopeGuard autoUnlock([&] { avDeviceCtx->unlock(avDeviceCtx->lock_ctx); });
252
253 // Populate the shared texture with one slice from the frame pool, cropping away
254 // extra surface alignment areas that FFmpeg adds to the textures
256 if (!m_bridge.copyToSharedTex(avDeviceCtx->device, avDeviceCtx->device_context,
257 ffmpegTex, index, frameSize)) {
258 return nullptr;
259 }
260 }
261
262 // Get a copy of the texture on the RHI device
263 ComPtr<ID3D11Texture2D> output = m_bridge.copyFromSharedTex(m_rhiDevice, m_rhiCtx);
264
265 if (!output)
266 return nullptr;
267
268 return new D3D11TextureSet(rhi, std::move(output));
269 }
270
271 return nullptr;
272}
273
274void D3D11TextureConverter::SetupDecoderTextures(AVCodecContext *s)
275{
276 // We are holding pool frames alive for quite long, which may cause
277 // codecs to run out of frames because FFmpeg has a fixed size
278 // decoder frame pool. We must therefore add extra frames to the pool
279 // to account for the frames we keep alive. First, we need to account
280 // for the maximum number of queued frames during rendering. In
281 // addition, we add one frame for the RHI rendering pipeline, and one
282 // additional frame because we may hold one in the Qt event loop.
283
285 constexpr qint32 framesHeldByRhi = 1;
286 constexpr qint32 framesHeldByQtEventLoop = 1;
287 s->extra_hw_frames = maxRenderQueueSize + framesHeldByRhi + framesHeldByQtEventLoop;
288
289 int ret = avcodec_get_hw_frames_parameters(s, s->hw_device_ctx, AV_PIX_FMT_D3D11,
290 &s->hw_frames_ctx);
291 if (ret < 0) {
292 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
293 return;
294 }
295
296 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
297 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
298 hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
299 hwctx->BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
300 ret = av_hwframe_ctx_init(s->hw_frames_ctx);
301 if (ret < 0) {
302 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
303 av_buffer_unref(&s->hw_frames_ctx);
304 }
305}
306
307} // namespace QFFmpeg
308
qint64 textureHandle(QRhi *rhi, int) override
D3D11TextureSet(QRhi *rhi, ComPtr< ID3D11Texture2D > &&tex)
static qint32 maxQueueSize(QPlatformMediaPlayer::TrackType type)
\variable QRhiD3D11InitParams::enableDebugLayer
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
@ D3D11
Definition qrhi.h:1810
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
\inmodule QtCore
Definition qsize.h:25
int width
the width of the widget excluding any window frame
Definition qwidget.h:114
int height
the height of the widget excluding any window frame
Definition qwidget.h:115
EGLContext ctx
Combined button and popup list for selecting options.
ComPtr< ID3D11Device1 > GetD3DDevice(QRhi *rhi)
INT_PTR intptr_t
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
GLint GLsizei GLsizei height
GLuint index
[2]
GLint GLsizei width
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
static constexpr QSize frameSize(const T &frame)
int qint32
Definition qtypes.h:49
long long qint64
Definition qtypes.h:60
QT_BEGIN_NAMESPACE typedef uchar * output
long HRESULT
QFrame frame
[0]