MythTV  master
mythvaapidrminterop.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "mythcorecontext.h"
3 #include "videocolourspace.h"
4 #include "fourcc.h"
5 #include "mythvaapidrminterop.h"
6 
7 // FFmpeg
8 extern "C" {
9 #include "libavutil/hwcontext_drm.h"
10 }
11 
12 // Std
13 #include <unistd.h>
14 
15 #define LOC QString("VAAPIDRM: ")
16 
18  : MythVAAPIInterop(Context, VAAPIEGLDRM),
20 {
21  QString device = gCoreContext->GetSetting("VAAPIDevice");
22  if (device.isEmpty())
23  device = "/dev/dri/renderD128";
24  m_drmFile.setFileName(device);
25  if (m_drmFile.open(QIODevice::ReadWrite))
26  {
27  m_vaDisplay = vaGetDisplayDRM(m_drmFile.handle());
28  if (!m_vaDisplay)
29  {
30  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create DRM VADisplay");
31  return;
32  }
33  }
34  else
35  {
36  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open %1").arg(device));
37  return;
38  }
40 
41  // DRM PRIME is preferred as it explicitly sets the fourcc's for each layer -
42  // so we don't have to guess. But it is not available with older libva and
43  // there are reports it does not work with some Radeon drivers
45  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Using %1 for interop")
46  .arg(m_usePrime ? "DRM PRIME" : "VAAPI handle"));
47 }
48 
50 {
51  OpenGLLocker locker(m_context);
56  if (m_drmFile.isOpen())
57  m_drmFile.close();
58 }
59 
61 {
62  OpenGLLocker locker(m_context);
63 
64  if (!m_openglTextures.isEmpty() && m_context->IsEGL())
65  {
66  int count = 0;
67  for (auto it = m_openglTextures.constBegin() ; it != m_openglTextures.constEnd(); ++it)
68  {
69  vector<MythVideoTexture*> textures = it.value();
70  for (auto & texture : textures)
71  {
72  if (texture->m_data)
73  {
75  texture->m_data = nullptr;
76  count++;
77  }
78  }
79  }
80  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Deleted %1 EGL images in %2 groups")
81  .arg(count).arg(m_openglTextures.size()));
82  }
83 
85 }
86 
88 {
89  if (m_filterGraph)
90  {
91  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting deinterlacer frame cache");
94  }
96 }
97 
99 {
100  // remove the old, non-deinterlaced frame cache
101  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting progressive frame cache");
102  DeleteTextures();
103  CleanupDRMPRIME();
104 }
105 
107 {
108  while (!m_referenceFrames.isEmpty())
109  {
110  AVBufferRef* ref = m_referenceFrames.takeLast();
111  av_buffer_unref(&ref);
112  }
113 }
114 
116 {
117  if (!Buffer)
118  return;
119 
120  // don't retain twice for double rate
121  if (!m_referenceFrames.empty() &&
122  (static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[0]->data)) ==
123  static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(Buffer->data))))
124  {
125  return;
126  }
127 
128  m_referenceFrames.push_front(av_buffer_ref(Buffer));
129 
130  // release old frames
131  while (m_referenceFrames.size() > 3)
132  {
133  AVBufferRef* ref = m_referenceFrames.takeLast();
134  av_buffer_unref(&ref);
135  }
136 }
137 
138 vector<MythVideoTexture*> MythVAAPIInteropDRM::GetReferenceFrames(void)
139 {
140  vector<MythVideoTexture*> result;
141  int size = m_referenceFrames.size();
142  if (size < 1)
143  return result;
144 
145  auto next = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[0]->data));
146  auto current = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[size > 1 ? 1 : 0]->data));
147  auto last = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[size > 2 ? 2 : 0]->data));
148 
149  if (!m_openglTextures.contains(next) || !m_openglTextures.contains(current) ||
150  !m_openglTextures.contains(last))
151  {
152  LOG(VB_GENERAL, LOG_ERR, LOC + "Reference frame error");
153  return result;
154  }
155 
156  result = m_openglTextures[last];
157  for (MythVideoTexture* tex : qAsConst(m_openglTextures[current]))
158  result.push_back(tex);
159  for (MythVideoTexture* tex : qAsConst(m_openglTextures[next]))
160  result.push_back(tex);
161  return result;
162 }
163 
165  VideoColourSpace *ColourSpace,
166  VideoFrame *Frame,
167  FrameScanType Scan)
168 {
169  vector<MythVideoTexture*> result;
170  if (!Frame)
171  return result;
172 
173  VASurfaceID id = VerifySurface(Context, Frame);
174  if (!id || !m_vaDisplay)
175  return result;
176 
177  // Update frame colourspace and initialise on first frame
178  if (ColourSpace)
179  {
180  if (m_openglTextures.isEmpty())
182  ColourSpace->UpdateColourSpace(Frame);
183  }
184 
185  // Deinterlacing
186  bool needreferenceframes = false;
187  auto discontinuity = abs(Frame->frameCounter - m_discontinuityCounter) > 1;
188 
189  if (is_interlaced(Scan))
190  {
191  // allow GLSL deinterlacers
192  Frame->deinterlace_allowed = Frame->deinterlace_allowed | DEINT_SHADER;
193 
194  // is GLSL preferred - and if so do we need reference frames
195  bool glsldeint = false;
196 
197  // we explicitly use a shader if preferred over driver. If CPU only
198  // is preferred, the default will be to use the driver instead and if that
199  // fails we fall back to GLSL
202  if (m_filterError)
204  if (shader && !driver)
205  {
206  glsldeint = true;
207  needreferenceframes = shader == DEINT_HIGH;
208  Frame->deinterlace_double = Frame->deinterlace_double | DEINT_SHADER;
209  }
210  else if (!shader && !driver) // singlerate
211  {
214  if (m_filterError)
216  if (shader && !driver)
217  {
218  glsldeint = true;
219  needreferenceframes = shader == DEINT_HIGH;
220  Frame->deinterlace_single = Frame->deinterlace_single | DEINT_SHADER;
221  }
222  }
223 
224  // driver deinterlacing
225  if (!glsldeint)
226  {
227  if (discontinuity)
229  id = Deinterlace(Frame, id, Scan);
230  }
231 
232  // fallback to shaders if VAAPI deints fail
233  if (m_filterError)
234  Frame->deinterlace_allowed = Frame->deinterlace_allowed & ~DEINT_DRIVER;
235  }
236  else if (m_deinterlacer)
237  {
239  }
240 
241  if (needreferenceframes)
242  {
243  if (discontinuity)
245  RotateReferenceFrames(reinterpret_cast<AVBufferRef*>(Frame->priv[0]));
246  }
247  else
248  {
250  }
251  m_discontinuityCounter = Frame->frameCounter;
252 
253  // return cached texture if available
254  if (m_openglTextures.contains(id))
255  {
256  if (needreferenceframes)
257  return GetReferenceFrames();
258  return m_openglTextures[id];
259  }
260 
261  OpenGLLocker locker(m_context);
263  m_openglTextures.insert(id, result);
264  if (needreferenceframes)
265  return GetReferenceFrames();
266  return result;
267 }
268 
269 #ifndef DRM_FORMAT_R8
270 #define MKTAG2(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | (static_cast<unsigned>(d) << 24))
271 #define DRM_FORMAT_R8 MKTAG2('R', '8', ' ', ' ')
272 #define DRM_FORMAT_GR88 MKTAG2('G', 'R', '8', '8')
273 #define DRM_FORMAT_R16 MKTAG2('R', '1', '6', ' ')
274 #define DRM_FORMAT_GR32 MKTAG2('G', 'R', '3', '2')
275 #endif
276 
277 vector<MythVideoTexture*> MythVAAPIInteropDRM::AcquireVAAPI(VASurfaceID Id,
279  VideoFrame *Frame)
280 {
281  vector<MythVideoTexture*> result;
282 
283  VAImage vaimage;
284  memset(&vaimage, 0, sizeof(vaimage));
285  vaimage.buf = vaimage.image_id = VA_INVALID_ID;
286  INIT_ST;
287  va_status = vaDeriveImage(m_vaDisplay, Id, &vaimage);
288  CHECK_ST;
289  uint numplanes = vaimage.num_planes;
290 
291  VABufferInfo vabufferinfo;
292  memset(&vabufferinfo, 0, sizeof(vabufferinfo));
293  vabufferinfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
294  va_status = vaAcquireBufferHandle(m_vaDisplay, vaimage.buf, &vabufferinfo);
295  CHECK_ST;
296 
297  VideoFrameType format = VATypeToMythType(vaimage.format.fourcc);
298  if (format == FMT_NONE)
299  {
300  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported VA fourcc: %1")
301  .arg(fourcc_str(static_cast<int32_t>(vaimage.format.fourcc))));
302  }
303  else
304  {
305  if (numplanes != planes(format))
306  {
307  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Inconsistent plane count %1 != %2")
308  .arg(numplanes).arg(planes(format)));
309  }
310  else
311  {
312  AVDRMFrameDescriptor drmdesc;
313  memset(&drmdesc, 0, sizeof(drmdesc));
314  drmdesc.nb_objects = 1;
315  drmdesc.nb_layers = static_cast<int>(numplanes);
316  drmdesc.objects[0].fd = static_cast<int>(vabufferinfo.handle);
317  drmdesc.objects[0].size = 0;
318  drmdesc.objects[0].format_modifier = 0;
319 
320  for (uint i = 0; i < numplanes; ++i)
321  {
322  uint32_t fourcc = (format == FMT_P010) ? DRM_FORMAT_R16 : DRM_FORMAT_R8;
323  if (i > 0)
324  fourcc = (format == FMT_P010) ? DRM_FORMAT_GR32 : DRM_FORMAT_GR88;
325  drmdesc.layers[i].nb_planes = 1;
326  drmdesc.layers[i].format = fourcc;
327  drmdesc.layers[i].planes[0].object_index = 0;
328  drmdesc.layers[i].planes[0].pitch = vaimage.pitches[i];
329  drmdesc.layers[i].planes[0].offset = vaimage.offsets[i];
330  }
331 
332  result = CreateTextures(&drmdesc, Context, Frame, false);
333  }
334  }
335 
336  va_status = vaReleaseBufferHandle(m_vaDisplay, vaimage.buf);
337  CHECK_ST;
338  va_status = vaDestroyImage(m_vaDisplay, vaimage.image_id);
339  CHECK_ST;
340 
341  return result;
342 }
343 
345 {
346  switch (Fourcc)
347  {
348  case VA_FOURCC_IYUV:
349  case VA_FOURCC_I420: return FMT_YV12;
350  case VA_FOURCC_NV12: return FMT_NV12;
351  case VA_FOURCC_YUY2:
352  case VA_FOURCC_UYVY: return FMT_YUY2;
353 #if defined (VA_FOURCC_P010)
354  case VA_FOURCC_P010: return FMT_P010;
355 #endif
356 #if defined (VA_FOURCC_P016)
357  case VA_FOURCC_P016: return FMT_P016;
358 #endif
359  case VA_FOURCC_ARGB: return FMT_ARGB32;
360  case VA_FOURCC_RGBA: return FMT_RGBA32;
361  }
362  return FMT_NONE;
363 }
364 
366 {
367  return HaveDMABuf(Context);
368 }
369 
370 #if VA_CHECK_VERSION(1, 1, 0)
371 static inline void VADRMtoPRIME(VADRMPRIMESurfaceDescriptor* VaDRM, AVDRMFrameDescriptor* Prime)
372 {
373  Prime->nb_objects = static_cast<int>(VaDRM->num_objects);
374  for (uint i = 0; i < VaDRM->num_objects; i++)
375  {
376  Prime->objects[i].fd = VaDRM->objects[i].fd;
377  Prime->objects[i].size = VaDRM->objects[i].size;
378  Prime->objects[i].format_modifier = VaDRM->objects[i].drm_format_modifier;
379  }
380  Prime->nb_layers = static_cast<int>(VaDRM->num_layers);
381  for (uint i = 0; i < VaDRM->num_layers; i++)
382  {
383  Prime->layers[i].format = VaDRM->layers[i].drm_format;
384  Prime->layers[i].nb_planes = static_cast<int>(VaDRM->layers[i].num_planes);
385  for (uint j = 0; j < VaDRM->layers[i].num_planes; j++)
386  {
387  Prime->layers[i].planes[j].object_index = static_cast<int>(VaDRM->layers[i].object_index[j]);
388  Prime->layers[i].planes[j].offset = VaDRM->layers[i].offset[j];
389  Prime->layers[i].planes[j].pitch = VaDRM->layers[i].pitch[j];
390  }
391  }
392 }
393 #endif
394 
400 vector<MythVideoTexture*> MythVAAPIInteropDRM::AcquirePrime(VASurfaceID Id,
402  VideoFrame *Frame)
403 {
404  vector<MythVideoTexture*> result;
405 
406 #if VA_CHECK_VERSION(1, 1, 0)
407  if (!m_drmFrames.contains(Id))
408  {
409  INIT_ST;
410  uint32_t exportflags = VA_EXPORT_SURFACE_SEPARATE_LAYERS | VA_EXPORT_SURFACE_READ_ONLY;
411  VADRMPRIMESurfaceDescriptor vadesc;
412  va_status = vaExportSurfaceHandle(m_vaDisplay, Id,
413  VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
414  exportflags, &vadesc);
415  CHECK_ST;
416 
417  auto *drmdesc = reinterpret_cast<AVDRMFrameDescriptor*>(av_mallocz(sizeof(AVDRMFrameDescriptor)));
418  VADRMtoPRIME(&vadesc, drmdesc);
419  m_drmFrames.insert(Id, drmdesc);
420  }
421 
422  if (!m_drmFrames.contains(Id))
423  return result;
424  result = CreateTextures(m_drmFrames[Id], Context, Frame, false);
425 #else
426  (void)Id;
427  (void)Context;
428  (void)Frame;
429 #endif
430  return result;
431 }
432 
434 {
435  if (m_drmFrames.isEmpty())
436  return;
437 
438  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Releasing %1 DRM descriptors").arg(m_drmFrames.size()));
439  for (auto * frame : qAsConst(m_drmFrames))
440  {
441  for (int i = 0; i < frame->nb_objects; i++)
442  close(frame->objects[i].fd);
443  av_freep(&frame);
444  }
445  m_drmFrames.clear();
446 }
447 
449 {
450  static bool s_supported = false;
451 #if VA_CHECK_VERSION(1, 1, 0)
452  static bool s_checked = false;
453 
454  if (s_checked)
455  return s_supported;
456  s_checked = true;
457 
458  OpenGLLocker locker(m_context);
459 
460  VASurfaceID surface = 0;
461 
462  VASurfaceAttrib attribs = {};
463  attribs.flags = VA_SURFACE_ATTRIB_SETTABLE;
464  attribs.type = VASurfaceAttribPixelFormat;
465  attribs.value.type = VAGenericValueTypeInteger;
466  attribs.value.value.i = VA_FOURCC_NV12;
467 
468  if (vaCreateSurfaces(m_vaDisplay, VA_RT_FORMAT_YUV420, 1920, 1080,
469  &surface, 1, &attribs, 1) == VA_STATUS_SUCCESS)
470  {
471  VADRMPRIMESurfaceDescriptor vadesc;
472  VAStatus status = vaExportSurfaceHandle(m_vaDisplay, surface, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
473  VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_SEPARATE_LAYERS,
474  &vadesc);
475  if (status == VA_STATUS_SUCCESS)
476  {
477  VideoFrame frame {};
478  init(&frame, FMT_DRMPRIME, nullptr, 1920, 1080, 0);
479  frame.sw_pix_fmt = AV_PIX_FMT_NV12;
480  AVDRMFrameDescriptor drmdesc;
481  memset(&drmdesc, 0, sizeof(drmdesc));
482  VADRMtoPRIME(&vadesc, &drmdesc);
483  vector<MythVideoTexture*> textures = CreateTextures(&drmdesc, m_context, &frame, false);
484 
485  if (!textures.empty())
486  {
487  s_supported = true;
488  for (auto & texture : textures)
489  s_supported &= texture->m_data && (texture->m_textureId != 0U);
490  ClearDMATextures(m_context, textures);
491  }
492  for (uint32_t i = 0; i < vadesc.num_objects; ++i)
493  close(vadesc.objects[i].fd);
494  }
495  vaDestroySurfaces(m_vaDisplay, &surface, 1);
496  }
497 #endif
498  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI DRM PRIME interop is %1supported")
499  .arg(s_supported ? "" : "not "));
500  return s_supported;
501 }
VideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
void eglDestroyImageKHR(void *Disp, void *Image)
Definition: mythegl.cpp:160
MythDeintType GetDoubleRateOption(const VideoFrame *Frame, MythDeintType Type, MythDeintType Override)
Definition: mythframe.cpp:847
long long m_discontinuityCounter
QHash< QString, Action * > Context
Definition: action.h:77
#define DRM_FORMAT_GR32
MythVAAPIInteropDRM(MythRenderOpenGL *Context)
static VideoFrameType VATypeToMythType(uint32_t Fourcc)
vector< MythVideoTexture * > Acquire(MythRenderOpenGL *Context, VideoColourSpace *ColourSpace, VideoFrame *Frame, FrameScanType Scan) override
AVFilterGraph * m_filterGraph
FrameScanType
Definition: videoouttypes.h:78
VideoFrameType
Definition: mythframe.h:23
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static char * fourcc_str(int i)
Definition: fourcc.h:25
static uint planes(VideoFrameType Type)
Definition: mythframe.h:569
VASurfaceID VerifySurface(MythRenderOpenGL *Context, VideoFrame *Frame)
vector< MythVideoTexture * > GetReferenceFrames(void)
MythDeintType
Definition: mythframe.h:120
static bool HaveDMABuf(MythRenderOpenGL *Context)
#define close
Definition: compat.h:16
QHash< unsigned long long, AVDRMFrameDescriptor * > m_drmFrames
#define CHECK_ST
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
#define DRM_FORMAT_GR88
VASurfaceID Deinterlace(VideoFrame *Frame, VASurfaceID Current, FrameScanType Scan)
#define INIT_ST
void DeleteTextures(void) override
QVector< AVBufferRef * > m_referenceFrames
unsigned int uint
Definition: compat.h:140
#define ALL_PICTURE_ATTRIBUTES
#define DRM_FORMAT_R16
void * GetEGLDisplay(void)
Definition: mythegl.cpp:80
void PostInitDeinterlacer(void) override
static void init(VideoFrame *vf, VideoFrameType _codec, unsigned char *_buf, int _width, int _height, int _size, const int *p=nullptr, const int *o=nullptr, float _aspect=-1.0F, double _rate=-1.0F, int _aligned=MYTH_WIDTH_ALIGNMENT)
Definition: mythframe.h:231
MythDeintType m_deinterlacer
static void ClearDMATextures(MythRenderOpenGL *Context, vector< MythVideoTexture * > &Textures)
void SetSupportedAttributes(PictureAttributeSupported Supported)
Enable the given set of picture attributes.
vector< MythVideoTexture * > AcquirePrime(VASurfaceID Id, MythRenderOpenGL *Context, VideoFrame *Frame)
Export the given VideoFrame as a DRM PRIME descriptor.
virtual void DeleteTextures(void)
MythRenderOpenGL * m_context
PictureAttribute next(PictureAttributeSupported Supported, PictureAttribute Attribute)
vector< MythVideoTexture * > CreateTextures(AVDRMFrameDescriptor *Desc, MythRenderOpenGL *Context, VideoFrame *Frame, bool UseSeparate, FrameScanType Scan=kScan_Progressive)
void InitaliseDisplay(void)
virtual void DestroyDeinterlacer(void)
QHash< unsigned long long, vector< MythVideoTexture * > > m_openglTextures
#define LOC
void DestroyDeinterlacer(void) override
vector< MythVideoTexture * > AcquireVAAPI(VASurfaceID Id, MythRenderOpenGL *Context, VideoFrame *Frame)
static bool IsSupported(MythRenderOpenGL *Context)
#define DRM_FORMAT_R8
bool is_interlaced(FrameScanType Scan)
bool IsEGL(void)
Definition: mythegl.cpp:29
MythDeintType GetSingleRateOption(const VideoFrame *Frame, MythDeintType Type, MythDeintType Override)
Definition: mythframe.cpp:834
bool UpdateColourSpace(const VideoFrame *Frame)
Set the current colourspace to use.
void RotateReferenceFrames(AVBufferRef *Buffer)
#define VA_FOURCC_I420
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23