Files
fmviewer3/project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9.cpp
2026-02-21 17:11:31 +09:00

207 lines
6.4 KiB
C++

/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2015)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#ifdef RM_USE_HW_DECODER
#include "SurfaceInteropD3D9.h"
#include "../VideoFrame.h"
#define DX_LOG_COMPONENT "D3D9 Interop"
#include "../DirectXHelper.h"
#include "../opengl/OpenGLHelper.h"
// no need to check qt4 because no ANGLE there
#if QTAV_HAVE(EGL_CAPI) // always use dynamic load
#if defined(QT_OPENGL_DYNAMIC) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_2_ANGLE)
#define QTAV_HAVE_D3D9_EGL 1
#endif
#endif //QTAV_HAVE(EGL_CAPI)
#if defined(QT_OPENGL_DYNAMIC) || !defined(QT_OPENGL_ES_2)
#define QTAV_HAVE_D3D9_GL 1
#endif
#include "../Logger.h"
#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
static const GUID name = { l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
namespace FAV {
extern VideoFormat::PixelFormat pixelFormatFromFourcc(int format);
MS_GUID(IID_IDirect3DDevice9Ex, 0xb18b10ce, 0x2649, 0x405a, 0x87, 0xf, 0x95, 0xf7, 0x77, 0xd4, 0x31, 0x3a);
namespace d3d9 {
bool InteropResource::isSupported(InteropType type)
{
Q_UNUSED(type);
#if QTAV_HAVE(D3D9_EGL)
if (type == InteropAuto || type == InteropEGL) {
if (OpenGLHelper::isOpenGLES())
return true;
}
#endif
#if QTAV_HAVE(D3D9_GL)
if (type == InteropAuto || type == InteropGL) {
if (!OpenGLHelper::isOpenGLES())
return true;
}
#endif
return false;
}
extern InteropResource* CreateInteropEGL(IDirect3DDevice9 *dev);
extern InteropResource* CreateInteropGL(IDirect3DDevice9 *dev);
InteropResource* InteropResource::create(IDirect3DDevice9 *dev, InteropType type)
{
Q_UNUSED(dev);
if (type == InteropAuto || type == InteropEGL) {
IDirect3DDevice9Ex *devEx;
dev->QueryInterface(IID_IDirect3DDevice9Ex, (void**)&devEx);
qDebug("using D3D9Ex: %d", !!devEx);
//
if (!devEx) {
qWarning("IDirect3DDevice9Ex is required to share d3d resource. It's available in vista and later. d3d9 can not CreateTexture with shared handle");
}
SafeRelease(&devEx);
#if QTAV_HAVE(D3D9_EGL)
if (OpenGLHelper::isOpenGLES())
return CreateInteropEGL(dev);
#endif
}
if (type == InteropAuto || type == InteropGL) {
#if QTAV_HAVE(D3D9_GL)
if (!OpenGLHelper::isOpenGLES())
return CreateInteropGL(dev);
#endif
}
return NULL;
}
InteropResource::InteropResource(IDirect3DDevice9 *d3device)
: d3ddev(d3device)
, dx_texture(NULL)
, dx_surface(NULL)
, width(0)
, height(0)
{
d3ddev->AddRef();
}
InteropResource::~InteropResource()
{
releaseDX();
SafeRelease(&d3ddev);
}
void InteropResource::releaseDX()
{
SafeRelease(&dx_surface);
SafeRelease(&dx_texture);
}
SurfaceInterop::~SurfaceInterop()
{
SafeRelease(&m_surface);
}
void SurfaceInterop::setSurface(IDirect3DSurface9 *surface, int frame_w, int frame_h)
{
m_surface = surface;
m_surface->AddRef();
frame_width = frame_w;
frame_height = frame_h;
}
void* SurfaceInterop::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane)
{
if (!handle)
return NULL;
if (!m_surface)
return 0;
if (type == GLTextureSurface) {
if (!fmt.isRGB())
return NULL;
if (m_resource->map(m_surface, *((GLuint*)handle), frame_width, frame_height, plane))
return handle;
} else if (type == HostMemorySurface) {
return mapToHost(fmt, handle, plane);
}
return NULL;
}
void SurfaceInterop::unmap(void *handle)
{
m_resource->unmap(*((GLuint*)handle));
}
void* SurfaceInterop::mapToHost(const VideoFormat &format, void *handle, int plane)
{
Q_UNUSED(plane);
class ScopedD3DLock {
IDirect3DSurface9 *mpD3D;
public:
ScopedD3DLock(IDirect3DSurface9* d3d, D3DLOCKED_RECT *rect) : mpD3D(d3d) {
if (FAILED(mpD3D->LockRect(rect, NULL, D3DLOCK_READONLY))) {
qWarning("Failed to lock surface");
mpD3D = 0;
}
}
~ScopedD3DLock() {
if (mpD3D)
mpD3D->UnlockRect();
}
};
D3DLOCKED_RECT lock;
ScopedD3DLock(m_surface, &lock);
if (lock.Pitch == 0)
return NULL;
// TODO: use the same code as VideoDecoderDXVA::frame() like vaapi.
//picth >= desc.Width
D3DSURFACE_DESC desc;
m_surface->GetDesc(&desc);
const VideoFormat fmt = VideoFormat(pixelFormatFromFourcc(desc.Format));
if (!fmt.isValid()) {
qWarning("unsupported D3D9 pixel format: %#x", desc.Format);
return NULL;
}
//YV12 need swap, not imc3?
// imc3 U V pitch == Y pitch, but half of the U/V plane is space. we convert to yuv420p here
// nv12 bpp(1)==1
// 3rd plane is not used for nv12
int pitch[3] = { lock.Pitch, 0, 0}; //compute chroma later
quint8 *src[] = { (quint8*)lock.pBits, 0, 0}; //compute chroma later
Q_ASSERT(src[0] && pitch[0] > 0);
const bool swap_uv = desc.Format == MAKEFOURCC('I','M','C','3');
// try to use SSE. fallback to normal copy if SSE is not supported
VideoFrame frame(VideoFrame::fromGPU(fmt, frame_width, frame_height, desc.Height, src, pitch, true, swap_uv));
// TODO: check rgb32 because d3d can use hw to convert
if (format != fmt)
frame = frame.to(format);
VideoFrame *f = reinterpret_cast<VideoFrame*>(handle);
frame.setTimestamp(f->timestamp());
*f = frame;
return f;
}
} //namespace d3d9
} //namespace QtAV
#endif // RM_USE_HW_DECODER