first commit

This commit is contained in:
2026-02-21 17:11:31 +09:00
commit 18b4338361
4001 changed files with 365464 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
/******************************************************************************
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

View File

@@ -0,0 +1,94 @@
/******************************************************************************
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
******************************************************************************/
#ifndef QTAV_SURFACEINTEROPD3D9_H
#define QTAV_SURFACEINTEROPD3D9_H
#ifdef RM_USE_HW_DECODER
#include <d3d9.h>
#include "../SurfaceInterop.h"
namespace FAV {
namespace d3d9 {
enum InteropType {
InteropAuto,
InteropEGL,
InteropGL
};
class InteropResource
{
public:
static bool isSupported(InteropType type = InteropAuto);
static InteropResource* create(IDirect3DDevice9 * dev, InteropType type = InteropAuto);
typedef unsigned int GLuint;
InteropResource(IDirect3DDevice9 * d3device);
virtual ~InteropResource();
/*!
* \brief map
* \param surface d3d9 surface
* \param tex opengl texture
* \param w frame width(visual width) without alignment, <= d3d9 surface width
* \param h frame height(visual height)
* \param plane useless now
* \return true if success
*/
virtual bool map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int plane) = 0;
virtual bool unmap(GLuint tex) { Q_UNUSED(tex); return true;}
protected:
void releaseDX();
IDirect3DDevice9 *d3ddev;
IDirect3DTexture9 *dx_texture;
IDirect3DSurface9 *dx_surface; // size is frame size(visual size) for display
int width, height; // video frame width and dx_surface width without alignment, not dxva decoded surface width
};
typedef QSharedPointer<InteropResource> InteropResourcePtr;
class SurfaceInterop Q_DECL_FINAL: public VideoSurfaceInterop
{
public:
SurfaceInterop(const InteropResourcePtr& res) : m_surface(0), m_resource(res), frame_width(0), frame_height(0) {}
~SurfaceInterop();
/*!
* \brief setSurface
* \param surface d3d9 surface
* \param frame_w frame width(visual width) without alignment, <= d3d9 surface width
* \param frame_h frame height(visual height)
*/
void setSurface(IDirect3DSurface9* surface, int frame_w, int frame_h);
/// GLTextureSurface only supports rgb32
void* map(SurfaceType type, const VideoFormat& fmt, void* handle, int plane) Q_DECL_OVERRIDE;
void unmap(void *handle) Q_DECL_OVERRIDE;
protected:
/// copy from gpu (optimized if possible) and convert to target format if necessary
void* mapToHost(const VideoFormat &format, void *handle, int plane);
private:
IDirect3DSurface9 *m_surface;
InteropResourcePtr m_resource;
int frame_width, frame_height;
};
} //namespace d3d9
} //namespace QtAV
#endif // RM_USE_HW_DECODER
#endif // QTAV_SURFACEINTEROPD3D9_H

View File

@@ -0,0 +1,210 @@
/******************************************************************************
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
#define REMOVE_SURFACE_D3D_EGL 1
#if !(REMOVE_SURFACE_D3D_EGL)
#include "SurfaceInteropD3D9.h"
#define DX_LOG_COMPONENT "D3D9EGL Interop"
#include "../DirectXHelper.h"
#include "../opengl/OpenGLHelper.h"
#ifdef QT_OPENGL_ES_2_ANGLE_STATIC
#define CAPI_LINK_EGL
#else
#define EGL_CAPI_NS
#endif //QT_OPENGL_ES_2_ANGLE_STATIC
#include "capi/egl_api.h"
#include <EGL/eglext.h> //include after egl_capi.h to match types
namespace FAV {
namespace d3d9 { //d3d9
class EGL {
public:
EGL() : dpy(EGL_NO_DISPLAY), surface(EGL_NO_SURFACE) {}
EGLDisplay dpy;
EGLSurface surface;
};
class EGLInteropResource Q_DECL_FINAL: public InteropResource
{
public:
EGLInteropResource(IDirect3DDevice9 * d3device);
~EGLInteropResource();
bool map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE;
private:
void releaseEGL();
bool ensureSurface(int w, int h);
EGL* egl;
IDirect3DQuery9 *dx_query;
};
InteropResource* CreateInteropEGL(IDirect3DDevice9 *dev)
{
return new EGLInteropResource(dev);
}
EGLInteropResource::EGLInteropResource(IDirect3DDevice9 * d3device)
: InteropResource(d3device)
, egl(new EGL())
, dx_query(NULL)
{
DX_ENSURE_OK(d3device->CreateQuery(D3DQUERYTYPE_EVENT, &dx_query));
dx_query->Issue(D3DISSUE_END);
}
EGLInteropResource::~EGLInteropResource()
{
releaseEGL();
if (egl) {
delete egl;
egl = NULL;
}
SafeRelease(&dx_query);
}
void EGLInteropResource::releaseEGL() {
if (egl->surface != EGL_NO_SURFACE) {
eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER);
eglDestroySurface(egl->dpy, egl->surface);
egl->surface = EGL_NO_SURFACE;
}
}
bool EGLInteropResource::ensureSurface(int w, int h) {
if (egl->surface && width == w && height == h)
return true;
releaseEGL(); //
egl->dpy = eglGetCurrentDisplay();
qDebug("EGL version: %s, client api: %s", eglQueryString(egl->dpy, EGL_VERSION), eglQueryString(egl->dpy, EGL_CLIENT_APIS));
EGLint cfg_attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8, //
EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, //remove?
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
EGLint nb_cfgs;
EGLConfig egl_cfg;
if (!eglChooseConfig(egl->dpy, cfg_attribs, &egl_cfg, 1, &nb_cfgs)) {
qWarning("Failed to create EGL configuration");
return false;
}
// check extensions
QList<QByteArray> extensions = QByteArray(eglQueryString(egl->dpy, EGL_EXTENSIONS)).split(' ');
// ANGLE_d3d_share_handle_client_buffer will be used if possible
// TODO: strstr is enough
const bool kEGL_ANGLE_d3d_share_handle_client_buffer = extensions.contains("EGL_ANGLE_d3d_share_handle_client_buffer");
const bool kEGL_ANGLE_query_surface_pointer = extensions.contains("EGL_ANGLE_query_surface_pointer");
if (!kEGL_ANGLE_d3d_share_handle_client_buffer && !kEGL_ANGLE_query_surface_pointer) {
qWarning("EGL extension 'kEGL_ANGLE_query_surface_pointer' or 'ANGLE_d3d_share_handle_client_buffer' is required!");
return false;
}
GLint has_alpha = 1; //QOpenGLContext::currentContext()->format().hasAlpha()
eglGetConfigAttrib(egl->dpy, egl_cfg, EGL_BIND_TO_TEXTURE_RGBA, &has_alpha); //EGL_ALPHA_SIZE
qDebug("choose egl display:%p config: %p/%d, has alpha: %d", egl->dpy, egl_cfg, nb_cfgs, has_alpha);
EGLint attribs[] = {
EGL_WIDTH, w,
EGL_HEIGHT, h,
EGL_TEXTURE_FORMAT, has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE
};
HANDLE share_handle = NULL;
if (!kEGL_ANGLE_d3d_share_handle_client_buffer && kEGL_ANGLE_query_surface_pointer) {
EGL_ENSURE((egl->surface = eglCreatePbufferSurface(egl->dpy, egl_cfg, attribs)) != EGL_NO_SURFACE, false);
qDebug("pbuffer surface: %p", egl->surface);
PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE = reinterpret_cast<PFNEGLQUERYSURFACEPOINTERANGLEPROC>(eglGetProcAddress("eglQuerySurfacePointerANGLE"));
if (!eglQuerySurfacePointerANGLE) {
qWarning("EGL_ANGLE_query_surface_pointer is not supported");
return false;
}
EGL_ENSURE(eglQuerySurfacePointerANGLE(egl->dpy, egl->surface, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle), false);
}
releaseDX();
// _A8 for a yuv plane
// d3d resource share requires windows >= vista: https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800(v=vs.85).aspx
// from extension files:
// d3d9: level must be 1, dimensions must match EGL surface's
// d3d9ex or d3d10:
DX_ENSURE_OK(d3ddev->CreateTexture(w, h, 1,
D3DUSAGE_RENDERTARGET,
has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&dx_texture,
&share_handle) , false);
DX_ENSURE_OK(dx_texture->GetSurfaceLevel(0, &dx_surface), false);
if (kEGL_ANGLE_d3d_share_handle_client_buffer) {
// requires extension EGL_ANGLE_d3d_share_handle_client_buffer
// egl surface size must match d3d texture's
// d3d9ex or d3d10 is required
EGL_ENSURE((egl->surface = eglCreatePbufferFromClientBuffer(egl->dpy, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, share_handle, egl_cfg, attribs)), false);
qDebug("pbuffer surface from client buffer: %p", egl->surface);
}
width = w;
height = h;
return true;
}
bool EGLInteropResource::map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int)
{
if (!ensureSurface(w, h)) {
releaseEGL();
releaseDX();
return false;
}
const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h};
DX_ENSURE(d3ddev->StretchRect(surface, &src, dx_surface, NULL, D3DTEXF_NONE), false);
if (dx_query) {
// Flush the draw command now. Ideally, this should be done immediately before the draw call that uses the texture. Flush it once here though.
dx_query->Issue(D3DISSUE_END); //StretchRect does not supports odd values
// ensure data is copied to egl surface. Solution and comment is from chromium
// The DXVA decoder has its own device which it uses for decoding. ANGLE has its own device which we don't have access to.
// The above code attempts to copy the decoded picture into a surface which is owned by ANGLE.
// As there are multiple devices involved in this, the StretchRect call above is not synchronous.
// We attempt to flush the batched operations to ensure that the picture is copied to the surface owned by ANGLE.
// We need to do this in a loop and call flush multiple times.
// We have seen the GetData call for flushing the command buffer fail to return success occassionally on multi core machines, leading to an infinite loop.
// Workaround is to have an upper limit of 10 on the number of iterations to wait for the Flush to finish.
int k = 0;
while ((dx_query->GetData(NULL, 0, D3DGETDATA_FLUSH) == FALSE) && ++k < 10) {
Sleep(1);
}
}
DYGL(glBindTexture(GL_TEXTURE_2D, tex));
eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER);
DYGL(glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
} //namespace d3d9
} //namespace FAV
#endif // REMOVE_SURFACE_D3D_EGL
#endif // #ifdef RM_USE_HW_DECODER

View File

@@ -0,0 +1,129 @@
/******************************************************************************
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"
#define DX_LOG_COMPONENT "D3D9GL Interop"
#include "../DirectXHelper.h"
#include "../opengl/OpenGLHelper.h"
//dynamic gl or desktop gl
namespace FAV {
namespace d3d9 {
class GLInteropResource Q_DECL_FINAL: public InteropResource
{
public:
GLInteropResource(IDirect3DDevice9 * d3device);
~GLInteropResource();
bool map(IDirect3DSurface9 *surface, GLuint tex, int frame_w, int frame_h, int) Q_DECL_OVERRIDE;
bool unmap(GLuint tex) Q_DECL_OVERRIDE;
private:
bool ensureResource(int w, int h, GLuint tex);
HANDLE interop_dev;
HANDLE interop_obj;
};
InteropResource* CreateInteropGL(IDirect3DDevice9 *dev)
{
return new GLInteropResource(dev);
}
GLInteropResource::GLInteropResource(IDirect3DDevice9 *d3device)
: InteropResource(d3device)
, interop_dev(NULL)
, interop_obj(NULL)
{
}
GLInteropResource::~GLInteropResource()
{
// FIXME: why unregister/close interop obj/dev here will crash(tested on intel driver)? must be in current opengl context?
}
bool GLInteropResource::map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int)
{
if (!ensureResource(w, h, tex)) {
releaseDX();
return false;
}
// open/close and register/unregster in every map/unmap to ensure called in current context and avoid crash (tested on intel driver)
// interop operations begin
WGL_ENSURE((interop_dev = gl().DXOpenDeviceNV(d3ddev)) != NULL, false);
// call in ensureResource or in map?
WGL_ENSURE((interop_obj = gl().DXRegisterObjectNV(interop_dev, dx_surface, tex, GL_TEXTURE_2D, WGL_ACCESS_READ_ONLY_NV)) != NULL, false);
// prepare dx resources for gl
const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h}; //StretchRect does not supports odd values
DX_ENSURE_OK(d3ddev->StretchRect(surface, &src, dx_surface, NULL, D3DTEXF_NONE), false);
// lock dx resources
WGL_ENSURE(gl().DXLockObjectsNV(interop_dev, 1, &interop_obj), false);
WGL_ENSURE(gl().DXObjectAccessNV(interop_obj, WGL_ACCESS_READ_ONLY_NV), false);
DYGL(glBindTexture(GL_TEXTURE_2D, tex));
return true;
}
bool GLInteropResource::unmap(GLuint tex)
{
Q_UNUSED(tex);
if (!interop_obj || !interop_dev)
return false;
DYGL(glBindTexture(GL_TEXTURE_2D, 0));
WGL_ENSURE(gl().DXUnlockObjectsNV(interop_dev, 1, &interop_obj), false);
WGL_WARN(gl().DXUnregisterObjectNV(interop_dev, interop_obj));
// interop operations end
WGL_WARN(gl().DXCloseDeviceNV(interop_dev));
interop_obj = NULL;
interop_dev = NULL;
return true;
}
// IDirect3DDevice9 can not be used on WDDM OSes(>=vista)
bool GLInteropResource::ensureResource(int w, int h, GLuint tex)
{
Q_UNUSED(tex);
Q_ASSERT(gl().DXRegisterObjectNV && "WGL_NV_DX_interop is required");
if (dx_surface && width == w && height == h)
return true;
releaseDX();
HANDLE share_handle = NULL;
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
const bool has_alpha = QOpenGLContext::currentContext()->format().hasAlpha();
#else
const bool has_alpha = QOpenGLContext::currentContext()->format().alpha();
#endif
// _A8 for a yuv plane
DX_ENSURE_OK(d3ddev->CreateTexture(w, h, 1,
D3DUSAGE_RENDERTARGET,
has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&dx_texture,
&share_handle) , false);
DX_ENSURE_OK(dx_texture->GetSurfaceLevel(0, &dx_surface), false);
// required by d3d9 not d3d10&11: https://www.opengl.org/registry/specs/NV/DX_interop2.txt
WGL_WARN(gl().DXSetResourceShareHandleNV(dx_surface, share_handle));
width = w;
height = h;
return true;
}
}
}
#endif // #ifdef RM_USE_HW_DECODER

View File

@@ -0,0 +1,516 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2016)
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 "VideoDecoderD3D.h"
#include <initguid.h> /* must be last included to not redefine existing GUIDs */
#if (FF_PROFILE_HEVC_MAIN == -1) //libav does not define it
#ifdef _MSC_VER
#pragma message("HEVC will not be supported. Update your FFmpeg")
#else
#warning "HEVC will not be supported. Update your FFmpeg"
#endif
#endif
namespace FAV {
static bool check_ffmpeg_hevc_dxva2()
{
avcodec_register_all();
AVHWAccel *hwa = av_hwaccel_next(0);
while (hwa) {
if (strncmp("hevc_dxva2", hwa->name, 10) == 0)
return true;
if (strncmp("hevc_d3d11va", hwa->name, 12) == 0)
return true;
hwa = av_hwaccel_next(hwa);
}
return false;
}
bool isHEVCSupported()
{
static const bool support_hevc = check_ffmpeg_hevc_dxva2();
return support_hevc;
}
// some MS_GUID are defined in mingw but some are not. move to namespace and define all is ok
#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}}
MS_GUID (DXVA_NoEncrypt, 0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
/* Codec capabilities GUID, sorted by codec */
MS_GUID (DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b, 0x61b0, 0x4563, 0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66);
MS_GUID (DXVA2_ModeMPEG2_IDCT, 0xbf22ad00, 0x03ea, 0x4690, 0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e);
MS_GUID (DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9);
DEFINE_GUID(DXVA_ModeMPEG1_A, 0x1b81be09, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeMPEG2_A, 0x1b81be0A, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeMPEG2_B, 0x1b81be0B, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeMPEG2_C, 0x1b81be0C, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeMPEG2_D, 0x1b81be0D, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60);
DEFINE_GUID(DXVA2_ModeMPEG1_VLD, 0x6f3ec719, 0x3735, 0x42cc, 0x80, 0x63, 0x65, 0xcc, 0x3c, 0xb3, 0x66, 0x16);
MS_GUID (DXVA2_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeH264_B, 0x1b81be65, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeH264_C, 0x1b81be66, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeH264_D, 0x1b81be67, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH264_VLD_Multiview, 0x9901CCD3, 0xca12, 0x4b7e, 0x86, 0x7a, 0xe2, 0x22, 0x3d, 0x92, 0x55, 0xc3); // MVC
DEFINE_GUID(DXVA_ModeH264_VLD_WithFMOASO_NoFGT, 0xd5f04ff9, 0x3418, 0x45d8, 0x95, 0x61, 0x32, 0xa7, 0x6a, 0xae, 0x2d, 0xdd);
DEFINE_GUID(DXVADDI_Intel_ModeH264_A, 0x604F8E64, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
DEFINE_GUID(DXVADDI_Intel_ModeH264_C, 0x604F8E66, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
DEFINE_GUID(DXVA_Intel_H264_NoFGT_ClearVideo, 0x604F8E68, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
DEFINE_GUID(DXVA_ModeH264_VLD_NoFGT_Flash, 0x4245F676, 0x2BBC, 0x4166, 0xa0, 0xBB, 0x54, 0xE7, 0xB8, 0x49, 0xC3, 0x80);
MS_GUID (DXVA2_ModeWMV8_A, 0x1b81be80, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeWMV8_B, 0x1b81be81, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeWMV9_A, 0x1b81be90, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeWMV9_B, 0x1b81be91, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeWMV9_C, 0x1b81be94, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeVC1_A, 0x1b81beA0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeVC1_B, 0x1b81beA1, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeVC1_C, 0x1b81beA2, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
MS_GUID (DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); // August 2010 update
DEFINE_GUID(DXVA_Intel_VC1_ClearVideo, 0xBCC5DB6D, 0xA2B6, 0x4AF0, 0xAC, 0xE4, 0xAD, 0xB1, 0xF7, 0x87, 0xBC, 0x89);
DEFINE_GUID(DXVA_Intel_VC1_ClearVideo_2, 0xE07EC519, 0xE651, 0x4CD6, 0xAC, 0x84, 0x13, 0x70, 0xCC, 0xEE, 0xC8, 0x51);
DEFINE_GUID(DXVA_nVidia_MPEG4_ASP, 0x9947EC6F, 0x689B, 0x11DC, 0xA3, 0x20, 0x00, 0x19, 0xDB, 0xBC, 0x41, 0x84);
DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_Simple, 0xefd64d74, 0xc9e8, 0x41d7, 0xa5, 0xe9, 0xe9, 0xb0, 0xe3, 0x9f, 0xa3, 0x19);
DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0xed418a9f, 0x010d, 0x4eda, 0x9a, 0xe3, 0x9a, 0x65, 0x35, 0x8d, 0x8d, 0x2e);
DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0xab998b5b, 0x4258, 0x44a9, 0x9f, 0xeb, 0x94, 0xe5, 0x97, 0xa6, 0xba, 0xae);
DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0x7C74ADC6, 0xe2ba, 0x4ade, 0x86, 0xde, 0x30, 0xbe, 0xab, 0xb4, 0x0c, 0xc1);
DEFINE_GUID(DXVA_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c, 0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0);
DEFINE_GUID(DXVA_ModeHEVC_VLD_Main10, 0x107af0e0, 0xef1a, 0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13);
DEFINE_GUID(DXVA_ModeH264_VLD_Stereo_Progressive_NoFGT, 0xd79be8da, 0x0cf1, 0x4c81,0xb8,0x2a,0x69,0xa4,0xe2,0x36,0xf4,0x3d);
DEFINE_GUID(DXVA_ModeH264_VLD_Stereo_NoFGT, 0xf9aaccbb, 0xc2b6, 0x4cfc,0x87,0x79,0x57,0x07,0xb1,0x76,0x05,0x52);
DEFINE_GUID(DXVA_ModeH264_VLD_Multiview_NoFGT, 0x705b9d82, 0x76cf, 0x49d6,0xb7,0xe6,0xac,0x88,0x72,0xdb,0x01,0x3c);
DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Scalable_Baseline, 0xc30700c4, 0xe384, 0x43e0, 0xb9, 0x82, 0x2d, 0x89, 0xee, 0x7f, 0x77, 0xc4);
DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Restricted_Scalable_Baseline, 0x9b8175d4, 0xd670, 0x4cf2, 0xa9, 0xf0, 0xfa, 0x56, 0xdf, 0x71, 0xa1, 0xae);
DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Scalable_High, 0x728012c9, 0x66a8, 0x422f, 0x97, 0xe9, 0xb5, 0xe3, 0x9b, 0x51, 0xc0, 0x53);
DEFINE_GUID(DXVA_ModeH264_VLD_SVC_Restricted_Scalable_High_Progressive, 0x8efa5926, 0xbd9e, 0x4b04, 0x8b, 0x72, 0x8f, 0x97, 0x7d, 0xc4, 0x4c, 0x36);
DEFINE_GUID(DXVA_ModeH261_A, 0x1b81be01, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH261_B, 0x1b81be02, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_A, 0x1b81be03, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_B, 0x1b81be04, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_C, 0x1b81be05, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_D, 0x1b81be06, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_E, 0x1b81be07, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
DEFINE_GUID(DXVA_ModeH263_F, 0x1b81be08, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
static const int PROF_MPEG2_SIMPLE[] = { FF_PROFILE_MPEG2_SIMPLE, 0 };
static const int PROF_MPEG2_MAIN[] = { FF_PROFILE_MPEG2_SIMPLE, FF_PROFILE_MPEG2_MAIN, 0 };
static const int PROF_H264_HIGH[] = { FF_PROFILE_H264_CONSTRAINED_BASELINE, FF_PROFILE_H264_MAIN, FF_PROFILE_H264_HIGH, 0 };
static const int PROF_HEVC_MAIN[] = { FF_PROFILE_HEVC_MAIN, 0 };
static const int PROF_HEVC_MAIN10[] = { FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10, 0 };
// guids are from VLC
struct dxva2_mode_t {
const char *name;
const GUID *guid;
int codec;
const int *profiles;
};
/* XXX Prefered modes must come first */
static const dxva2_mode_t dxva2_modes[] = {
/* MPEG-1/2 */
{ "MPEG-1 decoder, restricted profile A", &DXVA_ModeMPEG1_A, 0, NULL },
{ "MPEG-2 decoder, restricted profile A", &DXVA_ModeMPEG2_A, 0, NULL },
{ "MPEG-2 decoder, restricted profile B", &DXVA_ModeMPEG2_B, 0, NULL },
{ "MPEG-2 decoder, restricted profile C", &DXVA_ModeMPEG2_C, 0, NULL },
{ "MPEG-2 decoder, restricted profile D", &DXVA_ModeMPEG2_D, 0, NULL },
{ "MPEG-2 variable-length decoder", &DXVA2_ModeMPEG2_VLD, QTAV_CODEC_ID(MPEG2VIDEO), PROF_MPEG2_SIMPLE },
{ "MPEG-2 & MPEG-1 variable-length decoder", &DXVA2_ModeMPEG2and1_VLD, QTAV_CODEC_ID(MPEG2VIDEO), PROF_MPEG2_MAIN },
{ "MPEG-2 & MPEG-1 variable-length decoder", &DXVA2_ModeMPEG2and1_VLD, QTAV_CODEC_ID(MPEG1VIDEO), NULL },
{ "MPEG-2 motion compensation", &DXVA2_ModeMPEG2_MoComp, 0, NULL },
{ "MPEG-2 inverse discrete cosine transform", &DXVA2_ModeMPEG2_IDCT, 0, NULL },
/* MPEG-1 http://download.microsoft.com/download/B/1/7/B172A3C8-56F2-4210-80F1-A97BEA9182ED/DXVA_MPEG1_VLD.pdf */
{ "MPEG-1 variable-length decoder, no D pictures", &DXVA2_ModeMPEG1_VLD, 0, NULL },
/* H.264 http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=3d1c290b-310b-4ea2-bf76-714063a6d7a6 */
{ "H.264 variable-length decoder, film grain technology", &DXVA2_ModeH264_F, QTAV_CODEC_ID(H264), PROF_H264_HIGH },
{ "H.264 variable-length decoder, no film grain technology (Intel ClearVideo)", &DXVA_Intel_H264_NoFGT_ClearVideo, QTAV_CODEC_ID(H264), PROF_H264_HIGH },
{ "H.264 variable-length decoder, no film grain technology", &DXVA2_ModeH264_E, QTAV_CODEC_ID(H264), PROF_H264_HIGH },
{ "H.264 variable-length decoder, no film grain technology, FMO/ASO", &DXVA_ModeH264_VLD_WithFMOASO_NoFGT, QTAV_CODEC_ID(H264), PROF_H264_HIGH },
{ "H.264 variable-length decoder, no film grain technology, Flash", &DXVA_ModeH264_VLD_NoFGT_Flash, QTAV_CODEC_ID(H264), PROF_H264_HIGH },
{ "H.264 inverse discrete cosine transform, film grain technology", &DXVA2_ModeH264_D, 0, NULL },
{ "H.264 inverse discrete cosine transform, no film grain technology", &DXVA2_ModeH264_C, 0, NULL },
{ "H.264 inverse discrete cosine transform, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_C, 0, NULL },
{ "H.264 motion compensation, film grain technology", &DXVA2_ModeH264_B, 0, NULL },
{ "H.264 motion compensation, no film grain technology", &DXVA2_ModeH264_A, 0, NULL },
{ "H.264 motion compensation, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_A, 0, NULL },
/* http://download.microsoft.com/download/2/D/0/2D02E72E-7890-430F-BA91-4A363F72F8C8/DXVA_H264_MVC.pdf */
{ "H.264 stereo high profile, mbs flag set", &DXVA_ModeH264_VLD_Stereo_Progressive_NoFGT, 0, NULL },
{ "H.264 stereo high profile", &DXVA_ModeH264_VLD_Stereo_NoFGT, 0, NULL },
{ "H.264 multiview high profile", &DXVA_ModeH264_VLD_Multiview_NoFGT, 0, NULL },
/* SVC http://download.microsoft.com/download/C/8/A/C8AD9F1B-57D1-4C10-85A0-09E3EAC50322/DXVA_SVC_2012_06.pdf */
{ "H.264 scalable video coding, Scalable Baseline Profile", &DXVA_ModeH264_VLD_SVC_Scalable_Baseline, 0, NULL },
{ "H.264 scalable video coding, Scalable Constrained Baseline Profile", &DXVA_ModeH264_VLD_SVC_Restricted_Scalable_Baseline, 0, NULL },
{ "H.264 scalable video coding, Scalable High Profile", &DXVA_ModeH264_VLD_SVC_Scalable_High, 0, NULL },
{ "H.264 scalable video coding, Scalable Constrained High Profile", &DXVA_ModeH264_VLD_SVC_Restricted_Scalable_High_Progressive, 0, NULL },
/* WMV */
{ "Windows Media Video 8 motion compensation", &DXVA2_ModeWMV8_B, 0, NULL },
{ "Windows Media Video 8 post processing", &DXVA2_ModeWMV8_A, 0, NULL },
{ "Windows Media Video 9 IDCT", &DXVA2_ModeWMV9_C, 0, NULL },
{ "Windows Media Video 9 motion compensation", &DXVA2_ModeWMV9_B, 0, NULL },
{ "Windows Media Video 9 post processing", &DXVA2_ModeWMV9_A, 0, NULL },
/* VC-1 */
{ "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, QTAV_CODEC_ID(VC1), NULL },
{ "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, QTAV_CODEC_ID(WMV3), NULL },
{ "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, QTAV_CODEC_ID(VC1), NULL },
{ "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, QTAV_CODEC_ID(WMV3), NULL },
{ "VC-1 variable-length decoder 2 (Intel)", &DXVA_Intel_VC1_ClearVideo_2, 0, NULL },
{ "VC-1 variable-length decoder (Intel)", &DXVA_Intel_VC1_ClearVideo, 0, NULL },
{ "VC-1 inverse discrete cosine transform", &DXVA2_ModeVC1_C, 0, NULL },
{ "VC-1 motion compensation", &DXVA2_ModeVC1_B, 0, NULL },
{ "VC-1 post processing", &DXVA2_ModeVC1_A, 0, NULL },
/* Xvid/Divx: TODO */
{ "MPEG-4 Part 2 nVidia bitstream decoder", &DXVA_nVidia_MPEG4_ASP, 0, NULL },
{ "MPEG-4 Part 2 variable-length decoder, Simple Profile", &DXVA_ModeMPEG4pt2_VLD_Simple, 0, NULL },
{ "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, no GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0, NULL },
{ "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0, NULL },
{ "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, Avivo", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0, NULL },
/* HEVC */
{ "HEVC Main profile", &DXVA_ModeHEVC_VLD_Main, QTAV_CODEC_ID(HEVC), PROF_HEVC_MAIN },
{ "HEVC Main 10 profile", &DXVA_ModeHEVC_VLD_Main10, QTAV_CODEC_ID(HEVC), PROF_HEVC_MAIN10 },
/* H.261 */
{ "H.261 decoder, restricted profile A", &DXVA_ModeH261_A, 0, NULL },
{ "H.261 decoder, restricted profile B", &DXVA_ModeH261_B, 0, NULL },
/* H.263 */
{ "H.263 decoder, restricted profile A", &DXVA_ModeH263_A, 0, NULL },
{ "H.263 decoder, restricted profile B", &DXVA_ModeH263_B, 0, NULL },
{ "H.263 decoder, restricted profile C", &DXVA_ModeH263_C, 0, NULL },
{ "H.263 decoder, restricted profile D", &DXVA_ModeH263_D, 0, NULL },
{ "H.263 decoder, restricted profile E", &DXVA_ModeH263_E, 0, NULL },
{ "H.263 decoder, restricted profile F", &DXVA_ModeH263_F, 0, NULL },
{ NULL, NULL, 0, NULL }
};
static const dxva2_mode_t *Dxva2FindMode(const GUID *guid)
{
for (unsigned i = 0; dxva2_modes[i].name; i++) {
if (IsEqualGUID(*dxva2_modes[i].guid, *guid))
return &dxva2_modes[i];
}
return NULL;
}
bool isIntelClearVideo(const GUID *guid)
{
return IsEqualGUID(*guid, DXVA_Intel_H264_NoFGT_ClearVideo);
}
bool isNoEncrypt(const GUID *guid)
{
return IsEqualGUID(*guid, DXVA_NoEncrypt);
}
bool checkProfile(const dxva2_mode_t *mode, int profile)
{
if (!mode->profiles || !mode->profiles[0] || profile <= 0)
return true;
for (const int *p = &mode->profiles[0]; *p; ++p) {
if (*p == profile)
return true;
}
return false;
}
/* XXX Prefered format must come first */
//16-bit: https://msdn.microsoft.com/en-us/library/windows/desktop/bb970578(v=vs.85).aspx
static const d3d_format_t d3d_formats[] = {
{ "NV12", MAKEFOURCC('N','V','1','2'), VideoFormat::Format_NV12 },
{ "YV12", MAKEFOURCC('Y','V','1','2'), VideoFormat::Format_YUV420P },
{ "IMC3", MAKEFOURCC('I','M','C','3'), VideoFormat::Format_YUV420P },
{ "P010", MAKEFOURCC('P','0','1','0'), VideoFormat::Format_YUV420P10LE },
{ "P016", MAKEFOURCC('P','0','1','6'), VideoFormat::Format_YUV420P16LE }, //FIXME:
{ NULL, 0, VideoFormat::Format_Invalid }
};
static const d3d_format_t *D3dFindFormat(int fourcc)
{
for (unsigned i = 0; d3d_formats[i].name; i++) {
if (d3d_formats[i].fourcc == fourcc)
return &d3d_formats[i];
}
return NULL;
}
VideoFormat::PixelFormat pixelFormatFromFourcc(int format)
{
const d3d_format_t *fmt = D3dFindFormat(format);
if (fmt)
return fmt->pixfmt;
return VideoFormat::Format_Invalid;
}
int getSupportedFourcc(int *formats, UINT nb_formats)
{
for (const int *f = formats; f < &formats[nb_formats]; ++f) {
const d3d_format_t *format = D3dFindFormat(*f);
if (format) {
qDebug("%s is supported for output", format->name);
} else {
qDebug("%d is supported for output (%4.4s)", *f, (const char*)f);
}
}
for (const d3d_format_t *format = d3d_formats; format->name; ++format) {
bool is_supported = false;
for (unsigned k = 0; !is_supported && k < nb_formats; k++) {
if (format->fourcc == formats[k])
return format->fourcc;
}
}
return 0;
}
VideoDecoderD3D::VideoDecoderD3D(VideoDecoderD3DPrivate &d)
: VideoDecoderFFmpegHW(d)
{
// dynamic properties about static property details. used by UI
// format: detail_property
setProperty("detail_surfaces", tr("Decoding surfaces") + QStringLiteral(" ") + tr("0: auto"));
setProperty("threads", 1); //FIXME: mt crash on close
}
void VideoDecoderD3D::setSurfaces(int num)
{
DPTR_D(VideoDecoderD3D);
if (d.surface_count == num)
return;
d.surface_count = num;
d.surface_auto = num <= 0;
Q_EMIT surfacesChanged();
}
int VideoDecoderD3D::surfaces() const
{
return d_func().surface_count;
}
VideoDecoderD3DPrivate::VideoDecoderD3DPrivate()
: VideoDecoderFFmpegHWPrivate()
, surface_auto(true)
, surface_count(0)
, surface_width(0)
, surface_height(0)
, surface_order(0)
{
}
bool VideoDecoderD3DPrivate::open()
{
if (!prepare())
return false;
if (codec_ctx->codec_id == QTAV_CODEC_ID(HEVC)) {
// runtime hevc check
if (!isHEVCSupported()) {
qWarning("HEVC DXVA2/D3D11VA is not supported by current FFmpeg runtime.");
return false;
}
}
if (!createDevice())
return false;
format_fcc = 0;
QVector<GUID> codecs = getSupportedCodecs();
const d3d_format_t *fmt = getFormat(codec_ctx, codecs, &codec_guid);
if (!fmt)
return false;
format_fcc = fmt->fourcc;
if (!setupSurfaceInterop())
return false;
return true;
}
void VideoDecoderD3DPrivate::close()
{
qDeleteAll(surfaces);
surfaces.clear();
restore();
releaseUSWC();
destroyDecoder();
destroyDevice();
}
void* VideoDecoderD3DPrivate::setup(AVCodecContext *avctx)
{
const int w = codedWidth(avctx);
const int h = codedHeight(avctx);
width = avctx->width; // not necessary. set in decode()
height = avctx->height;
releaseUSWC();
destroyDecoder();
/* Allocates all surfaces needed for the decoder */
if (surface_auto) {
switch (codec_ctx->codec_id) {
case QTAV_CODEC_ID(HEVC):
case QTAV_CODEC_ID(H264):
surface_count = 16 + 4;
break;
case QTAV_CODEC_ID(MPEG1VIDEO):
case QTAV_CODEC_ID(MPEG2VIDEO):
surface_count = 2 + 4;
default:
surface_count = 2 + 4;
break;
}
if (avctx->active_thread_type & FF_THREAD_FRAME)
surface_count += avctx->thread_count;
}
qDebug(">>>>>>>>>>>>>>>>>>>>>surfaces: %d, active_thread_type: %d, threads: %d, refs: %d", surface_count, avctx->active_thread_type, avctx->thread_count, avctx->refs);
if (surface_count == 0) {
qWarning("internal error: wrong surface count. %u auto=%d", surface_count, surface_auto);
surface_count = 16 + 4;
}
qDeleteAll(surfaces);
surfaces.clear();
hw_surfaces.clear();
surfaces.resize(surface_count);
if (!createDecoder(codec_ctx->codec_id, w, h, surfaces))
return NULL;
hw_surfaces.resize(surface_count);
for (int i = 0; i < surfaces.size(); ++i) {
hw_surfaces[i] = surfaces[i]->getSurface();
}
surface_order = 0;
surface_width = aligned(w);
surface_height = aligned(h);
initUSWC(surface_width);
return setupAVVAContext(); //can not use codec_ctx for threaded mode!
}
/* FIXME it is nearly common with VAAPI */
bool VideoDecoderD3DPrivate::getBuffer(void **opaque, uint8_t **data)//vlc_va_t *external, AVFrame *ff)
{
if (!checkDevice())
return false;
/* Grab an unused surface, in case none are, try the oldest
* XXX using the oldest is a workaround in case a problem happens with libavcodec */
int i, old;
for (i = 0, old = 0; i < surfaces.size(); i++) {
const va_surface_t *s = surfaces[i];
if (!s->ref)
break;
if (s->order < surfaces[old]->order)
old = i;
}
if (i >= surfaces.size())
i = old;
va_surface_t *s = surfaces[i];
s->ref = 1;
s->order = surface_order++;
*data = (uint8_t*)s->getSurface();
*opaque = s;
return true;
}
void VideoDecoderD3DPrivate::releaseBuffer(void *opaque, uint8_t *data)
{
Q_UNUSED(data);
va_surface_t *surface = (va_surface_t*)opaque;
surface->ref--;
}
int VideoDecoderD3DPrivate::aligned(int x)
{
// from lavfilters
int align = 16;
// MPEG-2 needs higher alignment on Intel cards, and it doesn't seem to harm anything to do it for all cards.
if (codec_ctx->codec_id == QTAV_CODEC_ID(MPEG2VIDEO))
align <<= 1;
else if (codec_ctx->codec_id == QTAV_CODEC_ID(HEVC))
align = 128;
return FFALIGN(x, align);
}
const d3d_format_t* VideoDecoderD3DPrivate::getFormat(const AVCodecContext *avctx, const QVector<GUID> &guids, GUID* selected) const
{
foreach (const GUID& g, guids) {
const dxva2_mode_t *mode = Dxva2FindMode(&g);
if (mode) {
qDebug("- '%s' is supported by hardware", mode->name);
} else {
qDebug("- Unknown GUID = %08X-%04x-%04x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x",
(unsigned)g.Data1, g.Data2, g.Data3
, g.Data4[0], g.Data4[1]
, g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]);
}
}
/* Try all supported mode by our priority */
const dxva2_mode_t *mode = dxva2_modes;
for (; mode->name; ++mode) {
if (!mode->codec || mode->codec != avctx->codec_id) {
qDebug("codec does not match to %s: %s", avcodec_get_name(avctx->codec_id), avcodec_get_name((AVCodecID)mode->codec));
continue;
}
qDebug("D3D found codec: %s. Check runtime support for the codec.", mode->name);
bool is_supported = false;
//TODO: find_if
foreach (const GUID& g, guids) {
if (IsEqualGUID(*mode->guid, g)) {
is_supported = true;
break;
}
}
if (is_supported) {
qDebug("Check profile support: %s", AVDecoderPrivate::getProfileName(avctx));
is_supported = checkProfile(mode, avctx->profile);
}
if (!is_supported)
continue;
int dxfmt = fourccFor(mode->guid);
if (!dxfmt)
continue;
if (selected)
*selected = *mode->guid;
return D3dFindFormat(dxfmt);
}
return NULL;
}
} //namespace FAV
#endif // #ifdef RM_USE_HW_DECODER

View File

@@ -0,0 +1,145 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2016)
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
******************************************************************************/
#ifndef QTAV_VIDEODECODERD3D_H
#define QTAV_VIDEODECODERD3D_H
#ifdef RM_USE_HW_DECODER
#include <unknwn.h>
#include <inttypes.h>
#include "VideoDecoderFFmpegHW.h"
#include "VideoDecoderFFmpegHW_p.h"
namespace FAV {
struct d3d_format_t {
const char *name;
int fourcc;
VideoFormat::PixelFormat pixfmt;
};
bool isIntelClearVideo(const GUID *guid);
bool isNoEncrypt(const GUID* guid);
int getSupportedFourcc(int *formats, UINT nb_formats);
VideoFormat::PixelFormat pixelFormatFromFourcc(int format);
class VideoDecoderD3DPrivate;
class VideoDecoderD3D : public VideoDecoderFFmpegHW
{
DPTR_DECLARE_PRIVATE(VideoDecoderD3D)
Q_OBJECT
Q_PROPERTY(int surfaces READ surfaces WRITE setSurfaces NOTIFY surfacesChanged)
public:
// properties
void setSurfaces(int num);
int surfaces() const;
Q_SIGNALS:
void surfacesChanged();
protected:
VideoDecoderD3D(VideoDecoderD3DPrivate& d);
};
struct va_surface_t {
va_surface_t() : ref(0), order(0) {}
virtual ~va_surface_t() {}
virtual void setSurface(IUnknown* s) = 0;
virtual IUnknown* getSurface() const = 0;
int ref;
int order;
};
class VideoDecoderD3DPrivate : public VideoDecoderFFmpegHWPrivate
{
public:
VideoDecoderD3DPrivate();
bool open() Q_DECL_OVERRIDE;
void close() Q_DECL_OVERRIDE;
void* setup(AVCodecContext *avctx) Q_DECL_OVERRIDE;
bool getBuffer(void **opaque, uint8_t **data) Q_DECL_OVERRIDE;
void releaseBuffer(void *opaque, uint8_t *data) Q_DECL_OVERRIDE;
int aligned(int x);
private:
virtual bool setupSurfaceInterop() {return true;}
virtual bool createDevice() = 0; //d3d device, video context etc.
virtual void destroyDevice() = 0;
virtual bool checkDevice() {return true;}
virtual QVector<GUID> getSupportedCodecs() const = 0;
virtual void* setupAVVAContext() = 0;
/// create surfaces and decoder. width and height are coded value, maybe not aligned for d3d surface
/// surfaces count is given, but not allocated
virtual bool createDecoder(AVCodecID codec, int width, int height, QVector<va_surface_t*>& surf) = 0;
virtual void destroyDecoder() = 0;
virtual int fourccFor(const GUID *guid) const = 0;
const d3d_format_t *getFormat(const AVCodecContext* avctx, const QVector<GUID>& guids, GUID *selected) const;
public:
// set by user. don't reset in when call destroy
bool surface_auto;
int surface_count;
QVector<IUnknown*> hw_surfaces;
int format_fcc;
GUID codec_guid;
private:
int surface_width;
int surface_height;
unsigned surface_order;
QVector<va_surface_t*> surfaces; //TODO: delete on close()
};
template<typename T> int SelectConfig(AVCodecID codec_id, const T* cfgs, int nb_cfgs, T* cfg)
{
qDebug("Decoder configurations: %d", nb_cfgs);
/* Select the best decoder configuration */
int cfg_score = 0;
for (int i = 0; i < nb_cfgs; i++) {
const T &c = cfgs[i];
qDebug("configuration[%d] ConfigBitstreamRaw %d", i, c.ConfigBitstreamRaw);
/* */
int score = 0;
if (c.ConfigBitstreamRaw == 1)
score = 1;
else if (codec_id == QTAV_CODEC_ID(H264) && c.ConfigBitstreamRaw == 2)
score = 2;
else
continue;
if (isNoEncrypt(&c.guidConfigBitstreamEncryption))
score += 16;
if (cfg_score < score) {
*cfg = c;
cfg_score = score;
}
}
if (cfg_score <= 0)
qWarning("Failed to find a supported decoder configuration");
return cfg_score;
}
#ifndef MAKEFOURCC //winrt
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((DWORD)(BYTE)(ch0)|((DWORD)(BYTE)(ch1)<<8)|((DWORD)(BYTE)(ch2)<<16)|((DWORD)(BYTE)(ch3)<<24))
#endif //MAKEFOURCC
} //namespace QtAV
#endif // RM_USE_HW_DECODER
#endif //QTAV_VIDEODECODERD3D_H

View File

@@ -0,0 +1,457 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2013)
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
******************************************************************************/
/// egl support is added by: Andy Bell <andy.bell@displaynote.com>
#ifdef RM_USE_HW_DECODER
#ifdef _MSC_VER
#pragma comment(lib, "ole32.lib") //CoTaskMemFree. why link failed?
#endif
#include "VideoDecoderD3D.h"
#include "../AVCompat.h"
#include "../factory.h"
//#include "mkid.h"
#include "../Logger.h"
#include "SurfaceInteropD3D9.h"
#include <QtCore/QSysInfo>
#define DX_LOG_COMPONENT "DXVA2"
#include "../DirectXHelper.h"
// d3d9ex: http://dxr.mozilla.org/mozilla-central/source/dom/media/wmf/DXVA2Manager.cpp
// to use c api, add define COBJMACROS and CINTERFACE
#define DXVA2API_USE_BITFIELDS
extern "C" {
#include <libavcodec/dxva2.h> //will include d3d9.h, dxva2api.h
}
#include <d3d9.h>
#include <dxva2api.h>
#include <initguid.h> /* must be last included to not redefine existing GUIDs */
/* dxva2api.h GUIDs: http://msdn.microsoft.com/en-us/library/windows/desktop/ms697067(v=vs100).aspx
* assume that they are declared in dxva2api.h */
//#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) ///TODO: ???
#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}}
#ifdef __MINGW32__
# include <_mingw.h>
# if !defined(__MINGW64_VERSION_MAJOR)
# undef MS_GUID
# define MS_GUID DEFINE_GUID /* dxva2api.h fails to declare those, redefine as static */
# else
# include <dxva.h>
# endif
#endif /* __MINGW32__ */
namespace FAV {
MS_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
MS_GUID(IID_IDirectXVideoAccelerationService, 0xfc51a550, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
class VideoDecoderDXVAPrivate;
class VideoDecoderDXVA : public VideoDecoderD3D
{
DPTR_DECLARE_PRIVATE(VideoDecoderDXVA)
public:
VideoDecoderDXVA();
VideoDecoderId id() const Q_DECL_OVERRIDE;
QString description() const Q_DECL_OVERRIDE;
VideoFrame frame() Q_DECL_OVERRIDE;
};
extern VideoDecoderId VideoDecoderId_DXVA;
FACTORY_REGISTER(VideoDecoder, DXVA, "DXVA")
struct d3d9_surface_t : public va_surface_t {
d3d9_surface_t() : va_surface_t(), d3d(0) {}
~d3d9_surface_t() { SafeRelease(&d3d);}
void setSurface(IUnknown* s) Q_DECL_OVERRIDE {
d3d = (IDirect3DSurface9*)s;
}
IUnknown* getSurface() const {return d3d;}
private:
IDirect3DSurface9 *d3d;
};
/* */
//https://technet.microsoft.com/zh-cn/aa965266(v=vs.98).aspx
class VideoDecoderDXVAPrivate Q_DECL_FINAL: public VideoDecoderD3DPrivate
{
public:
VideoDecoderDXVAPrivate():
VideoDecoderD3DPrivate()
{
// d3d9+gl interop may not work on optimus moble platforms, 0-copy is enabled only for egl interop
if (d3d9::InteropResource::isSupported(d3d9::InteropEGL) &&
QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA)
copy_mode = VideoDecoderFFmpegHW::ZeroCopy;
hd3d9_dll = 0;
hdxva2_dll = 0;
d3dobj = 0;
d3ddev = 0;
token = 0;
devmng = 0;
device = 0;
vs = 0;
decoder = 0;
available = loadDll();
qInfo() << "DXVA Decoder:" << available;
}
virtual ~VideoDecoderDXVAPrivate() // can not unload dlls because dx resource will be released in VideoDecoderD3DPrivate::close
{
unloadDll();
}
AVPixelFormat vaPixelFormat() const Q_DECL_OVERRIDE { return QTAV_PIX_FMT_C(DXVA2_VLD);}
private:
bool loadDll();
bool unloadDll();
bool createDevice() Q_DECL_OVERRIDE;
void destroyDevice() Q_DECL_OVERRIDE; //d3d device and it's resources, device manager, video device and decoder service
QVector<GUID> getSupportedCodecs() const Q_DECL_OVERRIDE;
bool checkDevice() Q_DECL_OVERRIDE;
bool createDecoder(AVCodecID codec_id, int w, int h, QVector<va_surface_t*>& surf) Q_DECL_OVERRIDE;
void destroyDecoder() Q_DECL_OVERRIDE;
bool setupSurfaceInterop() Q_DECL_OVERRIDE;
void* setupAVVAContext() Q_DECL_OVERRIDE;
int fourccFor(const GUID *guid) const Q_DECL_OVERRIDE;
/* DLL */
HINSTANCE hd3d9_dll;
HINSTANCE hdxva2_dll;
IDirect3D9 *d3dobj;
IDirect3DDevice9 *d3ddev; // can be Ex
/* Device manager */
UINT token;
IDirect3DDeviceManager9 *devmng;
HANDLE device;
/* Video service */
IDirectXVideoDecoderService *vs;
/* Video decoder */
DXVA2_ConfigPictureDecode cfg;
IDirectXVideoDecoder *decoder;
struct dxva_context hw_ctx;
QString vendor;
public:
d3d9::InteropResourcePtr interop_res; //may be still used in video frames when decoder is destroyed
};
static D3DFORMAT fourccToD3D(int fcc) {
return (D3DFORMAT)fcc;
}
VideoDecoderDXVA::VideoDecoderDXVA()
: VideoDecoderD3D(*new VideoDecoderDXVAPrivate())
{
}
VideoDecoderId VideoDecoderDXVA::id() const
{
return VideoDecoderId_DXVA;
}
QString VideoDecoderDXVA::description() const
{
DPTR_D(const VideoDecoderDXVA);
if (!d.description.isEmpty())
return d.description;
return QStringLiteral("DirectX Video Acceleration");
}
VideoFrame VideoDecoderDXVA::frame()
{
DPTR_D(VideoDecoderDXVA);
//qDebug("frame size: %dx%d", d.frame->width, d.frame->height);
if (!d.frame->opaque || !d.frame->data[0])
return VideoFrame();
if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx)
return VideoFrame();
IDirect3DSurface9 *d3d = (IDirect3DSurface9*)(uintptr_t)d.frame->data[3];
if (copyMode() == ZeroCopy && d.interop_res) {
d3d9::SurfaceInterop *interop = new d3d9::SurfaceInterop(d.interop_res);
interop->setSurface(d3d, d.width, d.height);
VideoFrame f(d.width, d.height, VideoFormat::Format_RGB32);
f.setBytesPerLine(d.width * 4); //used by gl to compute texture size
f.setMetaData(QStringLiteral("surface_interop"), QVariant::fromValue(VideoSurfaceInteropPtr(interop)));
f.setTimestamp(d.frame->pkt_pts/1000.0);
f.setDisplayAspectRatio(d.getDAR(d.frame));
return f;
}
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(d3d, &lock);
if (lock.Pitch == 0) {
return VideoFrame();
}
//picth >= desc.Width
D3DSURFACE_DESC desc;
d3d->GetDesc(&desc);
const VideoFormat fmt = VideoFormat(pixelFormatFromFourcc(desc.Format));
if (!fmt.isValid()) {
qWarning("unsupported dxva pixel format: %#x", desc.Format);
return VideoFrame();
}
//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
uint8_t *src[] = { (uint8_t*)lock.pBits, 0, 0}; //compute chroma later
const bool swap_uv = desc.Format == MAKEFOURCC('I','M','C','3');
return copyToFrame(fmt, desc.Height, src, pitch, swap_uv);
}
bool VideoDecoderDXVAPrivate::loadDll() {
hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
if (!hd3d9_dll) {
qWarning("cannot load d3d9.dll");
return false;
}
hdxva2_dll = LoadLibrary(TEXT("DXVA2.DLL"));
if (!hdxva2_dll) {
qWarning("cannot load dxva2.dll");
FreeLibrary(hd3d9_dll);
return false;
}
return true;
}
bool VideoDecoderDXVAPrivate::unloadDll() {
if (hdxva2_dll)
FreeLibrary(hdxva2_dll);
if (hd3d9_dll)
FreeLibrary(hd3d9_dll); //TODO: don't unload. maybe it's used by others
return true;
}
bool VideoDecoderDXVAPrivate::createDevice()
{
// check whether they are created?
D3DADAPTER_IDENTIFIER9 d3dai;
ZeroMemory(&d3dai, sizeof(d3dai));
d3ddev = DXHelper::CreateDevice9Ex(hd3d9_dll, (IDirect3D9Ex**)(&d3dobj), &d3dai);
if (!d3ddev) {
qWarning("Failed to create d3d9 device ex, fallback to d3d9 device");
d3ddev = DXHelper::CreateDevice9(hd3d9_dll, &d3dobj, &d3dai);
}
if (!d3ddev) {
qWarning("Failed to create d3d9 device");
return false;
}
vendor = QString::fromLatin1(DXHelper::vendorName(d3dai.VendorId));
description = QString().sprintf("DXVA2 (%.*s, vendor %lu(%s), device %lu, revision %lu)",
sizeof(d3dai.Description), d3dai.Description,
d3dai.VendorId, qPrintable(vendor), d3dai.DeviceId, d3dai.Revision);
//if (copy_uswc)
// copy_uswc = vendor.toLower() == "intel";
qDebug("DXVA2 description: %s", description.toUtf8().constData());
if (!d3ddev)
return false;
typedef HRESULT (WINAPI *CreateDeviceManager9Func)(UINT *pResetToken, IDirect3DDeviceManager9 **);
CreateDeviceManager9Func CreateDeviceManager9 = (CreateDeviceManager9Func)GetProcAddress(hdxva2_dll, "DXVA2CreateDirect3DDeviceManager9");
if (!CreateDeviceManager9) {
qWarning("cannot load function DXVA2CreateDirect3DDeviceManager9");
return false;
}
qDebug("OurDirect3DCreateDeviceManager9 Success!");
DX_ENSURE_OK(CreateDeviceManager9(&token, &devmng), false);
qDebug("obtained IDirect3DDeviceManager9");
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms693525%28v=vs.85%29.aspx
DX_ENSURE_OK(devmng->ResetDevice(d3ddev, token), false);
DX_ENSURE_OK(devmng->OpenDeviceHandle(&device), false);
DX_ENSURE_OK(devmng->GetVideoService(device, IID_IDirectXVideoDecoderService, (void**)&vs), false);
return true;
}
void VideoDecoderDXVAPrivate::destroyDevice()
{
SafeRelease(&vs);
if (devmng && device && device != INVALID_HANDLE_VALUE) {
devmng->CloseDeviceHandle(device);
device = 0;
}
SafeRelease(&devmng);
SafeRelease(&d3ddev);
SafeRelease(&d3dobj);
}
QVector<GUID> VideoDecoderDXVAPrivate::getSupportedCodecs() const
{
/* Retreive supported modes from the decoder service */
UINT input_count = 0;
GUID *input_list = NULL;
QVector<GUID> guids;
DX_ENSURE_OK(vs->GetDecoderDeviceGuids(&input_count, &input_list), guids);
guids.resize(input_count);
memcpy(guids.data(), input_list, input_count*sizeof(GUID));
CoTaskMemFree(input_list);
return guids;
}
int VideoDecoderDXVAPrivate::fourccFor(const GUID *guid) const
{
UINT output_count = 0;
D3DFORMAT *output_list = NULL;
if (FAILED(vs->GetDecoderRenderTargets(*guid, &output_count, &output_list))) {
qWarning("IDirectXVideoDecoderService_GetDecoderRenderTargets failed");
return 0;
}
int fmt = getSupportedFourcc((int*)output_list, output_count);
CoTaskMemFree(output_list);
return fmt;
}
bool VideoDecoderDXVAPrivate::checkDevice()
{
// check pix fmt DXVA2_VLD and h264 profile?
HRESULT hr = devmng->TestDevice(device);
if (hr == DXVA2_E_NEW_VIDEO_DEVICE) {
//https://technet.microsoft.com/zh-cn/aa965266(v=vs.98).aspx
// CloseDeviceHandle,Release service&decoder, open a new device handle, create a new decoder
qWarning("DXVA2_E_NEW_VIDEO_DEVICE. Video decoder reset is not implemeted");
return false;
} else if (FAILED(hr)) {
qWarning() << "IDirect3DDeviceManager9.TestDevice (" << hr << "): " << qt_error_string(hr);
return false;
}
return true;
}
bool VideoDecoderDXVAPrivate::createDecoder(AVCodecID codec_id, int w, int h, QVector<va_surface_t*>& surf)
{
if (!vs || !d3ddev) {
qWarning("dx video surface is not ready. IDirectXVideoService: %p, IDirect3DDevice9: %p", vs, d3ddev);
return false;
}
const int nb_surfaces = surf.size();
static const int kMaxSurfaceCount = 64;
IDirect3DSurface9* surface_list[kMaxSurfaceCount];
qDebug("IDirectXVideoDecoderService=%p nb_surfaces=%d surface %dx%d", vs, nb_surfaces, aligned(w), aligned(h));
DX_ENSURE_OK(vs->CreateSurface(aligned(w),
aligned(h),
nb_surfaces - 1, //The number of back buffers. The method creates BackBuffers + 1 surfaces.
fourccToD3D(format_fcc),
D3DPOOL_DEFAULT,
0,
DXVA2_VideoDecoderRenderTarget,
surface_list,
NULL)
, false);
for (int i = 0; i < nb_surfaces; i++) {
d3d9_surface_t* s = new d3d9_surface_t();
s->setSurface(surface_list[i]);
surf[i] = s;
}
qDebug("IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)", nb_surfaces, w, h);
/* */
DXVA2_VideoDesc dsc;
ZeroMemory(&dsc, sizeof(dsc));
dsc.SampleWidth = w; //coded_width
dsc.SampleHeight = h; //coded_height
dsc.Format = fourccToD3D(format_fcc);
dsc.InputSampleFreq.Numerator = 0;
dsc.InputSampleFreq.Denominator = 0;
dsc.OutputFrameFreq = dsc.InputSampleFreq;
dsc.UABProtectionLevel = FALSE;
dsc.Reserved = 0;
// see xbmc
/* FIXME I am unsure we can let unknown everywhere */
DXVA2_ExtendedFormat *ext = &dsc.SampleFormat;
ext->SampleFormat = 0;//DXVA2_SampleProgressiveFrame;//xbmc. DXVA2_SampleUnknown;
ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown;
ext->NominalRange = 0;//DXVA2_NominalRange_Unknown;
ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown;
ext->VideoLighting = 0;//DXVA2_VideoLighting_dim;//xbmc. DXVA2_VideoLighting_Unknown;
ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown;
ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown;
/* List all configurations available for the decoder */
UINT cfg_count = 0;
DXVA2_ConfigPictureDecode *cfg_list = NULL;
DX_ENSURE_OK(vs->GetDecoderConfigurations(codec_guid,
&dsc,
NULL,
&cfg_count,
&cfg_list)
, false);
const int score = SelectConfig(codec_id, cfg_list, cfg_count, &cfg);
CoTaskMemFree(cfg_list);
if (score <= 0)
return false;
/* Create the decoder */
DX_ENSURE_OK(vs->CreateVideoDecoder(codec_guid, &dsc, &cfg, surface_list, nb_surfaces, &decoder), false);
qDebug("IDirectXVideoDecoderService.CreateVideoDecoder succeed. decoder=%p", decoder);
return true;
}
void VideoDecoderDXVAPrivate::destroyDecoder()
{
SafeRelease(&decoder);
}
void* VideoDecoderDXVAPrivate::setupAVVAContext()
{
// TODO: FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG
if (isIntelClearVideo(&codec_guid)) {
#ifdef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO //2014-03-07 - 8b2a130 - lavc 55.50.0 / 55.53.100 - dxva2.h
qDebug("FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO");
hw_ctx.workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
#endif
} else {
hw_ctx.workaround = 0;
}
hw_ctx.decoder = decoder;
hw_ctx.cfg = &cfg;
hw_ctx.surface_count = hw_surfaces.size();
hw_ctx.surface = (IDirect3DSurface9**)hw_surfaces.constData();
return &hw_ctx;
}
bool VideoDecoderDXVAPrivate::setupSurfaceInterop()
{
if (copy_mode == VideoDecoderFFmpegHW::ZeroCopy)
interop_res = d3d9::InteropResourcePtr(d3d9::InteropResource::create(d3ddev));
return true;
}
} //namespace QtAV
#endif // #ifdef RM_USE_HW_DECODER

View File

@@ -0,0 +1,339 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2013)
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 "VideoDecoderFFmpegHW.h"
#include "VideoDecoderFFmpegHW_p.h"
#include <algorithm>
#include "../Logger.h"
#ifndef Q_UNLIKELY
#define Q_UNLIKELY(x) (!!(x))
#endif
namespace FAV {
static AVPixelFormat ffmpeg_get_va_format(struct AVCodecContext *c, const AVPixelFormat * ff)
{
VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque;
return va->getFormat(c, ff);
}
#if QTAV_HAVE(AVBUFREF)
typedef struct ffmpeg_va_ref_t {
VideoDecoderFFmpegHWPrivate *va;
void *opaque; //va surface from AVFrame.opaque
} ffmpeg_va_ref_t;
static void ffmpeg_release_va_buffer2(void *opaque, uint8_t *data)
{
ffmpeg_va_ref_t *ref = (ffmpeg_va_ref_t*)opaque;
ref->va->releaseBuffer(ref->opaque, data);
delete ref;
}
static int ffmpeg_get_va_buffer2(struct AVCodecContext *ctx, AVFrame *frame, int flags)
{
Q_UNUSED(flags);
for (unsigned i = 0; i < AV_NUM_DATA_POINTERS; i++) {
frame->data[i] = NULL;
frame->linesize[i] = 0;
frame->buf[i] = NULL;
}
//frame->reordered_opaque = ctx->reordered_opaque; //?? xbmc
// va must be available here
VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)ctx->opaque;
if (!va->getBuffer(&frame->opaque, &frame->data[0])) {
qWarning("va->getBuffer failed");
return -1;
}
ffmpeg_va_ref_t *ref = new ffmpeg_va_ref_t;
ref->va = va;
ref->opaque = frame->opaque;
/* data[0] must be non-NULL for libavcodec internal checks. data[3] actually contains the format-specific surface handle. */
frame->data[3] = frame->data[0];
frame->buf[0] = av_buffer_create(frame->data[0], 0, ffmpeg_release_va_buffer2, ref, 0);
if (Q_UNLIKELY(!frame->buf[0])) {
ffmpeg_release_va_buffer2(ref, frame->data[0]);
return -1;
}
Q_ASSERT(frame->data[0] != NULL);
return 0;
}
#else
static int ffmpeg_get_va_buffer(struct AVCodecContext *c, AVFrame *ff)//vlc_va_t *external, AVFrame *ff)
{
VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque;
//ff->reordered_opaque = c->reordered_opaque; //TODO: dxva?
ff->opaque = 0;
#if !AV_MODULE_CHECK(LIBAVCODEC, 54, 34, 0, 79, 101)
ff->pkt_pts = c->pkt ? c->pkt->pts : AV_NOPTS_VALUE;
#endif
#if LIBAVCODEC_VERSION_MAJOR < 54
ff->age = 256*256*256*64;
#endif
/* hwaccel_context is not present in old ffmpeg version */
// va must be available here
if (!va->getBuffer(&ff->opaque, &ff->data[0]))
return -1;
//ffmpeg_va_GetFrameBuf
ff->data[3] = ff->data[0];
ff->type = FF_BUFFER_TYPE_USER;
return 0;
}
static void ffmpeg_release_va_buffer(struct AVCodecContext *c, AVFrame *ff)
{
VideoDecoderFFmpegHWPrivate *va = (VideoDecoderFFmpegHWPrivate*)c->opaque;
va->releaseBuffer(ff->opaque, ff->data[0]);
memset(ff->data, 0, sizeof(ff->data));
memset(ff->linesize, 0, sizeof(ff->linesize));
}
#endif //QTAV_HAVE(AVBUFREF)
bool VideoDecoderFFmpegHWPrivate::prepare()
{
//// From vlc begin
codec_ctx->thread_safe_callbacks = true; //?
codec_ctx->thread_count = threads;
#ifdef _MSC_VER
#pragma warning(disable:4065) //vc: switch has default but no case
#endif //_MSC_VER
switch (codec_ctx->codec_id) {
# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
/// tested libav-9.x + va-api. If remove this code: Bug detected, please report the issue. Context scratch buffers could not be allocated due to unknown size
case QTAV_CODEC_ID(H264):
case QTAV_CODEC_ID(VC1):
case QTAV_CODEC_ID(WMV3):
codec_ctx->thread_type &= ~FF_THREAD_FRAME;
# endif
default:
break;
}
//// From vlc end
codec_ctx->opaque = this;
pixfmt = codec_ctx->pix_fmt;
get_format = codec_ctx->get_format;
#if QTAV_HAVE(AVBUFREF)
get_buffer2 = codec_ctx->get_buffer2;
#else
get_buffer = codec_ctx->get_buffer;
reget_buffer = codec_ctx->reget_buffer;
release_buffer = codec_ctx->release_buffer;
#endif //QTAV_HAVE(AVBUFREF)
codec_ctx->get_format = ffmpeg_get_va_format;
#if QTAV_HAVE(AVBUFREF)
codec_ctx->get_buffer2 = ffmpeg_get_va_buffer2;
#else
// TODO: FF_API_GET_BUFFER
codec_ctx->get_buffer = ffmpeg_get_va_buffer;//ffmpeg_GetFrameBuf;
codec_ctx->reget_buffer = avcodec_default_reget_buffer;
codec_ctx->release_buffer = ffmpeg_release_va_buffer;//ffmpeg_ReleaseFrameBuf;
#endif //QTAV_HAVE(AVBUFREF)
return true;
}
AVPixelFormat VideoDecoderFFmpegHWPrivate::getFormat(struct AVCodecContext *avctx, const AVPixelFormat *pi_fmt)
{
bool can_hwaccel = false;
for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) {
const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]);
if (dsc == NULL)
continue;
bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
qDebug("available %sware decoder output format %d (%s)",
hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name);
if (hwaccel)
can_hwaccel = true;
}
if (!can_hwaccel)
goto end;
for (size_t i = 0; pi_fmt[i] != QTAV_PIX_FMT_C(NONE); i++) {
if (vaPixelFormat() != pi_fmt[i])
continue;
if (hw_w == codedWidth((avctx)) && hw_h == codedHeight(avctx)
&& hw_profile == avctx->profile // update decoder if profile changed. but now only surfaces are updated
&& avctx->hwaccel_context)
return pi_fmt[i];
// TODO: manage uswc here for x86 (surface size is decoder dependent)
avctx->hwaccel_context = setup(avctx);
if (!avctx->hwaccel_context) {
qWarning("acceleration setup failure");
break;
}
hw_w = codedWidth((avctx));
hw_h = codedHeight(avctx);
hw_profile = avctx->profile;
qDebug("Using %s for hardware decoding.", qPrintable(description));
return pi_fmt[i];
}
close();
end:
qWarning("hardware acceleration is not available" );
/* Fallback to default behaviour */
#if QTAV_HAVE(AVBUFREF)
avctx->get_buffer2 = avcodec_default_get_buffer2;
#endif
return avcodec_default_get_format(avctx, pi_fmt);
}
int VideoDecoderFFmpegHWPrivate::codedWidth(AVCodecContext *avctx) const
{
if (avctx->coded_width > 0)
return avctx->coded_width;
return avctx->width;
}
int VideoDecoderFFmpegHWPrivate::codedHeight(AVCodecContext *avctx) const
{
if (avctx->coded_height > 0)
return avctx->coded_height;
return avctx->height;
}
bool VideoDecoderFFmpegHWPrivate::initUSWC(int lineSize)
{
if (copy_mode != VideoDecoderFFmpegHW::OptimizedCopy)
return false;
return gpu_mem.initCache(lineSize);
}
void VideoDecoderFFmpegHWPrivate::releaseUSWC()
{
if (copy_mode == VideoDecoderFFmpegHW::OptimizedCopy)
gpu_mem.cleanCache();
}
VideoDecoderFFmpegHW::VideoDecoderFFmpegHW(VideoDecoderFFmpegHWPrivate &d):
VideoDecoderFFmpegBase(d)
{
setProperty("detail_copyMode", QStringLiteral("%1. %2\n%3. %4\n%5\n%6")
.arg(tr("ZeroCopy: fastest. Direct rendering without data copy between CPU and GPU"))
.arg(tr("Not implemented for all codecs"))
.arg(tr("Not implemented for all codecs"))
.arg(tr("OptimizedCopy: copy from USWC memory optimized by SSE4.1"))
.arg(tr("GenericCopy: slowest. Generic cpu copy")));
setProperty("detail_threads", QString("%1\n%2\n%3\n%4")
.arg(tr("Number of decoding threads. Set before open. Maybe no effect for some decoders"))
.arg(tr("Multithread decoding may crash"))
.arg(tr("0: auto"))
.arg(tr("1: single thread decoding")));
// Q_UNUSED("ZeroCopy");
// Q_UNUSED("OptimizedCopy");
// Q_UNUSED("GenericCopy");
// Q_UNUSED("copyMode");
}
void VideoDecoderFFmpegHW::setThreads(int value)
{
DPTR_D(VideoDecoderFFmpegHW);
if (d.threads == value)
return;
d.threads = value;
if (d.codec_ctx)
av_opt_set_int(d.codec_ctx, "threads", (int64_t)value, 0);
Q_EMIT threadsChanged();
}
int VideoDecoderFFmpegHW::threads() const
{
return d_func().threads;
}
void VideoDecoderFFmpegHW::setCopyMode(CopyMode value)
{
DPTR_D(VideoDecoderFFmpegHW);
if (d.copy_mode == value)
return;
d_func().copy_mode = value;
emit copyModeChanged();
}
VideoDecoderFFmpegHW::CopyMode VideoDecoderFFmpegHW::copyMode() const
{
return d_func().copy_mode;
}
VideoFrame VideoDecoderFFmpegHW::copyToFrame(const VideoFormat& fmt, int surface_h, quint8 *src[], int pitch[], bool swapUV)
{
DPTR_D(VideoDecoderFFmpegHW);
Q_ASSERT_X(src[0] && pitch[0] > 0, "VideoDecoderFFmpegHW::copyToFrame", "src[0] and pitch[0] must be set");
const int nb_planes = fmt.planeCount();
const int chroma_pitch = nb_planes > 1 ? fmt.bytesPerLine(pitch[0], 1) : 0;
const int chroma_h = fmt.chromaHeight(surface_h);
int h[] = { surface_h, 0, 0};
for (int i = 1; i < nb_planes; ++i) {
h[i] = chroma_h;
// set chroma address and pitch if not set
if (pitch[i] <= 0)
pitch[i] = chroma_pitch;
if (!src[i])
src[i] = src[i-1] + pitch[i-1]*h[i-1];
}
if (swapUV && nb_planes > 2) {
std::swap(src[1], src[2]);
std::swap(pitch[1], pitch[2]);
}
VideoFrame frame;
if (copyMode() == VideoDecoderFFmpegHW::OptimizedCopy && d.gpu_mem.isReady()) {
int yuv_size = 0;
for (int i = 0; i < nb_planes; ++i) {
yuv_size += pitch[i]*h[i];
}
// additional 15 bytes to ensure 16 bytes aligned
QByteArray buf(15 + yuv_size, 0);
const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f;
// plane 1, 2... is aligned?
uchar* plane_ptr = (uchar*)buf.data() + offset_16;
QVector<uchar*> dst(nb_planes, 0);
for (int i = 0; i < nb_planes; ++i) {
dst[i] = plane_ptr;
// TODO: add VideoFormat::planeWidth/Height() ?
// pitch instead of surface_width
plane_ptr += pitch[i] * h[i];
d.gpu_mem.copyFrame(src[i], dst[i], pitch[i], h[i], pitch[i]);
}
frame = VideoFrame(d.width, d.height, fmt, buf);
frame.setBits(dst);
frame.setBytesPerLine(pitch);
} else {
frame = VideoFrame(d.width, d.height, fmt);
frame.setBits(src);
frame.setBytesPerLine(pitch);
// TODO: why clone is faster()?
// TODO: buffer pool and create VideoFrame when needed to avoid copy? also for other va
frame = frame.clone();
}
frame.setTimestamp(double(d.frame->pkt_pts)/1000.0);
frame.setDisplayAspectRatio(d.getDAR(d.frame));
d.updateColorDetails(&frame);
return frame;
}
} //namespace QtAV
#endif // #ifdef RM_USE_HW_DECODER

View File

@@ -0,0 +1,62 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2013)
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
******************************************************************************/
#ifndef QTAV_VIDEODECODERFFMPEGHW_H
#define QTAV_VIDEODECODERFFMPEGHW_H
#ifdef RM_USE_HW_DECODER
#include "../VideoDecoderFFmpegBase.h"
namespace FAV {
class VideoDecoderFFmpegHWPrivate;
class VideoDecoderFFmpegHW : public VideoDecoderFFmpegBase
{
Q_OBJECT
Q_DISABLE_COPY(VideoDecoderFFmpegHW)
DPTR_DECLARE_PRIVATE(VideoDecoderFFmpegHW)
Q_PROPERTY(int threads READ threads WRITE setThreads NOTIFY threadsChanged) // <=0 is auto
Q_PROPERTY(CopyMode copyMode READ copyMode WRITE setCopyMode NOTIFY copyModeChanged)
Q_ENUMS(CopyMode)
public:
enum CopyMode {
ZeroCopy,
OptimizedCopy,
GenericCopy
};
VideoFrame copyToFrame(const VideoFormat& fmt, int surface_h, quint8* src[], int pitch[], bool swapUV);
// properties
int threads() const;
void setThreads(int value);
void setCopyMode(CopyMode value);
CopyMode copyMode() const;
Q_SIGNALS:
void copyModeChanged();
void threadsChanged();
protected:
VideoDecoderFFmpegHW(VideoDecoderFFmpegHWPrivate &d);
private:
VideoDecoderFFmpegHW();
};
} //namespace FAV
#endif // RM_USE_HW_DECODER
#endif // QTAV_VIDEODECODERFFMPEGHW_H

View File

@@ -0,0 +1,102 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV (from 2013)
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
******************************************************************************/
#ifndef QTAV_VIDEODECODERFFMPEGHW_P_H
#define QTAV_VIDEODECODERFFMPEGHW_P_H
#ifdef RM_USE_HW_DECODER
#include "VideoDecoderFFmpegHW.h"
#include "../GPUMemCopy.h"
/*!
QTAV_HAVE(AVBUFREF): use AVCodecContext.get_buffer2 instead of old callbacks. In order to avoid compile warnings, now disable old
callbacks if possible. maybe we can also do a runtime check and enable old callbacks
*/
namespace FAV {
class VideoDecoderFFmpegHWPrivate : public VideoDecoderFFmpegBasePrivate
{
public:
VideoDecoderFFmpegHWPrivate()
: VideoDecoderFFmpegBasePrivate()
, get_format(NULL)
, get_buffer(NULL)
, release_buffer(NULL)
, reget_buffer(NULL)
, get_buffer2(NULL)
, threads(0)
, copy_mode(VideoDecoderFFmpegHW::OptimizedCopy)
, hw_w(0)
, hw_h(0)
, hw_profile(0)
{}
virtual ~VideoDecoderFFmpegHWPrivate() {} //ctx is 0 now
bool enableFrameRef() const Q_DECL_OVERRIDE { return false;} //because of ffmpeg_get_va_buffer2?
bool prepare();
void restore() {
codec_ctx->pix_fmt = pixfmt;
codec_ctx->opaque = 0;
codec_ctx->get_format = get_format;
#if QTAV_HAVE(AVBUFREF)
codec_ctx->get_buffer2 = get_buffer2;
#else
codec_ctx->get_buffer = get_buffer;
codec_ctx->release_buffer = release_buffer;
codec_ctx->reget_buffer = reget_buffer;
#endif //QTAV_HAVE(AVBUFREF)
}
// return hwaccel_context or null
virtual void* setup(AVCodecContext* avctx) = 0;
AVPixelFormat getFormat(struct AVCodecContext *p_context, const AVPixelFormat *pi_fmt);
//TODO: remove opaque
virtual bool getBuffer(void **opaque, uint8_t **data) = 0;
virtual void releaseBuffer(void *opaque, uint8_t *data) = 0;
virtual AVPixelFormat vaPixelFormat() const = 0;
int codedWidth(AVCodecContext *avctx) const; //TODO: virtual int surfaceWidth(AVCodecContext*) const;
int codedHeight(AVCodecContext *avctx) const;
bool initUSWC(int lineSize);
void releaseUSWC();
AVPixelFormat pixfmt; //store old one
//store old values because it does not own AVCodecContext
AVPixelFormat (*get_format)(struct AVCodecContext *s, const AVPixelFormat * fmt);
int (*get_buffer)(struct AVCodecContext *c, AVFrame *pic);
void (*release_buffer)(struct AVCodecContext *c, AVFrame *pic);
int (*reget_buffer)(struct AVCodecContext *c, AVFrame *pic);
int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags);
QString description;
int threads; // multithread decoding may crash for some decoders (dxva, videotoolbox)
// false for not intel gpu. my test result is intel gpu is supper fast and lower cpu usage if use optimized uswc copy. but nv is worse.
// TODO: flag enable, disable, auto
VideoDecoderFFmpegHW::CopyMode copy_mode;
GPUMemCopy gpu_mem;
private:
int hw_w, hw_h, hw_profile;
};
} //namespace FAV
#endif // RM_USE_HW_DECODER
#endif // QTAV_VideoDecoderFFmpegHW_P_H