1 /******************************************************************************
2     QtAV:  Multimedia framework based on Qt and FFmpeg
3     Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV (from 2014)
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 ******************************************************************************/
21 
22 #ifndef QTAV_VAAPI_HELPER_H
23 #define QTAV_VAAPI_HELPER_H
24 
25 #include <assert.h>
26 #include <va/va.h>
27 #include <QtCore/QLibrary>
28 #include <QtCore/QSharedPointer>
29 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
30 #include <qopengl.h>
31 #elif defined(QT_OPENGL_LIB)
32 #include <qgl.h>
33 #endif
34 #include "utils/SharedPtr.h"
35 
36 namespace QtAV {
37 #ifndef VA_FOURCC_RGBX
38 #define VA_FOURCC_RGBX		0x58424752
39 #endif
40 #ifndef VA_FOURCC_BGRX
41     #define VA_FOURCC_BGRX		0x58524742
42 #endif
43 #ifndef VA_SURFACE_ATTRIB_SETTABLE
44 // travis-ci use old vaapi
45 struct VASurfaceAttrib;
vaCreateSurfaces(VADisplay dpy,unsigned int format,unsigned int width,unsigned int height,VASurfaceID * surfaces,unsigned int num_surfaces,VASurfaceAttrib * attrib_list,unsigned int num_attribs)46 inline VAStatus vaCreateSurfaces(VADisplay dpy, unsigned int format, unsigned int width, unsigned int height, VASurfaceID *surfaces, unsigned int num_surfaces, VASurfaceAttrib *attrib_list, unsigned int num_attribs
47 ) {
48     return ::vaCreateSurfaces(dpy, width, height, format, num_surfaces, surfaces);
49 }
50 #endif
51 
52 #define VA_ENSURE_TRUE(x, ...) \
53     do { \
54         VAStatus ret = x; \
55         if (ret != VA_STATUS_SUCCESS) { \
56             qWarning("VA-API error@%d. " #x ": %#x %s", __LINE__, ret, vaErrorStr(ret)); \
57             return __VA_ARGS__; \
58         } \
59     } while(0)
60 #define VA_ENSURE(...) VA_ENSURE_TRUE(__VA_ARGS__)
61 #define VAWARN(a) \
62 do { \
63   VAStatus res = a; \
64   if(res != VA_STATUS_SUCCESS) \
65     qWarning("VA-API error %s@%d. " #a ": %#x %s", __FILE__, __LINE__, res, vaErrorStr(res)); \
66 } while(0);
67 
68 namespace vaapi {
69 const char *profileName(VAProfile profile);
70 /*!
71  * \brief va_new_image
72  * create image (if img is not null)/find format for the first supported fourcc from given fourcc list.
73  * if s is not null, also test vaGetImage for the fourcc
74  */
75 VAImageFormat va_new_image(VADisplay display, const unsigned int* fourccs, VAImage* img = 0, int w = 0, int h = 0, VASurfaceID s = VA_INVALID_SURFACE);
76 class dll_helper {
77 public:
78     dll_helper(const QString& soname, int version = -1);
~dll_helper()79     virtual ~dll_helper() { m_lib.unload();}
isLoaded()80     bool isLoaded() const { return m_lib.isLoaded(); }
resolve(const char * symbol)81     void* resolve(const char *symbol) { return (void*)m_lib.resolve(symbol);}
82 private:
83     QLibrary m_lib;
84 };
85 
86 class va_0_38 : protected dll_helper {
87 public:
88     typedef struct {
89         uintptr_t           handle;
90         uint32_t            type;
91         uint32_t            mem_type;
92         size_t              mem_size;
93     } VABufferInfo;
instance()94     static va_0_38& instance() {
95         static va_0_38 self;
96         return self;
97     }
isValid()98     static bool isValid() { return instance().f_vaAcquireBufferHandle && instance().f_vaReleaseBufferHandle;}
vaAcquireBufferHandle(VADisplay dpy,VABufferID buf_id,VABufferInfo * buf_info)99     static VAStatus vaAcquireBufferHandle(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info) {
100         if (!instance().f_vaAcquireBufferHandle)
101             return VA_STATUS_ERROR_UNIMPLEMENTED;
102         return instance().f_vaAcquireBufferHandle(dpy, buf_id, buf_info);
103     }
vaReleaseBufferHandle(VADisplay dpy,VABufferID buf_id)104     static VAStatus vaReleaseBufferHandle(VADisplay dpy, VABufferID buf_id) {
105         if (!instance().f_vaReleaseBufferHandle)
106             return VA_STATUS_ERROR_UNIMPLEMENTED;
107         return instance().f_vaReleaseBufferHandle(dpy, buf_id);
108     }
109 protected:
va_0_38()110     va_0_38() : dll_helper(QString::fromLatin1("va"), 1) {
111         f_vaAcquireBufferHandle = (vaAcquireBufferHandle_t)resolve("vaAcquireBufferHandle");
112         f_vaReleaseBufferHandle = (vaReleaseBufferHandle_t)resolve("vaReleaseBufferHandle");
113     }
114 private:
115     typedef VAStatus (*vaAcquireBufferHandle_t)(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info);
116     typedef VAStatus (*vaReleaseBufferHandle_t)(VADisplay dpy, VABufferID buf_id);
117     static vaAcquireBufferHandle_t f_vaAcquireBufferHandle;
118     static vaReleaseBufferHandle_t f_vaReleaseBufferHandle;
119 };
120 class VAAPI_DRM : protected dll_helper {
121 public:
122     typedef VADisplay vaGetDisplayDRM_t(int fd);
VAAPI_DRM()123     VAAPI_DRM(): dll_helper(QString::fromLatin1("va-drm"),1) {
124         fp_vaGetDisplayDRM = (vaGetDisplayDRM_t*)resolve("vaGetDisplayDRM");
125     }
vaGetDisplayDRM(int fd)126     VADisplay vaGetDisplayDRM(int fd) {
127         assert(fp_vaGetDisplayDRM);
128         return fp_vaGetDisplayDRM(fd);
129     }
130 private:
131     vaGetDisplayDRM_t* fp_vaGetDisplayDRM;
132 };
133 
134 typedef struct _XDisplay Display;
135 class VAAPI_X11 : protected dll_helper {
136 public:
137     typedef unsigned long Drawable;
138     typedef VADisplay vaGetDisplay_t(Display *);
139     typedef VAStatus vaPutSurface_t(VADisplay, VASurfaceID,	Drawable,
140                                    short, short, unsigned short,  unsigned short,
141                                    short, short, unsigned short, unsigned short,
142                                    VARectangle *, unsigned int,  unsigned int);
VAAPI_X11()143     VAAPI_X11(): dll_helper(QString::fromLatin1("va-x11"),1) {
144         fp_vaGetDisplay = (vaGetDisplay_t*)resolve("vaGetDisplay");
145         fp_vaPutSurface = (vaPutSurface_t*)resolve("vaPutSurface");
146     }
vaGetDisplay(Display * dpy)147     VADisplay vaGetDisplay(Display *dpy) {
148         assert(fp_vaGetDisplay);
149         return fp_vaGetDisplay(dpy);
150     }
vaPutSurface(VADisplay dpy,VASurfaceID surface,Drawable draw,short srcx,short srcy,unsigned short srcw,unsigned short srch,short destx,short desty,unsigned short destw,unsigned short desth,VARectangle * cliprects,unsigned int number_cliprects,unsigned int flags)151     VAStatus vaPutSurface (VADisplay dpy, VASurfaceID surface,	Drawable draw, /* X Drawable */
152         short srcx, short srcy, unsigned short srcw,  unsigned short srch,
153         short destx, short desty, unsigned short destw, unsigned short desth,
154         VARectangle *cliprects, /* client supplied destination clip list */
155         unsigned int number_cliprects, /* number of clip rects in the clip list */
156         unsigned int flags /* PutSurface flags */
157     ) {
158         assert(fp_vaPutSurface);
159         return fp_vaPutSurface(dpy, surface, draw, srcx, srcy, srcw, srch, destx, desty, destw, desth, cliprects, number_cliprects, flags);
160     }
161 private:
162     vaGetDisplay_t* fp_vaGetDisplay;
163     vaPutSurface_t* fp_vaPutSurface;
164 };
165 
166 typedef void*   EGLClientBuffer;
167 class VAAPI_EGL : protected dll_helper { //not implemented
168     typedef VAStatus vaGetEGLClientBufferFromSurface_t(VADisplay dpy, VASurfaceID surface, EGLClientBuffer *buffer/* out*/);
169     vaGetEGLClientBufferFromSurface_t* fp_vaGetEGLClientBufferFromSurface;
170 public:
VAAPI_EGL()171     VAAPI_EGL(): dll_helper(QString::fromLatin1("va-egl"),1) {
172         fp_vaGetEGLClientBufferFromSurface = (vaGetEGLClientBufferFromSurface_t*)resolve("vaGetEGLClientBufferFromSurface");
173     }
vaGetEGLClientBufferFromSurface(VADisplay dpy,VASurfaceID surface,EGLClientBuffer * buffer)174     VAStatus vaGetEGLClientBufferFromSurface(VADisplay dpy, VASurfaceID surface, EGLClientBuffer *buffer/* out*/) {
175         assert(fp_vaGetEGLClientBufferFromSurface);
176         return fp_vaGetEGLClientBufferFromSurface(dpy, surface, buffer);
177     }
178 };
179 #ifndef QT_NO_OPENGL
180 class VAAPI_GLX : protected dll_helper {
181 public:
182     typedef VADisplay vaGetDisplayGLX_t(Display *);
183     typedef VAStatus vaCreateSurfaceGLX_t(VADisplay, GLenum, GLuint, void **);
184     typedef VAStatus vaDestroySurfaceGLX_t(VADisplay, void *);
185     typedef VAStatus vaCopySurfaceGLX_t(VADisplay, void *, VASurfaceID, unsigned int);
VAAPI_GLX()186     VAAPI_GLX(): dll_helper(QString::fromLatin1("va-glx"),1) {
187         fp_vaGetDisplayGLX = (vaGetDisplayGLX_t*)resolve("vaGetDisplayGLX");
188         fp_vaCreateSurfaceGLX = (vaCreateSurfaceGLX_t*)resolve("vaCreateSurfaceGLX");
189         fp_vaDestroySurfaceGLX = (vaDestroySurfaceGLX_t*)resolve("vaDestroySurfaceGLX");
190         fp_vaCopySurfaceGLX = (vaCopySurfaceGLX_t*)resolve("vaCopySurfaceGLX");
191     }
vaGetDisplayGLX(Display * dpy)192     VADisplay vaGetDisplayGLX(Display *dpy) {
193         assert(fp_vaGetDisplayGLX);
194         return fp_vaGetDisplayGLX(dpy);
195     }
vaCreateSurfaceGLX(VADisplay dpy,GLenum target,GLuint texture,void ** gl_surface)196     VAStatus vaCreateSurfaceGLX(VADisplay dpy, GLenum target, GLuint texture, void **gl_surface) {
197         assert(fp_vaCreateSurfaceGLX);
198         return fp_vaCreateSurfaceGLX(dpy, target, texture, gl_surface);
199     }
vaDestroySurfaceGLX(VADisplay dpy,void * gl_surface)200     VAStatus vaDestroySurfaceGLX(VADisplay dpy, void *gl_surface) {
201         assert(fp_vaDestroySurfaceGLX);
202         return fp_vaDestroySurfaceGLX(dpy, gl_surface);
203     }
204     /**
205      * Copy a VA surface to a VA/GLX surface
206      *
207      * This function will not return until the copy is completed. At this
208      * point, the underlying GL texture will contain the surface pixels
209      * in an RGB format defined by the user.
210      *
211      * The application shall maintain the live GLX context itself.
212      * Implementations are free to use glXGetCurrentContext() and
213      * glXGetCurrentDrawable() functions for internal purposes.
214      *
215      * @param[in]  dpy        the VA display
216      * @param[in]  gl_surface the VA/GLX destination surface
217      * @param[in]  surface    the VA source surface
218      * @param[in]  flags      the PutSurface flags
219      * @return VA_STATUS_SUCCESS if successful
220      */
vaCopySurfaceGLX(VADisplay dpy,void * gl_surface,VASurfaceID surface,unsigned int flags)221     VAStatus vaCopySurfaceGLX(VADisplay dpy, void *gl_surface, VASurfaceID surface, unsigned int flags) {
222         assert(fp_vaCopySurfaceGLX);
223         return fp_vaCopySurfaceGLX(dpy, gl_surface, surface, flags);
224     }
225 private:
226     vaGetDisplayGLX_t* fp_vaGetDisplayGLX;
227     vaCreateSurfaceGLX_t* fp_vaCreateSurfaceGLX;
228     vaDestroySurfaceGLX_t* fp_vaDestroySurfaceGLX;
229     vaCopySurfaceGLX_t* fp_vaCopySurfaceGLX;
230 };
231 #endif //QT_NO_OPENGL
232 
233 class NativeDisplayBase;
234 typedef QSharedPointer<NativeDisplayBase> NativeDisplayPtr;
235 struct NativeDisplay {
236     enum Type {
237         Auto,
238         X11,
239         GLX, //the same as X11 but use vaGetDisplayGLX()?
240         DRM,
241         Wayland,
242         VA
243     };
244     intptr_t handle;
245     Type type;
NativeDisplayNativeDisplay246     NativeDisplay() : handle(-1), type(Auto) {}
247 };
248 class display_t;
249 typedef QSharedPointer<display_t> display_ptr;
250 class display_t {
251 public:
252     // display can have a valid handle (!=-1, 0), then it's an external display. you have to manager the external display handle yourself
253     static display_ptr create(const NativeDisplay& display);
254     ~display_t();
VADisplay()255     operator VADisplay() const { return m_display;}
get()256     VADisplay get() const {return m_display;}
getVersion(int * majorV,int * minorV)257     void getVersion(int* majorV, int* minorV) { *majorV = m_major; *minorV = m_minor;}
258     NativeDisplay::Type nativeDisplayType() const;
259     intptr_t nativeHandle() const;
260 private:
261     VADisplay m_display;
262     NativeDisplayPtr m_native;
263     int m_major, m_minor;
264 };
265 
266 class surface_t {
267 public:
surface_t(int w,int h,VASurfaceID id,const display_ptr & display)268     surface_t(int w, int h, VASurfaceID id, const display_ptr& display)
269         : m_id(id)
270         , m_display(display)
271         , m_width(w)
272         , m_height(h)
273         , color_space(VA_SRC_BT709)
274     {}
~surface_t()275     ~surface_t() {
276         //qDebug("VAAPI - destroying surface 0x%x", (int)m_id);
277         if (m_id != VA_INVALID_SURFACE)
278             VAWARN(vaDestroySurfaces(m_display->get(), &m_id, 1))
279     }
VASurfaceID()280     operator VASurfaceID() const { return m_id;}
get()281     VASurfaceID get() const { return m_id;}
width()282     int width() const { return m_width;}
height()283     int height() const { return m_height;}
284     void setColorSpace(int cs = VA_SRC_BT709) { color_space = cs;}
colorSpace()285     int colorSpace() const { return color_space;}
display()286     display_ptr display() const { return m_display;}
vadisplay()287     VADisplay vadisplay() const { return m_display->get();}
288 private:
289     VASurfaceID m_id;
290     display_ptr m_display;
291     int m_width, m_height;
292     int color_space;
293 };
294 typedef SharedPtr<surface_t> surface_ptr;
295 #ifndef QT_NO_OPENGL
296 class surface_glx_t : public VAAPI_GLX {
297 public:
surface_glx_t(const display_ptr & dpy)298     surface_glx_t(const display_ptr& dpy) : m_dpy(dpy), m_glx(0) {}
~surface_glx_t()299     ~surface_glx_t() {destroy();}
create(GLuint tex)300     bool create(GLuint tex) {
301         destroy();
302         VA_ENSURE_TRUE(vaCreateSurfaceGLX(m_dpy->get(), GL_TEXTURE_2D, tex, &m_glx), false);
303         return true;
304     }
destroy()305     bool destroy() {
306         if (!m_glx)
307             return true;
308         VA_ENSURE_TRUE(vaDestroySurfaceGLX(m_dpy->get(), m_glx), false);
309         m_glx = 0;
310         return true;
311     }
copy(const surface_ptr & surface)312     bool copy(const surface_ptr& surface) {
313         if (!m_glx)
314             return false;
315         VA_ENSURE_TRUE(vaCopySurfaceGLX(m_dpy->get(), m_glx, surface->get(), VA_FRAME_PICTURE | surface->colorSpace()), false);
316         return true;
317     }
318 private:
319     display_ptr m_dpy;
320     void* m_glx;
321 };
322 typedef QSharedPointer<surface_glx_t> surface_glx_ptr; //store in a vector
323 #endif //QT_NO_OPENGL
324 } //namespace vaapi
325 } //namespace QtAV
326 #endif // QTAV_VAAPI_HELPER_H
327