File indexing completed on 2025-01-19 03:57:04

0001 /*********************************************************
0002  * Copyright (C) 2020, Val Doroshchuk <valbok@gmail.com> *
0003  *                                                       *
0004  * This file is part of QtAVPlayer.                      *
0005  * Free Qt Media Player based on FFmpeg.                 *
0006  *********************************************************/
0007 
0008 #include "qavhwdevice_vaapi_x11_glx_p.h"
0009 #include "qavvideocodec_p.h"
0010 #include "qavstream.h"
0011 #include "qavvideobuffer_gpu_p.h"
0012 #include <QDebug>
0013 
0014 #include <GL/glx.h>
0015 #include <va/va_x11.h>
0016 
0017 extern "C" {
0018 #include <libavutil/hwcontext_vaapi.h>
0019 #include <libavcodec/avcodec.h>
0020 }
0021 
0022 typedef void (*glXBindTexImageEXT_)(Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list);
0023 typedef void (*glXReleaseTexImageEXT_)(Display *dpy, GLXDrawable draw, int buffer);
0024 static glXBindTexImageEXT_ s_glXBindTexImageEXT = nullptr;
0025 static glXReleaseTexImageEXT_ s_glXReleaseTexImageEXT = nullptr;
0026 
0027 QT_BEGIN_NAMESPACE
0028 
0029 class QAVHWDevice_VAAPI_X11_GLXPrivate
0030 {
0031 public:
0032     Pixmap pixmap = 0;
0033     GLXPixmap glxpixmap = 0;
0034     Display *display = nullptr;
0035     GLuint texture = 0;
0036 };
0037 
0038 QAVHWDevice_VAAPI_X11_GLX::QAVHWDevice_VAAPI_X11_GLX()
0039     : d_ptr(new QAVHWDevice_VAAPI_X11_GLXPrivate)
0040 {
0041 }
0042 
0043 QAVHWDevice_VAAPI_X11_GLX::~QAVHWDevice_VAAPI_X11_GLX()
0044 {
0045     Q_D(QAVHWDevice_VAAPI_X11_GLX);
0046 
0047     if (d->glxpixmap) {
0048         s_glXReleaseTexImageEXT(d->display, d->glxpixmap, GLX_FRONT_EXT);
0049         glXDestroyPixmap(d->display, d->glxpixmap);
0050     }
0051     if (d->pixmap)
0052         XFreePixmap(d->display, d->pixmap);
0053 
0054     if (d->texture)
0055         glDeleteTextures(1, &d->texture);
0056 }
0057 
0058 AVPixelFormat QAVHWDevice_VAAPI_X11_GLX::format() const
0059 {
0060     return AV_PIX_FMT_VAAPI;
0061 }
0062 
0063 AVHWDeviceType QAVHWDevice_VAAPI_X11_GLX::type() const
0064 {
0065     return AV_HWDEVICE_TYPE_VAAPI;
0066 }
0067 
0068 class VideoBuffer_GLX : public QAVVideoBuffer_GPU
0069 {
0070 public:
0071     VideoBuffer_GLX(QAVHWDevice_VAAPI_X11_GLXPrivate *hw, const QAVVideoFrame &frame)
0072         : QAVVideoBuffer_GPU(frame)
0073         , m_hw(hw)
0074     {
0075         if (!s_glXBindTexImageEXT) {
0076             s_glXBindTexImageEXT = (glXBindTexImageEXT_) glXGetProcAddressARB((const GLubyte *)"glXBindTexImageEXT");
0077             s_glXReleaseTexImageEXT = (glXReleaseTexImageEXT_) glXGetProcAddressARB((const GLubyte *)"glXReleaseTexImageEXT");
0078         }
0079     }
0080 
0081     QAVVideoFrame::HandleType handleType() const override
0082     {
0083         return QAVVideoFrame::GLTextureHandle;
0084     }
0085 
0086     QVariant handle(QRhi */*rhi*/) const override
0087     {
0088         if (!s_glXBindTexImageEXT) {
0089             qWarning() << "Could not get proc address: s_glXBindTexImageEXT";
0090             return 0;
0091         }
0092 
0093         auto av_frame = frame().frame();
0094         AVHWDeviceContext *hwctx = (AVHWDeviceContext *)frame().stream().codec()->avctx()->hw_device_ctx->data;
0095         AVVAAPIDeviceContext *vactx = (AVVAAPIDeviceContext *)hwctx->hwctx;
0096         VADisplay va_display = vactx->display;
0097         VASurfaceID va_surface = (VASurfaceID)(uintptr_t)av_frame->data[3];
0098 
0099         int w = av_frame->width;
0100         int h = av_frame->height;
0101 
0102         if (!m_hw->display) {
0103             glGenTextures(1, &m_hw->texture);
0104             auto display = (Display *)glXGetCurrentDisplay();
0105             m_hw->display = display;
0106             int xscr = DefaultScreen(display);
0107             const char *glxext = glXQueryExtensionsString(display, xscr);
0108             if (!glxext || !strstr(glxext, "GLX_EXT_texture_from_pixmap")) {
0109                 qWarning() << "GLX_EXT_texture_from_pixmap is not supported";
0110                 return 0;
0111             }
0112 
0113             int attribs[] = {
0114                 GLX_RENDER_TYPE, GLX_RGBA_BIT,
0115                 GLX_X_RENDERABLE, True,
0116                 GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
0117                 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
0118                 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
0119                 GLX_Y_INVERTED_EXT, True,
0120                 GLX_DOUBLEBUFFER, False,
0121                 GLX_RED_SIZE, 8,
0122                 GLX_GREEN_SIZE, 8,
0123                 GLX_BLUE_SIZE, 8,
0124                 GLX_ALPHA_SIZE, 8,
0125                 None
0126             };
0127 
0128             int fbcount;
0129             GLXFBConfig *fbcs = glXChooseFBConfig(display, xscr, attribs, &fbcount);
0130             if (!fbcount) {
0131                 XFree(fbcs);
0132                 qWarning() << "No texture-from-pixmap support";
0133                 return 0;
0134             }
0135 
0136             GLXFBConfig fbc = fbcs[0];
0137             XFree(fbcs);
0138 
0139             XWindowAttributes xwa;
0140             XGetWindowAttributes(display, DefaultRootWindow(display), &xwa);
0141 
0142             m_hw->pixmap = XCreatePixmap(display, DefaultRootWindow(display), w, h, xwa.depth);
0143 
0144             const int attribs_pixmap[] = {
0145                 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
0146                 GLX_TEXTURE_FORMAT_EXT, xwa.depth == 32 ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
0147                 GLX_MIPMAP_TEXTURE_EXT, False,
0148                 None,
0149             };
0150 
0151             m_hw->glxpixmap = glXCreatePixmap(display, fbc, m_hw->pixmap, attribs_pixmap);
0152         }
0153 
0154         vaSyncSurface(va_display, va_surface);
0155         auto status = vaPutSurface(va_display, va_surface, m_hw->pixmap,
0156                                    0, 0, w, h,
0157                                    0, 0, w, h,
0158                                    NULL, 0, VA_FRAME_PICTURE | VA_SRC_BT709);
0159         if (status != VA_STATUS_SUCCESS) {
0160             qWarning() << "vaPutSurface failed" << status;
0161             return 0;
0162         }
0163 
0164         XSync(m_hw->display, False);
0165         glBindTexture(GL_TEXTURE_2D, m_hw->texture);
0166         s_glXBindTexImageEXT(m_hw->display, m_hw->glxpixmap, GLX_FRONT_EXT, NULL);
0167         glBindTexture(GL_TEXTURE_2D, 0);
0168 
0169         return m_hw->texture;
0170     }
0171 
0172     QAVHWDevice_VAAPI_X11_GLXPrivate *m_hw = nullptr;
0173 };
0174 
0175 QAVVideoBuffer *QAVHWDevice_VAAPI_X11_GLX::videoBuffer(const QAVVideoFrame &frame) const
0176 {
0177     return new VideoBuffer_GLX(d_ptr.get(), frame);
0178 }
0179 
0180 QT_END_NAMESPACE