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