Skip to content

Commit

Permalink
Merge pull request wang-bin#457 from wang-bin/vaapi_x11
Browse files Browse the repository at this point in the history
Vaapi x11 0-copy
  • Loading branch information
wang-bin committed May 31, 2015
2 parents b66929d + b13801a commit 61eedee
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 31 deletions.
32 changes: 19 additions & 13 deletions src/codec/video/VideoDecoderVAAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,15 @@
#include <list>
#include <QtCore/QList>
#include <QtCore/QMetaEnum>
#include <QtCore/QSharedPointer>
#include <QtCore/QStringList>
extern "C" {
#include <libavcodec/vaapi.h>
}
#include <fcntl.h> //open()
#include <unistd.h> //close()
#include "QtAV/Packet.h"
#include "QtAV/SurfaceInterop.h"
#include "QtAV/private/AVCompat.h"
#include "utils/GPUMemCopy.h"
#include "QtAV/private/prepost.h"
#include "vaapi/vaapi_helper.h"
#include "vaapi/SurfaceInteropVAAPI.h"
#include "utils/Logger.h"

Expand Down Expand Up @@ -163,12 +159,13 @@ class VideoDecoderVAAPIPrivate : public VideoDecoderFFmpegHWPrivate, public VAAP
VideoDecoderVAAPIPrivate()
: support_4k(true)
{
if (VAAPI_X11::isLoaded())
display_type = VideoDecoderVAAPI::X11;
copy_mode = VideoDecoderFFmpegHW::ZeroCopy;
if (VAAPI_DRM::isLoaded())
display_type = VideoDecoderVAAPI::DRM;
if (VAAPI_GLX::isLoaded())
display_type = VideoDecoderVAAPI::GLX;
if (VAAPI_X11::isLoaded())
display_type = VideoDecoderVAAPI::X11;
drm_fd = -1;
display_x11 = 0;
config_id = VA_INVALID_ID;
Expand All @@ -184,7 +181,6 @@ class VideoDecoderVAAPIPrivate : public VideoDecoderFFmpegHWPrivate, public VAAP
nb_surfaces = 0;
disable_derive = true;
}
~VideoDecoderVAAPIPrivate() {}
virtual bool open();
virtual void close();
bool createSurfaces(int count, void **hwctx, int w, int h);
Expand Down Expand Up @@ -227,7 +223,7 @@ class VideoDecoderVAAPIPrivate : public VideoDecoderFFmpegHWPrivate, public VAAP
VideoDecoderVAAPI::VideoDecoderVAAPI()
: VideoDecoderFFmpegHW(*new VideoDecoderVAAPIPrivate())
{
setDisplayPriority(QStringList() << "GLX" << "X11" << "DRM");
setDisplayPriority(QStringList() << "X11" << "GLX" << "DRM");
// dynamic properties about static property details. used by UI
// format: detail_property
setProperty("detail_surfaces", tr("Decoding surfaces.") + " " + tr("0: auto"));
Expand Down Expand Up @@ -289,7 +285,7 @@ VideoFrame VideoDecoderVAAPI::frame()
return VideoFrame();
VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3];
VAStatus status = VA_STATUS_SUCCESS;
if (display() == GLX) {
if (display() == GLX || (copyMode() == ZeroCopy && display() == X11)) {
surface_ptr p;
std::list<surface_ptr>::iterator it = d.surfaces_used.begin();
for (; it != d.surfaces_used.end() && !p; ++it) {
Expand All @@ -311,6 +307,7 @@ VideoFrame VideoDecoderVAAPI::frame()
return VideoFrame();
}
((SurfaceInteropVAAPI*)d.surface_interop.data())->setSurface(p);

VideoFrame f(d.width, d.height, VideoFormat::Format_RGB32); //p->width()
f.setBytesPerLine(d.width*4); //used by gl to compute texture size
f.setMetaData("surface_interop", QVariant::fromValue(d.surface_interop));
Expand Down Expand Up @@ -404,6 +401,8 @@ QStringList VideoDecoderVAAPI::displayPriority() const
bool VideoDecoderVAAPIPrivate::open()
{
const codec_profile_t* pe = findProfileEntry(codec_ctx->codec_id, codec_ctx->profile);
// TODO: allow wrong profile
// FIXME: sometimes get wrong profile (switch copyMode)
if (!pe) {
qWarning("codec(%s) or profile(%s) is not supported", avcodec_get_name(codec_ctx->codec_id), getProfileName(codec_ctx->codec_id, codec_ctx->profile));
return false;
Expand Down Expand Up @@ -530,7 +529,10 @@ bool VideoDecoderVAAPIPrivate::open()
//vaCreateConfig(display, pe->va_profile, VAEntrypointVLD, NULL, 0, &config_id)
VA_ENSURE_TRUE(vaCreateConfig(disp, pe->va_profile, VAEntrypointVLD, &attrib, 1, &config_id), false);
supports_derive = false;
surface_interop = VideoSurfaceInteropPtr(new SurfaceInteropVAAPI());
if (display_type == VideoDecoderVAAPI::GLX)
surface_interop = VideoSurfaceInteropPtr(new VAAPI_GLX_Interop());
else if (display_type == VideoDecoderVAAPI::X11)
surface_interop = VideoSurfaceInteropPtr(new VAAPI_X_GLX_Interop());
return true;
}

Expand All @@ -551,17 +553,21 @@ bool VideoDecoderVAAPIPrivate::createSurfaces(int count, void **pp_hw_ctx, int w
context_id = VA_INVALID_ID;
surface_width = FFALIGN(w, 16);
surface_height = FFALIGN(h, 16);
VA_ENSURE_TRUE(vaCreateSurfaces(display->get(), VA_RT_FORMAT_YUV420, width, height, surfaces.data() + old_size, count - old_size, NULL, 0), false);
VAStatus status = VA_STATUS_SUCCESS;
status = vaCreateSurfaces(display->get(), VA_RT_FORMAT_YUV420, width, height, surfaces.data() + old_size, count - old_size, NULL, 0); //VA_ENSURE_OK: travis-ci macro mismatch
if (status != VA_STATUS_SUCCESS) {
qWarning("vaCreateSurfaces error (%#x): %s", status, vaErrorStr(status));
return false;
}
for (int i = old_size; i < surfaces.size(); ++i) {
surfaces_free.push_back(surface_ptr(new surface_t(width, height, surfaces[i], display)));
}
/* Create a context */
if (context_id && context_id != VA_INVALID_ID)
VAWARN(vaDestroyContext(display->get(), context_id));
context_id = VA_INVALID_ID;
VAStatus status = VA_STATUS_SUCCESS;
if ((status = vaCreateContext(display->get(), config_id, width, height, VA_PROGRESSIVE, surfaces.data(), surfaces.size(), &context_id)) != VA_STATUS_SUCCESS) {
qWarning("vaCreateContext(VADisplay:%p, VAConfigID:%#x, %d, %d, VA_PROGRESSIVE, VASurfaceID*:%p, surfaces:%d, VAContextID*:%p) == %#x", display->get(), config_id, width, height, surfaces.constData(), surfaces.size(), &context_id, status);
qWarning("vaCreateContext(VADisplay:%p, VAConfigID:%#x, %d, %d, VA_PROGRESSIVE, VASurfaceID*:%p, surfaces:%d, VAContextID*:%p) == %#x: %s", display->get(), config_id, width, height, surfaces.constData(), surfaces.size(), &context_id, status, vaErrorStr(status));
context_id = VA_INVALID_ID;
destroySurfaces();
return false;
Expand Down
182 changes: 175 additions & 7 deletions src/vaapi/SurfaceInteropVAAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2014-2015 Wang Bin <[email protected]>
* This file is part of QtAV
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
******************************************************************************/

#include "SurfaceInteropVAAPI.h"
#include "utils/OpenGLHelper.h"

#ifndef QT_OPENGL_ES_2
#include <va/va_x11.h>
#endif
namespace QtAV {
namespace vaapi {

SurfaceInteropVAAPI::SurfaceInteropVAAPI()
VAAPI_GLX_Interop::VAAPI_GLX_Interop()
{
}

surface_glx_ptr SurfaceInteropVAAPI::createGLXSurface(void *handle)
surface_glx_ptr VAAPI_GLX_Interop::createGLXSurface(void *handle)
{
GLuint tex = *((GLuint*)handle);
surface_glx_ptr glx(new surface_glx_t());
Expand All @@ -19,10 +42,12 @@ surface_glx_ptr SurfaceInteropVAAPI::createGLXSurface(void *handle)
return glx;
}

void* SurfaceInteropVAAPI::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane)
void* VAAPI_GLX_Interop::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane)
{
if (!fmt.isRGB())
return 0;
QMutexLocker lock(&mutex);

if (!handle)
handle = createHandle(type, fmt, plane);
if (type == GLTextureSurface) {
Expand All @@ -37,7 +62,7 @@ void* SurfaceInteropVAAPI::map(SurfaceType type, const VideoFormat &fmt, void *h
glx->set(m_surface);
if (!glx->copy())
return 0;
glx->sync();
VAWARN(vaSyncSurface(m_surface->display(), m_surface->get()));
return handle;
} else
if (type == HostMemorySurface) {
Expand All @@ -47,7 +72,7 @@ void* SurfaceInteropVAAPI::map(SurfaceType type, const VideoFormat &fmt, void *h
return handle;
}

void SurfaceInteropVAAPI::unmap(void *handle)
void VAAPI_GLX_Interop::unmap(void *handle)
{
QMap<GLuint*,surface_glx_ptr>::iterator it(tmp_surfaces.find((GLuint*)handle));
if (it == tmp_surfaces.end())
Expand All @@ -57,7 +82,7 @@ void SurfaceInteropVAAPI::unmap(void *handle)
tmp_surfaces.erase(it);
}

void* SurfaceInteropVAAPI::createHandle(SurfaceType type, const VideoFormat &fmt, int plane)
void* VAAPI_GLX_Interop::createHandle(SurfaceType type, const VideoFormat &fmt, int plane)
{
Q_UNUSED(plane);
if (type == GLTextureSurface) {
Expand All @@ -78,6 +103,149 @@ void* SurfaceInteropVAAPI::createHandle(SurfaceType type, const VideoFormat &fmt
return 0;
}

#ifndef QT_OPENGL_ES_2
VAAPI_X_GLX_Interop::glXReleaseTexImage_t VAAPI_X_GLX_Interop::glXReleaseTexImage = 0;
VAAPI_X_GLX_Interop::glXBindTexImage_t VAAPI_X_GLX_Interop::glXBindTexImage = 0;

VAAPI_X_GLX_Interop::VAAPI_X_GLX_Interop()
: xdisplay(0)
, fbc(0)
, pixmap(0)
, glxpixmap(0)
, width(0)
, height(0)
{}

VAAPI_X_GLX_Interop::~VAAPI_X_GLX_Interop()
{
if (glxpixmap) {
glXReleaseTexImage(xdisplay, glxpixmap, GLX_FRONT_EXT);
XSync((::Display*)xdisplay, False);
glXDestroyPixmap((::Display*)xdisplay, glxpixmap);
}
glxpixmap = 0;

if (pixmap)
XFreePixmap((::Display*)xdisplay, pixmap);
pixmap = 0;
}

bool VAAPI_X_GLX_Interop::ensureGLX()
{
if (fbc)
return true;
xdisplay = (Display*)glXGetCurrentDisplay();
if (!xdisplay)
return false;
int xscr = DefaultScreen(xdisplay);
const char *glxext = glXQueryExtensionsString((::Display*)xdisplay, xscr);
if (!glxext || !strstr(glxext, "GLX_EXT_texture_from_pixmap"))
return false;
glXBindTexImage = (glXBindTexImage_t)glXGetProcAddressARB((const GLubyte*)"glXBindTexImageEXT");
if (!glXBindTexImage) {
qWarning("glXBindTexImage not found");
return false;
}
glXReleaseTexImage = (glXReleaseTexImage_t)glXGetProcAddressARB((const GLubyte*)"glXReleaseTexImageEXT");
if (!glXReleaseTexImage) {
qWarning("glXReleaseTexImage not found");
return false;
}
int attribs[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT, //xbmc
GLX_X_RENDERABLE, True, //xbmc
GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
GLX_Y_INVERTED_EXT, True,
GLX_DOUBLEBUFFER, False,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
None
};

int fbcount;
GLXFBConfig *fbcs = glXChooseFBConfig((::Display*)xdisplay, xscr, attribs, &fbcount);
if (!fbcount) {
qWarning("No texture-from-pixmap support");
return false;
}
if (fbcount)
fbc = fbcs[0];
XFree(fbcs);
return true;
}

bool VAAPI_X_GLX_Interop::ensurePixmaps(int w, int h)
{
if (pixmap && width == w && height == h)
return true;
if (!ensureGLX()) {
return false;
}

XWindowAttributes xwa;
XGetWindowAttributes((::Display*)xdisplay, RootWindow((::Display*)xdisplay, DefaultScreen((::Display*)xdisplay)), &xwa);
pixmap = XCreatePixmap((::Display*)xdisplay, RootWindow((::Display*)xdisplay, DefaultScreen((::Display*)xdisplay)), w, h, xwa.depth);
qDebug("XCreatePixmap: %lu, depth: %d", pixmap, xwa.depth);
if (!pixmap) {
qWarning("VAAPI_X_GLX_Interop could not create pixmap");
return false;
}

int attribs[] = {
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
GLX_MIPMAP_TEXTURE_EXT, False,
None,
};
glxpixmap = glXCreatePixmap((::Display*)xdisplay, fbc, pixmap, attribs);
width = w;
height = h;
return true;
}

void* VAAPI_X_GLX_Interop::map(SurfaceType type, const VideoFormat &fmt, void *handle, int plane)
{
Q_UNUSED(plane);
if (!handle)
return 0;
QMutexLocker lock(&mutex);
if (!m_surface)
return 0;
if (type != GLTextureSurface)
return 0;
if (!fmt.isRGB()) {
return 0;
}
if (!ensurePixmaps(m_surface->width(), m_surface->height()))
return 0;
VAWARN(vaSyncSurface(m_surface->display(), m_surface->get()));

VA_ENSURE_TRUE(vaPutSurface(m_surface->display(), m_surface->get(), pixmap
, 0, 0, m_surface->width(), m_surface->height()
, 0, 0, m_surface->width(), m_surface->height()
, NULL, 0, VA_FRAME_PICTURE | VA_SRC_BT709)
, NULL);

XSync((::Display*)xdisplay, False);
DYGL(glBindTexture(GL_TEXTURE_2D, *((GLuint*)handle)));
glXBindTexImage(xdisplay, glxpixmap, GLX_FRONT_EXT, NULL);
DYGL(glBindTexture(GL_TEXTURE_2D, 0));

return handle;
}

void VAAPI_X_GLX_Interop::unmap(void *handle)
{
DYGL(glBindTexture(GL_TEXTURE_2D, *((GLuint*)handle)));
glXReleaseTexImage(xdisplay, glxpixmap, GLX_FRONT_EXT);
DYGL(glBindTexture(GL_TEXTURE_2D, 0));
}

#endif //QT_OPENGL_ES_2
} //namespace QtAV
} //namespace vaapi

Loading

0 comments on commit 61eedee

Please sign in to comment.