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;
124 return plane == 0 ? m_tex.get() :
nullptr;
128 std::unique_ptr<QRhiTexture> m_tex;
129 ComPtr<ID3D11Texture2D> m_d3d11tex;
136 HANDLE sharedHandle, QRhi *rhi)
143 if (rhi.backend() != QRhi::D3D11)
146 auto nh =
static_cast<
const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
150 auto dev =
reinterpret_cast<ID3D11Device *>(nh->dev);
154 ComPtr<ID3D11Texture2D> d3d11tex;
155 HRESULT hr = dev->OpenSharedResource(m_sharedHandle, __uuidof(ID3D11Texture2D), (
void**)(d3d11tex.GetAddressOf()));
157 D3D11_TEXTURE2D_DESC desc = {};
158 d3d11tex->GetDesc(&desc);
159 QRhiTexture::Format format;
160 if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM)
161 format = QRhiTexture::BGRA8;
162 else if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
163 format = QRhiTexture::RGBA8;
167 std::unique_ptr<QRhiTexture> tex(rhi.newTexture(format, QSize{
int(desc.Width),
int(desc.Height)}, 1, {}));
168 tex->createFrom({quint64(d3d11tex.Get()), 0});
169 return std::make_unique<QVideoFrameD3D11Textures>(std::move(tex), std::move(d3d11tex));
172 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to obtain D3D11Texture2D from D3D9Texture2D handle";
178 HANDLE m_sharedHandle =
nullptr;
182class QVideoFrameOpenGlTextures :
public QVideoFrameTextures
184 struct InterOpHandles {
185 GLuint textureName = 0;
186 HANDLE device =
nullptr;
187 HANDLE texture =
nullptr;
191 Q_DISABLE_COPY(QVideoFrameOpenGlTextures);
193 QVideoFrameOpenGlTextures(std::unique_ptr<QRhiTexture> &&tex,
const WglNvDxInterop &wgl, InterOpHandles &handles)
194 : m_tex(std::move(tex))
199 ~QVideoFrameOpenGlTextures() override {
200 if (QOpenGLContext::currentContext()) {
201 if (!m_wgl.wglDXUnlockObjectsNV(m_handles.device, 1, &m_handles.texture))
202 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to unlock OpenGL texture";
204 if (!m_wgl.wglDXUnregisterObjectNV(m_handles.device, m_handles.texture))
205 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to unregister OpenGL texture";
207 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
209 funcs->glDeleteTextures(1, &m_handles.textureName);
211 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not delete texture, OpenGL context functions missing";
213 if (!m_wgl.wglDXCloseDeviceNV(m_handles.device))
214 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to close D3D-GL device";
217 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not release texture, OpenGL context missing";
221 static std::unique_ptr<QVideoFrameOpenGlTextures> create(
const WglNvDxInterop &wgl, QRhi *rhi,
222 IDirect3DDevice9Ex *device, IDirect3DTexture9 *texture,
225 if (!rhi || rhi->backend() != QRhi::OpenGLES2)
228 if (!QOpenGLContext::currentContext())
231 InterOpHandles handles = {};
232 handles.device = wgl.wglDXOpenDeviceNV(device);
233 if (!handles.device) {
234 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to open D3D device";
238 wgl.wglDXSetResourceShareHandleNV(texture, sharedHandle);
240 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
242 funcs->glGenTextures(1, &handles.textureName);
243 handles.texture = wgl.wglDXRegisterObjectNV(handles.device, texture, handles.textureName,
244 GL_TEXTURE_2D, WglNvDxInterop::WGL_ACCESS_READ_ONLY_NV);
245 if (handles.texture) {
246 if (wgl.wglDXLockObjectsNV(handles.device, 1, &handles.texture)) {
247 D3DSURFACE_DESC desc;
248 texture->GetLevelDesc(0, &desc);
249 QRhiTexture::Format format;
250 if (desc.Format == D3DFMT_A8R8G8B8)
251 format = QRhiTexture::BGRA8;
252 else if (desc.Format == D3DFMT_A8B8G8R8)
253 format = QRhiTexture::RGBA8;
257 std::unique_ptr<QRhiTexture> tex(rhi->newTexture(format, QSize{
int(desc.Width),
int(desc.Height)}, 1, {}));
258 tex->createFrom({quint64(handles.textureName), 0});
259 return std::make_unique<QVideoFrameOpenGlTextures>(std::move(tex), wgl, handles);
262 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed to lock OpenGL texture";
263 wgl.wglDXUnregisterObjectNV(handles.device, handles.texture);
265 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not register D3D9 texture in OpenGL";
268 funcs->glDeleteTextures(1, &handles.textureName);
270 qCDebug(qLcEvrD3DPresentEngine) <<
"Failed generate texture names, OpenGL context functions missing";
275 QRhiTexture *texture(uint plane)
const override
277 return plane == 0 ? m_tex.get() :
nullptr;
280 std::unique_ptr<QRhiTexture> m_tex;
281 WglNvDxInterop m_wgl;
282 InterOpHandles m_handles;
285class OpenGlVideoBuffer:
public IMFSampleVideoBuffer
288 OpenGlVideoBuffer(ComPtr<IDirect3DDevice9Ex> device,
const ComPtr<IMFSample> &sample,
289 const WglNvDxInterop &wglNvDxInterop, HANDLE sharedHandle, QRhi *rhi)
290 : IMFSampleVideoBuffer(std::move(device), sample, rhi, QVideoFrame::RhiTextureHandle)
291 , m_sharedHandle(sharedHandle)
292 , m_wgl(wglNvDxInterop)
295 QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr& ) override
298 ComPtr<IMFMediaBuffer> buffer;
299 HRESULT hr = m_sample->GetBufferByIndex(0, buffer.GetAddressOf());
303 ComPtr<IDirect3DSurface9> surface;
304 hr = MFGetService(buffer.Get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9,
305 (
void **)(surface.GetAddressOf()));
309 hr = surface->GetContainer(IID_IDirect3DTexture9, (
void **)m_texture.GetAddressOf());
314 return QVideoFrameOpenGlTextures::create(m_wgl, &rhi, m_device.Get(), m_texture.Get(), m_sharedHandle);
318 HANDLE m_sharedHandle =
nullptr;
319 WglNvDxInterop m_wgl;
320 ComPtr<IDirect3DTexture9> m_texture;
324D3DPresentEngine::D3DPresentEngine(QVideoSink *sink)
325 : m_deviceResetToken(0)
327 ZeroMemory(&m_displayMode,
sizeof(m_displayMode));
331D3DPresentEngine::~D3DPresentEngine()
336void D3DPresentEngine::setSink(QVideoSink *sink)
351 HRESULT hr = initializeD3D();
354 hr = createD3DDevice();
356 qWarning(
"Failed to create D3D device");
358 qWarning(
"Failed to initialize D3D");
362HRESULT D3DPresentEngine::initializeD3D()
364 HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.GetAddressOf());
367 hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.GetAddressOf());
374 auto nh =
static_cast<
const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
376 for (
auto i = 0u; i < D3D9->GetAdapterCount(); ++i) {
378 D3D9->GetAdapterLUID(i, &luid);
379 if (luid.LowPart == nh->adapterLuidLow && luid.HighPart == nh->adapterLuidHigh) {
391static bool getProc(
const QOpenGLContext *ctx, T &fn,
const char *fName)
393 fn =
reinterpret_cast<T>(ctx->getProcAddress(fName));
394 return fn !=
nullptr;
397static bool readWglNvDxInteropProc(WglNvDxInterop &f)
399 QScopedPointer<QOffscreenSurface> surface(
new QOffscreenSurface);
401 QScopedPointer<QOpenGLContext> ctx(
new QOpenGLContext);
403 ctx->makeCurrent(surface.get());
405 auto wglGetExtensionsStringARB =
reinterpret_cast<
const char* (WINAPI* )(HDC)>
406 (ctx->getProcAddress(
"wglGetExtensionsStringARB"));
407 if (!wglGetExtensionsStringARB) {
408 qCDebug(qLcEvrD3DPresentEngine) <<
"WGL extensions missing (no wglGetExtensionsStringARB function)";
412 HWND hwnd = ::GetShellWindow();
413 auto dc = ::GetDC(hwnd);
415 const char *wglExtString = wglGetExtensionsStringARB(dc);
417 qCDebug(qLcEvrD3DPresentEngine) <<
"WGL extensions missing (wglGetExtensionsStringARB returned null)";
419 bool hasExtension = wglExtString && strstr(wglExtString,
"WGL_NV_DX_interop");
422 qCDebug(qLcEvrD3DPresentEngine) <<
"WGL_NV_DX_interop missing";
426 return getProc(ctx.get(), f.wglDXOpenDeviceNV,
"wglDXOpenDeviceNV")
427 && getProc(ctx.get(), f.wglDXCloseDeviceNV,
"wglDXCloseDeviceNV")
428 && getProc(ctx.get(), f.wglDXSetResourceShareHandleNV,
"wglDXSetResourceShareHandleNV")
429 && getProc(ctx.get(), f.wglDXRegisterObjectNV,
"wglDXRegisterObjectNV")
430 && getProc(ctx.get(), f.wglDXUnregisterObjectNV,
"wglDXUnregisterObjectNV")
431 && getProc(ctx.get(), f.wglDXLockObjectsNV,
"wglDXLockObjectsNV")
432 && getProc(ctx.get(), f.wglDXUnlockObjectsNV,
"wglDXUnlockObjectsNV");
438bool hwTextureRenderingEnabled() {
441 static bool isDisableConversionSet =
false;
442 static const int disableHwConversion = qEnvironmentVariableIntValue(
443 "QT_DISABLE_HW_TEXTURES_CONVERSION", &isDisableConversionSet);
445 return !isDisableConversionSet || !disableHwConversion;
450HRESULT D3DPresentEngine::createD3DDevice()
452 if (!m_D3D9 || !m_devices)
453 return MF_E_NOT_INITIALIZED;
455 m_useTextureRendering =
false;
458 if (hwTextureRenderingEnabled()) {
459 QRhi *rhi = m_sink ? m_sink->rhi() :
nullptr;
461 if (rhi->backend() == QRhi::D3D11) {
462 m_useTextureRendering = findD3D11AdapterID(*rhi, m_D3D9.Get(), adapterID);
464 }
else if (rhi->backend() == QRhi::OpenGLES2) {
465 m_useTextureRendering = readWglNvDxInteropProc(m_wglNvDxInterop);
468 qCDebug(qLcEvrD3DPresentEngine) <<
"Not supported RHI backend type";
471 qCDebug(qLcEvrD3DPresentEngine) <<
"No RHI associated with this sink";
474 if (!m_useTextureRendering)
475 qCDebug(qLcEvrD3DPresentEngine) <<
"Could not find compatible RHI adapter, zero copy disabled";
479 ZeroMemory(&ddCaps,
sizeof(ddCaps));
481 HRESULT hr = m_D3D9->GetDeviceCaps(adapterID, D3DDEVTYPE_HAL, &ddCaps);
486 if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
487 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
489 vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
491 D3DPRESENT_PARAMETERS pp;
492 ZeroMemory(&pp,
sizeof(pp));
494 pp.BackBufferWidth = 1;
495 pp.BackBufferHeight = 1;
496 pp.BackBufferCount = 1;
498 pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
499 pp.BackBufferFormat = D3DFMT_UNKNOWN;
500 pp.hDeviceWindow =
nullptr;
501 pp.Flags = D3DPRESENTFLAG_VIDEO;
502 pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
504 ComPtr<IDirect3DDevice9Ex> device;
506 hr = m_D3D9->CreateDeviceEx(
510 vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
513 device.GetAddressOf()
518 hr = m_D3D9->GetAdapterDisplayMode(adapterID, &m_displayMode);
522 hr = m_devices->ResetDevice(device.Get(), m_deviceResetToken);
530bool D3DPresentEngine::isValid()
const
532 return m_device.Get() !=
nullptr;
535void D3DPresentEngine::releaseResources()
537 m_surfaceFormat = QVideoFrameFormat();
540HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid,
void** ppv)
544 if (riid == __uuidof(IDirect3DDeviceManager9)) {
546 hr = MF_E_UNSUPPORTED_SERVICE;
548 *ppv = m_devices.Get();
552 hr = MF_E_UNSUPPORTED_SERVICE;
558HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
560 if (!m_D3D9 || !m_device)
566 D3DDEVICE_CREATION_PARAMETERS params;
568 hr = m_device->GetCreationParameters(¶ms);
572 UINT uAdapter = params.AdapterOrdinal;
573 D3DDEVTYPE type = params.DeviceType;
575 hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
579 hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format,
580 D3DUSAGE_RENDERTARGET,
586 bool ok = format == D3DFMT_X8R8G8B8
587 || format == D3DFMT_A8R8G8B8
588 || format == D3DFMT_X8B8G8R8
589 || format == D3DFMT_A8B8G8R8;
591 return ok ? S_OK : D3DERR_NOTAVAILABLE;
594HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format,
595 QList<ComPtr<IMFSample>> &videoSampleQueue,
598 if (!format || !m_device)
599 return MF_E_UNEXPECTED;
604 UINT32 width = 0, height = 0;
605 hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
609 if (frameSize.isValid() && !frameSize.isEmpty()) {
610 width = frameSize.width();
611 height = frameSize.height();
615 hr = qt_evr_getFourCC(format, &d3dFormat);
620 if (d3dFormat == D3DFMT_X8R8G8B8)
621 d3dFormat = D3DFMT_A8R8G8B8;
622 else if (d3dFormat == D3DFMT_X8B8G8R8)
623 d3dFormat = D3DFMT_A8B8G8R8;
625 for (
int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
628 ComPtr<IDirect3DTexture9> texture;
629 HANDLE sharedHandle =
nullptr;
630 hr = m_device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, (D3DFORMAT)d3dFormat, D3DPOOL_DEFAULT, texture.GetAddressOf(), &sharedHandle);
634 ComPtr<IDirect3DSurface9> surface;
635 hr = texture->GetSurfaceLevel(0, surface.GetAddressOf());
639 ComPtr<IMFSample> videoSample;
640 hr = MFCreateVideoSampleFromSurface(surface.Get(), videoSample.GetAddressOf());
644 m_sampleTextureHandle[i] = {videoSample.Get(), sharedHandle};
645 videoSampleQueue.append(videoSample);
648 std::optional<QVideoFrameFormat::ColorRange> colorRange =
649 [&]() -> std::optional<QVideoFrameFormat::ColorRange> {
650 MFNominalRange range;
651 if (SUCCEEDED(format->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE,
652 reinterpret_cast<UINT32 *>(&range)))) {
653 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_VIDEO_NOMINAL_RANGE =" << range;
655 case MFNominalRange::MFNominalRange_16_235:
656 return QVideoFrameFormat::ColorRange::ColorRange_Video;
657 case MFNominalRange::MFNominalRange_0_255:
658 return QVideoFrameFormat::ColorRange::ColorRange_Full;
663 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_VIDEO_NOMINAL_RANGE not set";
668 std::optional<QVideoFrameFormat::ColorTransfer> colorTransfer =
669 [&]() -> std::optional<QVideoFrameFormat::ColorTransfer> {
670 MFVideoTransferMatrix transferMatrix;
671 if (SUCCEEDED(format->GetUINT32(MF_MT_YUV_MATRIX,
672 reinterpret_cast<UINT32 *>(&transferMatrix)))) {
673 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_YUV_MATRIX =" << transferMatrix;
675 switch (transferMatrix) {
676 case MFVideoTransferMatrix_BT709:
677 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT709;
678 case MFVideoTransferMatrix_BT601:
679 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT601;
680 case MFVideoTransferMatrix_SMPTE240M:
681 return QVideoFrameFormat::ColorTransfer::ColorTransfer_BT709;
682 case MFVideoTransferMatrix_BT2020_10:
683 case MFVideoTransferMatrix_BT2020_12:
684 return QVideoFrameFormat::ColorTransfer::ColorTransfer_ST2084;
689 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_YUV_MATRIX not set";
694 std::optional<QVideoFrameFormat::ColorSpace> colorSpace =
695 [&]() -> std::optional<QVideoFrameFormat::ColorSpace> {
696 MFVideoPrimaries primaries;
697 if (SUCCEEDED(format->GetUINT32(MF_MT_VIDEO_PRIMARIES,
698 reinterpret_cast<UINT32 *>(&primaries)))) {
699 qCDebug(qLcEvrD3DPresentEngine) <<
"MFVideoPrimaries =" << primaries;
702 case MFVideoPrimaries_BT709:
703 return QVideoFrameFormat::ColorSpace::ColorSpace_BT709;
704 case MFVideoPrimaries_BT470_2_SysM:
705 case MFVideoPrimaries_BT470_2_SysBG:
706 case MFVideoPrimaries_SMPTE170M:
707 case MFVideoPrimaries_EBU3213:
708 case MFVideoPrimaries_SMPTE_C:
709 return QVideoFrameFormat::ColorSpace::ColorSpace_BT601;
711 case MFVideoPrimaries_SMPTE240M:
712 return QVideoFrameFormat::ColorSpace::ColorSpace_BT709;
713 case MFVideoPrimaries_BT2020:
714 return QVideoFrameFormat::ColorSpace::ColorSpace_BT2020;
715 case MFVideoPrimaries_DCI_P3:
716 return QVideoFrameFormat::ColorSpace::ColorSpace_BT2020;
717 case MFVideoPrimaries_XYZ:
718 case MFVideoPrimaries_ACES:
719 return QVideoFrameFormat::ColorSpace::ColorSpace_Undefined;
721 return QVideoFrameFormat::ColorSpace::ColorSpace_Undefined;
724 qCDebug(qLcEvrD3DPresentEngine) <<
"MF_MT_YUV_MATRIX not set";
730 m_surfaceFormat = QVideoFrameFormat(QSize(width, height), qt_evr_pixelFormatFromD3DFormat(d3dFormat));
732 m_surfaceFormat.setColorRange(*colorRange);
734 m_surfaceFormat.setColorTransfer(*colorTransfer);
736 m_surfaceFormat.setColorSpace(*colorSpace);
744QVideoFrame D3DPresentEngine::makeVideoFrame(
const ComPtr<IMFSample> &sample,
745 QtVideo::Rotation rotation)
750 HANDLE sharedHandle =
nullptr;
751 for (
const auto &p : m_sampleTextureHandle)
752 if (p.first == sample.Get())
753 sharedHandle = p.second;
755 std::unique_ptr<IMFSampleVideoBuffer> vb;
756 QRhi *rhi = m_sink ? m_sink->rhi() :
nullptr;
757 if (m_useTextureRendering && sharedHandle && rhi) {
758 if (rhi->backend() == QRhi::D3D11) {
759 vb = std::make_unique<D3D11TextureVideoBuffer>(m_device, sample, sharedHandle, rhi);
761 }
else if (rhi->backend() == QRhi::OpenGLES2) {
762 vb = std::make_unique<OpenGlVideoBuffer>(m_device, sample, m_wglNvDxInterop,
769 vb = std::make_unique<IMFSampleVideoBuffer>(m_device, sample, rhi);
771 auto format = m_surfaceFormat;
772 format.setRotation(rotation);
773 QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(vb), format);
776 LONGLONG startTime = 0;
777 auto hr = sample->GetSampleTime(&startTime);
779 frame.setStartTime(startTime / 10);
781 LONGLONG duration = -1;
782 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
783 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)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)