MythTV master
mythvaapiglxinterop.cpp
Go to the documentation of this file.
1// MythTV
3
4#include "mythvideoout.h"
6
7#define LOC QString("VAAPIGLX: ")
8
10 : MythVAAPIInterop(Player, Context, Type)
11{
12}
13
15{
17}
18
20{
21 uint flags = VA_FRAME_PICTURE;
22 if (!Frame)
23 return flags;
24
25 // Set deinterlacing flags if VPP is not available
27 {
28 flags = VA_FRAME_PICTURE;
29 }
30 else if (is_interlaced(Scan))
31 {
32 // As for VDPAU, only VAAPI can deinterlace these frames - so accept any deinterlacer
33 bool doublerate = true;
34 MythDeintType driverdeint = Frame->GetDoubleRateOption(DEINT_DRIVER | DEINT_CPU | DEINT_SHADER);
35 if (!driverdeint)
36 {
37 doublerate = false;
38 driverdeint = Frame->GetSingleRateOption(DEINT_DRIVER | DEINT_CPU | DEINT_SHADER);
39 }
40
41 if (driverdeint)
42 {
43 driverdeint = DEINT_BASIC;
44 if (m_basicDeinterlacer != driverdeint)
45 {
46 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Enabled deinterlacer '%1'")
47 .arg(MythVideoFrame::DeinterlacerName(driverdeint | DEINT_DRIVER, doublerate, FMT_VAAPI)));
48 }
49
50 bool top = Frame->m_interlacedReverse ? !Frame->m_topFieldFirst : Frame->m_topFieldFirst;
51 if (Scan == kScan_Interlaced)
52 {
53 Frame->m_deinterlaceInuse = driverdeint | DEINT_DRIVER;
54 Frame->m_deinterlaceInuse2x = doublerate;
55 flags = top ? VA_TOP_FIELD : VA_BOTTOM_FIELD;
56 }
57 else if (Scan == kScan_Intr2ndField)
58 {
59 Frame->m_deinterlaceInuse = driverdeint | DEINT_DRIVER;
60 Frame->m_deinterlaceInuse2x = doublerate;
61 flags = top ? VA_BOTTOM_FIELD : VA_TOP_FIELD;
62 }
63 m_basicDeinterlacer = driverdeint;
64 }
65 else if (m_basicDeinterlacer)
66 {
67 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled basic VAAPI deinterlacer");
69 }
70 }
71
72 // Update colourspace
74 {
75 switch (Frame->m_colorspace)
76 {
77 case AVCOL_SPC_BT709: m_vaapiColourSpace = VA_SRC_BT709; break;
78 case AVCOL_SPC_SMPTE170M:
79 case AVCOL_SPC_BT470BG: m_vaapiColourSpace = VA_SRC_BT601; break;
80 case AVCOL_SPC_SMPTE240M: m_vaapiColourSpace = VA_SRC_SMPTE_240; break;
81 default:
82 m_vaapiColourSpace = ((Frame->m_width < 1280) ? VA_SRC_BT601 : VA_SRC_BT709); break;
83 }
84 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using '%1' VAAPI colourspace")
85 .arg((m_vaapiColourSpace == VA_SRC_BT709) ? "bt709" : ((m_vaapiColourSpace == VA_SRC_BT601) ? "bt601" : "smpte240")));
86 }
87 flags |= m_vaapiColourSpace;
88 return flags;
89}
90
92{
94 if (!ColourSpace || !m_vaDisplay)
95 return;
96
98
101 int supported_controls = kPictureAttributeSupported_None;
102 QVector<VADisplayAttribute> supported;
103 int num = vaMaxNumDisplayAttributes(m_vaDisplay);
104 auto* attribs = new VADisplayAttribute[static_cast<unsigned int>(num)];
105
106 int actual = 0;
107 INIT_ST;
108 va_status = vaQueryDisplayAttributes(m_vaDisplay, attribs, &actual);
109 CHECK_ST;
110
111 for (int i = 0; i < actual; i++)
112 {
113 int type = attribs[i].type;
114 if ((attribs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE) &&
115 (type == VADisplayAttribBrightness ||
116 type == VADisplayAttribContrast ||
117 type == VADisplayAttribHue ||
118 type == VADisplayAttribSaturation ||
119 type == VADisplayAttribCSCMatrix))
120 {
121 supported.push_back(attribs[i]);
122 if (type == VADisplayAttribBrightness)
123 supported_controls += kPictureAttributeSupported_Brightness;
124 if (type == VADisplayAttribHue)
125 supported_controls += kPictureAttributeSupported_Hue;
126 if (type == VADisplayAttribContrast)
127 supported_controls += kPictureAttributeSupported_Contrast;
128 if (type == VADisplayAttribSaturation)
129 supported_controls += kPictureAttributeSupported_Colour;
130 }
131 }
132
133 // Set the supported attributes
134 ColourSpace->SetSupportedAttributes(static_cast<PictureAttributeSupported>(supported_controls));
135 // and listen for changes
137
138 // create
139 delete [] attribs;
140
141 if (supported.isEmpty())
142 return;
143
144 m_vaapiPictureAttributeCount = supported.size();
145 m_vaapiPictureAttributes = new VADisplayAttribute[static_cast<unsigned int>(m_vaapiPictureAttributeCount)];
146 for (int i = 0; i < m_vaapiPictureAttributeCount; i++)
147 m_vaapiPictureAttributes[i] = supported.at(i);
148
149 if (supported_controls & kPictureAttributeSupported_Brightness)
151 if (supported_controls & kPictureAttributeSupported_Hue)
153 if (supported_controls & kPictureAttributeSupported_Contrast)
155 if (supported_controls & kPictureAttributeSupported_Colour)
157}
158
160{
161 if (!m_vaDisplay)
162 return -1;
163
164 int adjustment = 0;
165 VADisplayAttribType attrib = VADisplayAttribBrightness;
166 switch (Attribute)
167 {
169 attrib = VADisplayAttribBrightness;
170 break;
172 attrib = VADisplayAttribContrast;
173 break;
175 attrib = VADisplayAttribHue;
176 adjustment = 50;
177 break;
179 attrib = VADisplayAttribSaturation;
180 break;
181 default:
182 return -1;
183 }
184
185 bool found = false;
186 for (int i = 0; i < m_vaapiPictureAttributeCount; i++)
187 {
188 if (m_vaapiPictureAttributes[i].type == attrib)
189 {
190 Value = std::clamp(Value, 0, 100);
191 int newval = Value + adjustment;
192 if (newval > 100) newval -= 100;
193 qreal range = (m_vaapiPictureAttributes[i].max_value - m_vaapiPictureAttributes[i].min_value) / 100.0;
194 int val = m_vaapiPictureAttributes[i].min_value + qRound(newval * range);
195 m_vaapiPictureAttributes[i].value = val;
196 found = true;
197 break;
198 }
199 }
200
201
202 if (found)
203 {
205 INIT_ST;
206 va_status = vaSetDisplayAttributes(m_vaDisplay,
209 CHECK_ST;
211 return Value;
212 }
213 return -1;
214}
215
217 : MythVAAPIInteropGLX(Player, Context, GL_VAAPIGLXCOPY)
218{
219 Display *display = glXGetCurrentDisplay();
220 if (!display)
221 {
222 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open GLX display");
223 return;
224 }
225
226 m_vaDisplay = vaGetDisplayGLX(display);
227 if (!m_vaDisplay)
228 {
229 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create GLX VADisplay");
230 return;
231 }
233}
234
236{
238 {
239 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting GLX surface");
241 INIT_ST;
242 va_status = vaDestroySurfaceGLX(m_vaDisplay, m_glxSurface);
243 CHECK_ST;
244 }
245}
246
247std::vector<MythVideoTextureOpenGL*>
249 MythVideoColourSpace* ColourSpace,
251 FrameScanType Scan)
252{
253 std::vector<MythVideoTextureOpenGL*> result;
254 if (!Frame)
255 return result;
256
257 // Retrieve the VASurface
258 VASurfaceID id = VerifySurface(Context, Frame);
259 if (!id || !m_vaDisplay)
260 return result;
261
262 // Initialise colourspace on first frame
263 if (ColourSpace && m_openglTextures.isEmpty())
264 InitPictureAttributes(ColourSpace);
265
266 // Lock
268
269 // we only ever use one glx surface which is updated on each call
270 if (m_openglTextures.isEmpty())
271 {
272 // create a texture
273 // N.B. No apparent 10/12/16bit support here. Can't encourage vaCreateSurfaceGLX
274 // to work with a 16bit texture
276 if (!texture)
277 return result;
278
279 texture->m_plane = 0;
280 texture->m_planeCount = 1;
281 texture->m_frameType = FMT_VAAPI;
282 texture->m_frameFormat = FMT_RGBA32;
283
284 // create an associated GLX surface
285 INIT_ST;
286 va_status = vaCreateSurfaceGLX(m_vaDisplay, texture->m_texture->target(),
287 texture->m_texture->textureId(), &m_glxSurface);
288 CHECK_ST;
289 if (!m_glxSurface)
290 {
291 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create GLX surface.");
293 return result;
294 }
295
296 result.push_back(texture);
297 m_openglTextures.insert(DUMMY_INTEROP_ID, result);
298 }
299
300 if (m_openglTextures.isEmpty() || !m_glxSurface)
301 return result;
303
304 // VPP deinterlacing
305 id = Deinterlace(Frame, id, Scan);
306
307 // Copy surface to texture
308 INIT_ST;
309 va_status = vaCopySurfaceGLX(m_vaDisplay, m_glxSurface, id, GetFlagsForFrame(Frame, Scan));
310 CHECK_ST;
311 return result;
312}
313
315 : MythVAAPIInteropGLX(Player, Context, GL_VAAPIGLXPIX)
316{
317 m_vaDisplay = vaGetDisplay(glXGetCurrentDisplay());
318 if (!m_vaDisplay)
319 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create X11 VADisplay");
320 else
322 InitPixmaps();
323}
324
326{
328 Display* display = glXGetCurrentDisplay();
329 if (!InitPixmaps() || !display)
330 return;
331
332 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting GLX Pixmaps");
333 if (m_glxPixmap)
334 {
335 m_glxReleaseTexImageEXT(display, m_glxPixmap, GLX_FRONT_EXT);
336 XSync(display, False);
337 glXDestroyPixmap(display, m_glxPixmap);
338 }
339
340 if (m_pixmap)
341 XFreePixmap(display, m_pixmap);
342}
343
344std::vector<MythVideoTextureOpenGL*>
346 MythVideoColourSpace* ColourSpace,
348 FrameScanType Scan)
349{
350 std::vector<MythVideoTextureOpenGL*> result;
351 if (!Frame)
352 return result;
353
354 // Retrieve the VASurface
355 VASurfaceID id = VerifySurface(Context, Frame);
356 if (!id || !m_vaDisplay)
357 return result;
358
359 // Initialise colourspace on first frame
360 if (ColourSpace && m_openglTextures.isEmpty())
361 InitPictureAttributes(ColourSpace);
362
363 // Lock
365 if (!InitPixmaps())
366 return result;
367
368 // we only ever use one pixmap which is updated on each call
369 if (m_openglTextures.isEmpty())
370 {
371 // Get display
372 Display* display = glXGetCurrentDisplay();
373 if (!display)
374 return result;
375
376 // Get a framebuffer config
377 const std::array<const int,23> fbattribs {
378 GLX_RENDER_TYPE, GLX_RGBA_BIT,
379 GLX_X_RENDERABLE, True,
380 GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
381 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
382 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
383 GLX_Y_INVERTED_EXT, True,
384 GLX_DOUBLEBUFFER, False,
385 GLX_RED_SIZE, 8,
386 GLX_GREEN_SIZE, 8,
387 GLX_BLUE_SIZE, 8,
388 GLX_ALPHA_SIZE, 8,
389 0 };
390 int fbcount = 0;
391 GLXFBConfig *fbs = glXChooseFBConfig(display, DefaultScreen(display), fbattribs.data(), &fbcount);
392 if (!fbcount)
393 {
394 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve GLX framebuffer config");
395 return result;
396 }
397
398 GLXFBConfig fbconfig = fbs[0];
399 XFree(reinterpret_cast<void*>(fbs));
400
401 // create pixmaps
402 uint width = static_cast<uint>(m_textureSize.width());
403 uint height = static_cast<uint>(m_textureSize.height());
404 XWindowAttributes xwattribs;
405 XGetWindowAttributes(display, DefaultRootWindow(display), &xwattribs);
406 m_pixmap = XCreatePixmap(display, DefaultRootWindow(display),
407 width, height, static_cast<uint>(xwattribs.depth));
408 if (!m_pixmap)
409 {
410 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create Pixmap");
411 return result;
412 }
413
414 const std::array<const int,7> attribs {
415 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
416 GLX_TEXTURE_FORMAT_EXT, xwattribs.depth == 32 ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
417 GLX_MIPMAP_TEXTURE_EXT, False, 0};
418
419 m_glxPixmap = glXCreatePixmap(display, fbconfig, m_pixmap, attribs.data());
420 if (!m_glxPixmap)
421 {
422 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create GLXPixmap");
423 return result;
424 }
425
426 // Create a texture
427 // N.B. as for GLX Copy there is no obvious 10/12/16bit support here.
428 // too many unknowns in this pipeline
429 std::vector<QSize> size;
430 size.push_back(m_textureSize);
431 std::vector<MythVideoTextureOpenGL*> textures = MythVideoTextureOpenGL::CreateTextures(m_openglContext, FMT_VAAPI, FMT_RGBA32, size);
432 if (textures.empty())
433 return result;
434 result.push_back(textures[0]);
435 m_openglTextures.insert(DUMMY_INTEROP_ID, result);
436 }
437
438 if (m_openglTextures.isEmpty() || !m_glxPixmap || !m_pixmap)
439 return result;
441
442 // VPP deinterlacing
443 id = Deinterlace(Frame, id, Scan);
444
445 // Copy the surface to the texture
446 INIT_ST;
447 va_status = vaSyncSurface(m_vaDisplay, id);
448 CHECK_ST;
449 auto width = static_cast<unsigned short>(m_textureSize.width());
450 auto height = static_cast<unsigned short>(m_textureSize.height());
451 va_status = vaPutSurface(m_vaDisplay, id, m_pixmap,
452 0, 0, width, height, 0, 0, width, height,
453 nullptr, 0, GetFlagsForFrame(Frame, Scan));
454 CHECK_ST;
455
456 Display* glxdisplay = glXGetCurrentDisplay();
457 if (glxdisplay)
458 {
459 XSync(glxdisplay, False);
460 m_openglContext->glBindTexture(QOpenGLTexture::Target2D, result[0]->m_textureId);
461 m_glxBindTexImageEXT(glxdisplay, m_glxPixmap, GLX_FRONT_EXT, nullptr);
462 m_openglContext->glBindTexture(QOpenGLTexture::Target2D, 0);
463 }
464 return result;
465}
466
468{
470 return true;
471
473 m_glxBindTexImageEXT = reinterpret_cast<MYTH_GLXBINDTEXIMAGEEXT>(glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("glXBindTexImageEXT")));
474 m_glxReleaseTexImageEXT = reinterpret_cast<MYTH_GLXRELEASETEXIMAGEEXT>(glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("glXReleaseTexImageEXT")));
476 {
477 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to resolve 'texture_from_pixmap' functions");
478 return false;
479 }
480 return true;
481}
482
484{
485 if (!Context)
486 return false;
487
488 OpenGLLocker locker(Context);
489 Display* display = glXGetCurrentDisplay();
490 if (!display)
491 return false;
492 int screen = DefaultScreen(display);
493 QByteArray extensions(glXQueryExtensionsString(display, screen));
494 return extensions.contains("GLX_EXT_texture_from_pixmap");
495}
QOpenGLTexture * m_texture
MythRenderOpenGL * m_openglContext
QHash< unsigned long long, std::vector< MythVideoTextureOpenGL * > > m_openglTextures
std::vector< MythVideoTextureOpenGL * > Acquire(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan) override
MythVAAPIInteropGLXCopy(MythPlayerUI *Player, MythRenderOpenGL *Context)
MYTH_GLXRELEASETEXIMAGEEXT m_glxReleaseTexImageEXT
MythVAAPIInteropGLXPixmap(MythPlayerUI *Player, MythRenderOpenGL *Context)
std::vector< MythVideoTextureOpenGL * > Acquire(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan) override
static bool IsSupported(MythRenderOpenGL *Context)
MYTH_GLXBINDTEXIMAGEEXT m_glxBindTexImageEXT
MythVAAPIInteropGLX(MythPlayerUI *Player, MythRenderOpenGL *Context, InteropType Type)
void InitPictureAttributes(MythVideoColourSpace *ColourSpace)
VADisplayAttribute * m_vaapiPictureAttributes
MythDeintType m_basicDeinterlacer
uint GetFlagsForFrame(MythVideoFrame *Frame, FrameScanType Scan)
int SetPictureAttribute(PictureAttribute Attribute, int Value)
void InitaliseDisplay(void)
MythDeintType m_deinterlacer
VASurfaceID VerifySurface(MythRenderOpenGL *Context, MythVideoFrame *Frame)
VASurfaceID Deinterlace(MythVideoFrame *Frame, VASurfaceID Current, FrameScanType Scan)
MythVideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
void PictureAttributeChanged(PictureAttribute Attribute, int Value)
void SetSupportedAttributes(PictureAttributeSupported Supported)
Enable the given set of picture attributes.
int GetPictureAttribute(PictureAttribute Attribute)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
Definition: mythframe.cpp:462
static std::vector< MythVideoTextureOpenGL * > CreateTextures(MythRenderOpenGL *Context, VideoFrameType Type, VideoFrameType Format, std::vector< QSize > Sizes, GLenum Target=QOpenGLTexture::Target2D)
Create a set of textures suitable for the given Type and Format.
static MythVideoTextureOpenGL * CreateTexture(MythRenderOpenGL *Context, QSize Size, GLenum Target=QOpenGLTexture::Target2D, QOpenGLTexture::PixelType PixelType=QOpenGLTexture::UInt8, QOpenGLTexture::PixelFormat PixelFormat=QOpenGLTexture::RGBA, QOpenGLTexture::TextureFormat Format=QOpenGLTexture::NoFormat, QOpenGLTexture::Filter Filter=QOpenGLTexture::Linear, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
Create and initialise a MythVideoTexture that is backed by a QOpenGLTexture.
static void DeleteTexture(MythRenderOpenGL *Context, MythVideoTextureOpenGL *Texture)
unsigned int uint
Definition: freesurround.h:24
MythDeintType
Definition: mythframe.h:67
@ DEINT_DRIVER
Definition: mythframe.h:74
@ DEINT_BASIC
Definition: mythframe.h:69
@ DEINT_NONE
Definition: mythframe.h:68
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_CPU
Definition: mythframe.h:72
@ FMT_RGBA32
Definition: mythframe.h:34
@ FMT_VAAPI
Definition: mythframe.h:57
static constexpr uint64_t DUMMY_INTEROP_ID
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
void(*)(Display *, GLXDrawable, int) MYTH_GLXRELEASETEXIMAGEEXT
void(*)(Display *, GLXDrawable, int, int *) MYTH_GLXBINDTEXIMAGEEXT
#define CHECK_ST
#define INIT_ST
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
PictureAttributeSupported
@ kPictureAttributeSupported_Colour
@ kPictureAttributeSupported_Brightness
@ kPictureAttributeSupported_Hue
@ kPictureAttributeSupported_Contrast
@ kPictureAttributeSupported_None
FrameScanType
Definition: videoouttypes.h:95
@ kScan_Intr2ndField
Definition: videoouttypes.h:99
@ kScan_Interlaced
Definition: videoouttypes.h:98
PictureAttribute
@ kPictureAttribute_Contrast
@ kPictureAttribute_Brightness
@ kPictureAttribute_Colour
@ kPictureAttribute_Hue
bool is_interlaced(FrameScanType Scan)