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
58void freeTextureAndData(void *opaque, uint8_t *data)
59{
60 static_cast<ID3D11Texture2D *>(opaque)->Release();
61 av_free(data);
62}
63
64AVBufferRef *wrapTextureAsBuffer(const ComPtr<ID3D11Texture2D> &tex)
65{
66 AVD3D11FrameDescriptor *avFrameDesc =
67 static_cast<AVD3D11FrameDescriptor *>(av_mallocz(sizeof(AVD3D11FrameDescriptor)));
68 avFrameDesc->index = 0;
69 avFrameDesc->texture = tex.Get();
70
71 return av_buffer_create(reinterpret_cast<uint8_t *>(avFrameDesc),
72 sizeof(AVD3D11FrameDescriptor *), freeTextureAndData, tex.Get(), 0);
73}
74
75ComPtr<ID3D11Texture2D> copyTexture(const AVD3D11VADeviceContext *hwDevCtx, const AVFrame *src)
76{
77 const int poolIndex = getAvFramePoolIndex(src);
78 const ComPtr<ID3D11Texture2D> poolTex = getAvFrameTexture(src);
79
80 D3D11_TEXTURE2D_DESC texDesc{};
81 poolTex->GetDesc(&texDesc);
82
83 texDesc.ArraySize = 1;
84 texDesc.MiscFlags = 0;
85 texDesc.BindFlags = 0;
86
87 ComPtr<ID3D11Texture2D> destTex;
88 if (hwDevCtx->device->CreateTexture2D(&texDesc, nullptr, &destTex) != S_OK) {
89 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
90 return {};
91 }
92
93 hwDevCtx->device_context->CopySubresourceRegion(destTex.Get(), 0, 0, 0, 0, poolTex.Get(),
94 poolIndex, nullptr);
95
96 return destTex;
97}
98
99} // namespace
100namespace QFFmpeg {
101
104 const QSize &frameSize)
105{
107 return false;
108
109 // Flush to ensure that texture is fully updated before we share it.
110 ctx->Flush();
111
113 return false;
114
115 const UINT width = static_cast<UINT>(frameSize.width());
116 const UINT height = static_cast<UINT>(frameSize.height());
117
118 // A crop box is needed because FFmpeg may have created textures
119 // that are bigger than the frame size to account for the decoder's
120 // surface alignment requirements.
121 const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
122 ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
123
125 return true;
126}
127
130{
131 if (!ensureDestTex(dev))
132 return {};
133
135 return {};
136
137 ctx->CopySubresourceRegion(m_outputTex.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
138
140
141 return m_outputTex;
142}
143
145{
146 if (m_destDevice.Get() != dev.Get()) {
147 // Destination device changed. Recreate texture.
148 m_destTex = nullptr;
150 }
151
152 if (m_destTex)
153 return true;
154
156 return false;
157
160
161 desc.MiscFlags = 0;
163
165 return false;
166
167 if (m_destTex.As(&m_destMutex) != S_OK)
168 return false;
169
170 return true;
171}
172
174{
176 return recreateSrc(dev, tex, frameSize);
177
178 return true;
179}
180
182 const ComPtr<ID3D11Texture2D> &tex,
183 const QSize &frameSize) const
184{
185 if (!m_srcTex)
186 return false;
187
188 // Check if device has changed
191 if (dev != texDevice.Get())
192 return false;
193
194 // Check if shared texture has correct size and format
197
200
202 return false;
203
204 const UINT width = static_cast<UINT>(frameSize.width());
205 const UINT height = static_cast<UINT>(frameSize.height());
206
208 return false;
209
210 return true;
211}
212
214{
216
218 tex->GetDesc(&desc);
219
220 const UINT width = static_cast<UINT>(frameSize.width());
221 const UINT height = static_cast<UINT>(frameSize.height());
222
224 texDesc.MipLevels = 1;
226
228 return false;
229
231 if (m_srcTex.As(&res) != S_OK)
232 return false;
233
234 const HRESULT hr =
236
237 if (hr != S_OK || !m_sharedHandle)
238 return false;
239
240 if (m_srcTex.As(&m_srcMutex) != S_OK || !m_srcMutex)
241 return false;
242
243 m_destTex = nullptr;
244 m_destMutex = nullptr;
245 return true;
246}
247
248namespace {
249class D3D11TextureHandles : public QVideoFrameTexturesHandles
250{
251public:
252 D3D11TextureHandles(TextureConverterBackendPtr &&converterBackend, QRhi *rhi,
253 ComPtr<ID3D11Texture2D> &&tex)
254 : m_parentConverterBackend(std::move(converterBackend)), m_owner{ rhi }, m_tex(std::move(tex))
255 {
256 }
257
258 quint64 textureHandle(QRhi &rhi, int /*plane*/) override
259 {
260 if (&rhi != m_owner)
261 return 0u;
262 return reinterpret_cast<qint64>(m_tex.Get());
263 }
264
265private:
266 TextureConverterBackendPtr m_parentConverterBackend; // ensures the backend is deleted after the texture
267 QRhi *m_owner = nullptr;
268 ComPtr<ID3D11Texture2D> m_tex;
269};
270}
271
274{
275 if (!m_rhiDevice)
276 return;
277
279}
280
283 QVideoFrameTexturesHandlesUPtr /*oldHandles*/)
284{
285 if (!m_rhiDevice)
286 return nullptr;
287
289 return nullptr;
290
291 const auto *ctx = avFrameDeviceContext(frame);
292
294 return nullptr;
295
297 const int index = getAvFramePoolIndex(frame);
298
299 if (rhi->backend() == QRhi::D3D11) {
300 {
301 const auto *avDeviceCtx = getHwDeviceContext(ctx);
302
303 if (!avDeviceCtx)
304 return nullptr;
305
306 // Lock the FFmpeg device context while we copy from FFmpeg's
307 // frame pool into a shared texture because the underlying ID3D11DeviceContext
308 // is not thread safe.
311
312 // Populate the shared texture with one slice from the frame pool, cropping away
313 // extra surface alignment areas that FFmpeg adds to the textures
317 return nullptr;
318 }
319 }
320
321 // Get a copy of the texture on the RHI device
323
324 if (!output)
325 return nullptr;
326
328 }
329
330 return nullptr;
331}
332
334{
336 &s->hw_frames_ctx);
337 if (ret < 0) {
338 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
339 return;
340 }
341
342 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
343 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
347 if (ret < 0) {
348 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
350 }
351}
352
354{
355 if (!src || !src->hw_frames_ctx || src->format != AV_PIX_FMT_D3D11)
356 return src;
357
358 const AVHWDeviceContext *avDevCtx = avFrameDeviceContext(src.get());
359 if (!avDevCtx || avDevCtx->type != AV_HWDEVICE_TYPE_D3D11VA)
360 return src;
361
362 AVFrameUPtr dest = makeAVFrame();
363 if (av_frame_copy_props(dest.get(), src.get()) != 0) {
364 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
365 return src;
366 }
367
368 const AVD3D11VADeviceContext *hwDevCtx = getHwDeviceContext(avDevCtx);
369 ComPtr<ID3D11Texture2D> destTex;
370 {
371 hwDevCtx->lock(hwDevCtx->lock_ctx);
372 destTex = copyTexture(hwDevCtx, src.get());
373 hwDevCtx->unlock(hwDevCtx->lock_ctx);
374 }
375
376 dest->buf[0] = wrapTextureAsBuffer(destTex);
377 dest->data[0] = reinterpret_cast<uint8_t *>(destTex.Detach());
378 dest->data[1] = reinterpret_cast<uint8_t *>(0); // This texture is not a texture array
379
380 dest->width = src->width;
381 dest->height = src->height;
382 dest->format = src->format;
383 dest->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
384
385 return dest;
386}
387
388} // namespace QFFmpeg
389
390QT_END_NAMESPACE
AVFrameUPtr copyFromHwPoolD3D11(AVFrameUPtr src)
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
void freeTextureAndData(void *opaque, uint8_t *data)
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,...)