MythTV master
mythvaapidrminterop.cpp
Go to the documentation of this file.
2
3#include <va/va_drm.h>
4#include <va/va_drmcommon.h>
5
6// MythTV
9
11#include "fourcc.h"
12
13// FFmpeg
14extern "C" {
15#include "libavutil/hwcontext_drm.h"
16}
17
18// Std
19#include <unistd.h>
20
21#define LOC QString("VAAPIDRM: ")
22
24 : MythVAAPIInterop(Player, Context, Type),
25 MythEGLDMABUF(Context)
26{
27 QString device = gCoreContext->GetSetting("VAAPIDevice");
28 if (device.isEmpty())
29 device = "/dev/dri/renderD128";
30 m_drmFile.setFileName(device);
31 if (m_drmFile.open(QIODevice::ReadWrite))
32 {
33 m_vaDisplay = vaGetDisplayDRM(m_drmFile.handle());
34 if (!m_vaDisplay)
35 {
36 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create DRM VADisplay");
37 return;
38 }
39 }
40 else
41 {
42 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open %1").arg(device));
43 return;
44 }
46
47 // DRM PRIME is preferred as it explicitly sets the fourcc's for each layer -
48 // so we don't have to guess. But it is not available with older libva and
49 // there are reports it does not work with some Radeon drivers
51 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Using %1 for interop")
52 .arg(m_usePrime ? "DRM PRIME" : "VAAPI handle"));
53}
54
56{
57#if CONFIG_DRM_VIDEO
58 delete m_drm;
59#endif
65 if (m_drmFile.isOpen())
66 m_drmFile.close();
67}
68
70{
72
73 if (!m_openglTextures.isEmpty() && m_openglContext->IsEGL())
74 {
75 int count = 0;
76 for (auto it = m_openglTextures.constBegin() ; it != m_openglTextures.constEnd(); ++it)
77 {
78 std::vector<MythVideoTextureOpenGL*> textures = it.value();
79 for (auto & texture : textures)
80 {
81 if (texture->m_data)
82 {
84 texture->m_data = nullptr;
85 count++;
86 }
87 }
88 }
89 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Deleted %1 EGL images in %2 groups")
90 .arg(count).arg(m_openglTextures.size()));
91 }
92
94}
95
97{
98 if (m_filterGraph)
99 {
100 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting deinterlacer frame cache");
103 }
105}
106
108{
109 // remove the old, non-deinterlaced frame cache
110 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleting progressive frame cache");
113}
114
116{
117 while (!m_referenceFrames.isEmpty())
118 {
119 AVBufferRef* ref = m_referenceFrames.takeLast();
120 av_buffer_unref(&ref);
121 }
122}
123
125{
126 if (!Buffer)
127 return;
128
129 // don't retain twice for double rate
130 if (!m_referenceFrames.empty() &&
131 (static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[0]->data)) ==
132 static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(Buffer->data))))
133 {
134 return;
135 }
136
137 m_referenceFrames.push_front(av_buffer_ref(Buffer));
138
139 // release old frames
140 while (m_referenceFrames.size() > 3)
141 {
142 AVBufferRef* ref = m_referenceFrames.takeLast();
143 av_buffer_unref(&ref);
144 }
145}
146
147std::vector<MythVideoTextureOpenGL*> MythVAAPIInteropDRM::GetReferenceFrames()
148{
149 std::vector<MythVideoTextureOpenGL*> result;
150 int size = m_referenceFrames.size();
151 if (size < 1)
152 return result;
153
154 auto next = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[0]->data));
155 auto current = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[size > 1 ? 1 : 0]->data));
156 auto last = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(m_referenceFrames[size > 2 ? 2 : 0]->data));
157
158 if (!m_openglTextures.contains(next) || !m_openglTextures.contains(current) ||
159 !m_openglTextures.contains(last))
160 {
161 LOG(VB_GENERAL, LOG_ERR, LOC + "Reference frame error");
162 return result;
163 }
164
165 std::copy(m_openglTextures[last].cbegin(), m_openglTextures[last].cend(), std::back_inserter(result));
166 std::copy(m_openglTextures[current].cbegin(), m_openglTextures[current].cend(), std::back_inserter(result));
167 std::copy(m_openglTextures[next].cbegin(), m_openglTextures[next].cend(), std::back_inserter(result));
168 return result;
169}
170
171std::vector<MythVideoTextureOpenGL*>
173 MythVideoColourSpace* ColourSpace,
175 FrameScanType Scan)
176{
177 std::vector<MythVideoTextureOpenGL*> result;
178 if (!Frame)
179 return result;
180
181 VASurfaceID id = VerifySurface(Context, Frame);
182 if (!id || !m_vaDisplay)
183 return result;
184
185 // Update frame colourspace and initialise on first frame
186 if (ColourSpace)
187 {
188 if (m_openglTextures.isEmpty())
190 ColourSpace->UpdateColourSpace(Frame);
191 }
192
193 // Deinterlacing
194 bool needreferenceframes = false;
195 auto discontinuity = qAbs(Frame->m_frameCounter - m_discontinuityCounter) > 1;
196
197 if (is_interlaced(Scan))
198 {
199 // allow GLSL deinterlacers
200 Frame->m_deinterlaceAllowed = Frame->m_deinterlaceAllowed | DEINT_SHADER;
201
202 // is GLSL preferred - and if so do we need reference frames
203 bool glsldeint = false;
204
205 // we explicitly use a shader if preferred over driver. If CPU only
206 // is preferred, the default will be to use the driver instead and if that
207 // fails we fall back to GLSL
208 MythDeintType shader = Frame->GetDoubleRateOption(DEINT_SHADER);
209 MythDeintType driver = Frame->GetDoubleRateOption(DEINT_DRIVER);
210 if (m_filterError)
211 shader = Frame->GetDoubleRateOption(DEINT_SHADER | DEINT_CPU | DEINT_DRIVER, DEINT_ALL);
212 if (shader && !driver)
213 {
214 glsldeint = true;
215 needreferenceframes = shader == DEINT_HIGH;
216 Frame->m_deinterlaceDouble = Frame->m_deinterlaceDouble | DEINT_SHADER;
217 }
218 else if (!shader && !driver) // singlerate
219 {
220 shader = Frame->GetSingleRateOption(DEINT_SHADER);
221 driver = Frame->GetSingleRateOption(DEINT_DRIVER);
222 if (m_filterError)
223 shader = Frame->GetSingleRateOption(DEINT_SHADER | DEINT_CPU | DEINT_DRIVER, DEINT_ALL);
224 if (shader && !driver)
225 {
226 glsldeint = true;
227 needreferenceframes = shader == DEINT_HIGH;
228 Frame->m_deinterlaceSingle = Frame->m_deinterlaceSingle | DEINT_SHADER;
229 }
230 }
231
232 // driver deinterlacing
233 if (!glsldeint)
234 {
235 if (discontinuity)
237 id = Deinterlace(Frame, id, Scan);
238 }
239
240 // fallback to shaders if VAAPI deints fail
241 if (m_filterError)
242 Frame->m_deinterlaceAllowed = Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
243 }
244 else if (m_deinterlacer)
245 {
247 }
248
249 if (needreferenceframes)
250 {
251 if (discontinuity)
253 RotateReferenceFrames(reinterpret_cast<AVBufferRef*>(Frame->m_priv[0]));
254 }
255 else
256 {
258 }
259 m_discontinuityCounter = Frame->m_frameCounter;
260
261#if CONFIG_DRM_VIDEO
262 if (!m_drmTriedAndFailed)
263 if (HandleDRMVideo(ColourSpace, id, Frame))
264 return result;
265#endif
266
267 // return cached texture if available
268 if (m_openglTextures.contains(id))
269 {
270 if (needreferenceframes)
271 return GetReferenceFrames();
272 return m_openglTextures[id];
273 }
274
276 result = m_usePrime ? AcquirePrime(id, Context, Frame): AcquireVAAPI(id, Context, Frame);
277 m_openglTextures.insert(id, result);
278 if (needreferenceframes)
279 return GetReferenceFrames();
280 return result;
281}
282
283#ifndef DRM_FORMAT_R8
284#define MKTAG2(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | (static_cast<unsigned>(d) << 24))
285#define DRM_FORMAT_R8 MKTAG2('R', '8', ' ', ' ')
286#define DRM_FORMAT_GR88 MKTAG2('G', 'R', '8', '8')
287#define DRM_FORMAT_R16 MKTAG2('R', '1', '6', ' ')
288#define DRM_FORMAT_GR1616 MKTAG2('G', 'R', '3', '2')
289#endif
290
291std::vector<MythVideoTextureOpenGL*>
293 MythRenderOpenGL* Context,
295{
296 std::vector<MythVideoTextureOpenGL*> result;
297
298 VAImage vaimage;
299 memset(&vaimage, 0, sizeof(vaimage));
300 vaimage.buf = vaimage.image_id = VA_INVALID_ID;
301 INIT_ST;
302 va_status = vaDeriveImage(m_vaDisplay, Id, &vaimage);
303 CHECK_ST;
304 uint numplanes = vaimage.num_planes;
305
306 VABufferInfo vabufferinfo;
307 memset(&vabufferinfo, 0, sizeof(vabufferinfo));
308 vabufferinfo.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
309 va_status = vaAcquireBufferHandle(m_vaDisplay, vaimage.buf, &vabufferinfo);
310 CHECK_ST;
311
312 VideoFrameType format = VATypeToMythType(vaimage.format.fourcc);
313 if (format == FMT_NONE)
314 {
315 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported VA fourcc: %1")
316 .arg(fourcc_str(static_cast<int32_t>(vaimage.format.fourcc))));
317 }
318 else
319 {
320 if (numplanes != MythVideoFrame::GetNumPlanes(format))
321 {
322 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Inconsistent plane count %1 != %2")
323 .arg(numplanes).arg(MythVideoFrame::GetNumPlanes(format)));
324 }
325 else
326 {
327 AVDRMFrameDescriptor drmdesc;
328 memset(&drmdesc, 0, sizeof(drmdesc));
329 drmdesc.nb_objects = 1;
330 drmdesc.nb_layers = static_cast<int>(numplanes);
331 drmdesc.objects[0].fd = static_cast<int>(vabufferinfo.handle);
332 drmdesc.objects[0].size = 0;
333 drmdesc.objects[0].format_modifier = 0;
334
335 for (uint i = 0; i < numplanes; ++i)
336 {
337 uint32_t fourcc = (format == FMT_P010) ? DRM_FORMAT_R16 : DRM_FORMAT_R8;
338 if (i > 0)
339 fourcc = (format == FMT_P010) ? DRM_FORMAT_GR1616 : DRM_FORMAT_GR88;
340 drmdesc.layers[i].nb_planes = 1;
341 drmdesc.layers[i].format = fourcc;
342 drmdesc.layers[i].planes[0].object_index = 0;
343 drmdesc.layers[i].planes[0].pitch = vaimage.pitches[i];
344 drmdesc.layers[i].planes[0].offset = vaimage.offsets[i];
345 }
346
347 result = CreateTextures(&drmdesc, Context, Frame, false);
348 }
349 }
350
351 va_status = vaReleaseBufferHandle(m_vaDisplay, vaimage.buf);
352 CHECK_ST;
353 va_status = vaDestroyImage(m_vaDisplay, vaimage.image_id);
354 CHECK_ST;
355
356 return result;
357}
358
360{
361 switch (Fourcc)
362 {
363 case VA_FOURCC_IYUV:
364 case VA_FOURCC_I420: return FMT_YV12;
365 case VA_FOURCC_NV12: return FMT_NV12;
366 case VA_FOURCC_YUY2:
367 case VA_FOURCC_UYVY: return FMT_YUY2;
368#ifdef VA_FOURCC_P010
369 case VA_FOURCC_P010: return FMT_P010;
370#endif
371#ifdef VA_FOURCC_P016
372 case VA_FOURCC_P016: return FMT_P016;
373#endif
374 case VA_FOURCC_ARGB: return FMT_ARGB32;
375 case VA_FOURCC_RGBA: return FMT_RGBA32;
376 }
377 return FMT_NONE;
378}
379
381{
382 return HaveDMABuf(Context);
383}
384
385static inline void VADRMtoPRIME(VADRMPRIMESurfaceDescriptor* VaDRM, AVDRMFrameDescriptor* Prime)
386{
387 Prime->nb_objects = static_cast<int>(VaDRM->num_objects);
388 for (uint i = 0; i < VaDRM->num_objects; i++)
389 {
390 Prime->objects[i].fd = VaDRM->objects[i].fd;
391 Prime->objects[i].size = VaDRM->objects[i].size;
392 Prime->objects[i].format_modifier = VaDRM->objects[i].drm_format_modifier;
393 }
394 Prime->nb_layers = static_cast<int>(VaDRM->num_layers);
395 for (uint i = 0; i < VaDRM->num_layers; i++)
396 {
397 Prime->layers[i].format = VaDRM->layers[i].drm_format;
398 Prime->layers[i].nb_planes = static_cast<int>(VaDRM->layers[i].num_planes);
399 for (uint j = 0; j < VaDRM->layers[i].num_planes; j++)
400 {
401 Prime->layers[i].planes[j].object_index = static_cast<int>(VaDRM->layers[i].object_index[j]);
402 Prime->layers[i].planes[j].offset = VaDRM->layers[i].offset[j];
403 Prime->layers[i].planes[j].pitch = VaDRM->layers[i].pitch[j];
404 }
405 }
406}
407
413std::vector<MythVideoTextureOpenGL*>
414MythVAAPIInteropDRM::AcquirePrime([[maybe_unused]] VASurfaceID Id,
415 [[maybe_unused]] MythRenderOpenGL* Context,
416 [[maybe_unused]] MythVideoFrame* Frame)
417{
418 std::vector<MythVideoTextureOpenGL*> result;
419
420 if (!m_drmFrames.contains(Id))
421 m_drmFrames.insert(Id, GetDRMFrameDescriptor(Id));
422 if (!m_drmFrames.contains(Id))
423 return result;
424 result = CreateTextures(m_drmFrames[Id], Context, Frame, false);
425 return result;
426}
427
428AVDRMFrameDescriptor* MythVAAPIInteropDRM::GetDRMFrameDescriptor([[maybe_unused]] VASurfaceID Id)
429{
430 INIT_ST;
431 uint32_t exportflags = VA_EXPORT_SURFACE_SEPARATE_LAYERS | VA_EXPORT_SURFACE_READ_ONLY;
432 VADRMPRIMESurfaceDescriptor vadesc;
433 va_status = vaExportSurfaceHandle(m_vaDisplay, Id,
434 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
435 exportflags, &vadesc);
436 CHECK_ST;
437
438 auto * drmdesc = reinterpret_cast<AVDRMFrameDescriptor*>(av_mallocz(sizeof(AVDRMFrameDescriptor)));
439 VADRMtoPRIME(&vadesc, drmdesc);
440 return drmdesc;
441}
442
444{
445 if (m_drmFrames.isEmpty())
446 return;
447
448 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Releasing %1 DRM descriptors").arg(m_drmFrames.size()));
449 for (auto * frame : std::as_const(m_drmFrames))
450 {
451 for (int i = 0; i < frame->nb_objects; i++)
452 close(frame->objects[i].fd);
453 av_freep(reinterpret_cast<void*>(&frame));
454 }
455 m_drmFrames.clear();
456}
457
459{
460 static bool s_supported = false;
461 static bool s_checked = false;
462
463 if (s_checked)
464 return s_supported;
465 s_checked = true;
466
468
469 VASurfaceID surface = 0;
470
471 VASurfaceAttrib attribs = {};
472 attribs.flags = VA_SURFACE_ATTRIB_SETTABLE;
473 attribs.type = VASurfaceAttribPixelFormat;
474 attribs.value.type = VAGenericValueTypeInteger;
475 attribs.value.value.i = VA_FOURCC_NV12;
476
477 if (vaCreateSurfaces(m_vaDisplay, VA_RT_FORMAT_YUV420, 1920, 1080,
478 &surface, 1, &attribs, 1) == VA_STATUS_SUCCESS)
479 {
480 VADRMPRIMESurfaceDescriptor vadesc;
481 VAStatus status = vaExportSurfaceHandle(m_vaDisplay, surface, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
482 VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_SEPARATE_LAYERS,
483 &vadesc);
484 if (status == VA_STATUS_SUCCESS)
485 {
486 MythVideoFrame frame(FMT_DRMPRIME, nullptr, 0, 1920, 1080);
487 frame.m_swPixFmt = AV_PIX_FMT_NV12;
488 AVDRMFrameDescriptor drmdesc;
489 memset(&drmdesc, 0, sizeof(drmdesc));
490 VADRMtoPRIME(&vadesc, &drmdesc);
491 std::vector<MythVideoTextureOpenGL*> textures =
492 CreateTextures(&drmdesc, m_openglContext, &frame, false);
493
494 if (!textures.empty())
495 {
496 s_supported = true;
497 for (auto & texture : textures)
498 s_supported &= texture->m_data && (texture->m_textureId != 0U);
500 }
501 for (uint32_t i = 0; i < vadesc.num_objects; ++i)
502 close(vadesc.objects[i].fd);
503 }
504 vaDestroySurfaces(m_vaDisplay, &surface, 1);
505 }
506 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI DRM PRIME interop is %1supported")
507 .arg(s_supported ? "" : "not "));
508 return s_supported;
509}
510
511#if CONFIG_DRM_VIDEO
512bool MythVAAPIInteropDRM::HandleDRMVideo(MythVideoColourSpace* ColourSpace, VASurfaceID Id, MythVideoFrame* Frame)
513{
514 if (!((m_type == DRM_DRMPRIME) && m_usePrime && Id && Frame && ColourSpace))
515 return false;
516
517 if (!m_drm)
518 m_drm = new MythVideoDRM(ColourSpace);
519
520 if (m_drm)
521 {
522 if (m_drm->IsValid())
523 {
524 if (!m_drmFrames.contains(Id))
525 m_drmFrames.insert(Id, GetDRMFrameDescriptor(Id));
526 if (m_drm->RenderFrame(m_drmFrames[Id], Frame))
527 return true;
528 }
529
530 // RenderFrame may have decided we should give up
531 if (!m_drm->IsValid())
532 {
533 LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling DRM video");
534 m_drmTriedAndFailed = true;
535 delete m_drm;
536 m_drm = nullptr;
537 }
538 }
539 return false;
540}
541#endif
QString GetSetting(const QString &key, const QString &defaultval="")
static void ClearDMATextures(MythRenderOpenGL *Context, std::vector< MythVideoTextureOpenGL * > &Textures)
static bool HaveDMABuf(MythRenderOpenGL *Context)
std::vector< MythVideoTextureOpenGL * > CreateTextures(AVDRMFrameDescriptor *Desc, MythRenderOpenGL *Context, MythVideoFrame *Frame, bool UseSeparate, FrameScanType Scan=kScan_Progressive)
void * GetEGLDisplay(void)
Definition: mythegl.cpp:78
bool IsEGL(void)
Definition: mythegl.cpp:31
void eglDestroyImageKHR(void *Disp, void *Image)
Definition: mythegl.cpp:158
uint64_t m_discontinuityCounter
InteropType m_type
virtual void DeleteTextures()
MythRenderOpenGL * m_openglContext
QHash< unsigned long long, std::vector< MythVideoTextureOpenGL * > > m_openglTextures
static VideoFrameType VATypeToMythType(uint32_t Fourcc)
void PostInitDeinterlacer() override
void RotateReferenceFrames(AVBufferRef *Buffer)
void DeleteTextures() override
static bool IsSupported(MythRenderOpenGL *Context)
QHash< unsigned long long, AVDRMFrameDescriptor * > m_drmFrames
AVDRMFrameDescriptor * GetDRMFrameDescriptor(VASurfaceID Id)
QVector< AVBufferRef * > m_referenceFrames
std::vector< MythVideoTextureOpenGL * > GetReferenceFrames()
std::vector< MythVideoTextureOpenGL * > AcquireVAAPI(VASurfaceID Id, MythRenderOpenGL *Context, MythVideoFrame *Frame)
MythVAAPIInteropDRM(MythPlayerUI *Player, MythRenderOpenGL *Context, InteropType Type)
void DestroyDeinterlacer() override
std::vector< MythVideoTextureOpenGL * > Acquire(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan) override
std::vector< MythVideoTextureOpenGL * > AcquirePrime(VASurfaceID Id, MythRenderOpenGL *Context, MythVideoFrame *Frame)
Export the given VideoFrame as a DRM PRIME descriptor.
virtual void DestroyDeinterlacer(void)
void InitaliseDisplay(void)
MythDeintType m_deinterlacer
AVFilterGraph * m_filterGraph
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 SetSupportedAttributes(PictureAttributeSupported Supported)
Enable the given set of picture attributes.
bool UpdateColourSpace(const MythVideoFrame *Frame)
Set the current colourspace to use.
static uint GetNumPlanes(VideoFrameType Type)
Definition: mythframe.h:213
unsigned int uint
Definition: compat.h:68
#define close
Definition: compat.h:30
static const char * fourcc_str(int i)
Definition: fourcc.h:26
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDeintType
Definition: mythframe.h:67
@ DEINT_HIGH
Definition: mythframe.h:71
@ DEINT_DRIVER
Definition: mythframe.h:74
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_ALL
Definition: mythframe.h:75
@ DEINT_CPU
Definition: mythframe.h:72
VideoFrameType
Definition: mythframe.h:20
@ FMT_RGBA32
Definition: mythframe.h:34
@ FMT_ARGB32
Definition: mythframe.h:33
@ FMT_YV12
Definition: mythframe.h:23
@ FMT_P016
Definition: mythframe.h:54
@ FMT_DRMPRIME
Definition: mythframe.h:63
@ FMT_NONE
Definition: mythframe.h:21
@ FMT_P010
Definition: mythframe.h:53
@ FMT_NV12
Definition: mythframe.h:52
@ FMT_YUY2
Definition: mythframe.h:50
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
#define DRM_FORMAT_R16
static void VADRMtoPRIME(VADRMPRIMESurfaceDescriptor *VaDRM, AVDRMFrameDescriptor *Prime)
#define DRM_FORMAT_R8
#define DRM_FORMAT_GR88
#define DRM_FORMAT_GR1616
#define VA_FOURCC_I420
#define CHECK_ST
#define INIT_ST
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
FrameScanType
Definition: videoouttypes.h:95
bool is_interlaced(FrameScanType Scan)
#define ALL_PICTURE_ATTRIBUTES