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 QVideoFrameTexturesHandlesUPtr /*oldHandles*/)
165{
167 return nullptr;
168
169 if (!isSupported(*frame, *rhi))
170 return nullptr;
171
172 const auto *ctx = avFrameDeviceContext(frame);
173
175 return nullptr;
176
177 const auto *avDeviceCtx = getHwDeviceContext(ctx);
178 if (!avDeviceCtx)
179 return nullptr;
180
182
184 const int index = getAvFramePoolIndex(frame);
187
188 bool canImportDirectly =
190
191 const QSize size = frameSize(frame);
193 {
194 // Lock the FFmpeg device context while we read from FFmpeg's frame pool because the
195 // underlying ID3D11DeviceContext is not thread safe.
197
198 if (canImportDirectly) {
199 // Import a copy of the slice, cropping away FFmpeg's surface alignment padding.
200 handles =
203 } else {
204 // The video processor reads the (possibly padded) pool slice directly, so no
205 // separate cropping copy is needed here.
210 if (converted) {
214 }
215 }
216 }
217
218 if (!handles)
219 return nullptr;
220
222}
223
225{
227 &s->hw_frames_ctx);
228 if (ret < 0) {
229 qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << AVError{ ret };
230 return;
231 }
232
233 const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
234 auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
238 if (ret < 0) {
240 << "Failed to initialize HW frames context" << AVError{ ret };
242 }
243}
244
246{
247 if (!src || !src->hw_frames_ctx || src->format != AV_PIX_FMT_D3D11)
248 return src;
249
250 const AVHWDeviceContext *avDevCtx = avFrameDeviceContext(src.get());
251 if (!avDevCtx || avDevCtx->type != AV_HWDEVICE_TYPE_D3D11VA)
252 return src;
253
254 AVFrameUPtr dest = makeAVFrame();
255 if (int status = av_frame_copy_props(dest.get(), src.get()); status != 0) {
256 qCCritical(qLcMediaFFmpegHWAccel)
257 << "Unable to copy frame from decoder pool" << AVError{ status };
258 return src;
259 }
260
261 const AVD3D11VADeviceContext *hwDevCtx = getHwDeviceContext(avDevCtx);
262 ComPtr<ID3D11Texture2D> destTex = [&] {
263 std::lock_guard<AVD3D11VADeviceContext> guard(*hwDevCtx);
264 return copyTexture(hwDevCtx, src.get());
265 }();
266
267 if (!destTex)
268 return src;
269
270 dest->buf[0] = wrapTextureAsBuffer(destTex);
271 dest->data[0] = reinterpret_cast<uint8_t *>(destTex.Detach());
272 dest->data[1] = reinterpret_cast<uint8_t *>(0); // This texture is not a texture array
273
274 dest->width = src->width;
275 dest->height = src->height;
276 dest->format = src->format;
277 dest->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
278
279 return dest;
280}
281
282} // namespace QFFmpeg
283
284QT_END_NAMESPACE
lock_guard & operator=(const lock_guard &)=delete
lock_guard(const lock_guard &)=delete
lock_guard(const AVD3D11VADeviceContext &ctx)
AVFrameUPtr copyFromHwPoolD3D11(AVFrameUPtr src)