first commit
This commit is contained in:
206
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9.cpp
Normal file
206
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9.cpp
Normal 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
|
||||
94
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9.h
Normal file
94
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9.h
Normal 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
|
||||
210
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9EGL.cpp
Normal file
210
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9EGL.cpp
Normal 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
|
||||
|
||||
129
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9GL.cpp
Normal file
129
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9GL.cpp
Normal 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
|
||||
516
project/fm_viewer/fav/hw_decoder/VideoDecoderD3D.cpp
Normal file
516
project/fm_viewer/fav/hw_decoder/VideoDecoderD3D.cpp
Normal 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
|
||||
145
project/fm_viewer/fav/hw_decoder/VideoDecoderD3D.h
Normal file
145
project/fm_viewer/fav/hw_decoder/VideoDecoderD3D.h
Normal 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
|
||||
457
project/fm_viewer/fav/hw_decoder/VideoDecoderDXVA.cpp
Normal file
457
project/fm_viewer/fav/hw_decoder/VideoDecoderDXVA.cpp
Normal 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
|
||||
339
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW.cpp
Normal file
339
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW.cpp
Normal 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
|
||||
62
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW.h
Normal file
62
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW.h
Normal 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
|
||||
102
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW_p.h
Normal file
102
project/fm_viewer/fav/hw_decoder/VideoDecoderFFmpegHW_p.h
Normal 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
|
||||
Reference in New Issue
Block a user