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
7
8#include <QtMultimedia/qvideoframeformat.h>
9#include <QtMultimedia/private/qvideotexturehelper_p.h>
10
11#include <QtGui/rhi/qrhi.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/private/qcomptr_p.h>
14
15#include <libavutil/hwcontext_d3d11va.h>
16#include <d3d11_1.h>
17
18#include <mutex>
19
20namespace std {
21
22template <>
23class lock_guard<AVD3D11VADeviceContext>
24{
25public:
26 explicit lock_guard(const AVD3D11VADeviceContext &ctx) : m_ctx(ctx)
27 {
28 m_ctx.lock(m_ctx.lock_ctx);
29 }
30
31 ~lock_guard() { m_ctx.unlock(m_ctx.lock_ctx); }
32
33 lock_guard(const lock_guard &) = delete;
34 lock_guard &operator=(const lock_guard &) = delete;
35
36private:
37 const AVD3D11VADeviceContext &m_ctx;
38};
39
40} // namespace std
41
42QT_BEGIN_NAMESPACE
43
44namespace QFFmpeg {
45
46namespace {
47
48Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel");
49
50ComPtr<ID3D11Texture2D> getAvFrameTexture(const AVFrame *frame)
51{
52 return reinterpret_cast<ID3D11Texture2D *>(frame->data[0]);
53}
54
55int getAvFramePoolIndex(const AVFrame *frame)
56{
57 return static_cast<int>(reinterpret_cast<intptr_t>(frame->data[1]));
58}
59
60const AVD3D11VADeviceContext *getHwDeviceContext(const AVHWDeviceContext *ctx)
61{
62 return static_cast<AVD3D11VADeviceContext *>(ctx->hwctx);
63}
64
65AVBufferRef *wrapTextureAsBuffer(const ComPtr<ID3D11Texture2D> &tex)
66{
67 Q_ASSERT(tex);
68
69 AVD3D11FrameDescriptor *avFrameDesc =
70 static_cast<AVD3D11FrameDescriptor *>(av_mallocz(sizeof(AVD3D11FrameDescriptor)));
71 avFrameDesc->index = 0;
72 avFrameDesc->texture = tex.Get();
73
74 return av_buffer_create(reinterpret_cast<uint8_t *>(avFrameDesc),
75 sizeof(AVD3D11FrameDescriptor *), [](void *opaque, uint8_t *data) {
76 static_cast<ID3D11Texture2D *>(opaque)->Release();
77 av_free(data);
78 }, tex.Get(), 0);
79}
80
81ComPtr<ID3D11Texture2D> copyTexture(const AVD3D11VADeviceContext *hwDevCtx, const AVFrame *src)
82{
83 const int poolIndex = getAvFramePoolIndex(src);
84 const ComPtr<ID3D11Texture2D> poolTex = getAvFrameTexture(src);
85
86 D3D11_TEXTURE2D_DESC texDesc{};
87 poolTex->GetDesc(&texDesc);
88
89 texDesc.ArraySize = 1;
90 texDesc.MiscFlags = 0;
91 texDesc.BindFlags = 0;
92
93 ComPtr<ID3D11Texture2D> destTex;
94 if (hwDevCtx->device->CreateTexture2D(&texDesc, nullptr, &destTex) != S_OK) {
95 qCCritical(qLcMediaFFmpegHWAccel) << "Unable to copy frame from decoder pool";
96 return {};
97 }
98
99 hwDevCtx->device_context->CopySubresourceRegion(destTex.Get(), 0, 0, 0, 0, poolTex.Get(),
100 poolIndex, nullptr);
101
102 return destTex;
103}
104
105class ImportedTextureHandles : public QVideoFrameTexturesHandles
106{
107public:
108 ImportedTextureHandles(TextureConverterBackendPtr &&converterBackend,
109 QVideoFrameTexturesHandlesUPtr &&handles)
110 : m_parentConverterBackend(std::move(converterBackend)), m_handles(std::move(handles))
111 {
112 }
113
114 quint64 textureHandle(QRhi &rhi, int plane) override
115 {
116 return m_handles ? m_handles->textureHandle(rhi, plane) : 0;
117 }
118
119private:
120 TextureConverterBackendPtr
121 m_parentConverterBackend; // ensures the backend is deleted after the texture
122 QVideoFrameTexturesHandlesUPtr m_handles;
123};
124
125} // namespace
126
128{
129 if (!rhi)
130 return nullptr;
131
133 return nullptr;
134
136}
137
139
141{
143 return false;
144
147
149 return true;
150
151 switch (rhi.backend()) {
152 case QRhi::D3D12:
153#if QT_CONFIG(opengl)
154 case QRhi::OpenGLES2:
155#endif
157 default:
158 return false;
159 }
160}
161
164{
166 return nullptr;
167
168 if (!isSupported(*frame, *rhi))
169 return nullptr;
170
171 const auto *ctx = avFrameDeviceContext(frame);
172
174 return nullptr;
175
176 const auto *avDeviceCtx = getHwDeviceContext(ctx);
177 if (!avDeviceCtx)
178 return nullptr;
179
181
183 const int index = getAvFramePoolIndex(frame);
186
187 bool canImportDirectly =
189
190 const QSize size = frameSize(frame);
192 {
193 // Lock the FFmpeg device context while we read from FFmpeg's frame pool because the
194 // underlying ID3D11DeviceContext is not thread safe.
196
197 if (canImportDirectly) {
198 // Import a copy of the slice, cropping away FFmpeg's surface alignment padding.
199 handles =
202 } else {
203 // The video processor reads the (possibly padded) pool slice directly, so no
204 // separate cropping copy is needed here.
209 if (converted) {
213 }
214 }
215 }
216
217 if (!handles)
218 return nullptr;
219
221}
222
224{
226 &s->hw_frames_ctx);
227 if (ret < 0) {
228 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << AVError{ ret };
229 return;
230 }
231
232 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
233 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
237 if (ret < 0) {
239 << "Failed to initialize HW frames context" << AVError{ ret };
241 }
242}
243
245{
246 if (!src || !src->hw_frames_ctx || src->format != AV_PIX_FMT_D3D11)
247 return src;
248
249 const AVHWDeviceContext *avDevCtx = avFrameDeviceContext(src.get());
250 if (!avDevCtx || avDevCtx->type != AV_HWDEVICE_TYPE_D3D11VA)
251 return src;
252
253 AVFrameUPtr dest = makeAVFrame();
254 if (int status = av_frame_copy_props(dest.get(), src.get()); status != 0) {
255 qCCritical(qLcMediaFFmpegHWAccel)
256 << "Unable to copy frame from decoder pool" << AVError{ status };
257 return src;
258 }
259
260 const AVD3D11VADeviceContext *hwDevCtx = getHwDeviceContext(avDevCtx);
261 ComPtr<ID3D11Texture2D> destTex = [&] {
262 std::lock_guard<AVD3D11VADeviceContext> guard(*hwDevCtx);
263 return copyTexture(hwDevCtx, src.get());
264 }();
265
266 if (!destTex)
267 return src;
268
269 dest->buf[0] = wrapTextureAsBuffer(destTex);
270 dest->data[0] = reinterpret_cast<uint8_t *>(destTex.Detach());
271 dest->data[1] = reinterpret_cast<uint8_t *>(0); // This texture is not a texture array
272
273 dest->width = src->width;
274 dest->height = src->height;
275 dest->format = src->format;
276 dest->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
277
278 return dest;
279}
280
281} // namespace QFFmpeg
282
283QT_END_NAMESPACE
lock_guard & operator=(const lock_guard &)=delete
lock_guard(const lock_guard &)=delete
lock_guard(const AVD3D11VADeviceContext &ctx)
AVFrameUPtr copyFromHwPoolD3D11(AVFrameUPtr src)