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#include "playbackengine/qffmpegstreamdecoder_p.h"
6
7#include <qvideoframeformat.h>
9
10#include <private/qvideotexturehelper_p.h>
11#include <QtCore/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
24QT_BEGIN_NAMESPACE
25
26namespace {
27
28Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel");
29
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
46{
47 return reinterpret_cast<ID3D11Texture2D *>(frame->data[0]);
48}
49
50int getAvFramePoolIndex(const AVFrame *frame)
51{
52 return static_cast<int>(reinterpret_cast<intptr_t>(frame->data[1]));
53}
54
55const AVD3D11VADeviceContext *getHwDeviceContext(const AVHWDeviceContext *ctx)
56{
57 return static_cast<AVD3D11VADeviceContext *>(ctx->hwctx);
58}
59
60void freeTextureAndData(void *opaque, uint8_t *data)
61{
62 static_cast<ID3D11Texture2D *>(opaque)->Release();
63 av_free(data);
64}
65
66AVBufferRef *wrapTextureAsBuffer(const ComPtr<ID3D11Texture2D> &tex)
67{
68 AVD3D11FrameDescriptor *avFrameDesc =
69 static_cast<AVD3D11FrameDescriptor *>(av_mallocz(sizeof(AVD3D11FrameDescriptor)));
70 avFrameDesc->index = 0;
71 avFrameDesc->texture = tex.Get();
72
73 return av_buffer_create(reinterpret_cast<uint8_t *>(avFrameDesc),
74 sizeof(AVD3D11FrameDescriptor *), freeTextureAndData, tex.Get(), 0);
75}
76
77ComPtr<ID3D11Texture2D> copyTexture(const AVD3D11VADeviceContext *hwDevCtx, const AVFrame *src)
78{
79 const int poolIndex = getAvFramePoolIndex(src);
80 const ComPtr<ID3D11Texture2D> poolTex = getAvFrameTexture(src);
81
82 D3D11_TEXTURE2D_DESC texDesc{};
83 poolTex->GetDesc(&texDesc);
84
85 texDesc.ArraySize = 1;
86 texDesc.MiscFlags = 0;
87 texDesc.BindFlags = 0;
88
89 ComPtr<ID3D11Texture2D> destTex;
90 if (hwDevCtx->device->CreateTexture2D(&texDesc, nullptr, &destTex) != S_OK) {
91 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
92 return {};
93 }
94
95 hwDevCtx->device_context->CopySubresourceRegion(destTex.Get(), 0, 0, 0, 0, poolTex.Get(),
96 poolIndex, nullptr);
97
98 return destTex;
99}
100
101} // namespace
102namespace QFFmpeg {
103
106 const QSize &frameSize)
107{
109 return false;
110
111 // Flush to ensure that texture is fully updated before we share it.
112 ctx->Flush();
113
115 return false;
116
117 const UINT width = static_cast<UINT>(frameSize.width());
118 const UINT height = static_cast<UINT>(frameSize.height());
119
120 // A crop box is needed because FFmpeg may have created textures
121 // that are bigger than the frame size to account for the decoder's
122 // surface alignment requirements.
123 const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
124 ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
125
127 return true;
128}
129
132{
133 if (!ensureDestTex(dev))
134 return {};
135
137 return {};
138
139 ctx->CopySubresourceRegion(m_outputTex.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
140
142
143 return m_outputTex;
144}
145
147{
148 if (m_destDevice.Get() != dev.Get()) {
149 // Destination device changed. Recreate texture.
150 m_destTex = nullptr;
152 }
153
154 if (m_destTex)
155 return true;
156
158 return false;
159
162
163 desc.MiscFlags = 0;
165
167 return false;
168
169 if (m_destTex.As(&m_destMutex) != S_OK)
170 return false;
171
172 return true;
173}
174
176{
178 return recreateSrc(dev, tex, frameSize);
179
180 return true;
181}
182
184 const ComPtr<ID3D11Texture2D> &tex,
185 const QSize &frameSize) const
186{
187 if (!m_srcTex)
188 return false;
189
190 // Check if device has changed
193 if (dev != texDevice.Get())
194 return false;
195
196 // Check if shared texture has correct size and format
199
202
204 return false;
205
206 const UINT width = static_cast<UINT>(frameSize.width());
207 const UINT height = static_cast<UINT>(frameSize.height());
208
210 return false;
211
212 return true;
213}
214
216{
218
220 tex->GetDesc(&desc);
221
222 const UINT width = static_cast<UINT>(frameSize.width());
223 const UINT height = static_cast<UINT>(frameSize.height());
224
226 texDesc.MipLevels = 1;
228
230 return false;
231
233 if (m_srcTex.As(&res) != S_OK)
234 return false;
235
236 const HRESULT hr =
238
239 if (hr != S_OK || !m_sharedHandle)
240 return false;
241
242 if (m_srcTex.As(&m_srcMutex) != S_OK || !m_srcMutex)
243 return false;
244
245 m_destTex = nullptr;
246 m_destMutex = nullptr;
247 return true;
248}
249
250namespace {
251class D3D11TextureHandles : public QVideoFrameTexturesHandles
252{
253public:
254 D3D11TextureHandles(TextureConverterBackendPtr &&converterBackend, QRhi *rhi,
255 ComPtr<ID3D11Texture2D> &&tex)
256 : m_parentConverterBackend(std::move(converterBackend)), m_owner{ rhi }, m_tex(std::move(tex))
257 {
258 }
259
260 quint64 textureHandle(QRhi &rhi, int /*plane*/) override
261 {
262 if (&rhi != m_owner)
263 return 0u;
264 return reinterpret_cast<qint64>(m_tex.Get());
265 }
266
267private:
268 TextureConverterBackendPtr m_parentConverterBackend; // ensures the backend is deleted after the texture
269 QRhi *m_owner = nullptr;
270 ComPtr<ID3D11Texture2D> m_tex;
271};
272}
273
276{
277 if (!m_rhiDevice)
278 return;
279
281}
282
285 QVideoFrameTexturesHandlesUPtr /*oldHandles*/)
286{
287 if (!m_rhiDevice)
288 return nullptr;
289
291 return nullptr;
292
293 const auto *ctx = avFrameDeviceContext(frame);
294
296 return nullptr;
297
299 const int index = getAvFramePoolIndex(frame);
300
301 if (rhi->backend() == QRhi::D3D11) {
302 {
303 const auto *avDeviceCtx = getHwDeviceContext(ctx);
304
305 if (!avDeviceCtx)
306 return nullptr;
307
308 // Lock the FFmpeg device context while we copy from FFmpeg's
309 // frame pool into a shared texture because the underlying ID3D11DeviceContext
310 // is not thread safe.
313
314 // Populate the shared texture with one slice from the frame pool, cropping away
315 // extra surface alignment areas that FFmpeg adds to the textures
319 return nullptr;
320 }
321 }
322
323 // Get a copy of the texture on the RHI device
325
326 if (!output)
327 return nullptr;
328
330 }
331
332 return nullptr;
333}
334
336{
338 &s->hw_frames_ctx);
339 if (ret < 0) {
340 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
341 return;
342 }
343
344 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
345 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
349 if (ret < 0) {
350 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
352 }
353}
354
356{
357 if (!src || !src->hw_frames_ctx || src->format != AV_PIX_FMT_D3D11)
358 return src;
359
360 const AVHWDeviceContext *avDevCtx = avFrameDeviceContext(src.get());
361 if (!avDevCtx || avDevCtx->type != AV_HWDEVICE_TYPE_D3D11VA)
362 return src;
363
364 AVFrameUPtr dest = makeAVFrame();
365 if (av_frame_copy_props(dest.get(), src.get()) != 0) {
366 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
367 return src;
368 }
369
370 const AVD3D11VADeviceContext *hwDevCtx = getHwDeviceContext(avDevCtx);
371 ComPtr<ID3D11Texture2D> destTex;
372 {
373 hwDevCtx->lock(hwDevCtx->lock_ctx);
374 destTex = copyTexture(hwDevCtx, src.get());
375 hwDevCtx->unlock(hwDevCtx->lock_ctx);
376 }
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
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,...)