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
evrd3dpresentengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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 "evrhelpers_p.h"
7
8#include <private/qhwvideobuffer_p.h>
9#include <private/qvideoframe_p.h>
10#include <qvideoframe.h>
11#include <QDebug>
12#include <qthread.h>
13#include <qvideosink.h>
14#include <qloggingcategory.h>
15
16#include <d3d11_1.h>
17
18#include <rhi/qrhi.h>
19
20#if QT_CONFIG(opengl)
21# include <qopenglcontext.h>
22# include <qopenglfunctions.h>
23# include <qoffscreensurface.h>
24#endif
25
27
28Q_STATIC_LOGGING_CATEGORY(qLcEvrD3DPresentEngine, "qt.multimedia.evrd3dpresentengine");
29
31{
32public:
33 IMFSampleVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
34 QRhi *rhi, QVideoFrame::HandleType type = QVideoFrame::NoHandle)
35 : QHwVideoBuffer(type, rhi),
36 m_device(device),
37 m_sample(sample),
38 m_mapMode(QVideoFrame::NotMapped)
39 {
40 }
41
47
49 {
51 return {};
52
54 if (m_memSurface) {
56 return {};
57
58 } else {
61 if (FAILED(hr))
62 return {};
63
66 if (FAILED(hr))
67 return {};
68
69 if (FAILED(surface->GetDesc(&desc)))
70 return {};
71
73 return {};
74
77 return {};
78 }
79 }
80
83 return {};
84
86
90 mapData.data[0] = reinterpret_cast<uchar *>(rect.pBits);
91 mapData.dataSize[0] = (int)(rect.Pitch * desc.Height);
92 return mapData;
93 }
94
96 {
98 return;
99
101 if (m_memSurface)
103 }
104
105protected:
108
109private:
110 ComPtr<IDirect3DSurface9> m_memSurface;
112};
113
115{
116public:
117 QVideoFrameD3D11Textures(std::unique_ptr<QRhiTexture> &&tex, ComPtr<ID3D11Texture2D> &&d3d11tex)
118 : m_tex(std::move(tex))
120 {}
121
122 QRhiTexture *texture(uint plane) const override { return plane == 0 ? m_tex.get() : nullptr; }
123
124private:
125 std::unique_ptr<QRhiTexture> m_tex;
126 ComPtr<ID3D11Texture2D> m_d3d11tex;
127};
128
130{
131public:
132 D3D11TextureVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
133 HANDLE sharedHandle, QRhi *rhi)
136 {}
137
138 QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr& /*oldTextures*/) override
139 {
140 if (rhi.backend() != QRhi::D3D11)
141 return {};
142
143 auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
144 if (!nh)
145 return {};
146
147 auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
148 if (!dev)
149 return {};
150
151 ComPtr<ID3D11Texture2D> d3d11tex;
152 HRESULT hr = dev->OpenSharedResource(m_sharedHandle, __uuidof(ID3D11Texture2D), (void**)(d3d11tex.GetAddressOf()));
153 if (SUCCEEDED(hr)) {
154 D3D11_TEXTURE2D_DESC desc = {};
155 d3d11tex->GetDesc(&desc);
156 QRhiTexture::Format format;
157 if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM)
158 format = QRhiTexture::BGRA8;
159 else if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
160 format = QRhiTexture::RGBA8;
161 else
162 return {};
163
164 std::unique_ptr<QRhiTexture> tex(rhi.newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
165 tex->createFrom({quint64(d3d11tex.Get()), 0});
166 return std::make_unique<QVideoFrameD3D11Textures>(std::move(tex), std::move(d3d11tex));
167
168 } else {
169 qCDebug(qLcEvrD3DPresentEngine) << "Failed to obtain D3D11Texture2D from D3D9Texture2D handle";
170 }
171 return {};
172 }
173
174private:
175 HANDLE m_sharedHandle = nullptr;
176};
177
178#if QT_CONFIG(opengl)
179class QVideoFrameOpenGlTextures : public QVideoFrameTextures
180{
181 struct InterOpHandles {
182 GLuint textureName = 0;
183 HANDLE device = nullptr;
184 HANDLE texture = nullptr;
185 };
186
187public:
188 Q_DISABLE_COPY(QVideoFrameOpenGlTextures)
189
190 QVideoFrameOpenGlTextures(std::unique_ptr<QRhiTexture> &&tex, const WglNvDxInterop &wgl, InterOpHandles &handles)
191 : m_tex(std::move(tex))
192 , m_wgl(wgl)
193 , m_handles(handles)
194 {}
195
196 ~QVideoFrameOpenGlTextures() override {
197 if (QOpenGLContext::currentContext()) {
198 if (!m_wgl.wglDXUnlockObjectsNV(m_handles.device, 1, &m_handles.texture))
199 qCDebug(qLcEvrD3DPresentEngine) << "Failed to unlock OpenGL texture";
200
201 if (!m_wgl.wglDXUnregisterObjectNV(m_handles.device, m_handles.texture))
202 qCDebug(qLcEvrD3DPresentEngine) << "Failed to unregister OpenGL texture";
203
204 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
205 if (funcs)
206 funcs->glDeleteTextures(1, &m_handles.textureName);
207 else
208 qCDebug(qLcEvrD3DPresentEngine) << "Could not delete texture, OpenGL context functions missing";
209
210 if (!m_wgl.wglDXCloseDeviceNV(m_handles.device))
211 qCDebug(qLcEvrD3DPresentEngine) << "Failed to close D3D-GL device";
212
213 } else {
214 qCDebug(qLcEvrD3DPresentEngine) << "Could not release texture, OpenGL context missing";
215 }
216 }
217
218 static std::unique_ptr<QVideoFrameOpenGlTextures> create(const WglNvDxInterop &wgl, QRhi *rhi,
219 IDirect3DDevice9Ex *device, IDirect3DTexture9 *texture,
220 HANDLE sharedHandle)
221 {
222 if (!rhi || rhi->backend() != QRhi::OpenGLES2)
223 return {};
224
225 if (!QOpenGLContext::currentContext())
226 return {};
227
228 InterOpHandles handles = {};
229 handles.device = wgl.wglDXOpenDeviceNV(device);
230 if (!handles.device) {
231 qCDebug(qLcEvrD3DPresentEngine) << "Failed to open D3D device";
232 return {};
233 }
234
235 wgl.wglDXSetResourceShareHandleNV(texture, sharedHandle);
236
237 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
238 if (funcs) {
239 funcs->glGenTextures(1, &handles.textureName);
240 handles.texture = wgl.wglDXRegisterObjectNV(handles.device, texture, handles.textureName,
241 GL_TEXTURE_2D, WglNvDxInterop::WGL_ACCESS_READ_ONLY_NV);
242 if (handles.texture) {
243 if (wgl.wglDXLockObjectsNV(handles.device, 1, &handles.texture)) {
244 D3DSURFACE_DESC desc;
245 texture->GetLevelDesc(0, &desc);
246 QRhiTexture::Format format;
247 if (desc.Format == D3DFMT_A8R8G8B8)
248 format = QRhiTexture::BGRA8;
249 else if (desc.Format == D3DFMT_A8B8G8R8)
250 format = QRhiTexture::RGBA8;
251 else
252 return {};
253
254 std::unique_ptr<QRhiTexture> tex(rhi->newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
255 tex->createFrom({quint64(handles.textureName), 0});
256 return std::make_unique<QVideoFrameOpenGlTextures>(std::move(tex), wgl, handles);
257 }
258
259 qCDebug(qLcEvrD3DPresentEngine) << "Failed to lock OpenGL texture";
260 wgl.wglDXUnregisterObjectNV(handles.device, handles.texture);
261 } else {
262 qCDebug(qLcEvrD3DPresentEngine) << "Could not register D3D9 texture in OpenGL";
263 }
264
265 funcs->glDeleteTextures(1, &handles.textureName);
266 } else {
267 qCDebug(qLcEvrD3DPresentEngine) << "Failed generate texture names, OpenGL context functions missing";
268 }
269 return {};
270 }
271
272 QRhiTexture *texture(uint plane) const override { return plane == 0 ? m_tex.get() : nullptr; }
273
274private:
275 std::unique_ptr<QRhiTexture> m_tex;
276 WglNvDxInterop m_wgl;
277 InterOpHandles m_handles;
278};
279
280class OpenGlVideoBuffer: public IMFSampleVideoBuffer
281{
282public:
283 OpenGlVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
284 const WglNvDxInterop &wglNvDxInterop, HANDLE sharedHandle, QRhi *rhi)
285 : IMFSampleVideoBuffer(std::move(device), sample, rhi, QVideoFrame::RhiTextureHandle)
286 , m_sharedHandle(sharedHandle)
287 , m_wgl(wglNvDxInterop)
288 {}
289
290 QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr& /*oldTextures*/) override
291 {
292 if (!m_texture) {
293 ComPtr<IMFMediaBuffer> buffer;
294 HRESULT hr = m_sample->GetBufferByIndex(0, buffer.GetAddressOf());
295 if (FAILED(hr))
296 return {};
297
298 ComPtr<IDirect3DSurface9> surface;
299 hr = MFGetService(buffer.Get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9,
300 (void **)(surface.GetAddressOf()));
301 if (FAILED(hr))
302 return {};
303
304 hr = surface->GetContainer(IID_IDirect3DTexture9, (void **)m_texture.GetAddressOf());
305 if (FAILED(hr))
306 return {};
307 }
308
309 return QVideoFrameOpenGlTextures::create(m_wgl, &rhi, m_device.Get(), m_texture.Get(), m_sharedHandle);
310 }
311
312private:
313 HANDLE m_sharedHandle = nullptr;
314 WglNvDxInterop m_wgl;
315 ComPtr<IDirect3DTexture9> m_texture;
316};
317#endif
318
319D3DPresentEngine::D3DPresentEngine(QVideoSink *sink)
320 : m_deviceResetToken(0)
321{
322 ZeroMemory(&m_displayMode, sizeof(m_displayMode));
323 setSink(sink);
324}
325
326D3DPresentEngine::~D3DPresentEngine()
327{
328 releaseResources();
329}
330
331void D3DPresentEngine::setSink(QVideoSink *sink)
332{
333 if (sink == m_sink)
334 return;
335
336 m_sink = sink;
337
338 releaseResources();
339 m_device.Reset();
340 m_devices.Reset();
341 m_D3D9.Reset();
342
343 if (!m_sink)
344 return;
345
346 HRESULT hr = initializeD3D();
347
348 if (SUCCEEDED(hr)) {
349 hr = createD3DDevice();
350 if (FAILED(hr))
351 qWarning("Failed to create D3D device");
352 } else {
353 qWarning("Failed to initialize D3D");
354 }
355}
356
357HRESULT D3DPresentEngine::initializeD3D()
358{
359 HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.GetAddressOf());
360
361 if (SUCCEEDED(hr))
362 hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.GetAddressOf());
363
364 return hr;
365}
366
367static bool findD3D11AdapterID(QRhi &rhi, IDirect3D9Ex *D3D9, UINT &adapterID)
368{
369 auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
370 if (D3D9 && nh) {
371 for (auto i = 0u; i < D3D9->GetAdapterCount(); ++i) {
372 LUID luid = {};
373 D3D9->GetAdapterLUID(i, &luid);
374 if (luid.LowPart == nh->adapterLuidLow && luid.HighPart == nh->adapterLuidHigh) {
375 adapterID = i;
376 return true;
377 }
378 }
379 }
380
381 return false;
382}
383
384#if QT_CONFIG(opengl)
385template <typename T>
386static bool getProc(const QOpenGLContext *ctx, T &fn, const char *fName)
387{
388 fn = reinterpret_cast<T>(ctx->getProcAddress(fName));
389 return fn != nullptr;
390}
391
392static bool readWglNvDxInteropProc(WglNvDxInterop &f)
393{
394 QScopedPointer<QOffscreenSurface> surface(new QOffscreenSurface);
395 surface->create();
396 QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
397 ctx->create();
398 ctx->makeCurrent(surface.get());
399
400 auto wglGetExtensionsStringARB = reinterpret_cast<const char* (WINAPI* )(HDC)>
401 (ctx->getProcAddress("wglGetExtensionsStringARB"));
402 if (!wglGetExtensionsStringARB) {
403 qCDebug(qLcEvrD3DPresentEngine) << "WGL extensions missing (no wglGetExtensionsStringARB function)";
404 return false;
405 }
406
407 HWND hwnd = ::GetShellWindow();
408 auto dc = ::GetDC(hwnd);
409
410 const char *wglExtString = wglGetExtensionsStringARB(dc);
411 if (!wglExtString)
412 qCDebug(qLcEvrD3DPresentEngine) << "WGL extensions missing (wglGetExtensionsStringARB returned null)";
413
414 bool hasExtension = wglExtString && strstr(wglExtString, "WGL_NV_DX_interop");
415 ReleaseDC(hwnd, dc);
416 if (!hasExtension) {
417 qCDebug(qLcEvrD3DPresentEngine) << "WGL_NV_DX_interop missing";
418 return false;
419 }
420
421 return getProc(ctx.get(), f.wglDXOpenDeviceNV, "wglDXOpenDeviceNV")
422 && getProc(ctx.get(), f.wglDXCloseDeviceNV, "wglDXCloseDeviceNV")
423 && getProc(ctx.get(), f.wglDXSetResourceShareHandleNV, "wglDXSetResourceShareHandleNV")
424 && getProc(ctx.get(), f.wglDXRegisterObjectNV, "wglDXRegisterObjectNV")
425 && getProc(ctx.get(), f.wglDXUnregisterObjectNV, "wglDXUnregisterObjectNV")
426 && getProc(ctx.get(), f.wglDXLockObjectsNV, "wglDXLockObjectsNV")
427 && getProc(ctx.get(), f.wglDXUnlockObjectsNV, "wglDXUnlockObjectsNV");
428}
429#endif
430
431namespace {
432
433bool hwTextureRenderingEnabled() {
434 // add possibility for an user to opt-out HW video rendering
435 // using the same env. variable as for FFmpeg backend
436 static bool isDisableConversionSet = false;
437 static const int disableHwConversion = qEnvironmentVariableIntValue(
438 "QT_DISABLE_HW_TEXTURES_CONVERSION", &isDisableConversionSet);
439
440 return !isDisableConversionSet || !disableHwConversion;
441}
442
443}
444
445HRESULT D3DPresentEngine::createD3DDevice()
446{
447 if (!m_D3D9 || !m_devices)
448 return MF_E_NOT_INITIALIZED;
449
450 m_useTextureRendering = false;
451 UINT adapterID = 0;
452
453 if (hwTextureRenderingEnabled()) {
454 QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
455 if (rhi) {
456 if (rhi->backend() == QRhi::D3D11) {
457 m_useTextureRendering = findD3D11AdapterID(*rhi, m_D3D9.Get(), adapterID);
458#if QT_CONFIG(opengl)
459 } else if (rhi->backend() == QRhi::OpenGLES2) {
460 m_useTextureRendering = readWglNvDxInteropProc(m_wglNvDxInterop);
461#endif
462 } else {
463 qCDebug(qLcEvrD3DPresentEngine) << "Not supported RHI backend type";
464 }
465 } else {
466 qCDebug(qLcEvrD3DPresentEngine) << "No RHI associated with this sink";
467 }
468
469 if (!m_useTextureRendering)
470 qCDebug(qLcEvrD3DPresentEngine) << "Could not find compatible RHI adapter, zero copy disabled";
471 }
472
473 D3DCAPS9 ddCaps;
474 ZeroMemory(&ddCaps, sizeof(ddCaps));
475
476 HRESULT hr = m_D3D9->GetDeviceCaps(adapterID, D3DDEVTYPE_HAL, &ddCaps);
477 if (FAILED(hr))
478 return hr;
479
480 DWORD vp = 0;
481 if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
482 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
483 else
484 vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
485
486 D3DPRESENT_PARAMETERS pp;
487 ZeroMemory(&pp, sizeof(pp));
488
489 pp.BackBufferWidth = 1;
490 pp.BackBufferHeight = 1;
491 pp.BackBufferCount = 1;
492 pp.Windowed = TRUE;
493 pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
494 pp.BackBufferFormat = D3DFMT_UNKNOWN;
495 pp.hDeviceWindow = nullptr;
496 pp.Flags = D3DPRESENTFLAG_VIDEO;
497 pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
498
499 ComPtr<IDirect3DDevice9Ex> device;
500
501 hr = m_D3D9->CreateDeviceEx(
502 adapterID,
503 D3DDEVTYPE_HAL,
504 pp.hDeviceWindow,
505 vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
506 &pp,
507 NULL,
508 device.GetAddressOf()
509 );
510 if (FAILED(hr))
511 return hr;
512
513 hr = m_D3D9->GetAdapterDisplayMode(adapterID, &m_displayMode);
514 if (FAILED(hr))
515 return hr;
516
517 hr = m_devices->ResetDevice(device.Get(), m_deviceResetToken);
518 if (FAILED(hr))
519 return hr;
520
521 m_device = device;
522 return hr;
523}
524
525bool D3DPresentEngine::isValid() const
526{
527 return m_device.Get() != nullptr;
528}
529
530void D3DPresentEngine::releaseResources()
531{
532 m_surfaceFormat = QVideoFrameFormat();
533}
534
535HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
536{
537 HRESULT hr = S_OK;
538
539 if (riid == __uuidof(IDirect3DDeviceManager9)) {
540 if (!m_devices) {
541 hr = MF_E_UNSUPPORTED_SERVICE;
542 } else {
543 *ppv = m_devices.Get();
544 m_devices->AddRef();
545 }
546 } else {
547 hr = MF_E_UNSUPPORTED_SERVICE;
548 }
549
550 return hr;
551}
552
553HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
554{
555 if (!m_D3D9 || !m_device)
556 return E_FAIL;
557
558 HRESULT hr = S_OK;
559
560 D3DDISPLAYMODE mode;
561 D3DDEVICE_CREATION_PARAMETERS params;
562
563 hr = m_device->GetCreationParameters(&params);
564 if (FAILED(hr))
565 return hr;
566
567 UINT uAdapter = params.AdapterOrdinal;
568 D3DDEVTYPE type = params.DeviceType;
569
570 hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
571 if (FAILED(hr))
572 return hr;
573
574 hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format,
575 D3DUSAGE_RENDERTARGET,
576 D3DRTYPE_SURFACE,
577 format);
578 if (FAILED(hr))
579 return hr;
580
581 bool ok = format == D3DFMT_X8R8G8B8
582 || format == D3DFMT_A8R8G8B8
583 || format == D3DFMT_X8B8G8R8
584 || format == D3DFMT_A8B8G8R8;
585
586 return ok ? S_OK : D3DERR_NOTAVAILABLE;
587}
588
589HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format,
590 QList<ComPtr<IMFSample>> &videoSampleQueue,
591 QSize frameSize)
592{
593 if (!format || !m_device)
594 return MF_E_UNEXPECTED;
595
596 HRESULT hr = S_OK;
597 releaseResources();
598
599 UINT32 width = 0, height = 0;
600 hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
601 if (FAILED(hr))
602 return hr;
603
604 if (frameSize.isValid() && !frameSize.isEmpty()) {
605 width = frameSize.width();
606 height = frameSize.height();
607 }
608
609 DWORD d3dFormat = 0;
610 hr = qt_evr_getFourCC(format, &d3dFormat);
611 if (FAILED(hr))
612 return hr;
613
614 // FIXME: RHI defines only RGBA, thus add the alpha channel to the selected format
615 if (d3dFormat == D3DFMT_X8R8G8B8)
616 d3dFormat = D3DFMT_A8R8G8B8;
617 else if (d3dFormat == D3DFMT_X8B8G8R8)
618 d3dFormat = D3DFMT_A8B8G8R8;
619
620 for (int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
621 // texture ref cnt is increased by GetSurfaceLevel()/MFCreateVideoSampleFromSurface()
622 // below, so it will be destroyed only when the sample pool is released.
623 ComPtr<IDirect3DTexture9> texture;
624 HANDLE sharedHandle = nullptr;
625 hr = m_device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, (D3DFORMAT)d3dFormat, D3DPOOL_DEFAULT, texture.GetAddressOf(), &sharedHandle);
626 if (FAILED(hr))
627 break;
628
629 ComPtr<IDirect3DSurface9> surface;
630 hr = texture->GetSurfaceLevel(0, surface.GetAddressOf());
631 if (FAILED(hr))
632 break;
633
634 ComPtr<IMFSample> videoSample;
635 hr = MFCreateVideoSampleFromSurface(surface.Get(), videoSample.GetAddressOf());
636 if (FAILED(hr))
637 break;
638
639 m_sampleTextureHandle[i] = {videoSample.Get(), sharedHandle};
640 videoSampleQueue.append(videoSample);
641 }
642
643 std::optional<QVideoFrameFormat::ColorRange> colorRange =
644 [&]() -> std::optional<QVideoFrameFormat::ColorRange> {
645 MFNominalRange range;
646 if (SUCCEEDED(format->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE,
647 reinterpret_cast<UINT32 *>(&range)))) {
648 qCDebug(qLcEvrD3DPresentEngine) << "MF_MT_VIDEO_NOMINAL_RANGE =" << range;
649 switch (range) {
650 case MFNominalRange::MFNominalRange_16_235:
651 return QVideoFrameFormat::ColorRange::ColorRange_Video;
652 case MFNominalRange::MFNominalRange_0_255:
653 return QVideoFrameFormat::ColorRange::ColorRange_Full;
654 default:
655 break;
656 }
657 } else {
658 qCDebug(qLcEvrD3DPresentEngine) << "MF_MT_VIDEO_NOMINAL_RANGE not set";
659 }
660 return std::nullopt;
661 }();
662
663 std::optional<QVideoFrameFormat::ColorTransfer> colorTransfer =
664 [&]() -> std::optional<QVideoFrameFormat::ColorTransfer> {
665 MFVideoTransferMatrix transferMatrix;
666 if (SUCCEEDED(format->GetUINT32(MF_MT_YUV_MATRIX,
667 reinterpret_cast<UINT32 *>(&transferMatrix)))) {
668 qCDebug(qLcEvrD3DPresentEngine) << "MF_MT_YUV_MATRIX =" << transferMatrix;
669
670 switch (transferMatrix) {
671 case MFVideoTransferMatrix_BT709:
672 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT709;
673 case MFVideoTransferMatrix_BT601:
674 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT601;
675 case MFVideoTransferMatrix_SMPTE240M:
676 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT709;
677 case MFVideoTransferMatrix_BT2020_10:
678 case MFVideoTransferMatrix_BT2020_12:
679 return QVideoFrameFormat::ColorTransfer::ColorTransfer_ST2084;
680 default:
681 break;
682 }
683 } else {
684 qCDebug(qLcEvrD3DPresentEngine) << "MF_MT_YUV_MATRIX not set";
685 }
686 return std::nullopt;
687 }();
688
689 std::optional<QVideoFrameFormat::ColorSpace> colorSpace =
690 [&]() -> std::optional<QVideoFrameFormat::ColorSpace> {
691 MFVideoPrimaries primaries;
692 if (SUCCEEDED(format->GetUINT32(MF_MT_VIDEO_PRIMARIES,
693 reinterpret_cast<UINT32 *>(&primaries)))) {
694 qCDebug(qLcEvrD3DPresentEngine) << "MFVideoPrimaries =" << primaries;
695
696 switch (primaries) {
697 case MFVideoPrimaries_BT709:
698 return QVideoFrameFormat::ColorSpace::ColorSpace_BT709;
699 case MFVideoPrimaries_BT470_2_SysM:
700 case MFVideoPrimaries_BT470_2_SysBG:
701 case MFVideoPrimaries_SMPTE170M:
702 case MFVideoPrimaries_EBU3213:
703 case MFVideoPrimaries_SMPTE_C:
704 return QVideoFrameFormat::ColorSpace::ColorSpace_BT601;
705
706 case MFVideoPrimaries_SMPTE240M:
707 return QVideoFrameFormat::ColorSpace::ColorSpace_BT709;
708 case MFVideoPrimaries_BT2020:
709 return QVideoFrameFormat::ColorSpace::ColorSpace_BT2020;
710 case MFVideoPrimaries_DCI_P3:
711 return QVideoFrameFormat::ColorSpace::ColorSpace_BT2020;
712 case MFVideoPrimaries_XYZ:
713 case MFVideoPrimaries_ACES:
714 return QVideoFrameFormat::ColorSpace::ColorSpace_Undefined;
715 default:
716 return QVideoFrameFormat::ColorSpace::ColorSpace_Undefined;
717 }
718 } else {
719 qCDebug(qLcEvrD3DPresentEngine) << "MF_MT_YUV_MATRIX not set";
720 }
721 return std::nullopt;
722 }();
723
724 if (SUCCEEDED(hr)) {
725 m_surfaceFormat = QVideoFrameFormat(QSize(width, height), qt_evr_pixelFormatFromD3DFormat(d3dFormat));
726 if (colorRange)
727 m_surfaceFormat.setColorRange(*colorRange);
728 if (colorTransfer)
729 m_surfaceFormat.setColorTransfer(*colorTransfer);
730 if (colorSpace)
731 m_surfaceFormat.setColorSpace(*colorSpace);
732 } else {
733 releaseResources();
734 }
735
736 return hr;
737}
738
739QVideoFrame D3DPresentEngine::makeVideoFrame(const ComPtr<IMFSample> &sample,
740 QtVideo::Rotation rotation)
741{
742 if (!sample)
743 return {};
744
745 HANDLE sharedHandle = nullptr;
746 for (const auto &p : m_sampleTextureHandle)
747 if (p.first == sample.Get())
748 sharedHandle = p.second;
749
750 std::unique_ptr<IMFSampleVideoBuffer> vb;
751 QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
752 if (m_useTextureRendering && sharedHandle && rhi) {
753 if (rhi->backend() == QRhi::D3D11) {
754 vb = std::make_unique<D3D11TextureVideoBuffer>(m_device, sample, sharedHandle, rhi);
755#if QT_CONFIG(opengl)
756 } else if (rhi->backend() == QRhi::OpenGLES2) {
757 vb = std::make_unique<OpenGlVideoBuffer>(m_device, sample, m_wglNvDxInterop,
758 sharedHandle, rhi);
759#endif
760 }
761 }
762
763 if (!vb)
764 vb = std::make_unique<IMFSampleVideoBuffer>(m_device, sample, rhi);
765
766 auto format = m_surfaceFormat;
767 format.setRotation(rotation);
768 QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(vb), format);
769
770 // WMF uses 100-nanosecond units, Qt uses microseconds
771 LONGLONG startTime = 0;
772 auto hr = sample->GetSampleTime(&startTime);
773 if (SUCCEEDED(hr)) {
774 frame.setStartTime(startTime / 10);
775
776 LONGLONG duration = -1;
777 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
778 frame.setEndTime((startTime + duration) / 10);
779 }
780
781 return frame;
782}
783
784QT_END_NAMESPACE
D3D11TextureVideoBuffer(ComPtr< IDirect3DDevice9Ex > device, const ComPtr< IMFSample > &sample, HANDLE sharedHandle, QRhi *rhi)
QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr &) override
QVideoFrameD3D11Textures(std::unique_ptr< QRhiTexture > &&tex, ComPtr< ID3D11Texture2D > &&d3d11tex)
QRhiTexture * texture(uint plane) const override
static bool findD3D11AdapterID(QRhi &rhi, IDirect3D9Ex *D3D9, UINT &adapterID)
Combined button and popup list for selecting options.
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)