modules/dgraft: added module for ports of Donald Graft's GPL filters.
[melted] / 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