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