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
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
5
6#include <qvideoframeformat.h>
7
8#include <private/qvideotexturehelper_p.h>
9#include <QtCore/private/qcomptr_p.h>
10#include <private/quniquehandle_p.h>
11
12#include <rhi/qrhi.h>
13
14#include <qopenglfunctions.h>
15#include <qdebug.h>
16#include <qloggingcategory.h>
17
18#include <libavutil/hwcontext_d3d11va.h>
19#include <d3d11_1.h>
20#include <dxgi1_2.h>
21
22QT_BEGIN_NAMESPACE
23
24namespace {
25
26Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel");
27
29{
30 const auto native = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
31 if (!native)
32 return {};
33
34 const ComPtr<ID3D11Device> rhiDevice = static_cast<ID3D11Device *>(native->dev);
35
36 ComPtr<ID3D11Device1> dev1;
37 if (rhiDevice.As(&dev1) != S_OK)
38 return nullptr;
39
40 return dev1;
41}
42
44{
45 return reinterpret_cast<ID3D11Texture2D *>(frame->data[0]);
46}
47
48int getAvFramePoolIndex(const AVFrame *frame)
49{
50 return static_cast<int>(reinterpret_cast<intptr_t>(frame->data[1]));
51}
52
53const AVD3D11VADeviceContext *getHwDeviceContext(const AVHWDeviceContext *ctx)
54{
55 return static_cast<AVD3D11VADeviceContext *>(ctx->hwctx);
56}
57
58AVBufferRef *wrapTextureAsBuffer(const ComPtr<ID3D11Texture2D> &tex)
59{
60 Q_ASSERT(tex);
61
62 AVD3D11FrameDescriptor *avFrameDesc =
63 static_cast<AVD3D11FrameDescriptor *>(av_mallocz(sizeof(AVD3D11FrameDescriptor)));
64 avFrameDesc->index = 0;
65 avFrameDesc->texture = tex.Get();
66
67 return av_buffer_create(reinterpret_cast<uint8_t *>(avFrameDesc),
68 sizeof(AVD3D11FrameDescriptor *), [](void *opaque, uint8_t *data) {
69 static_cast<ID3D11Texture2D *>(opaque)->Release();
70 av_free(data);
71 }, tex.Get(), 0);
72}
73
74ComPtr<ID3D11Texture2D> copyTexture(const AVD3D11VADeviceContext *hwDevCtx, const AVFrame *src)
75{
76 const int poolIndex = getAvFramePoolIndex(src);
77 const ComPtr<ID3D11Texture2D> poolTex = getAvFrameTexture(src);
78
79 D3D11_TEXTURE2D_DESC texDesc{};
80 poolTex->GetDesc(&texDesc);
81
82 texDesc.ArraySize = 1;
83 texDesc.MiscFlags = 0;
84 texDesc.BindFlags = 0;
85
86 ComPtr<ID3D11Texture2D> destTex;
87 if (hwDevCtx->device->CreateTexture2D(&texDesc, nullptr, &destTex) != S_OK) {
88 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
89 return {};
90 }
91
92 hwDevCtx->device_context->CopySubresourceRegion(destTex.Get(), 0, 0, 0, 0, poolTex.Get(),
93 poolIndex, nullptr);
94
95 return destTex;
96}
97
98} // namespace
99namespace QFFmpeg {
100
103 const QSize &frameSize)
104{
106 return false;
107
108 // Flush to ensure that texture is fully updated before we share it.
109 ctx->Flush();
110
112 return false;
113
114 const UINT width = static_cast<UINT>(frameSize.width());
115 const UINT height = static_cast<UINT>(frameSize.height());
116
117 // A crop box is needed because FFmpeg may have created textures
118 // that are bigger than the frame size to account for the decoder's
119 // surface alignment requirements.
120 const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
121 ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
122
124 return true;
125}
126
129{
130 if (!ensureDestTex(dev))
131 return {};
132
134 return {};
135
136 ctx->CopySubresourceRegion(m_outputTex.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
137
139
140 return m_outputTex;
141}
142
144{
145 if (m_destDevice.Get() != dev.Get()) {
146 // Destination device changed. Recreate texture.
147 m_destTex = nullptr;
149 }
150
151 if (m_destTex)
152 return true;
153
155 return false;
156
159
160 desc.MiscFlags = 0;
162
164 return false;
165
166 if (m_destTex.As(&m_destMutex) != S_OK)
167 return false;
168
169 return true;
170}
171
173{
175 return recreateSrc(dev, tex, frameSize);
176
177 return true;
178}
179
181 const ComPtr<ID3D11Texture2D> &tex,
182 const QSize &frameSize) const
183{
184 if (!m_srcTex)
185 return false;
186
187 // Check if device has changed
190 if (dev != texDevice.Get())
191 return false;
192
193 // Check if shared texture has correct size and format
196
199
201 return false;
202
203 const UINT width = static_cast<UINT>(frameSize.width());
204 const UINT height = static_cast<UINT>(frameSize.height());
205
207 return false;
208
209 return true;
210}
211
213{
215
217 tex->GetDesc(&desc);
218
219 const UINT width = static_cast<UINT>(frameSize.width());
220 const UINT height = static_cast<UINT>(frameSize.height());
221
223 texDesc.MipLevels = 1;
225
227 return false;
228
230 if (m_srcTex.As(&res) != S_OK)
231 return false;
232
233 const HRESULT hr =
235
236 if (hr != S_OK || !m_sharedHandle)
237 return false;
238
239 if (m_srcTex.As(&m_srcMutex) != S_OK || !m_srcMutex)
240 return false;
241
242 m_destTex = nullptr;
243 m_destMutex = nullptr;
244 return true;
245}
246
247namespace {
248class D3D11TextureHandles : public QVideoFrameTexturesHandles
249{
250public:
251 D3D11TextureHandles(TextureConverterBackendPtr &&converterBackend, QRhi *rhi,
252 ComPtr<ID3D11Texture2D> &&tex)
253 : m_parentConverterBackend(std::move(converterBackend)), m_owner{ rhi }, m_tex(std::move(tex))
254 {
255 }
256
257 quint64 textureHandle(QRhi &rhi, int /*plane*/) override
258 {
259 if (&rhi != m_owner)
260 return 0u;
261 return reinterpret_cast<qint64>(m_tex.Get());
262 }
263
264private:
265 TextureConverterBackendPtr m_parentConverterBackend; // ensures the backend is deleted after the texture
266 QRhi *m_owner = nullptr;
267 ComPtr<ID3D11Texture2D> m_tex;
268};
269}
270
273{
274 if (!m_rhiDevice)
275 return;
276
278}
279
282 QVideoFrameTexturesHandlesUPtr /*oldHandles*/)
283{
284 if (!m_rhiDevice)
285 return nullptr;
286
288 return nullptr;
289
290 const auto *ctx = avFrameDeviceContext(frame);
291
293 return nullptr;
294
296 const int index = getAvFramePoolIndex(frame);
297
298 if (rhi->backend() == QRhi::D3D11) {
299 {
300 const auto *avDeviceCtx = getHwDeviceContext(ctx);
301
302 if (!avDeviceCtx)
303 return nullptr;
304
305 // Lock the FFmpeg device context while we copy from FFmpeg's
306 // frame pool into a shared texture because the underlying ID3D11DeviceContext
307 // is not thread safe.
310
311 // Populate the shared texture with one slice from the frame pool, cropping away
312 // extra surface alignment areas that FFmpeg adds to the textures
316 return nullptr;
317 }
318 }
319
320 // Get a copy of the texture on the RHI device
322
323 if (!output)
324 return nullptr;
325
327 }
328
329 return nullptr;
330}
331
333{
335 &s->hw_frames_ctx);
336 if (ret < 0) {
337 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
338 return;
339 }
340
341 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
342 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
346 if (ret < 0) {
347 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
349 }
350}
351
353{
354 if (!src || !src->hw_frames_ctx || src->format != AV_PIX_FMT_D3D11)
355 return src;
356
357 const AVHWDeviceContext *avDevCtx = avFrameDeviceContext(src.get());
358 if (!avDevCtx || avDevCtx->type != AV_HWDEVICE_TYPE_D3D11VA)
359 return src;
360
361 AVFrameUPtr dest = makeAVFrame();
362 if (av_frame_copy_props(dest.get(), src.get()) != 0) {
363 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
364 return src;
365 }
366
367 const AVD3D11VADeviceContext *hwDevCtx = getHwDeviceContext(avDevCtx);
368 ComPtr<ID3D11Texture2D> destTex;
369 {
370 hwDevCtx->lock(hwDevCtx->lock_ctx);
371 destTex = copyTexture(hwDevCtx, src.get());
372 hwDevCtx->unlock(hwDevCtx->lock_ctx);
373 }
374
375 if (!destTex)
376 return src;
377
378 dest->buf[0] = wrapTextureAsBuffer(destTex);
379 dest->data[0] = reinterpret_cast<uint8_t *>(destTex.Detach());
380 dest->data[1] = reinterpret_cast<uint8_t *>(0); // This texture is not a texture array
381
382 dest->width = src->width;
383 dest->height = src->height;
384 dest->format = src->format;
385 dest->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
386
387 return dest;
388}
389
390} // namespace QFFmpeg
391
392QT_END_NAMESPACE
AVFrameUPtr copyFromHwPoolD3D11(AVFrameUPtr src)
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
AVBufferRef * wrapTextureAsBuffer(const ComPtr< ID3D11Texture2D > &tex)
ComPtr< ID3D11Device1 > GetD3DDevice(QRhi *rhi)
const AVD3D11VADeviceContext * getHwDeviceContext(const AVHWDeviceContext *ctx)
ComPtr< ID3D11Texture2D > getAvFrameTexture(const AVFrame *frame)
int getAvFramePoolIndex(const AVFrame *frame)
ComPtr< ID3D11Texture2D > copyTexture(const AVD3D11VADeviceContext *hwDevCtx, const AVFrame *src)
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCDebug(category,...)