1 /***************************************************************************
2 
3 		Put a logon on video
4 
5     copyright            : (C) 2007 by mean
6     email                : fixounet@free.fr
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 
17 #include "ADM_default.h"
18 #include "ADM_coreVideoFilter.h"
19 #include "ADM_vidMisc.h"
20 #include "DIA_factory.h"
21 #include "DIA_coreToolkit.h"
22 #include "ivtcDupeRemover.h"
23 #include "ivtcDupeRemover_desc.cpp"
24 
25 #if defined( ADM_CPU_X86) && !defined(_MSC_VER)
26         #define CAN_DO_INLINE_X86_ASM
27 #endif
28 
29 #define PERIOD 4
30 #if 0
31 #define aprintf printf
32 #else
33 #define aprintf(...) {}
34 #endif
35 
36 
37 #define MARK_PROGRESSIVE 'PRGS'
38 #define MARK_DUPLICATE   'DUPE'
39 
40 /**
41     \class ivtcDupeRemover
42 */
43 class ivtcDupeRemover : public  ADM_coreVideoFilterCached
44 {
45 public:
46                 enum dupeState
47                 {
48                     dupeSyncing,
49                     dupeSynced,
50                     dupePassThrough
51                 };
52                 enum searchMode
53                 {
54                     full=0,
55                     fast=1,
56                     veryFast=2
57                 };
58                 int             incomingNum; // Incoming frame number
59                 int             currentNum;  // outgoing frame number
60                 int             phaseStart;  // Frame Number of 1st frame of cycle
61                 uint64_t        phaseStartPts; // its PTS
62                 int             dupeOffset;  // offset of the duplicate to drop,  i.e. abs number = phaseStart+dupeOffset
63                 dupeState       state;       // Current finite state machine
64                 uint32_t        delta[PERIOD+1]; // List of image difference
65                 unsigned int    hints[PERIOD+1]; // From D. Graft ivtc if used
66 protected:
67                 dupeRemover        configuration;
68                 dupeState          searchSync();
69                 bool               postProcess(ADMImage *in,ADMImage *out,uint64_t pts);
70                 uint32_t           computeDelta(ADMImage *left,ADMImage *right, int threshold);
71 public:
72                                    ivtcDupeRemover(ADM_coreVideoFilter *previous,CONFcouple *conf);
73                                    ~ivtcDupeRemover();
74                 bool               goToTime(uint64_t usSeek);
75         virtual const char         *getConfiguration(void);                   /// Return  current configuration as a human readable string
76         virtual bool               getNextFrame(uint32_t *fn,ADMImage *image);    /// Return the next image
77 	 //  virtual FilterInfo  *getInfo(void);                             /// Return picture parameters after this filter
78         virtual bool               getCoupledConf(CONFcouple **couples) ;     /// Return the current filter configuration
79         virtual void               setCoupledConf(CONFcouple *couples);
80         virtual bool               configure(void);                           /// Start graphical user interface
81                 uint32_t           lumaDiff(ADMImage *src1,ADMImage *src2,uint32_t noise);
82 
83 };
84 
85 // Add the hook to make it valid plugin
86 DECLARE_VIDEO_FILTER(   ivtcDupeRemover,   // Class
87                         1,0,0,              // Version
88                         ADM_UI_ALL,         // UI
89                         VF_INTERLACING,            // Category
90                         "ivtcRemover",            // internal name (must be uniq!)
91                         QT_TRANSLATE_NOOP("ivtcRemover","Remove IVTC dupe."),            // Display name
92                         QT_TRANSLATE_NOOP("ivtcRemover","Remove the duplicate frames present after ivtc.") // Description
93                     );
94 
95 // Now implements the interesting parts
96 /**
97     \fn ivtcDupeRemover
98     \brief constructor
99 */
ivtcDupeRemover(ADM_coreVideoFilter * in,CONFcouple * setup)100 ivtcDupeRemover::ivtcDupeRemover(  ADM_coreVideoFilter *in,CONFcouple *setup) : ADM_coreVideoFilterCached(11,in,setup)
101 {
102     if(!setup || !ADM_paramLoad(setup,dupeRemover_param,&configuration))
103     {
104         // Default value
105         configuration.threshold=5;
106         configuration.show=false;
107         configuration.mode=1; // fast!
108 
109     }
110     myName="ivtcDupeRemover";
111 
112     incomingNum=0;
113     currentNum=0;
114     phaseStart=0;
115     dupeOffset=0;
116     state=dupeSyncing;
117     // ajust fps
118     double fps=info.frameIncrement;
119 
120     fps*=PERIOD+1;
121     fps/=PERIOD;
122     info.frameIncrement=fps;
123 }
124 /**
125     \fn ivtcDupeRemover
126     \brief destructor
127 */
~ivtcDupeRemover()128 ivtcDupeRemover::~ivtcDupeRemover()
129 {
130 }
131 /**
132  * \fn computeDelta
133  * @param left
134  * @param right
135  * @param threshold
136  * @return
137  */
computeDelta(ADMImage * left,ADMImage * right,int threshold)138 uint32_t ivtcDupeRemover::computeDelta(ADMImage *left,ADMImage *right, int threshold)
139 {
140     if(!configuration.mode)
141         return lumaDiff(left,right,threshold);
142     // Else we take line every 5 or 9 lines
143 
144     int scale=1+((int)configuration.mode*4);
145 
146     ADMImageRef refLeft(left->GetWidth(PLANAR_Y),left->GetHeight(PLANAR_Y)/scale);
147     ADMImageRef refRight(right->GetWidth(PLANAR_Y),right->GetHeight(PLANAR_Y)/scale);
148 
149     refLeft._planes[0]=left->_planes[0];
150     refLeft._planeStride[0]=left->_planeStride[0]/scale;
151 
152     refRight._planes[0]=right->_planes[0];
153     refRight._planeStride[0]=right->_planeStride[0]/scale;
154 
155      return lumaDiff(&refLeft,&refRight,threshold);
156 
157 
158 }
159 /**
160  * \fn lookupSync
161  * \brief Try to search for a sequence
162  * @return
163  */
searchSync()164 ivtcDupeRemover::dupeState ivtcDupeRemover::searchSync()
165 {
166     ADMImage *images[PERIOD+1];
167 
168 
169     aprintf("dupeRemover : Searching sync\n");
170 
171     for(int i=0;i<(PERIOD+1);i++)
172     {
173         images[i]=vidCache->getImage(incomingNum+i);
174         if(!images[i])
175         {
176             vidCache->unlockAll();
177             aprintf("No image (%d)\n",i);
178             return dupeSyncing;
179         }
180         if(GetHintingData(images[i]->GetReadPtr(PLANAR_Y),hints+i)) // Returns true on error
181         {
182             aprintf("[%d] No hint\n",i);
183             hints[i]=0;
184         }else
185         {
186             aprintf("[%d] Got hint %x\n",i,hints[i]);
187         }
188     }
189 
190     int film=0;
191     for(int i=0;i<PERIOD;i++)
192     {
193         int deltaPts=(int)(images[i+1]->Pts-images[i]->Pts);
194         delta[i]=0;
195         aprintf("DeltaPts=%d\n",deltaPts);
196         if(deltaPts> 41000) // 24 fps
197             film++;
198     }
199     // All of it is 24 fps, pass through
200     if(film==PERIOD)
201     {
202         aprintf("It is all film\n");
203         vidCache->unlockAll();
204         return dupePassThrough;
205     }
206     // It is mixed, keep syncing
207     if(film)
208     {
209         aprintf("It is mixed fps\n");
210         vidCache->unlockAll();
211         return dupeSyncing;
212     }
213 
214     // Do we have hints ?
215     int nbProgressiveHint=0;
216     int nbDupeHint=0;
217     bool found=false;
218     for(int i=0;i<PERIOD+1;i++)
219     {
220         switch(hints[i])
221         {
222             case MARK_PROGRESSIVE: nbProgressiveHint++;break;
223             case MARK_DUPLICATE:  nbDupeHint++;dupeOffset=i;break;
224             default:break;
225         }
226     }
227     if(nbProgressiveHint==PERIOD && nbDupeHint==1)
228     {
229         aprintf("Hinting is giving the dupe at %d\n",dupeOffset);
230         found=true;
231     }
232 
233 
234     // Ok, they are all NTSC,  let's find the minimum one
235     if(!found)
236     {
237         aprintf("Lets look for the closest one\n");
238         for(int i=0;i<PERIOD;i++)
239         {
240             if(images[i] && images[i+1])
241                 delta[i]=computeDelta(images[i],images[i+1],configuration.threshold);
242             else
243                 delta[i]=0x70000000; // Big number
244         }
245         uint32_t minDelta=0x7f000000;
246         for(int i=0;i<PERIOD;i++)
247         {
248             if(minDelta>delta[i])
249             {
250                 minDelta=delta[i];
251                 dupeOffset=i;
252 
253             }
254         }
255     }
256     phaseStart=incomingNum;
257     phaseStartPts=images[0]->Pts;
258     vidCache->unlockAll();
259     return dupeSynced;
260 }
261 /**
262  * \fn dupeState2string
263  * @return
264  */
dupeState2string(ivtcDupeRemover::dupeState state)265 static const char *dupeState2string(ivtcDupeRemover::dupeState state)
266 {
267       const char *m="";
268       switch(state)
269             {
270                 case    ivtcDupeRemover::dupeSyncing:     m="Syncing";break;
271                 case    ivtcDupeRemover::dupeSynced:      m="dupeSynced";break;
272                 case    ivtcDupeRemover::dupePassThrough: m="dupePassThrough";break;
273                 default: ADM_assert(0);break;
274             }
275       return m;
276 }
277 /**
278  *
279  * @param img
280  * @return
281  */
postProcess(ADMImage * in,ADMImage * out,uint64_t newPts)282 bool ivtcDupeRemover::postProcess(ADMImage *in,ADMImage *out,uint64_t newPts)
283 {
284     if(in)
285     {
286         out->duplicateFull(in);
287         if(newPts!=ADM_NO_PTS)
288             out->Pts=newPts;
289         if(configuration.show)
290         {
291             char s[256];
292             const char *m=dupeState2string(state);
293             out->printString(2,2,m);
294             for(int i=0;i<PERIOD;i++)
295             {
296                 sprintf(s,"Diff:%u",delta[i]);
297                 out->printString(2,4+i,s);
298                 sprintf(s,"Hint:%x",hints[i]);
299                 out->printString(2,11+i,s);
300             }
301             sprintf(s,"Hint:%x",hints[PERIOD]);
302             out->printString(2,11+PERIOD,s);
303 
304         }
305     }
306     return true;
307 }
308 /**
309     \fn getFrame
310     \brief Get a processed frame
311 */
312 #define END_PROCESS(i,image,newPts,nextState) \
313                 *fn=currentNum; \
314                 currentNum++; \
315                 postProcess(i,image,newPts); \
316                 state=nextState; \
317                 vidCache->unlockAll(); \
318                 if(i) \
319                     return true; \
320                 return false;
321 
322 
getNextFrame(uint32_t * fn,ADMImage * image)323 bool ivtcDupeRemover::getNextFrame(uint32_t *fn,ADMImage *image)
324 {
325     aprintf("Current state is %s\n",dupeState2string(state));
326     switch(state)
327     {
328         case dupeSynced:
329             {
330               ivtcDupeRemover::dupeState nextState=dupeSynced;
331               int count=incomingNum-phaseStart;
332               if(count>dupeOffset)
333                   count--;
334               if((incomingNum-phaseStart)==dupeOffset)
335               {
336                   aprintf("This is the dupe (%d), skipping\n",incomingNum);
337                   incomingNum++;
338               }
339               aprintf("Cound in sequence is %d\n",count);
340               ADMImage *i=vidCache->getImage(incomingNum);
341               incomingNum++;
342               if((incomingNum-phaseStart)>PERIOD)
343               {
344                   nextState=dupeSyncing;
345               }
346               uint64_t newPts=phaseStartPts+count*41666;
347               END_PROCESS(i,image,newPts,nextState);
348               break;
349             }
350             break;
351         case dupePassThrough:
352             {
353               ADMImage *i=vidCache->getImage(incomingNum);
354               incomingNum++;
355               if((incomingNum-phaseStart)>(PERIOD))
356                       state=dupeSyncing;
357              END_PROCESS(i,image,ADM_NO_PTS,dupePassThrough);
358              break;
359             }
360         case dupeSyncing:
361             {
362                 ivtcDupeRemover::dupeState nextState=searchSync();
363                 // 1st one gets through
364                 aprintf(">>State = %s, in =%d, out=%d\n",dupeState2string(nextState),incomingNum,currentNum);
365                 ADMImage *i=vidCache->getImage(incomingNum);
366                 if(i)
367                     aprintf(">>PTS=%s\n",ADM_us2plain(i->Pts));
368                 incomingNum++;
369                 END_PROCESS(i,image,ADM_NO_PTS,nextState);
370                 break;
371             }
372         default:
373               ADM_assert(0);
374 
375               break;
376 
377       }
378 }
379 /**
380     \fn getCoupledConf
381     \brief Return our current configuration as couple name=value
382 */
getCoupledConf(CONFcouple ** couples)383 bool         ivtcDupeRemover::getCoupledConf(CONFcouple **couples)
384 {
385 
386     return ADM_paramSave(couples, dupeRemover_param,&configuration);
387 }
388 
setCoupledConf(CONFcouple * couples)389 void ivtcDupeRemover::setCoupledConf(CONFcouple *couples)
390 {
391     ADM_paramLoad(couples, dupeRemover_param, &configuration);
392 }
393 
394 /**
395     \fn getConfiguration
396     \brief Return current setting as a string
397 */
getConfiguration(void)398 const char *ivtcDupeRemover::getConfiguration(void)
399 {
400     static char bfer[1024];
401 
402     sprintf(bfer,"IVTC Dupe Removed : Threshold =%u",(unsigned int)configuration.threshold);
403     return bfer;
404 }
405 /**
406  *
407  * @param usSeek
408  * @return
409  */
goToTime(uint64_t usSeek)410 bool         ivtcDupeRemover::goToTime(uint64_t usSeek)
411 {
412     vidCache->flush();
413     state=dupeSyncing;
414     incomingNum=0;
415     currentNum=0;
416     return previousFilter->goToTime(usSeek);
417 }
418 /**
419     \fn configure
420 */
configure(void)421 bool ivtcDupeRemover::configure( void)
422 {
423 
424 #define PX(x) &(configuration.x)
425         diaElemUInteger   threshold(PX(threshold),QT_TRANSLATE_NOOP("ivtcRemover","_Noise:"),0,255);
426         diaElemToggle     show(PX(show),QT_TRANSLATE_NOOP("ivtcRemover","_Show:"));
427 
428 
429         diaMenuEntry menuMode[]={
430                 {0,      QT_TRANSLATE_NOOP("ivtcRemover","Full")},
431                 {1,      QT_TRANSLATE_NOOP("ivtcRemover","Fast")},
432                 {2,      QT_TRANSLATE_NOOP("ivtcRemover","VeryFast")}
433                 };
434 
435 
436 
437         diaElemMenu      eMode(&configuration.mode,QT_TRANSLATE_NOOP("ivtcRemover","_Frame rate change:"),3,menuMode);
438 
439         diaElem *elems[3]={&threshold,&show,&eMode};
440         return diaFactoryRun(QT_TRANSLATE_NOOP("ivtcRemover","DupeRemover"),3,elems);
441 }
442 #ifdef CAN_DO_INLINE_X86_ASM
443 static uint64_t __attribute__((used)) FUNNY_MANGLE(noise64);
444 /**
445 */
smallDiff(uint8_t * s1,uint8_t * s2,uint32_t noise,int count)446 static uint32_t smallDiff(uint8_t  *s1,uint8_t *s2,uint32_t noise, int count)
447 {
448 uint32_t df=0;
449 uint32_t delta;
450     for(int x=0;x<count;x++)
451     {
452             delta=abs((int)(s1[x])-(int)(s2[x]));
453             if(delta>noise)
454                     df+=delta;
455     }
456     return df;
457 }
458 
459 /**
460  * \fn computeDiffMMX
461  * @param s1
462  * @param s2
463  * @param noise
464  * @param w
465  * @param l
466  * @param pitch1
467  * @param pitch2
468  * @return
469  */
computeDiffMMX(uint8_t * s1,uint8_t * s2,uint32_t noise,uint32_t w,uint32_t l,uint32_t pitch1,uint32_t pitch2)470 static uint32_t computeDiffMMX(uint8_t  *s1,uint8_t *s2,uint32_t noise,uint32_t w,uint32_t l, uint32_t pitch1, uint32_t pitch2)
471 {
472 uint32_t mod4,leftOver;
473 uint64_t noise2=(uint64_t )noise;
474 
475 uint32_t result=0,tmpResult;
476         noise64=noise2+(noise2<<16)+(noise2<<32)+(noise2<<48);
477 
478         leftOver=w&7;
479 
480          __asm__ volatile(
481                          "pxor %%mm7,%%mm7\n"
482                          "movq " Mangle(noise64)", %%mm6\n"
483                 :::  "memory"
484                  );
485 
486           for(int y=0;y<l;y++)
487           {
488                 mod4=w>>3;
489                 if(leftOver)
490                     result+=smallDiff(s1+mod4*8,s2+mod4*8,noise,leftOver);
491                 uint8_t *tmpS1=s1;
492                 uint8_t *tmpS2=s2;
493 
494                 __asm__ volatile(
495                         "pxor           %%mm3,%%mm3\n"
496                         "1:"
497                 // LEFT
498                         "movd           (%0),  %%mm0 \n"
499                         "movd           (%1),  %%mm1 \n"
500                         "punpcklbw      %%mm7, %%mm0 \n"
501                         "punpcklbw      %%mm7, %%mm1 \n"
502 
503                         "movq           %%mm0, %%mm2 \n"
504                         "psubusw        %%mm1, %%mm2 \n"
505                         "psubusw        %%mm0, %%mm1 \n"
506                         "por            %%mm1, %%mm2 \n" // SAD
507 
508                         "movq           %%mm2, %%mm0 \n"
509                         "pcmpgtw        %%mm6, %%mm2 \n" // Threshold against noise
510                         "pand           %%mm2, %%mm0 \n" //
511                         "movq           %%mm0, %%mm5 \n" //  %mm5 is the  A1 A2 A3 A4, we want the sum later
512                 // RIGHT
513                         "movd           4(%0),  %%mm0 \n"
514                         "movd           4(%1),  %%mm1 \n"
515                         "punpcklbw      %%mm7, %%mm0 \n"
516                         "punpcklbw      %%mm7, %%mm1 \n"
517 
518                         "movq           %%mm0, %%mm2 \n"
519                         "psubusw        %%mm1, %%mm2 \n"
520                         "psubusw        %%mm0, %%mm1 \n"
521                         "por            %%mm1, %%mm2 \n" // SAD
522 
523                         "movq           %%mm2, %%mm0 \n"
524                         "pcmpgtw        %%mm6, %%mm2 \n" // Threshold against noise
525                         "pand           %%mm2, %%mm0 \n" // mm0 is B1 B2 B3 B4
526 
527                         "paddW          %%mm5, %%mm0 \n"
528 
529                 // PACK
530                         "movq           %%mm0, %%mm1 \n" // MM0 is a b c d and we want
531                         "psrlq          $16,  %%mm1 \n"  // mm3+=a+b+c+d
532 
533                         "movq           %%mm0, %%mm2 \n"
534                         "psrlq          $32,  %%mm2 \n"
535 
536                         "movq           %%mm0, %%mm4 \n"
537                         "psrlq          $48,  %%mm4 \n"
538 
539                         "paddw          %%mm1, %%mm0 \n"
540                         "paddw          %%mm2, %%mm4 \n"
541                         "paddw          %%mm4, %%mm0 \n" // MM0 is the sum
542 
543                         "psllq          $48,  %%mm0 \n"
544                         "psrlq          $48,  %%mm0 \n" // Only keep 16 bits
545 
546                         "paddw          %%mm0, %%mm3 \n" /* PADDQ is SSE2 */
547                         "add            $8,%0      \n"
548                         "add            $8,%1      \n"
549                         "sub            $1,%2      \n"
550                         "jnz            1b         \n"
551 
552                 : "=r" (tmpS1),"=r" (tmpS2),"=r"(mod4)
553                 : "0"(tmpS1),"1"(tmpS2),"2"(mod4)
554                 : "memory","0","1","2"
555                 );
556                 __asm__ volatile(
557 
558                         "movd           %%mm3,(%0)\n"
559                         "emms\n"
560                 :: "r"(&tmpResult)
561                 );
562                 result+=tmpResult;
563                 s1+=pitch1;
564                 s2+=pitch2;
565          }
566         // Pack result
567         return result;
568 }
569 
570 
571 #endif
572 /**
573 
574 */
575 /* 3000 * 3000 max size, using uint32_t is safe... */
computeDiff(uint8_t * s1,uint8_t * s2,uint32_t noise,int w,int h,int stride1,int stride2)576 static uint32_t computeDiff(uint8_t  *s1,uint8_t *s2,uint32_t noise,int w,int h, int stride1, int stride2)
577 {
578 uint32_t df=0;
579 uint32_t delta;
580 
581     for(int y=0;y<h;y++)
582     {
583         for(int x=0;x<w;x++)
584         {
585                 delta=abs((int)(s1[x])-(int)(s2[x]));
586                 if(delta>noise)
587                         df+=delta;
588         }
589         s1+=stride1;
590         s2+=stride2;
591     }
592     return df;
593 }
594 /**
595 */
lumaDiff(ADMImage * src1,ADMImage * src2,uint32_t noise)596 uint32_t ivtcDupeRemover::lumaDiff(ADMImage *src1,ADMImage *src2,uint32_t noise)
597 {
598 
599 #ifdef CAN_DO_INLINE_X86_ASM
600 uint32_t r1,r2;
601         if(CpuCaps::hasMMX())
602         {
603                 uint32_t a= computeDiffMMX(YPLANE(src1),YPLANE(src2),noise,
604                     src1->GetWidth(PLANAR_Y),src1->GetHeight(PLANAR_Y),
605                     src1->GetPitch(PLANAR_Y),src2->GetPitch(PLANAR_Y));
606 #if 0
607                 uint32_t b= computeDiff(YPLANE(src1),YPLANE(src2),noise,
608                     src1->GetWidth(PLANAR_Y),src1->GetHeight(PLANAR_Y),
609                     src1->GetPitch(PLANAR_Y),src2->GetPitch(PLANAR_Y));
610                 printf("MMX = %u, native =%u\n",a,b);
611 #endif
612                 return a;
613 
614         }
615 #endif
616         return computeDiff(YPLANE(src1),YPLANE(src2),noise,
617                 src1->GetWidth(PLANAR_Y),src1->GetHeight(PLANAR_Y),
618                 src1->GetPitch(PLANAR_Y),src2->GetPitch(PLANAR_Y));
619 }
620 
621 /************************************************/
622 //EOF
623