1 /**
2     \brief vaapi filters
3     \author mean (C) 2016
4  *  Use VA-API video processing API
5  * (c) mean 2016
6  *
7 */
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 #include "ADM_default.h"
18 #include <list>
19 #include "ADM_coreLibVA.h"
20 #include "ADM_coreVideoFilter.h"
21 #include "ADM_videoFilterCache.h"
22 #include "ADM_vidMisc.h"
23 #include "DIA_factory.h"
24 #include "DIA_coreToolkit.h"
25 #include "vaapiFilterDeint.h"
26 #include "vaapiFilterDeint_desc.cpp"
27 
28 /**
29     \class vaapiSlot
30 */
31 class vaapiSlot
32 {
33 public:
34     ADM_vaSurface           *surface;
35     bool                    external;
36     uint64_t                pts;
37 
38                             vaapiSlot();
39                             ~vaapiSlot();
40     void                    reset(void);
41 };
42 
vaapiSlot()43 vaapiSlot::vaapiSlot()
44 {
45     reset();
46 }
~vaapiSlot()47 vaapiSlot::~vaapiSlot() {}
48 
reset(void)49 void vaapiSlot::reset(void)
50 {
51     surface=NULL;
52     external=false;
53     pts=ADM_NO_PTS;
54 }
55 #define ADM_VAAPI_DEINT_MAX_REF 8
56 
57 /**
58     \class vaapiVideoFilterDeint
59 */
60 class vaapiVideoFilterDeint : public  ADM_coreVideoFilterCached
61 {
62 protected:
63     enum
64     {
65         ADM_VAAPI_DEINT_TOP_FIELD_FIRST=0,
66         ADM_VAAPI_DEINT_BOTTOM_FIELD_FIRST=1
67     };
68     enum
69     {
70         ADM_VAAPI_DEINT_SEND_FRAME=0,
71         ADM_VAAPI_DEINT_SEND_FIELD=1
72     };
73 
74     vaapiSlot               *inputQueue;
75     uint32_t                queueLength;
76     std::list <ADM_vaSurface *> freeSurfaces;
77     ADM_vaSurface           *surfacePool[ADM_VAAPI_DEINT_MAX_REF];
78     ADM_vaSurface           *outputSurface;
79     VAConfigID              configId;
80     VAContextID             contextId;
81     VABufferID              filterBuffer;
82     uint32_t                algoCount; // actually supported by hardware and driver
83     uint32_t                unsupported; // specified deint method rejected by driver
84     VASurfaceID             *forwardRefs, *backwardRefs;
85     uint32_t                nbForwardRefs,nbBackwardRefs;
86 
87     vaapiFilterDeint        configuration;
88     uint64_t                deltaPts;
89     bool                    passThrough;
90     bool                    preloadCompleted;
91     bool                    secondField;
92 
93     bool                    setupVaapi(void);
94     bool                    cleanupVaapi(void);
95 
96     bool                    rotateSlots(void);
97     bool                    clearSlots(void);
98     bool                    fillSlot(uint32_t slot,ADMImage *image);
99 
100     bool                    updateInfo(bool status);
101 
102 public:
103 
104                             vaapiVideoFilterDeint(ADM_coreVideoFilter *previous,CONFcouple *conf);
105                             ~vaapiVideoFilterDeint();
106 
107     virtual const char      *getConfiguration(void); // Return  current configuration as a human readable string
108     virtual bool            getNextFrame(uint32_t *fn,ADMImage *image); // Return the next image
109     virtual bool            getCoupledConf(CONFcouple **couples); // Return the current filter configuration
110     virtual void            setCoupledConf(CONFcouple *couples);
111     virtual bool            configure(void); // Start graphical user interface
112     virtual bool            goToTime(uint64_t usSeek);
113 };
114 
115 // Add the hook to make it valid plugin
116 DECLARE_VIDEO_FILTER(       vaapiVideoFilterDeint, // Class
117                             1,0,0, // Version
118                             (ADM_UI_QT4+ADM_FEATURE_LIBVA), // We need a display for VA-API; so no cli...
119                             VF_INTERLACING, // Category
120                             "vaapiDeint", // internal name (must be uniq!)
121                             QT_TRANSLATE_NOOP("vaapiDeint","VA-API Deinterlacer"), // Display name
122                             QT_TRANSLATE_NOOP("vaapiDeint","Deinterlace and optionally resize video using VA-API.") // Description
123 )
124 
deintModeToString(uint32_t mode)125 static const char *deintModeToString(uint32_t mode)
126 {
127     switch(mode)
128     {
129 #define DEINT(algo,name) case VAProcDeinterlacing##algo: return #name;
130         DEINT(Bob,Bob)
131         DEINT(Weave,Weave)
132         DEINT(MotionAdaptive,Motion-Adaptive)
133         DEINT(MotionCompensated,Motion-Compensated)
134         default:
135             return "Invalid";
136 #undef DEINT
137     }
138 }
139 
140 /**
141     \fn setupVaapi
142 */
setupVaapi(void)143 bool vaapiVideoFilterDeint::setupVaapi(void)
144 {
145     unsupported = 0;
146 
147     if(!admLibVA::isOperationnal())
148     {
149         ADM_warning("HW accel is not available.\n");
150         return false;
151     }
152 
153     configId = admLibVA::createFilterConfig();
154     if(configId == VA_INVALID)
155     {
156         ADM_warning("Cannot create config\n");
157         return false;
158     }
159     // Allocate output surface
160     uint32_t outWidth=configuration.targetWidth;
161     uint32_t outHeight=configuration.targetHeight;
162     FilterInfo *prevInfo=previousFilter->getInfo();
163     ADM_assert(prevInfo);
164 
165     if(!configuration.resize)
166     {
167         outWidth=prevInfo->width;
168         outHeight=prevInfo->height;
169     }
170 
171     outputSurface = ADM_vaSurface::allocateWithSurface(outWidth, outHeight);
172     if(!outputSurface)
173     {
174         ADM_warning("Cannot allocate output surface with size %u x %u\n", outWidth, outHeight);
175         cleanupVaapi();
176         return false;
177     }
178 
179     VAStatus status = vaCreateContext(admLibVA::getDisplay(), configId,
180                         outWidth, outHeight, VA_PROGRESSIVE,
181                         &outputSurface->surface, 1, &contextId);
182 
183     if(status != VA_STATUS_SUCCESS)
184     {
185         ADM_warning("Cannot create context: error %d (%s)\n",status,vaErrorStr(status));
186         cleanupVaapi();
187         return false;
188     }
189     // Query supported deinterlacing algorithms
190     VAProcFilterCapDeinterlacing deintCaps[VAProcDeinterlacingCount];
191     algoCount = VAProcDeinterlacingCount;
192 
193     status = vaQueryVideoProcFilterCaps(admLibVA::getDisplay(), contextId,
194                         VAProcFilterDeinterlacing, &deintCaps, &algoCount);
195 
196     if(status != VA_STATUS_SUCCESS)
197     {
198         ADM_warning("Cannot query deinterlacing capabilities: error %d (%s)\n",status,vaErrorStr(status));
199         cleanupVaapi();
200         return false;
201     }
202     if(algoCount)
203         ADM_info("Driver reports %u deinterlacing methods as supported.\n",algoCount);
204     else
205     {
206         ADM_error("Driver reports that deinterlacing is not supported.\n");
207         cleanupVaapi();
208         return false;
209     }
210     uint32_t i,best=(uint32_t)deintCaps[algoCount-1].type;
211     int algo = -1;
212     for(i=0; i < algoCount; i++)
213     {
214         if((uint32_t)deintCaps[i].type != configuration.deintMode)
215             continue;
216         algo = i;
217         break;
218     }
219     if(algo == -1)
220     {
221         ADM_warning("Requested deinterlacing mode %s is not supported.\n",deintModeToString(configuration.deintMode));
222         ADM_warning("Using %s instead.\n",deintModeToString(best));
223         unsupported = configuration.deintMode;
224         configuration.deintMode = best;
225     }
226     // Query required number of reference surfaces
227     VAProcFilterParameterBufferDeinterlacing deintParams;
228 
229     deintParams.type = VAProcFilterDeinterlacing;
230     deintParams.algorithm = (VAProcDeinterlacingType)configuration.deintMode;
231     deintParams.flags = 0;
232 
233     status = vaCreateBuffer(admLibVA::getDisplay(), contextId,
234                         VAProcFilterParameterBufferType, sizeof(deintParams),
235                         1, &deintParams, &filterBuffer);
236     if(status != VA_STATUS_SUCCESS)
237     {
238         ADM_warning("Cannot create parameter buffer: error %d (%s)\n",status,vaErrorStr(status));
239         cleanupVaapi();
240         return false;
241     }
242 
243     VAProcPipelineCaps caps;
244 
245     status = vaQueryVideoProcPipelineCaps(admLibVA::getDisplay(), contextId,
246                         &filterBuffer, 1, &caps);
247     if(status != VA_STATUS_SUCCESS)
248     {
249         ADM_warning("Cannot query video pipeline capabilities: error %d (%s)\n",status,vaErrorStr(status));
250         cleanupVaapi();
251         return false;
252     }
253     nbForwardRefs = caps.num_forward_references;
254     nbBackwardRefs = caps.num_backward_references;
255     if(nbForwardRefs)
256     {
257         forwardRefs = (VASurfaceID *)malloc(nbForwardRefs * sizeof(VASurfaceID));
258         if(!forwardRefs)
259         {
260             cleanupVaapi();
261             return false;
262         }
263     }
264     if(nbBackwardRefs)
265     {
266         backwardRefs = (VASurfaceID *)malloc(nbBackwardRefs * sizeof(VASurfaceID));
267         if(!backwardRefs)
268         {
269             cleanupVaapi();
270             return false;
271         }
272     }
273     queueLength = nbForwardRefs + nbBackwardRefs + 1;
274     ADM_info("Video processing pipeline for mode %s operates with %u forward and %u backward references.\n",
275                         deintModeToString(configuration.deintMode),
276                         nbForwardRefs,nbBackwardRefs);
277     if(queueLength + 1 > ADM_VAAPI_DEINT_MAX_REF)
278     {
279         ADM_error("Pipeline requires too many references (%u forward, %u back).\n",nbForwardRefs,nbBackwardRefs);
280         cleanupVaapi();
281         return false;
282     }
283     // Allocate source surfaces
284     for(i=0; i < queueLength; i++)
285     {
286         ADM_vaSurface *s = ADM_vaSurface::allocateWithSurface(prevInfo->width,prevInfo->height);
287         if(!s)
288         {
289             ADM_warning("Cannot allocate input surface %d\n",i);
290             cleanupVaapi();
291             return false;
292         }
293         surfacePool[i] = s;
294     }
295     freeSurfaces.clear();
296     for(i=0; i < queueLength; i++)
297         freeSurfaces.push_back(surfacePool[i]);
298     inputQueue = new vaapiSlot[queueLength];
299     return true;
300 }
301 /**
302     \fn cleanupVaapi
303 */
cleanupVaapi(void)304 bool vaapiVideoFilterDeint::cleanupVaapi(void)
305 {
306     for(uint32_t i=0; i < queueLength; i++)
307     {
308         ADM_vaSurface *s = surfacePool[i];
309         if(s)
310         {
311             delete s;
312             surfacePool[i]=NULL;
313         }
314     }
315     if(filterBuffer!=VA_INVALID)
316     {
317         vaDestroyBuffer(admLibVA::getDisplay(), filterBuffer);
318         filterBuffer=VA_INVALID;
319     }
320     if(outputSurface)
321     {
322         delete outputSurface;
323         outputSurface=NULL;
324     }
325     if(configId!=VA_INVALID)
326     {
327         admLibVA::destroyFilterConfig(configId);
328         configId=VA_INVALID;
329     }
330     if(contextId!=VA_INVALID)
331     {
332         admLibVA::destroyFilterContext(contextId);
333         contextId=VA_INVALID;
334     }
335     if(forwardRefs)
336         free(forwardRefs);
337     forwardRefs=NULL;
338     if(backwardRefs)
339         free(backwardRefs);
340     backwardRefs=NULL;
341     if(inputQueue)
342         delete [] inputQueue;
343     inputQueue=NULL;
344     unsupported=0;
345     return true;
346 }
347 /**
348  * \fn updateInfo
349  * @param status
350  * @return
351  */
updateInfo(bool status)352 bool vaapiVideoFilterDeint::updateInfo(bool status)
353 {
354     passThrough=!status;
355     memcpy(&info,previousFilter->getInfo(),sizeof(info));
356     if(passThrough)
357     {
358         ADM_warning("PassThrough mode\n");
359         return true;
360     }
361     if(configuration.framePerField==ADM_VAAPI_DEINT_SEND_FIELD)
362     {
363         info.frameIncrement /= 2;
364         if(info.timeBaseNum && info.timeBaseDen)
365         {
366             if(info.timeBaseDen <= 30000 || (info.timeBaseNum & 1))
367                 info.timeBaseDen *= 2;
368             else
369                 info.timeBaseNum /= 2;
370             /* The frame increment passed along the filter chain may be based on
371             the average frame rate, but we need the minimum increment here.
372             Check whether the time base ~ matches the average increment and derive
373             the minimum increment from time base if possible. */
374             double f=1000.*1000.;
375             f /= info.timeBaseDen;
376             f *= info.timeBaseNum;
377             f += 0.49;
378             if((uint64_t)f > (uint64_t)info.frameIncrement*3/4)
379                 info.frameIncrement = (uint32_t)f;
380         }
381         ADM_info("New frame increment: %u us, new time base: %u / %u\n", info.frameIncrement, info.timeBaseNum, info.timeBaseDen);
382     }
383     if(configuration.resize)
384     {
385         info.width=configuration.targetWidth;
386         info.height=configuration.targetHeight;
387     }
388     return true;
389 }
390 /**
391     \fn constructor
392 */
vaapiVideoFilterDeint(ADM_coreVideoFilter * in,CONFcouple * setup)393 vaapiVideoFilterDeint::vaapiVideoFilterDeint(ADM_coreVideoFilter *in, CONFcouple *setup)
394         : ADM_coreVideoFilterCached(ADM_VAAPI_DEINT_MAX_REF,in,setup)
395 {
396     preloadCompleted=false;
397     secondField=false;
398     configId=VA_INVALID;
399     contextId=VA_INVALID;
400     for(int i=0; i < ADM_VAAPI_DEINT_MAX_REF; i++)
401         surfacePool[i]=NULL;
402     outputSurface=NULL;
403     forwardRefs=NULL;
404     backwardRefs=NULL;
405     inputQueue=NULL;
406     queueLength=0;
407     nbForwardRefs=0;
408     nbBackwardRefs=0;
409     deltaPts=0;
410     if(!setup || !ADM_paramLoad(setup,vaapiFilterDeint_param,&configuration))
411     {
412         // Default value
413         configuration.deintMode=4;
414         configuration.fieldOrder=0;
415         configuration.framePerField=0;
416         configuration.targetWidth=info.width;
417         configuration.targetHeight=info.height;
418         configuration.resize=false;
419     }
420 
421     myName="vaapiDeint";
422     bool status=setupVaapi();
423     updateInfo(status);
424 }
425 /**
426     \fn destructor
427 */
~vaapiVideoFilterDeint()428 vaapiVideoFilterDeint::~vaapiVideoFilterDeint()
429 {
430     cleanupVaapi();
431 }
432 /**
433     \fn configure
434 */
configure(void)435 bool vaapiVideoFilterDeint::configure( void)
436 {
437     diaMenuEntry deintMethod[]={
438         { VAProcDeinterlacingBob,               QT_TRANSLATE_NOOP("vaapiDeint","Bob"),NULL },
439         { VAProcDeinterlacingWeave,             QT_TRANSLATE_NOOP("vaapiDeint","Weave"),NULL },
440         { VAProcDeinterlacingMotionAdaptive,    QT_TRANSLATE_NOOP("vaapiDeint","Motion-Adaptive"),NULL },
441         { VAProcDeinterlacingMotionCompensated, QT_TRANSLATE_NOOP("vaapiDeint","Motion-Compensated"),NULL }
442     };
443     diaMenuEntry fieldOrder[]={
444         { ADM_VAAPI_DEINT_TOP_FIELD_FIRST,          QT_TRANSLATE_NOOP("vaapiDeint","Top Field First"),NULL },
445         { ADM_VAAPI_DEINT_BOTTOM_FIELD_FIRST,       QT_TRANSLATE_NOOP("vaapiDeint","Bottom Field First"),NULL }
446     };
447     diaMenuEntry outputPolicy[]={
448         { ADM_VAAPI_DEINT_SEND_FRAME,   QT_TRANSLATE_NOOP("vaapiDeint","Frame per Frame"),NULL },
449         { ADM_VAAPI_DEINT_SEND_FIELD,   QT_TRANSLATE_NOOP("vaapiDeint","Double Framerate"),NULL }
450     };
451 
452     diaElemMenu dMode(&configuration.deintMode, QT_TRANSLATE_NOOP("vaapiDeint","_Mode:"), 4, deintMethod);
453     diaElemMenu fOrder(&configuration.fieldOrder, QT_TRANSLATE_NOOP("vaapiDeint","_Field Order:"), 2, fieldOrder);
454     diaElemMenu outPol(&configuration.framePerField, QT_TRANSLATE_NOOP("vaapiDeint","_Output:"), 2, outputPolicy);
455 
456     diaElemFrame frameDeint(QT_TRANSLATE_NOOP("vaapiDeint","Deinterlacing"));
457     frameDeint.swallow(&dMode);
458     frameDeint.swallow(&fOrder);
459     frameDeint.swallow(&outPol);
460 
461     diaElemToggle tResize(&configuration.resize, QT_TRANSLATE_NOOP("vaapiDeint","_Resize"));
462     diaElemUInteger tWidth(&configuration.targetWidth, QT_TRANSLATE_NOOP("vaapiDeint","Width:"), 16, MAXIMUM_SIZE);
463     diaElemUInteger tHeight(&configuration.targetHeight, QT_TRANSLATE_NOOP("vaapiDeint","Height:"), 16, MAXIMUM_SIZE);
464 
465     diaElemFrame frameResize(QT_TRANSLATE_NOOP("vaapiDeint","Transformation"));
466     frameResize.swallow(&tResize);
467     frameResize.swallow(&tWidth);
468     frameResize.swallow(&tHeight);
469 
470     tResize.link(1,&tWidth);
471     tResize.link(1,&tHeight);
472 
473     diaElem *elems[]={&frameDeint,&frameResize};
474     if(diaFactoryRun(QT_TRANSLATE_NOOP("vaapiDeint","VA-API Deinterlacer and Resizer"),2,elems))
475     {
476         cleanupVaapi();
477         bool status=setupVaapi();
478         if(unsupported)
479         {
480 
481             GUI_Info_HIG( ADM_LOG_IMPORTANT,
482                           QT_TRANSLATE_NOOP("vaapiDeint","Unsupported Mode"),
483                           QT_TRANSLATE_NOOP("vaapiDeint","Specified deinterlacing mode %s is not supported, replaced with %s."),
484                               deintModeToString(unsupported), deintModeToString(configuration.deintMode));
485             unsupported = 0;
486         }
487         if(!status)
488         {
489             GUI_Error_HIG(QT_TRANSLATE_NOOP("vaapiDeint","VA-API Setup Error"),
490                           QT_TRANSLATE_NOOP("vaapiDeint","Could not setup VA-API, purely passthrough operation."));
491         }
492         updateInfo(status);
493         return true;
494     }
495     return false;
496 }
497 /**
498     \fn getCoupledConf
499     \brief Return our current configuration as couple name=value
500 */
getCoupledConf(CONFcouple ** couples)501 bool vaapiVideoFilterDeint::getCoupledConf(CONFcouple **couples)
502 {
503     return ADM_paramSave(couples, vaapiFilterDeint_param, &configuration);
504 }
505 
setCoupledConf(CONFcouple * couples)506 void vaapiVideoFilterDeint::setCoupledConf(CONFcouple *couples)
507 {
508     ADM_paramLoad(couples, vaapiFilterDeint_param, &configuration);
509 }
510 
511 /**
512     \fn getConfiguration
513     \brief Return current setting as a string
514 */
getConfiguration(void)515 const char *vaapiVideoFilterDeint::getConfiguration(void)
516 {
517     static char conf[256];
518     sprintf(conf,"VA-API deint. mode: %s, parity: %s, double fps: %s",
519                         deintModeToString(configuration.deintMode),
520                         (configuration.fieldOrder==ADM_VAAPI_DEINT_TOP_FIELD_FIRST)? "top field first" : "bottom field first",
521                         (configuration.framePerField==ADM_VAAPI_DEINT_SEND_FIELD)? "yes" : "no");
522     if(configuration.resize)
523     {
524         char part2[80]={0};
525         sprintf(part2,", resize from %dx%d to %dx%d",
526                 previousFilter->getInfo()->width, previousFilter->getInfo()->height,
527                 configuration.targetWidth, configuration.targetHeight);
528         strcat(conf,part2);
529     }
530     conf[255]=0;
531     return conf;
532 }
533 
534 /**
535     \fn goToTime
536     \brief called when seeking. Need to cleanup our stuff.
537 */
goToTime(uint64_t usSeek)538 bool vaapiVideoFilterDeint::goToTime(uint64_t usSeek)
539 {
540     secondField=false;
541     preloadCompleted=false;
542     clearSlots();
543     uint32_t oldFrameIncrement=info.frameIncrement;
544     if(!passThrough && configuration.framePerField==ADM_VAAPI_DEINT_SEND_FIELD)
545         info.frameIncrement*=2;
546     bool r=ADM_coreVideoFilterCached::goToTime(usSeek);
547     info.frameIncrement=oldFrameIncrement;
548     return r;
549 }
550 
551 /**
552     \fn fillSlot
553     \brief upload the image to the slot.
554 */
fillSlot(uint32_t slot,ADMImage * image)555 bool vaapiVideoFilterDeint::fillSlot(uint32_t slot,ADMImage *image)
556 {
557     ADM_assert(slot<queueLength);
558     ADM_vaSurface *target;
559     bool external=false;
560     if(image->refType!=ADM_HW_LIBVA)
561     {
562         // provide a surface from our pool
563         ADM_assert(freeSurfaces.size());
564         target=freeSurfaces.front();
565         freeSurfaces.pop_front();
566         if(!target->fromAdmImage(image))
567             return false;
568     }else
569     {
570         // use the provided surface
571         target=(ADM_vaSurface *)image->refDescriptor.refHwImage;
572         //printf("Source image is already VAAPI, surface %d, slot %d, pts %s\n",(uint32_t)target->surface,slot,ADM_us2plain(image->Pts));
573         ADM_assert(target->refCount);
574         image->hwIncRefCount();
575         external=true;
576     }
577     inputQueue[slot].pts=image->Pts;
578     inputQueue[slot].surface=target;
579     inputQueue[slot].external=external;
580     return true;
581 }
582 
583 /**
584     \fn rotateSlots
585 */
rotateSlots(void)586 bool vaapiVideoFilterDeint::rotateSlots(void)
587 {
588     ADM_assert(queueLength);
589     vaapiSlot *s = &inputQueue[0];
590     if(s->surface)
591     {
592         if(!s->external)
593             freeSurfaces.push_back(s->surface);
594         else if(s->surface->refCount>0)
595             s->surface->refCount--;
596     }
597     for(int i=0; i < (int)queueLength-1; i++)
598         inputQueue[i] = inputQueue[i+1];
599     s = &inputQueue[queueLength-1];
600     s->reset();
601     return true;
602 }
603 
604 /**
605     \fn clearSlots
606 */
clearSlots(void)607 bool vaapiVideoFilterDeint::clearSlots(void)
608 {
609     for(int i=0; i < (int)queueLength; i++)
610     {
611         vaapiSlot *s = &inputQueue[i];
612         if(s->surface)
613         {
614             if(!s->external)
615                 freeSurfaces.push_back(s->surface);
616             else if(s->surface->refCount>0)
617                 s->surface->refCount--;
618         }
619         s->reset();
620     }
621     return true;
622 }
623 
624 /**
625     \fn getNextFrame
626 */
getNextFrame(uint32_t * fn,ADMImage * image)627 bool vaapiVideoFilterDeint::getNextFrame(uint32_t *fn,ADMImage *image)
628 {
629     bool r=false;
630     uint32_t i;
631     if(passThrough)
632     {
633         //ADM_info("VA-API deinterlacer: passthrough\n");
634         return previousFilter->getNextFrame(fn,image);
635     }
636     if(!secondField)
637     {
638         // shift frames;... free slot[0]
639         rotateSlots();
640 
641         if(!preloadCompleted)
642         {
643             for(i=0; i < queueLength; i++)
644             {
645                 ADMImage *ref = vidCache->getImageAs(ADM_HW_LIBVA,i);
646                 if(!ref || false==fillSlot(i,ref))
647                 {
648                     vidCache->unlockAll();
649                     ADM_error("Cannot fill the queue, need %u pictures, got %u, aborting.\n",queueLength,i);
650                     return false;
651                 }
652             }
653             preloadCompleted=true;
654             nextFrame+=nbForwardRefs;
655             //ADM_info("Preloaded %u pictures, nextFrame = %u\n",queueLength,nextFrame);
656         }else
657         {
658             //printf("[vaapiVideoFilterDeint::getNextFrame] requesting frame %u from cache.\n",nextFrame);
659             ADMImage *next = vidCache->getImageAs(ADM_HW_LIBVA,nextFrame);
660             if(!next)
661             {
662                 vidCache->unlockAll();
663                 return false;
664             }
665             if(false==fillSlot(queueLength-1,next))
666             {
667                 vidCache->unlockAll();
668                 return false;
669             }
670         }
671     }
672     vaapiSlot *src = &inputQueue[nbForwardRefs];
673     ADM_assert(src);
674     vaapiSlot *prev = NULL;
675     if(nbForwardRefs)
676         prev = &inputQueue[nbForwardRefs-1];
677     if(prev && prev->pts!=ADM_NO_PTS && src->pts!=ADM_NO_PTS && src->pts > prev->pts)
678         deltaPts = src->pts - prev->pts;
679     image->Pts = src->pts;
680     if(secondField && image->Pts != ADM_NO_PTS)
681     {
682         if(deltaPts < info.frameIncrement*2)
683             image->Pts += deltaPts/2;
684         else
685             image->Pts += info.frameIncrement;
686     }
687 
688     for(i=0; i < nbForwardRefs; i++)
689     {
690         forwardRefs[i] = inputQueue[nbForwardRefs-i-1].surface->surface;
691         //printf("forward ref %d is VASurfaceID %u\n",i,(uint32_t)forwardReferences[i]);
692     }
693     for(i=0; i < nbBackwardRefs; i++)
694     {
695         backwardRefs[i] = inputQueue[nbForwardRefs+i+1].surface->surface;
696         //printf("backward ref %d is VASurfaceID %u\n",i,(uint32_t)backwardReferences[i]);
697     }
698 
699     //-- Perform --
700     VAProcPipelineParameterBuffer param;
701     VABufferID paramId;
702     VAStatus status;
703 
704     memset(&param,0,sizeof(param));
705 
706     param.surface = src->surface->surface;
707     param.surface_region = NULL;
708     param.surface_color_standard = VAProcColorStandardBT709 ; // FIXME
709     param.output_region = NULL;
710     param.output_background_color = 0xff000000;
711     param.output_color_standard = VAProcColorStandardBT709; // FIXME
712 
713     param.filter_flags = VA_FRAME_PICTURE;
714     param.filter_flags |= VA_FILTER_SCALING_HQ;
715     param.pipeline_flags = 0;
716     param.filters = &filterBuffer;
717     param.num_filters = 1;
718 
719     param.forward_references = forwardRefs;
720     param.num_forward_references = nbForwardRefs;
721     param.backward_references = backwardRefs;
722     param.num_backward_references = nbBackwardRefs;
723 
724     VAProcFilterParameterBufferDeinterlacing *deintParams;
725     void *deintParamsPtr;
726 
727 #define CHECK(x) { status=x; if(status!=VA_STATUS_SUCCESS) { ADM_warning( #x " failed with error %d: %s\n",status,vaErrorStr(status)); goto failed; }}
728     CHECK(vaMapBuffer(admLibVA::getDisplay(), filterBuffer, &deintParamsPtr))
729 
730     deintParams = (VAProcFilterParameterBufferDeinterlacing *)deintParamsPtr;
731     deintParams->flags = 0;
732     if(configuration.fieldOrder == ADM_VAAPI_DEINT_BOTTOM_FIELD_FIRST)
733         deintParams->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
734     if(secondField == (configuration.fieldOrder == ADM_VAAPI_DEINT_TOP_FIELD_FIRST))
735         deintParams->flags |= VA_DEINTERLACING_BOTTOM_FIELD;
736     deintParamsPtr=NULL;
737 
738     CHECK(vaUnmapBuffer(admLibVA::getDisplay(), filterBuffer))
739 
740     CHECK(vaBeginPicture(admLibVA::getDisplay(), contextId, outputSurface->surface))
741     CHECK(vaCreateBuffer(admLibVA::getDisplay(), contextId,
742                          VAProcPipelineParameterBufferType,
743                          sizeof(param), 1,
744                          &param, &paramId))
745     // Go
746     CHECK(vaRenderPicture(admLibVA::getDisplay(), contextId, &paramId, 1))
747     CHECK(vaEndPicture(admLibVA::getDisplay(), contextId))
748 
749     // Download result to regular ADMImage
750     r = outputSurface->toAdmImage(image);
751     //printf("Result is %d\n",r);
752 failed:
753     if(paramId!=VA_INVALID)
754         vaDestroyBuffer(admLibVA::getDisplay(), paramId);
755     if(configuration.framePerField==ADM_VAAPI_DEINT_SEND_FIELD)
756     {
757         *fn=(nextFrame-nbForwardRefs)*2+secondField;
758         secondField=!secondField;
759         //printf("%s, frame number: %u, pts: %s\n",secondField? "First field" : "Second field",*fn,ADM_us2plain(image->Pts));
760     }else
761         *fn=nextFrame-nbForwardRefs;
762     if(!secondField) nextFrame++;
763     vidCache->unlockAll();
764     return r;
765 }
766 // EOF
767