]> git.sesse.net Git - mlt/blob - src/modules/dgraft/filter_telecide.c
9bb11497b5da5f7080323142f89f62050bfcdf08
[mlt] / src / modules / dgraft / filter_telecide.c
1 /*
2  * filter_telecide.c -- Donald Graft's Inverse Telecine Filter
3  * Copyright (C) 2003 Donald A. Graft
4  * Copyright (C) 2008 Dan Dennedy <dan@dennedy.org>
5  * Author: Dan Dennedy <dan@dennedy.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <framework/mlt.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h>
28
29 //#define DEBUG_PATTERN_GUIDANCE
30
31 #define MAX_CYCLE 6
32 #define BLKSIZE 24
33 #define BLKSIZE_TIMES2 (2 * BLKSIZE)
34 #define GUIDE_32 1
35 #define GUIDE_22 2
36 #define GUIDE_32322 3
37 #define AHEAD 0
38 #define BEHIND 1
39 #define POST_METRICS 1
40 #define POST_FULL 2
41 #define POST_FULL_MAP 3
42 #define POST_FULL_NOMATCH 4
43 #define POST_FULL_NOMATCH_MAP 5
44 #define CACHE_SIZE 100000
45 #define P 0
46 #define C 1
47 #define N 2
48 #define PBLOCK 3
49 #define CBLOCK 4
50
51 #define NO_BACK 0
52 #define BACK_ON_COMBED 1
53 #define ALWAYS_BACK 2
54
55 struct CACHE_ENTRY
56 {
57         unsigned int frame;
58         unsigned int metrics[5];
59         unsigned int chosen;
60 };
61
62 struct PREDICTION
63 {
64         unsigned int metric;
65         unsigned int phase;
66         unsigned int predicted;
67         unsigned int predicted_metric;
68 };
69
70 struct context_s {
71         int is_configured;
72         mlt_properties image_cache;
73         int out;
74         
75         int tff, chroma, blend, hints, show, debug;
76         float dthresh, gthresh, vthresh, vthresh_saved, bthresh;
77         int y0, y1, nt, guide, post, back, back_saved;
78         int pitch, dpitch, pitchover2, pitchtimes4;
79         int w, h, wover2, hover2, hplus1over2, hminus2;
80         int xblocks, yblocks;
81 #ifdef WINDOWED_MATCH
82         unsigned int *matchc, *matchp, highest_matchc, highest_matchp;
83 #endif
84         unsigned int *sumc, *sump, highest_sumc, highest_sump;
85         int vmetric;
86         unsigned int *overrides, *overrides_p;
87         int film, override, inpattern, found;
88         int force;
89
90         // Used by field matching.
91         unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp;
92 //      unsigned char *fprpU, *fcrpU, *fcrp_savedU, *fnrpU;
93 //      unsigned char *fprpV, *fcrpV, *fcrp_savedV, *fnrpV;
94         unsigned char *dstp, *finalp;
95 //      unsigned char *dstpU, *dstpV;
96         int chosen;
97         unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric;
98         unsigned int np, nc, npblock, ncblock, nframe;
99         float mismatch;
100         int pframe, x, y;
101         unsigned char *crp, *prp;
102         unsigned char *crpU, *prpU;
103         unsigned char *crpV, *prpV;
104         int hard;
105         char status[80];
106
107         // Metrics cache.
108         struct CACHE_ENTRY *cache;
109
110         // Pattern guidance data.
111         int cycle;
112         struct PREDICTION pred[MAX_CYCLE+1];
113 };
114 typedef struct context_s *context;
115
116
117 static inline
118 void BitBlt(uint8_t* dstp, int dst_pitch, const uint8_t* srcp,
119             int src_pitch, int row_size, int height)
120 {
121         uint32_t y;
122         for(y=0;y<height;y++)
123         {
124                 memcpy(dstp,srcp,row_size);
125                 dstp+=dst_pitch;
126                 srcp+=src_pitch;
127         }
128 }
129
130 static void Show(context cx, int frame, mlt_properties properties)
131 {
132         char use;
133         char buf[512];
134
135         if (cx->chosen == P) use = 'p';
136         else if (cx->chosen == C) use = 'c';
137         else use = 'n';
138         snprintf(buf, sizeof(buf), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
139         if ( cx->post )
140                 snprintf(buf, sizeof(buf), "%sTelecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", buf, frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
141         if ( cx->guide )
142                 snprintf(buf, sizeof(buf), "%spattern mismatch=%0.2f%%\n", buf, cx->mismatch);
143         snprintf(buf, sizeof(buf), "%sTelecide: frame %d: [%s %c]%s %s\n", buf, frame, cx->found ? "forcing" : "using", use,
144                 cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
145                 cx->guide ? cx->status : "");
146         mlt_properties_set( properties, "meta.attr.telecide.markup", buf );
147 }
148
149 static void Debug(context cx, int frame)
150 {
151         char use;
152
153         if (cx->chosen == P) use = 'p';
154         else if (cx->chosen == C) use = 'c';
155         else use = 'n';
156         fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
157         if ( cx->post )
158                 fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
159         if ( cx->guide )
160                 fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch); 
161         fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use,
162                 cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
163                 cx->guide ? cx->status : "");
164 }
165
166 static void WriteHints(int film, int inpattern, mlt_properties frame_properties)
167 {
168         mlt_properties_set_int( frame_properties, "telecide.progressive", film);
169         mlt_properties_set_int( frame_properties, "telecide.in_pattern", inpattern);
170 }
171
172 static void PutChosen(context cx, int frame, unsigned int chosen)
173 {
174         int f = frame % CACHE_SIZE;
175         if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame)
176                 return;
177         cx->cache[f].chosen = chosen;
178 }
179
180 static void CacheInsert(context cx, int frame, unsigned int p, unsigned int pblock,
181                                          unsigned int c, unsigned int cblock)
182 {
183         int f = frame % CACHE_SIZE;
184         if (frame < 0 || frame > cx->out)
185                 fprintf( stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame);
186         cx->cache[f].frame = frame;
187         cx->cache[f].metrics[P] = p;
188         if (f) cx->cache[f-1].metrics[N] = p;
189         cx->cache[f].metrics[C] = c;
190         cx->cache[f].metrics[PBLOCK] = pblock;
191         cx->cache[f].metrics[CBLOCK] = cblock;
192         cx->cache[f].chosen = 0xff;
193 }
194
195 static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock,
196                                         unsigned int *c, unsigned int *cblock)
197 {
198         int f;
199
200         f = frame % CACHE_SIZE;
201         if (frame < 0 || frame > cx->out)
202                 fprintf( stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame);
203         if (cx->cache[f].frame != frame)
204         {
205                 return 0;
206         }
207         *p = cx->cache[f].metrics[P];
208         *c = cx->cache[f].metrics[C];
209         *pblock = cx->cache[f].metrics[PBLOCK];
210         *cblock = cx->cache[f].metrics[CBLOCK];
211         return 1;
212 }
213
214 static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric)
215 {
216         // Look for pattern in the actual delivered matches of the previous cycle of frames.
217         // If a pattern is found, use that to predict the current match.
218         if ( cx->guide == GUIDE_22 )
219         {
220                 if (cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
221                         cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen == 0xff)
222                         return 0;
223                 switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen << 4) +
224                                 (cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen))
225                 {
226                 case 0x11:
227                         *predicted = C;
228                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
229                         break;
230                 case 0x22:
231                         *predicted = N;
232                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
233                         break;
234                 default: return 0;
235                 }
236         }
237         else if ( cx->guide == GUIDE_32 )
238         {
239                 if (cx->cache[(frame-cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
240                         cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen == 0xff ||
241                         cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen == 0xff ||
242                         cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen == 0xff ||
243                         cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen == 0xff)
244                         return 0;
245
246                 switch ((cx->cache[(frame-cx->cycle)%CACHE_SIZE  ].chosen << 16) +
247                                 (cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen << 12) +
248                                 (cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen <<  8) +
249                                 (cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen <<  4) +
250                                 (cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen))
251                 {
252                 case 0x11122:
253                 case 0x11221:
254                 case 0x12211:
255                 case 0x12221: 
256                 case 0x21122: 
257                 case 0x11222: 
258                         *predicted = C;
259                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
260                         break;
261                 case 0x22111:
262                 case 0x21112:
263                 case 0x22112: 
264                 case 0x22211: 
265                         *predicted = N;
266                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
267                         break;
268                 default: return 0;
269                 }
270         }
271         else if ( cx->guide == GUIDE_32322 )
272         {
273                 if (cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
274                         cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen == 0xff ||
275                         cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen == 0xff ||
276                         cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen == 0xff ||
277                         cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen == 0xff ||
278                         cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen == 0xff)
279                         return 0;
280
281                 switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen << 20) +
282                                 (cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen << 16) +
283                                 (cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen << 12) +
284                                 (cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen <<  8) +
285                                 (cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen <<  4) +
286                                 (cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen))
287                 {
288                 case 0x111122:
289                 case 0x111221:
290                 case 0x112211:
291                 case 0x122111:
292                 case 0x111222: 
293                 case 0x112221:
294                 case 0x122211:
295                 case 0x222111: 
296                         *predicted = C;
297                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
298                         break;
299                 case 0x221111:
300                 case 0x211112:
301
302                 case 0x221112: 
303                 case 0x211122: 
304                         *predicted = N;
305                         *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
306                         break;
307                 default: return 0;
308                 }
309         }
310 #ifdef DEBUG_PATTERN_GUIDANCE
311         fprintf( stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted);
312 #endif
313         return 1;
314 }
315
316 static struct PREDICTION *PredictSoftYUY2(context cx, int frame )
317 {
318         // Use heuristics to look forward for a match.
319         int i, j, y, c, n, phase;
320         unsigned int metric;
321
322         cx->pred[0].metric = 0xffffffff;
323         if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred;
324
325         // Look at the next cycle of frames.
326         for (y = frame + 1; y <= frame + cx->cycle; y++)
327         {
328                 // Look for a frame where the current and next match values are
329                 // very close. Those are candidates to predict the phase, because
330                 // that condition should occur only once per cycle. Store the candidate
331                 // phases and predictions in a list sorted by goodness. The list will
332                 // be used by the caller to try the phases in order.
333                 c = cx->cache[y%CACHE_SIZE].metrics[C]; 
334                 n = cx->cache[y%CACHE_SIZE].metrics[N];
335                 if (c == 0) c = 1;
336                 metric = (100 * abs (c - n)) / c;
337                 phase = y % cx->cycle;
338                 if (metric < 5)
339                 {
340                         // Place the new candidate phase in sorted order in the list.
341                         // Find the insertion point.
342                         i = 0;
343                         while (metric > cx->pred[i].metric) i++;
344                         // Find the end-of-list marker.
345                         j = 0;
346                         while (cx->pred[j].metric != 0xffffffff) j++;
347                         // Shift all items below the insertion point down by one to make
348                         // room for the insertion.
349                         j++;
350                         for (; j > i; j--)
351                         {
352                                 cx->pred[j].metric = cx->pred[j-1].metric;
353                                 cx->pred[j].phase = cx->pred[j-1].phase;
354                                 cx->pred[j].predicted = cx->pred[j-1].predicted;
355                                 cx->pred[j].predicted_metric = cx->pred[j-1].predicted_metric;
356                         }
357                         // Insert the new candidate data.
358                         cx->pred[j].metric = metric;
359                         cx->pred[j].phase = phase;
360                         if ( cx->guide == GUIDE_32 )
361                         {
362                                 switch ((frame % cx->cycle) - phase)
363                                 {
364                                 case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
365                                 case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
366                                 case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
367                                 case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
368                                 case  0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
369                                 case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
370                                 case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
371                                 case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
372                                 case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
373                                 }
374                         }
375                         else if ( cx->guide == GUIDE_32322 )
376                         {
377                                 switch ((frame % cx->cycle) - phase)
378                                 {
379                                 case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
380                                 case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
381                                 case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
382                                 case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
383                                 case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
384                                 case  0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
385                                 case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
386                                 case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
387                                 case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
388                                 case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
389                                 case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
390                                 }
391                         }
392                 }
393 #ifdef DEBUG_PATTERN_GUIDANCE
394                 fprintf( stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase);
395 #endif
396         }
397         return cx->pred;
398 }
399
400 static
401 void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV,
402                                         unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV)
403 {
404         int x, y, p, c, tmp1, tmp2, skip;
405         int vc;
406     unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2;
407         unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4;
408         unsigned char *a0, *a2, *b0, *b2, *b4;
409         unsigned int diff, index;
410 #       define T 4
411
412         /* Clear the block sums. */
413         for (y = 0; y < cx->yblocks; y++)
414         {
415                 for (x = 0; x < cx->xblocks; x++)
416                 {
417 #ifdef WINDOWED_MATCH
418                         matchp[y*xblocks+x] = 0;
419                         matchc[y*xblocks+x] = 0;
420 #endif
421                         cx->sump[y * cx->xblocks + x] = 0;
422                         cx->sumc[y * cx->xblocks + x] = 0;
423                 }
424         }
425
426         /* Find the best field match. Subsample the frames for speed. */
427         currbot0  = fcrp + cx->pitch;
428         currbot2  = fcrp + 3 * cx->pitch;
429         currtop0 = fcrp;
430         currtop2 = fcrp + 2 * cx->pitch;
431         currtop4 = fcrp + 4 * cx->pitch;
432         prevbot0  = fprp + cx->pitch;
433         prevbot2  = fprp + 3 * cx->pitch;
434         prevtop0 = fprp;
435         prevtop2 = fprp + 2 * cx->pitch;
436         prevtop4 = fprp + 4 * cx->pitch;
437         if ( cx->tff )
438         {
439                 a0 = prevbot0;
440                 a2 = prevbot2;
441                 b0 = currtop0;
442                 b2 = currtop2;
443                 b4 = currtop4;
444         }
445         else
446         {
447                 a0 = currbot0;
448                 a2 = currbot2;
449                 b0 = prevtop0;
450                 b2 = prevtop2;
451                 b4 = prevtop4;
452         }
453         p = c = 0;
454
455         // Calculate the field match and film/video metrics.
456 //      if (vi.IsYV12()) skip = 1;
457 //      else 
458         skip = 1 + ( !cx->chroma );
459         for (y = 0, index = 0; y < cx->h - 4; y+=4)
460         {
461                 /* Exclusion band. Good for ignoring subtitles. */
462                 if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1)
463                 {
464                         for (x = 0; x < cx->w;)
465                         {
466 //                              if (vi.IsYV12())
467 //                                      index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
468 //                              else
469                                         index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2;
470
471                                 // Test combination with current frame.
472                                 tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
473 //                              diff = abs((long)currtop0[x] - (tmp1 >> 1));
474                                 diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
475                                 if (diff > cx->nt)
476                                 {
477                                         c += diff;
478 #ifdef WINDOWED_MATCH
479                                         matchc[index] += diff;
480 #endif
481                                 }
482
483                                 tmp1 = currbot0[x] + T;
484                                 tmp2 = currbot0[x] - T;
485                                 vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
486                                          (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
487                                 if (vc)
488                                 {
489                                         cx->sumc[index]++;
490                                 }
491
492                                 // Test combination with previous frame.
493                                 tmp1 = ((long)a0[x] + (long)a2[x]);
494                                 diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
495                                 if (diff > cx->nt)
496                                 {
497                                         p += diff;
498 #ifdef WINDOWED_MATCH
499                                         matchp[index] += diff;
500 #endif
501                                 }
502
503                                 tmp1 = a0[x] + T;
504                                 tmp2 = a0[x] - T;
505                                 vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
506                                          (tmp2 > b0[x] && tmp2 > b2[x]);
507                                 if (vc)
508                                 {
509                                         cx->sump[index]++;
510                                 }
511
512                                 x += skip;
513                                 if (!(x&3)) x += 4;
514                         }
515                 }
516                 currbot0 += cx->pitchtimes4;
517                 currbot2 += cx->pitchtimes4;
518                 currtop0 += cx->pitchtimes4;
519                 currtop2 += cx->pitchtimes4;
520                 currtop4 += cx->pitchtimes4;
521                 a0               += cx->pitchtimes4;
522                 a2               += cx->pitchtimes4;
523                 b0               += cx->pitchtimes4;
524                 b2               += cx->pitchtimes4;
525                 b4               += cx->pitchtimes4;
526         }
527
528 //      if (vi.IsYV12() && chroma == true)
529 //      {
530 //              int z;
531 //
532 //              for (z = 0; z < 2; z++)
533 //              {
534 //                      // Do the same for the U plane.
535 //                      if (z == 0)
536 //                      {
537 //                              currbot0  = fcrpU + pitchover2;
538 //                              currbot2  = fcrpU + 3 * pitchover2;
539 //                              currtop0 = fcrpU;
540 //                              currtop2 = fcrpU + 2 * pitchover2;
541 //                              currtop4 = fcrpU + 4 * pitchover2;
542 //                              prevbot0  = fprpU + pitchover2;
543 //                              prevbot2  = fprpU + 3 * pitchover2;
544 //                              prevtop0 = fprpU;
545 //                              prevtop2 = fprpU + 2 * pitchover2;
546 //                              prevtop4 = fprpU + 4 * pitchover2;
547 //                      }
548 //                      else
549 //                      {
550 //                              currbot0  = fcrpV + pitchover2;
551 //                              currbot2  = fcrpV + 3 * pitchover2;
552 //                              currtop0 = fcrpV;
553 //                              currtop2 = fcrpV + 2 * pitchover2;
554 //                              currtop4 = fcrpV + 4 * pitchover2;
555 //                              prevbot0  = fprpV + pitchover2;
556 //                              prevbot2  = fprpV + 3 * pitchover2;
557 //                              prevtop0 = fprpV;
558 //                              prevtop2 = fprpV + 2 * pitchover2;
559 //                              prevtop4 = fprpV + 4 * pitchover2;
560 //                      }
561 //                      if (tff == true)
562 //                      {
563 //                              a0 = prevbot0;
564 //                              a2 = prevbot2;
565 //                              b0 = currtop0;
566 //                              b2 = currtop2;
567 //                              b4 = currtop4;
568 //                      }
569 //                      else
570 //                      {
571 //                              a0 = currbot0;
572 //                              a2 = currbot2;
573 //                              b0 = prevtop0;
574 //                              b2 = prevtop2;
575 //                              b4 = prevtop4;
576 //                      }
577 //
578 //                      for (y = 0, index = 0; y < hover2 - 4; y+=4)
579 //                      {
580 //                              /* Exclusion band. Good for ignoring subtitles. */
581 //                              if (y0 == y1 || y < y0/2 || y > y1/2)
582 //                              {
583 //                                      for (x = 0; x < wover2;)
584 //                                      {
585 //                                              if (vi.IsYV12())
586 //                                                      index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
587 //                                              else
588 //                                                      index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2;
589 //
590 //                                              // Test combination with current frame.
591 //                                              tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
592 //                                              diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
593 //                                              if (diff > nt)
594 //                                              {
595 //                                                      c += diff;
596 //#ifdef WINDOWED_MATCH
597 //                                                      matchc[index] += diff;
598 //#endif
599 //                                              }
600 //
601 //                                              tmp1 = currbot0[x] + T;
602 //                                              tmp2 = currbot0[x] - T;
603 //                                              vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
604 //                                                       (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
605 //                                              if (vc)
606 //                                              {
607 //                                                      sumc[index]++;
608 //                                              }
609 //
610 //                                              // Test combination with previous frame.
611 //                                              tmp1 = ((long)a0[x] + (long)a2[x]);
612 //                                              diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
613 //                                              if (diff > nt)
614 //                                              {
615 //                                                      p += diff;
616 //#ifdef WINDOWED_MATCH
617 //                                                      matchp[index] += diff;
618 //#endif
619 //                                              }
620 //
621 //                                              tmp1 = a0[x] + T;
622 //                                              tmp2 = a0[x] - T;
623 //                                              vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
624 //                                                       (tmp2 > b0[x] && tmp2 > b2[x]);
625 //                                              if (vc)
626 //                                              {
627 //                                                      sump[index]++;
628 //                                              }
629 //
630 //                                              x ++;
631 //                                              if (!(x&3)) x += 4;
632 //                                      }
633 //                              }
634 //                              currbot0 += 4*pitchover2;
635 //                              currbot2 += 4*pitchover2;
636 //                              currtop0 += 4*pitchover2;
637 //                              currtop2 += 4*pitchover2;
638 //                              currtop4 += 4*pitchover2;
639 //                              a0               += 4*pitchover2;
640 //                              a2               += 4*pitchover2;
641 //                              b0               += 4*pitchover2;
642 //                              b2               += 4*pitchover2;
643 //                              b4               += 4*pitchover2;
644 //                      }
645 //              }
646 //      }
647 //
648 //      // Now find the blocks that have the greatest differences.
649 //#ifdef WINDOWED_MATCH
650 //      highest_matchp = 0;
651 //      for (y = 0; y < yblocks; y++)
652 //      {
653 //              for (x = 0; x < xblocks; x++)
654 //              {
655 //if (frame == 45 && matchp[y * xblocks + x] > 2500)
656 //{
657 //      sprintf(buf, "%d/%d = %d\n", x, y, matchp[y * xblocks + x]);
658 //      OutputDebugString(buf);
659 //}
660 //                      if (matchp[y * xblocks + x] > highest_matchp)
661 //                      {
662 //                              highest_matchp = matchp[y * xblocks + x];
663 //                      }
664 //              }
665 //      }
666 //      highest_matchc = 0;
667 //      for (y = 0; y < yblocks; y++)
668 //      {
669 //              for (x = 0; x < xblocks; x++)
670 //              {
671 //if (frame == 44 && matchc[y * xblocks + x] > 2500)
672 //{
673 //      sprintf(buf, "%d/%d = %d\n", x, y, matchc[y * xblocks + x]);
674 //      OutputDebugString(buf);
675 //}
676 //                      if (matchc[y * xblocks + x] > highest_matchc)
677 //                      {
678 //                              highest_matchc = matchc[y * xblocks + x];
679 //                      }
680 //              }
681 //      }
682 //#endif
683         if ( cx->post )
684         {
685                 cx->highest_sump = 0;
686                 for (y = 0; y < cx->yblocks; y++)
687                 {
688                         for (x = 0; x < cx->xblocks; x++)
689                         {
690                                 if (cx->sump[y * cx->xblocks + x] > cx->highest_sump)
691                                 {
692                                         cx->highest_sump = cx->sump[y * cx->xblocks + x];
693                                 }
694                         }
695                 }
696                 cx->highest_sumc = 0;
697                 for (y = 0; y < cx->yblocks; y++)
698                 {
699                         for (x = 0; x < cx->xblocks; x++)
700                         {
701                                 if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc)
702                                 {
703                                         cx->highest_sumc = cx->sumc[y * cx->xblocks + x];
704                                 }
705                         }
706                 }
707         }
708 #ifdef WINDOWED_MATCH
709         CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc);
710 #else
711         CacheInsert( cx, frame, p, cx->highest_sump, c, cx->highest_sumc);
712 #endif
713 }
714
715
716 /** Process the image.
717 */
718
719 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
720 {
721         // Get the filter service
722         mlt_filter filter = mlt_frame_pop_service( frame );
723         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
724         mlt_properties frame_properties = mlt_frame_properties( frame );
725         context cx = mlt_properties_get_data( properties, "context", NULL );
726         mlt_service producer = mlt_service_producer( mlt_filter_service( filter ) );
727         cx->out = producer? mlt_producer_get_playtime( MLT_PRODUCER( producer ) ) : 999999;
728
729         if ( ! cx->is_configured )
730         {
731                 cx->back = mlt_properties_get_int( properties, "back" );
732                 cx->chroma = mlt_properties_get_int( properties, "chroma" );
733                 cx->guide = mlt_properties_get_int( properties, "guide" );
734                 cx->gthresh = mlt_properties_get_double( properties, "gthresh" );
735                 cx->post = mlt_properties_get_int( properties, "post" );
736                 cx->vthresh = mlt_properties_get_double( properties, "vthresh" );
737                 cx->bthresh = mlt_properties_get_double( properties, "bthresh" );
738                 cx->dthresh = mlt_properties_get_double( properties, "dthresh" );
739                 cx->blend = mlt_properties_get_int( properties, "blend" );
740                 cx->nt = mlt_properties_get_int( properties, "nt" );
741                 cx->y0 = mlt_properties_get_int( properties, "y0" );
742                 cx->y1 = mlt_properties_get_int( properties, "y1" );
743                 cx->hints = mlt_properties_get_int( properties, "hints" );
744                 cx->debug = mlt_properties_get_int( properties, "debug" );
745                 cx->show = mlt_properties_get_int( properties, "show" );
746         }
747
748         // Get the image
749         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
750
751         if ( ! cx->sump )
752         {
753                 int guide = mlt_properties_get_int( properties, "guide" );
754                 cx->cycle = 0;
755                 if ( guide == GUIDE_32 )
756                 {
757                         // 24fps to 30 fps telecine.
758                         cx->cycle = 5;
759                 }
760                 else if ( guide == GUIDE_22 )
761                 {
762                         // PAL guidance (expect the current match to be continued).
763                         cx->cycle = 2;
764                 }
765                 else if ( guide == GUIDE_32322 )
766                 {
767                         // 25fps to 30 fps telecine.
768                         cx->cycle = 6;
769                 }
770
771                 cx->xblocks = (*width+BLKSIZE-1) / BLKSIZE;
772                 cx->yblocks = (*height+BLKSIZE-1) / BLKSIZE;
773                 cx->sump = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
774                 cx->sumc = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
775                 mlt_properties_set_data( properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
776                 mlt_properties_set_data( properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
777                 cx->tff = mlt_properties_get_int( frame_properties, "top_field_first" );
778         //      fprintf(stderr, "%s: TOP FIELD FIRST %d\n", __FUNCTION__, cx->tff );
779         }
780
781         // Only process if we have no error and a valid colour space
782         if ( error == 0 && *format == mlt_image_yuv422 )
783         {
784                 // Put the current image into the image cache, keyed on position
785                 size_t image_size = (*width * *height) << 1;
786                 mlt_position pos = mlt_frame_get_position( frame );
787                 uint8_t *image_copy = mlt_pool_alloc( image_size );
788                 memcpy( image_copy, *image, image_size );
789                 char key[20];
790                 sprintf( key, "%d", pos );
791                 mlt_properties_set_data( cx->image_cache, key, image_copy, image_size, (mlt_destructor)mlt_pool_release, NULL );
792                 
793                 // Only if we have enough frame images cached
794                 if ( pos > 1 && pos > cx->cycle + 1 )
795                 {
796                         pos -= cx->cycle + 1;
797                         // Get the current frame image
798                         sprintf( key, "%d", pos );
799                         cx->fcrp = mlt_properties_get_data( cx->image_cache, key, NULL );
800                         if (!cx->fcrp) return error;
801                          
802                         // Get the previous frame image
803                         cx->pframe = pos == 0 ? 0 : pos - 1;
804                         sprintf( key, "%d", cx->pframe );
805                         cx->fprp = mlt_properties_get_data( cx->image_cache, key, NULL );
806                         if (!cx->fprp) return error;
807                         
808                         // Get the next frame image
809                         cx->nframe = pos > cx->out ? cx->out : pos + 1;
810                         sprintf( key, "%d", cx->nframe );
811                         cx->fnrp = mlt_properties_get_data( cx->image_cache, key, NULL );
812                         if (!cx->fnrp) return error;
813                         
814                         cx->pitch = *width << 1;
815                         cx->pitchover2 = cx->pitch >> 1;
816                         cx->pitchtimes4 = cx->pitch << 2;
817                         cx->w = *width << 1;
818                         cx->h = *height;
819                         if ((cx->w/2) & 1)
820                                 fprintf( stderr, "%s: width must be a multiple of 2\n", __FUNCTION__ );
821                         if (cx->h & 1)
822                                 fprintf( stderr, "%s: height must be a multiple of 2\n", __FUNCTION__ );
823                         cx->wover2 = cx->w/2;
824                         cx->hover2 = cx->h/2;
825                         cx->hplus1over2 = (cx->h+1)/2;
826                         cx->hminus2 = cx->h - 2;
827                         cx->dpitch = cx->pitch;
828                         
829                         // Ensure that the metrics for the frames
830                         // after the current frame are in the cache. They will be used for
831                         // pattern guidance.
832                         if ( cx->guide )
833                         {
834                                 for ( cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++ )
835                                 {
836                                         if ( ! CacheQuery( cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
837                                         {
838                                                 sprintf( key, "%d", cx->y );
839                                                 cx->crp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
840                                                 sprintf( key, "%d", cx->y ? cx->y - 1 : 1 );
841                                                 cx->prp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
842                                                 CalculateMetrics( cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL );                                        }
843                                 }
844                         }
845                         
846                         // Check for manual overrides of the field matching.
847                         cx->found = 0;
848                         cx->film = 1;
849                         cx->override = 0;
850                         cx->inpattern = 0;
851                         cx->vthresh = cx->vthresh;
852                         cx->back = cx->back_saved;
853                         
854                         // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates.
855                         if ( ! CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
856                         {
857                                 CalculateMetrics( cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL );
858                                 CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock );
859                         }                               
860                         if ( ! CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ) )
861                         {
862                                 CalculateMetrics( cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL );
863                                 CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock );
864                         }                               
865                         
866                         // Determine the best candidate match.
867                         if ( !cx->found )
868                         {
869                                 cx->lowest = cx->c;
870                                 cx->chosen = C;
871                                 if ( cx->back == ALWAYS_BACK && cx->p < cx->lowest )
872                                 {
873                                         cx->lowest = cx->p;
874                                         cx->chosen = P;
875                                 }
876                                 if ( cx->np < cx->lowest )
877                                 {
878                                         cx->lowest = cx->np;
879                                         cx->chosen = N;
880                                 }
881                         }
882                         if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N))
883                         {
884                                 cx->chosen = C;
885                                 cx->lowest = cx->c;
886                         }
887
888                         // See if we can apply pattern guidance.
889                         cx->mismatch = 100.0;
890                         if ( cx->guide )
891                         {
892                                 cx->hard = 0;
893                                 if ( pos >= cx->cycle && PredictHardYUY2( cx, pos, &cx->predicted, &cx->predicted_metric) )
894                                 {
895                                         cx->inpattern = 1;
896                                         cx->mismatch = 0.0;
897                                         cx->hard = 1;
898                                         if ( cx->chosen != cx->predicted )
899                                         {
900                                                 // The chosen frame doesn't match the prediction.
901                                                 if ( cx->predicted_metric == 0 )
902                                                         cx->mismatch = 0.0;
903                                                 else
904                                                         cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest ) ) / cx->predicted_metric;
905                                                 if ( cx->mismatch < cx->gthresh )
906                                                 {
907                                                         // It's close enough, so use the predicted one.
908                                                         if ( !cx->found )
909                                                         {
910                                                                 cx->chosen = cx->predicted;
911                                                                 cx->override = 1;
912                                                         }
913                                                 }
914                                                 else
915                                                 {
916                                                         cx->hard = 0;
917                                                         cx->inpattern = 0;
918                                                 }
919                                         }
920                                 }
921                 
922                                 if ( !cx->hard && cx->guide != GUIDE_22 )
923                                 {
924                                         int i;
925                                         struct PREDICTION *pred = PredictSoftYUY2( cx, pos );
926                 
927                                         if ( ( pos <= cx->out - cx->cycle) && ( pred[0].metric != 0xffffffff ) )
928                                         {
929                                                 // Apply pattern guidance.
930                                                 // If the predicted match metric is within defined percentage of the
931                                                 // best calculated one, then override the calculated match with the
932                                                 // predicted match.
933                                                 i = 0;
934                                                 while ( pred[i].metric != 0xffffffff )
935                                                 {
936                                                         cx->predicted = pred[i].predicted;
937                                                         cx->predicted_metric = pred[i].predicted_metric;
938 #ifdef DEBUG_PATTERN_GUIDANCE
939                                                         fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted);
940 #endif
941                                                         if ( cx->chosen != cx->predicted )
942                                                         {
943                                                                 // The chosen frame doesn't match the prediction.
944                                                                 if ( cx->predicted_metric == 0 )
945                                                                         cx->mismatch = 0.0;
946                                                                 else
947                                                                         cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest )) / cx->predicted_metric;
948                                                                 if ( (int) cx->mismatch <= cx->gthresh )
949                                                                 {
950                                                                         // It's close enough, so use the predicted one.
951                                                                         if ( !cx->found )
952                                                                         {
953                                                                                 cx->chosen = cx->predicted;
954                                                                                 cx->override = 1;
955                                                                         }
956                                                                         cx->inpattern = 1;
957                                                                         break;
958                                                                 }
959                                                                 else
960                                                                 {
961                                                                         // Looks like we're not in a predictable pattern.
962                                                                         cx->inpattern = 0;
963                                                                 }
964                                                         }
965                                                         else
966                                                         {
967                                                                 cx->inpattern = 1;
968                                                                 cx->mismatch = 0.0;
969                                                                 break;
970                                                         }
971                                                         i++;
972                                                 }
973                                         }
974                                 }
975                         }
976
977                         // Check the match for progressive versus interlaced.
978                         if ( cx->post )
979                         {
980                                 if (cx->chosen == P) cx->vmetric = cx->pblock;
981                                 else if (cx->chosen == C) cx->vmetric = cx->cblock;
982                                 else if (cx->chosen == N) cx->vmetric = cx->npblock;
983                 
984                                 if ( !cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest )
985                                 {
986                                         // Backward match.
987                                         cx->vmetric = cx->pblock;
988                                         cx->chosen = P;
989                                         cx->inpattern = 0;
990                                         cx->mismatch = 100;
991                                 }
992                                 if ( cx->vmetric > cx->vthresh )
993                                 {
994                                         // After field matching and pattern guidance the frame is still combed.
995                                         cx->film = 0;
996                                         if ( !cx->found && ( cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP ) )
997                                         {
998                                                 cx->chosen = C;
999                                                 cx->vmetric = cx->cblock;
1000                                                 cx->inpattern = 0;
1001                                                 cx->mismatch = 100;
1002                                         }
1003                                 }
1004                         }
1005                         cx->vthresh = cx->vthresh_saved;
1006                 
1007                         // Setup strings for debug info.
1008                         if ( cx->inpattern && !cx->override ) strcpy( cx->status, "[in-pattern]" );
1009                         else if ( cx->inpattern && cx->override ) strcpy( cx->status, "[in-pattern*]" );
1010                         else strcpy( cx->status, "[out-of-pattern]" );
1011         
1012                         // Assemble and output the reconstructed frame according to the final match.
1013                         cx->dstp = *image;
1014                         if ( cx->chosen == N )
1015                         {
1016                                 // The best match was with the next frame.
1017                                 if ( cx->tff )
1018                                 {
1019                                         BitBlt( cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2 );
1020                                         BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
1021                                 }
1022                                 else
1023                                 {
1024                                         BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
1025                                         BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
1026                                 }
1027                         }
1028                         else if ( cx->chosen == C )
1029                         {
1030                                 // The best match was with the current frame.
1031                                 BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
1032                                 BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
1033                         }
1034                         else if ( ! cx->tff )
1035                         {
1036                                 // The best match was with the previous frame.
1037                                 BitBlt( cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
1038                                 BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
1039                         }
1040                         else
1041                         {
1042                                 // The best match was with the previous frame.
1043                                 BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
1044                                 BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
1045                         }
1046                         if ( cx->guide )
1047                                 PutChosen( cx, pos, cx->chosen );
1048
1049                         if ( !cx->post || cx->post == POST_METRICS )
1050                         {
1051                                 if ( cx->force == '+') cx->film = 0;
1052                                 else if ( cx->force == '-' ) cx->film = 1;
1053                         }
1054                         else if ((cx->force == '+') ||
1055                                 ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP)
1056                                          && (cx->film == 0 && cx->force != '-')))
1057                         {
1058                                 unsigned char *dstpp, *dstpn;
1059                                 int v1, v2;
1060                 
1061                                 if ( cx->blend )
1062                                 {
1063                                         // Do first and last lines.
1064                                         uint8_t *final = mlt_pool_alloc( image_size );
1065                                         cx->finalp = final;
1066                                         mlt_properties_set_data( frame_properties, "image", final, image_size, (mlt_destructor)mlt_pool_release, NULL );
1067                                         dstpn = cx->dstp + cx->dpitch;
1068                                         for ( cx->x = 0; cx->x < cx->w; cx->x++ )
1069                                         {
1070                                                 cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpn[cx->x]) >> 1);
1071                                         }
1072                                         cx->finalp = final + (cx->h-1)*cx->dpitch;
1073                                         cx->dstp = *image + (cx->h-1)*cx->dpitch;
1074                                         dstpp = cx->dstp - cx->dpitch;
1075                                         for ( cx->x = 0; cx->x < cx->w; cx->x++ )
1076                                         {
1077                                                 cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpp[cx->x]) >> 1);
1078                                         }
1079                                         // Now do the rest.
1080                                         cx->dstp = *image + cx->dpitch;
1081                                         dstpp = cx->dstp - cx->dpitch;
1082                                         dstpn = cx->dstp + cx->dpitch;
1083                                         cx->finalp = final + cx->dpitch;
1084                                         for ( cx->y = 1; cx->y < cx->h - 1; cx->y++ )
1085                                         {
1086                                                 for ( cx->x = 0; cx->x < cx->w; cx->x++ )
1087                                                 {
1088                                                         v1 = (int) cx->dstp[cx->x] - cx->dthresh;
1089                                                         if ( v1 < 0 )
1090                                                                 v1 = 0; 
1091                                                         v2 = (int) cx->dstp[cx->x] + cx->dthresh;
1092                                                         if (v2 > 235) v2 = 235; 
1093                                                         if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
1094                                                         {
1095                                                                 if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
1096                                                                 {
1097                                                                         if (cx->x & 1) cx->finalp[cx->x] = 128;
1098                                                                         else cx->finalp[cx->x] = 235;
1099                                                                 }
1100                                                                 else
1101                                                                         cx->finalp[cx->x] = ((int)dstpp[cx->x] + (int)dstpn[cx->x] + (int)cx->dstp[cx->x] + (int)cx->dstp[cx->x]) >> 2;
1102                                                         }
1103                                                         else cx->finalp[cx->x] = cx->dstp[cx->x];
1104                                                 }
1105                                                 cx->finalp += cx->dpitch;
1106                                                 cx->dstp += cx->dpitch;
1107                                                 dstpp += cx->dpitch;
1108                                                 dstpn += cx->dpitch;
1109                                         }
1110                 
1111                 
1112                                         if (cx->show ) Show( cx, pos, frame_properties);
1113                                         if (cx->debug) Debug(cx, pos);
1114                                         if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
1115                                         goto final;
1116                                 }
1117                 
1118                                 // Interpolate mode.
1119                                 cx->dstp = *image + cx->dpitch;
1120                                 dstpp = cx->dstp - cx->dpitch;
1121                                 dstpn = cx->dstp + cx->dpitch;
1122                                 for ( cx->y = 1; cx->y < cx->h - 1; cx->y+=2 )
1123                                 {
1124                                         for ( cx->x = 0; cx->x < cx->w; cx->x++ )
1125                                         {
1126                                                 v1 = (int) cx->dstp[cx->x] - cx->dthresh;
1127                                                 if (v1 < 0) v1 = 0; 
1128                                                 v2 = (int) cx->dstp[cx->x] + cx->dthresh;
1129                                                 if (v2 > 235) v2 = 235; 
1130                                                 if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
1131                                                 {
1132                                                         if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
1133                                                         {
1134                                                                 if (cx->x & 1) cx->dstp[cx->x] = 128;
1135                                                                 else cx->dstp[cx->x] = 235;
1136                                                         }
1137                                                         else
1138                                                                 cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1;
1139                                                 }
1140                                         }
1141                                         cx->dstp += 2 * cx->dpitch;
1142                                         dstpp += 2 * cx->dpitch;
1143                                         dstpn += 2 * cx->dpitch;
1144                                 }
1145                         }
1146                         if (cx->show ) Show( cx, pos, frame_properties);
1147                         if (cx->debug) Debug(cx, pos);
1148                         if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
1149
1150 final:                  
1151                         // Flush frame at tail of period from the cache
1152                         sprintf( key, "%d", pos - 1 );
1153                         mlt_properties_set_data( cx->image_cache, key, NULL, 0, NULL, NULL );
1154                 }
1155                 else
1156                 {
1157                         // Signal the first {cycle} frames as invalid
1158                         mlt_properties_set_int( frame_properties, "garbage", 1 );
1159                 }
1160         }
1161         else if ( error == 0 && *format == mlt_image_yuv420p )
1162         {
1163                 fprintf(stderr,"%s: %d pos %d\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) );
1164         }
1165
1166         return error;
1167 }
1168
1169 /** Process the frame object.
1170 */
1171
1172 static mlt_frame process( mlt_filter this, mlt_frame frame )
1173 {
1174         // Push the filter on to the stack
1175         mlt_frame_push_service( frame, this );
1176
1177         // Push the frame filter
1178         mlt_frame_push_get_image( frame, get_image );
1179
1180         return frame;
1181 }
1182
1183 /** Constructor for the filter.
1184 */
1185
1186 mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
1187 {
1188         mlt_filter this = mlt_filter_new( );
1189         if ( this != NULL )
1190         {
1191                 this->process = process;
1192
1193                 // Allocate the context and set up for garbage collection               
1194                 context cx = (context) mlt_pool_alloc( sizeof(struct context_s) );
1195                 memset( cx, 0, sizeof( struct context_s ) );
1196                 mlt_properties properties = MLT_FILTER_PROPERTIES( this );
1197                 mlt_properties_set_data( properties, "context", cx, sizeof(struct context_s), (mlt_destructor)mlt_pool_release, NULL );
1198
1199                 // Allocate the metrics cache and set up for garbage collection
1200                 cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY ));
1201                 mlt_properties_set_data( properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor)mlt_pool_release, NULL );
1202                 int i;
1203                 for (i = 0; i < CACHE_SIZE; i++)
1204                 {
1205                         cx->cache[i].frame = 0xffffffff;
1206                         cx->cache[i].chosen = 0xff;
1207                 }
1208                 
1209                 // Allocate the image cache and set up for garbage collection
1210                 cx->image_cache = mlt_properties_new();
1211                 mlt_properties_set_data( properties, "image_cache", cx->image_cache, 0, (mlt_destructor)mlt_properties_close, NULL );
1212                 
1213                 // Initialize the parameter defaults
1214                 mlt_properties_set_int( properties, "guide", 0 );
1215                 mlt_properties_set_int( properties, "back", 0 );
1216                 mlt_properties_set_int( properties, "chroma", 0 );
1217                 mlt_properties_set_int( properties, "post", POST_FULL );
1218                 mlt_properties_set_double( properties, "gthresh", 10.0 );
1219                 mlt_properties_set_double( properties, "vthresh", 50.0 );
1220                 mlt_properties_set_double( properties, "bthresh", 50.0 );
1221                 mlt_properties_set_double( properties, "dthresh", 7.0 );
1222                 mlt_properties_set_int( properties, "blend", 0 );
1223                 mlt_properties_set_int( properties, "nt", 10 );
1224                 mlt_properties_set_int( properties, "y0", 0 );
1225                 mlt_properties_set_int( properties, "y1", 0 );
1226                 mlt_properties_set_int( properties, "hints", 1 );
1227         }
1228         return this;
1229 }
1230