8#include <private/qhwvideobuffer_p.h>
9#include <private/qvideoframe_p.h>
10#include <qvideoframe.h>
13#include <qvideosink.h>
14#include <qloggingcategory.h>
21# include <qopenglcontext.h>
22# include <qopenglfunctions.h>
23# include <qoffscreensurface.h>
34 QRhi *rhi, QVideoFrame::HandleType type = QVideoFrame::NoHandle)
35 : QHwVideoBuffer(type, rhi),
38 m_mapMode(QVideoFrame::NotMapped)
110 ComPtr<IDirect3DSurface9> m_memSurface;
122 QRhiTexture *
texture(uint plane)
const override {
return plane == 0 ? m_tex.get() :
nullptr; }
125 std::unique_ptr<QRhiTexture> m_tex;
126 ComPtr<ID3D11Texture2D> m_d3d11tex;
133 HANDLE sharedHandle, QRhi *rhi)
140 if (rhi.backend() != QRhi::D3D11)
143 auto nh =
static_cast<
const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
147 auto dev =
reinterpret_cast<ID3D11Device *>(nh->dev);
151 ComPtr<ID3D11Texture2D> d3d11tex;
152 HRESULT hr = dev->OpenSharedResource(m_sharedHandle, __uuidof(ID3D11Texture2D), (
void**)(d3d11tex.GetAddressOf()));
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;
164 std::unique_ptr<QRhiTexture> tex(rhi.newTexture(format, QSize{
int(desc.Width),
int(desc.Height)}, 1, {}));
165 tex->createFrom({quint64(d3d11tex.Get()), 0});
169 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to obtain D3D11Texture2D from D3D9Texture2D handle";
175 HANDLE m_sharedHandle =
nullptr;
179class QVideoFrameOpenGlTextures :
public QVideoFrameTextures
181 struct InterOpHandles {
182 GLuint textureName = 0;
183 HANDLE device =
nullptr;
184 HANDLE texture =
nullptr;
188 Q_DISABLE_COPY(QVideoFrameOpenGlTextures)
190 QVideoFrameOpenGlTextures(std::unique_ptr<QRhiTexture> &&tex,
const WglNvDxInterop &wgl, InterOpHandles &handles)
191 : m_tex(std::move(tex))
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";
201 if (!m_wgl.wglDXUnregisterObjectNV(m_handles.device, m_handles.texture))
202 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to unregister OpenGL texture";
204 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
206 funcs->glDeleteTextures(1, &m_handles.textureName);
208 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not delete texture, OpenGL context functions missing";
210 if (!m_wgl.wglDXCloseDeviceNV(m_handles.device))
211 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to close D3D-GL device";
214 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not release texture, OpenGL context missing";
218 static std::unique_ptr<QVideoFrameOpenGlTextures> create(
const WglNvDxInterop &wgl, QRhi *rhi,
219 IDirect3DDevice9Ex *device, IDirect3DTexture9 *texture,
222 if (!rhi || rhi->backend() != QRhi::OpenGLES2)
225 if (!QOpenGLContext::currentContext())
228 InterOpHandles handles = {};
229 handles.device = wgl.wglDXOpenDeviceNV(device);
230 if (!handles.device) {
231 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to open D3D device";
235 wgl.wglDXSetResourceShareHandleNV(texture, sharedHandle);
237 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
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;
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);
259 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to lock OpenGL texture";
260 wgl.wglDXUnregisterObjectNV(handles.device, handles.texture);
262 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not register D3D9 texture in OpenGL";
265 funcs->glDeleteTextures(1, &handles.textureName);
267 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed generate texture names, OpenGL context functions missing";
272 QRhiTexture *texture(uint plane)
const override {
return plane == 0 ? m_tex.get() :
nullptr; }
275 std::unique_ptr<QRhiTexture> m_tex;
276 WglNvDxInterop m_wgl;
277 InterOpHandles m_handles;
280class OpenGlVideoBuffer:
public IMFSampleVideoBuffer
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)
290 QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr& ) override
293 ComPtr<IMFMediaBuffer> buffer;
294 HRESULT hr = m_sample->GetBufferByIndex(0, buffer.GetAddressOf());
298 ComPtr<IDirect3DSurface9> surface;
299 hr = MFGetService(buffer.Get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9,
300 (
void **)(surface.GetAddressOf()));
304 hr = surface->GetContainer(IID_IDirect3DTexture9, (
void **)m_texture.GetAddressOf());
309 return QVideoFrameOpenGlTextures::create(m_wgl, &rhi, m_device.Get(), m_texture.Get(), m_sharedHandle);
313 HANDLE m_sharedHandle =
nullptr;
314 WglNvDxInterop m_wgl;
315 ComPtr<IDirect3DTexture9> m_texture;
319D3DPresentEngine::D3DPresentEngine(QVideoSink *sink)
320 : m_deviceResetToken(0)
322 ZeroMemory(&m_displayMode,
sizeof(m_displayMode));
326D3DPresentEngine::~D3DPresentEngine()
331void D3DPresentEngine::setSink(QVideoSink *sink)
346 HRESULT hr = initializeD3D();
349 hr = createD3DDevice();
351 qWarning(
"Failed to create D3D device");
353 qWarning(
"Failed to initialize D3D");
357HRESULT D3DPresentEngine::initializeD3D()
359 HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.GetAddressOf());
362 hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.GetAddressOf());
369 auto nh =
static_cast<
const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
371 for (
auto i = 0u; i < D3D9->GetAdapterCount(); ++i) {
373 D3D9->GetAdapterLUID(i, &luid);
374 if (luid.LowPart == nh->adapterLuidLow && luid.HighPart == nh->adapterLuidHigh) {
386static bool getProc(
const QOpenGLContext *ctx, T &fn,
const char *fName)
388 fn =
reinterpret_cast<T>(ctx->getProcAddress(fName));
389 return fn !=
nullptr;
392static bool readWglNvDxInteropProc(WglNvDxInterop &f)
394 QScopedPointer<QOffscreenSurface> surface(
new QOffscreenSurface);
396 QScopedPointer<QOpenGLContext> ctx(
new QOpenGLContext);
398 ctx->makeCurrent(surface.get());
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)";
407 HWND hwnd = ::GetShellWindow();
408 auto dc = ::GetDC(hwnd);
410 const char *wglExtString = wglGetExtensionsStringARB(dc);
412 qCDebug(qLcEvrD3DPresentEngine) <<
"WGL extensions missing (wglGetExtensionsStringARB returned null)";
414 bool hasExtension = wglExtString && strstr(wglExtString,
"WGL_NV_DX_interop");
417 qCDebug(qLcEvrD3DPresentEngine) <<
"WGL_NV_DX_interop missing";
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");
433bool hwTextureRenderingEnabled() {
436 static bool isDisableConversionSet =
false;
437 static const int disableHwConversion = qEnvironmentVariableIntValue(
438 "QT_DISABLE_HW_TEXTURES_CONVERSION", &isDisableConversionSet);
440 return !isDisableConversionSet || !disableHwConversion;
445HRESULT D3DPresentEngine::createD3DDevice()
447 if (!m_D3D9 || !m_devices)
448 return MF_E_NOT_INITIALIZED;
450 m_useTextureRendering =
false;
453 if (hwTextureRenderingEnabled()) {
454 QRhi *rhi = m_sink ? m_sink->rhi() :
nullptr;
456 if (rhi->backend() == QRhi::D3D11) {
457 m_useTextureRendering = findD3D11AdapterID(*rhi, m_D3D9.Get(), adapterID);
459 }
else if (rhi->backend() == QRhi::OpenGLES2) {
460 m_useTextureRendering = readWglNvDxInteropProc(m_wglNvDxInterop);
463 qCDebug(qLcEvrD3DPresentEngine) <<
"Not supported RHI backend type";
466 qCDebug(qLcEvrD3DPresentEngine) <<
"No RHI associated with this sink";
469 if (!m_useTextureRendering)
470 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not find compatible RHI adapter, zero copy disabled";
474 ZeroMemory(&ddCaps,
sizeof(ddCaps));
476 HRESULT hr = m_D3D9->GetDeviceCaps(adapterID, D3DDEVTYPE_HAL, &ddCaps);
481 if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
482 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
484 vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
486 D3DPRESENT_PARAMETERS pp;
487 ZeroMemory(&pp,
sizeof(pp));
489 pp.BackBufferWidth = 1;
490 pp.BackBufferHeight = 1;
491 pp.BackBufferCount = 1;
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;
499 ComPtr<IDirect3DDevice9Ex> device;
501 hr = m_D3D9->CreateDeviceEx(
505 vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
508 device.GetAddressOf()
513 hr = m_D3D9->GetAdapterDisplayMode(adapterID, &m_displayMode);
517 hr = m_devices->ResetDevice(device.Get(), m_deviceResetToken);
525bool D3DPresentEngine::isValid()
const
527 return m_device.Get() !=
nullptr;
530void D3DPresentEngine::releaseResources()
532 m_surfaceFormat = QVideoFrameFormat();
535HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid,
void** ppv)
539 if (riid == __uuidof(IDirect3DDeviceManager9)) {
541 hr = MF_E_UNSUPPORTED_SERVICE;
543 *ppv = m_devices.Get();
547 hr = MF_E_UNSUPPORTED_SERVICE;
553HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
555 if (!m_D3D9 || !m_device)
561 D3DDEVICE_CREATION_PARAMETERS params;
563 hr = m_device->GetCreationParameters(¶ms);
567 UINT uAdapter = params.AdapterOrdinal;
568 D3DDEVTYPE type = params.DeviceType;
570 hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
574 hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format,
575 D3DUSAGE_RENDERTARGET,
581 bool ok = format == D3DFMT_X8R8G8B8
582 || format == D3DFMT_A8R8G8B8
583 || format == D3DFMT_X8B8G8R8
584 || format == D3DFMT_A8B8G8R8;
586 return ok ? S_OK : D3DERR_NOTAVAILABLE;
589HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format,
590 QList<ComPtr<IMFSample>> &videoSampleQueue,
593 if (!format || !m_device)
594 return MF_E_UNEXPECTED;
599 UINT32 width = 0, height = 0;
600 hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
604 if (frameSize.isValid() && !frameSize.isEmpty()) {
605 width = frameSize.width();
606 height = frameSize.height();
610 hr = qt_evr_getFourCC(format, &d3dFormat);
615 if (d3dFormat == D3DFMT_X8R8G8B8)
616 d3dFormat = D3DFMT_A8R8G8B8;
617 else if (d3dFormat == D3DFMT_X8B8G8R8)
618 d3dFormat = D3DFMT_A8B8G8R8;
620 for (
int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
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);
629 ComPtr<IDirect3DSurface9> surface;
630 hr = texture->GetSurfaceLevel(0, surface.GetAddressOf());
634 ComPtr<IMFSample> videoSample;
635 hr = MFCreateVideoSampleFromSurface(surface.Get(), videoSample.GetAddressOf());
639 m_sampleTextureHandle[i] = {videoSample.Get(), sharedHandle};
640 videoSampleQueue.append(videoSample);
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;
650 case MFNominalRange::MFNominalRange_16_235:
651 return QVideoFrameFormat::ColorRange::ColorRange_Video;
652 case MFNominalRange::MFNominalRange_0_255:
653 return QVideoFrameFormat::ColorRange::ColorRange_Full;
658 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_VIDEO_NOMINAL_RANGE not set";
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;
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;
684 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_YUV_MATRIX not set";
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;
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;
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;
716 return QVideoFrameFormat::ColorSpace::ColorSpace_Undefined;
719 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_YUV_MATRIX not set";
725 m_surfaceFormat = QVideoFrameFormat(QSize(width, height), qt_evr_pixelFormatFromD3DFormat(d3dFormat));
727 m_surfaceFormat.setColorRange(*colorRange);
729 m_surfaceFormat.setColorTransfer(*colorTransfer);
731 m_surfaceFormat.setColorSpace(*colorSpace);
739QVideoFrame D3DPresentEngine::makeVideoFrame(
const ComPtr<IMFSample> &sample,
740 QtVideo::Rotation rotation)
745 HANDLE sharedHandle =
nullptr;
746 for (
const auto &p : m_sampleTextureHandle)
747 if (p.first == sample.Get())
748 sharedHandle = p.second;
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) {
756 }
else if (rhi->backend() == QRhi::OpenGLES2) {
757 vb = std::make_unique<OpenGlVideoBuffer>(m_device, sample, m_wglNvDxInterop,
766 auto format = m_surfaceFormat;
767 format.setRotation(rotation);
768 QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(vb), format);
771 LONGLONG startTime = 0;
772 auto hr = sample->GetSampleTime(&startTime);
774 frame.setStartTime(startTime / 10);
776 LONGLONG duration = -1;
777 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
778 frame.setEndTime((startTime + duration) / 10);
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,...)