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