4 * Copyright (C) Georg Martius - June 2007
5 * georg dot martius at web dot de
7 * This file is part of transcode, a video stream processing tool
9 * transcode is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * transcode is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with GNU Make; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 * transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi
26 #include "transform_image.h"
29 #include <framework/mlt_log.h>
31 #define MAX (a,b) (((a)>(b)?(a):(b)))
32 #define MIN (a,b) (((a)<(b)?(a):(b)))
33 #define CLAMP(a,x,y) MIN( MAX( (a),(x) ,y))
35 #define TC_MAX(a, b) (((a) > (b)) ?(a) :(b))
36 #define TC_MIN(a, b) (((a) < (b)) ?(a) :(b))
37 /* clamp x between a and b */
38 #define TC_CLAMP(x, a, b) TC_MIN(TC_MAX((a), (x)), (b))
41 static const char* interpoltypes[5] = {"No (0)", "Linear (1)", "Bi-Linear (2)",
42 "Quadratic (3)", "Bi-Cubic (4)"};
44 void (*interpolate)(unsigned char *rv, float x, float y,
45 unsigned char* img, int width, int height,
46 unsigned char def) = 0;
48 /** interpolateBiLinBorder: bi-linear interpolation function that also works at the border.
49 This is used by many other interpolation methods at and outsize the border, see interpolate */
50 void interpolateBiLinBorder(unsigned char *rv, float x, float y,
51 unsigned char* img, int width, int height,
58 short v1 = PIXEL(img, x_c, y_c, width, height, def);
59 short v2 = PIXEL(img, x_c, y_f, width, height, def);
60 short v3 = PIXEL(img, x_f, y_c, width, height, def);
61 short v4 = PIXEL(img, x_f, y_f, width, height, def);
62 float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
63 (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
64 *rv = (unsigned char)s;
67 /* taken from http://en.wikipedia.org/wiki/Bicubic_interpolation for alpha=-0.5
69 a0-a3 are the neigthboring points where the target point is between a1 and a2
70 t is the point of interpolation (position between a1 and a2) value between 0 and 1
73 (1,t,t^2,t^3) | 2,-5, 4,-1 | |a2|
76 static short bicub_kernel(float t, short a0, short a1, short a2, short a3){
77 return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2;
80 /** interpolateBiCub: bi-cubic interpolation function using 4x4 pixel, see interpolate */
81 void interpolateBiCub(unsigned char *rv, float x, float y,
82 unsigned char* img, int width, int height, unsigned char def)
84 // do a simple linear interpolation at the border
85 if (x < 1 || x > width-2 || y < 1 || y > height - 2) {
86 interpolateBiLinBorder(rv, x,y,img,width,height,def);
91 short v1 = bicub_kernel(tx,
92 PIX(img, x_f-1, y_f-1, width, height),
93 PIX(img, x_f, y_f-1, width, height),
94 PIX(img, x_f+1, y_f-1, width, height),
95 PIX(img, x_f+2, y_f-1, width, height));
96 short v2 = bicub_kernel(tx,
97 PIX(img, x_f-1, y_f, width, height),
98 PIX(img, x_f, y_f, width, height),
99 PIX(img, x_f+1, y_f, width, height),
100 PIX(img, x_f+2, y_f, width, height));
101 short v3 = bicub_kernel(tx,
102 PIX(img, x_f-1, y_f+1, width, height),
103 PIX(img, x_f, y_f+1, width, height),
104 PIX(img, x_f+1, y_f+1, width, height),
105 PIX(img, x_f+2, y_f+1, width, height));
106 short v4 = bicub_kernel(tx,
107 PIX(img, x_f-1, y_f+2, width, height),
108 PIX(img, x_f, y_f+2, width, height),
109 PIX(img, x_f+1, y_f+2, width, height),
110 PIX(img, x_f+2, y_f+2, width, height));
111 *rv = (unsigned char)bicub_kernel(y-y_f, v1, v2, v3, v4);
115 /** interpolateSqr: bi-quatratic interpolation function, see interpolate */
116 void interpolateSqr(unsigned char *rv, float x, float y,
117 unsigned char* img, int width, int height, unsigned char def)
119 if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
120 interpolateBiLinBorder(rv, x, y, img, width, height, def);
122 int x_f = myfloor(x);
124 int y_f = myfloor(y);
126 short v1 = PIX(img, x_c, y_c, width, height);
127 short v2 = PIX(img, x_c, y_f, width, height);
128 short v3 = PIX(img, x_f, y_c, width, height);
129 short v4 = PIX(img, x_f, y_f, width, height);
130 float f1 = 1 - sqrt((x_c - x) * (y_c - y));
131 float f2 = 1 - sqrt((x_c - x) * (y - y_f));
132 float f3 = 1 - sqrt((x - x_f) * (y_c - y));
133 float f4 = 1 - sqrt((x - x_f) * (y - y_f));
134 float s = (v1*f1 + v2*f2 + v3*f3+ v4*f4)/(f1 + f2 + f3 + f4);
135 *rv = (unsigned char)s;
139 /** interpolateBiLin: bi-linear interpolation function, see interpolate */
140 void interpolateBiLin(unsigned char *rv, float x, float y,
141 unsigned char* img, int width, int height,
144 if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
145 interpolateBiLinBorder(rv, x, y, img, width, height, def);
147 int x_f = myfloor(x);
149 int y_f = myfloor(y);
151 short v1 = PIX(img, x_c, y_c, width, height);
152 short v2 = PIX(img, x_c, y_f, width, height);
153 short v3 = PIX(img, x_f, y_c, width, height);
154 short v4 = PIX(img, x_f, y_f, width, height);
155 float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
156 (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
157 *rv = (unsigned char)s;
162 /** interpolateLin: linear (only x) interpolation function, see interpolate */
163 void interpolateLin(unsigned char *rv, float x, float y,
164 unsigned char* img, int width, int height,
167 int x_f = myfloor(x);
169 int y_n = myround(y);
170 float v1 = PIXEL(img, x_c, y_n, width, height, def);
171 float v2 = PIXEL(img, x_f, y_n, width, height, def);
172 float s = v1*(x - x_f) + v2*(x_c - x);
173 *rv = (unsigned char)s;
176 /** interpolateZero: nearest neighbor interpolation function, see interpolate */
177 void interpolateZero(unsigned char *rv, float x, float y,
178 unsigned char* img, int width, int height, unsigned char def)
180 int x_n = myround(x);
181 int y_n = myround(y);
182 *rv = (unsigned char) PIXEL(img, x_n, y_n, width, height, def);
187 * interpolateN: Bi-linear interpolation function for N channel image.
190 * rv: destination pixel (call by reference)
191 * x,y: the source coordinates in the image img. Note this
192 * are real-value coordinates, that's why we interpolate
194 * width,height: dimension of image
195 * N: number of channels
196 * channel: channel number (0..N-1)
197 * def: default value if coordinates are out of range
200 void interpolateN(unsigned char *rv, float x, float y,
201 unsigned char* img, int width, int height,
202 unsigned char N, unsigned char channel,
205 if (x < - 1 || x > width || y < -1 || y > height) {
208 int x_f = myfloor(x);
210 int y_f = myfloor(y);
212 short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def);
213 short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def);
214 short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def);
215 short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
216 float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
217 (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
218 *rv = (unsigned char)s;
224 * transformRGB: applies current transformation to frame
226 * td: private data structure of this filter
228 * 0 for failture, 1 for success
230 * The frame must be in RGB format
232 int transformRGB(TransformData* td)
235 int x = 0, y = 0, z = 0;
236 unsigned char *D_1, *D_2;
237 t = td->trans[td->current_trans];
241 float c_s_x = td->width_src/2.0;
242 float c_s_y = td->height_src/2.0;
243 float c_d_x = td->width_dest/2.0;
244 float c_d_y = td->height_dest/2.0;
246 /* for each pixel in the destination image we calc the source
247 * coordinate and make an interpolation:
248 * p_d = c_d + M(p_s - c_s) + t
249 * where p are the points, c the center coordinate,
250 * _s source and _d destination,
251 * t the translation, and M the rotation matrix
252 * p_s = M^{-1}(p_d - c_d - t) + c_s
255 if (fabs(t.alpha) > td->rotation_threshhold) {
256 for (x = 0; x < td->width_dest; x++) {
257 for (y = 0; y < td->height_dest; y++) {
258 float x_d1 = (x - c_d_x);
259 float y_d1 = (y - c_d_y);
260 float x_s = cos(-t.alpha) * x_d1
261 + sin(-t.alpha) * y_d1 + c_s_x -t.x;
262 float y_s = -sin(-t.alpha) * x_d1
263 + cos(-t.alpha) * y_d1 + c_s_y -t.y;
264 for (z = 0; z < 3; z++) { // iterate over colors
265 unsigned char* dest = &D_2[(x + y * td->width_dest)*3+z];
266 interpolateN(dest, x_s, y_s, D_1,
267 td->width_src, td->height_src,
268 3, z, td->crop ? 16 : *dest);
273 /* no rotation, just translation
274 *(also no interpolation, since no size change (so far)
276 int round_tx = myround(t.x);
277 int round_ty = myround(t.y);
278 for (x = 0; x < td->width_dest; x++) {
279 for (y = 0; y < td->height_dest; y++) {
280 for (z = 0; z < 3; z++) { // iterate over colors
281 short p = PIXELN(D_1, x - round_tx, y - round_ty,
282 td->width_src, td->height_src, 3, z, -1);
285 D_2[(x + y * td->width_dest)*3+z] = 16;
287 D_2[(x + y * td->width_dest)*3+z] = (unsigned char)p;
297 * transformYUV: applies current transformation to frame
300 * td: private data structure of this filter
302 * 0 for failture, 1 for success
304 * The frame must be in YUV format
306 int transformYUV(TransformData* td)
310 unsigned char *Y_1, *Y_2, *Cb_1, *Cb_2, *Cr_1, *Cr_2;
311 t = td->trans[td->current_trans];
315 Cb_1 = td->src + td->width_src * td->height_src;
316 Cb_2 = td->dest + td->width_dest * td->height_dest;
317 Cr_1 = td->src + 5*td->width_src * td->height_src/4;
318 Cr_2 = td->dest + 5*td->width_dest * td->height_dest/4;
319 float c_s_x = td->width_src/2.0;
320 float c_s_y = td->height_src/2.0;
321 float c_d_x = td->width_dest/2.0;
322 float c_d_y = td->height_dest/2.0;
324 float z = 1.0-t.zoom/100;
325 float zcos_a = z*cos(-t.alpha); // scaled cos
326 float zsin_a = z*sin(-t.alpha); // scaled sin
328 /* for each pixel in the destination image we calc the source
329 * coordinate and make an interpolation:
330 * p_d = c_d + M(p_s - c_s) + t
331 * where p are the points, c the center coordinate,
332 * _s source and _d destination,
333 * t the translation, and M the rotation and scaling matrix
334 * p_s = M^{-1}(p_d - c_d - t) + c_s
336 /* Luminance channel */
337 if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
338 for (x = 0; x < td->width_dest; x++) {
339 for (y = 0; y < td->height_dest; y++) {
340 float x_d1 = (x - c_d_x);
341 float y_d1 = (y - c_d_y);
342 float x_s = zcos_a * x_d1
343 + zsin_a * y_d1 + c_s_x -t.x;
344 float y_s = -zsin_a * x_d1
345 + zcos_a * y_d1 + c_s_y -t.y;
346 unsigned char* dest = &Y_2[x + y * td->width_dest];
347 interpolate(dest, x_s, y_s, Y_1,
348 td->width_src, td->height_src,
349 td->crop ? 16 : *dest);
353 /* no rotation, no zooming, just translation
354 *(also no interpolation, since no size change)
356 int round_tx = myround(t.x);
357 int round_ty = myround(t.y);
358 for (x = 0; x < td->width_dest; x++) {
359 for (y = 0; y < td->height_dest; y++) {
360 short p = PIXEL(Y_1, x - round_tx, y - round_ty,
361 td->width_src, td->height_src, -1);
364 Y_2[x + y * td->width_dest] = 16;
366 Y_2[x + y * td->width_dest] = (unsigned char)p;
373 int ws2 = td->width_src/2;
374 int wd2 = td->width_dest/2;
375 int hs2 = td->height_src/2;
376 int hd2 = td->height_dest/2;
377 if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
378 for (x = 0; x < wd2; x++) {
379 for (y = 0; y < hd2; y++) {
380 float x_d1 = x - (c_d_x)/2;
381 float y_d1 = y - (c_d_y)/2;
382 float x_s = zcos_a * x_d1
383 + zsin_a * y_d1 + (c_s_x -t.x)/2;
384 float y_s = -zsin_a * x_d1
385 + zcos_a * y_d1 + (c_s_y -t.y)/2;
386 unsigned char* dest = &Cr_2[x + y * wd2];
387 interpolate(dest, x_s, y_s, Cr_1, ws2, hs2,
388 td->crop ? 128 : *dest);
389 dest = &Cb_2[x + y * wd2];
390 interpolate(dest, x_s, y_s, Cb_1, ws2, hs2,
391 td->crop ? 128 : *dest);
394 } else { // no rotation, no zoom, no interpolation, just translation
395 int round_tx2 = myround(t.x/2.0);
396 int round_ty2 = myround(t.y/2.0);
397 for (x = 0; x < wd2; x++) {
398 for (y = 0; y < hd2; y++) {
399 short cr = PIXEL(Cr_1, x - round_tx2, y - round_ty2,
401 short cb = PIXEL(Cb_1, x - round_tx2, y - round_ty2,
405 Cr_2[x + y * wd2] = 128;
406 Cb_2[x + y * wd2] = 128;
409 Cr_2[x + y * wd2] = (unsigned char)cr;
410 Cb_2[x + y * wd2] = (unsigned char)cb;
420 * preprocess_transforms: does smoothing, relative to absolute conversion,
421 * and cropping of too large transforms.
422 * This is actually the core algorithm for canceling the jiggle in the
423 * movie. We perform a low-pass filter in terms of transformation size.
424 * This enables still camera movement, but in a smooth fasion.
427 * td: tranform private data structure
429 * 1 for success and 0 for failture
433 * td->trans will be modified
435 int preprocess_transforms(TransformData* td)
437 Transform* ts = td->trans;
440 if (td->trans_len < 1)
443 mlt_log_warning(NULL,"Preprocess transforms:");
445 if (td->smoothing>0) {
447 Transform* ts2 = malloc(sizeof(Transform) * td->trans_len);
448 memcpy(ts2, ts, sizeof(Transform) * td->trans_len);
450 /* we will do a sliding average with minimal update
451 * \hat x_{n/2} = x_1+x_2 + .. + x_n
452 * \hat x_{n/2+1} = x_2+x_3 + .. + x_{n+1} = x_{n/2} - x_1 + x_{n+1}
455 int s = td->smoothing * 2 + 1;
456 Transform null = null_transform();
457 /* avg is the average over [-smoothing, smoothing] transforms
458 around the current point */
460 /* avg2 is a sliding average over the filtered signal! (only to past)
461 * with smoothing * 10 horizont to kill offsets */
462 Transform avg2 = null_transform();
463 double tau = 1.0/(3 * s);
464 /* initialise sliding sum with hypothetic sum centered around
465 * -1st element. We have two choices:
466 * a) assume the camera is not moving at the beginning
467 * b) assume that the camera moves and we use the first transforms
469 Transform s_sum = null;
470 for (i = 0; i < td->smoothing; i++){
471 s_sum = add_transforms(&s_sum, i < td->trans_len ? &ts2[i]:&null);
473 mult_transform(&s_sum, 2); // choice b (comment out for choice a)
475 for (i = 0; i < td->trans_len; i++) {
476 Transform* old = ((i - td->smoothing - 1) < 0)
477 ? &null : &ts2[(i - td->smoothing - 1)];
478 Transform* new = ((i + td->smoothing) >= td->trans_len)
479 ? &null : &ts2[(i + td->smoothing)];
480 s_sum = sub_transforms(&s_sum, old);
481 s_sum = add_transforms(&s_sum, new);
483 avg = mult_transform(&s_sum, 1.0/s);
486 * meaning high frequency must be transformed away
488 ts[i] = sub_transforms(&ts2[i], &avg);
489 /* kill accumulating offset in the filtered signal*/
490 avg2 = add_transforms_(mult_transform(&avg2, 1 - tau),
491 mult_transform(&ts[i], tau));
492 ts[i] = sub_transforms(&ts[i], &avg2);
494 if (0 /*verbose*/ ) {
495 mlt_log_warning(NULL,"s_sum: %5lf %5lf %5lf, ts: %5lf, %5lf, %5lf\n",
496 s_sum.x, s_sum.y, s_sum.alpha,
497 ts[i].x, ts[i].y, ts[i].alpha);
498 mlt_log_warning(NULL,
499 " avg: %5lf, %5lf, %5lf avg2: %5lf, %5lf, %5lf",
500 avg.x, avg.y, avg.alpha,
501 avg2.x, avg2.y, avg2.alpha);
510 for (i = 0; i < td->trans_len; i++) {
511 ts[i] = mult_transform(&ts[i], -1);
515 /* relative to absolute */
518 for (i = 1; i < td->trans_len; i++) {
520 mlt_log_warning(NULL, "shift: %5lf %5lf %lf \n",
521 t.x, t.y, t.alpha *180/M_PI);
523 ts[i] = add_transforms(&ts[i], &t);
527 /* crop at maximal shift */
528 if (td->maxshift != -1)
529 for (i = 0; i < td->trans_len; i++) {
530 ts[i].x = TC_CLAMP(ts[i].x, -td->maxshift, td->maxshift);
531 ts[i].y = TC_CLAMP(ts[i].y, -td->maxshift, td->maxshift);
533 if (td->maxangle != - 1.0)
534 for (i = 0; i < td->trans_len; i++)
535 ts[i].alpha = TC_CLAMP(ts[i].alpha, -td->maxangle, td->maxangle);
538 * cheap algo is to only consider transformations
539 * uses cleaned max and min
541 if (td->optzoom != 0 && td->trans_len > 1){
542 Transform min_t, max_t;
543 cleanmaxmin_xy_transform(ts, td->trans_len, 10, &min_t, &max_t);
544 // the zoom value only for x
545 double zx = 2*TC_MAX(max_t.x,fabs(min_t.x))/td->width_src;
546 // the zoom value only for y
547 double zy = 2*TC_MAX(max_t.y,fabs(min_t.y))/td->height_src;
548 td->zoom += 100* TC_MAX(zx,zy); // use maximum
549 mlt_log_warning(NULL,"Final zoom: %lf\n", td->zoom);
552 /* apply global zoom */
554 for (i = 0; i < td->trans_len; i++)
555 ts[i].zoom += td->zoom;
562 * transform_init: Initialize this instance of the module. See
563 * tcmodule-data.h for function details.
568 static int transform_init(TCModuleInstance *self, uint32_t features)
571 TransformData* td = NULL;
572 TC_MODULE_SELF_CHECK(self, "init");
573 TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
575 td = tc_zalloc(sizeof(TransformData));
577 tc_log_error(MOD_NAME, "init: out of memory!");
582 tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
590 * transform_configure: Configure this instance of the module. See
591 * tcmodule-data.h for function details.
593 int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image ,Transform* tx,int trans_len)
595 TransformData *td = self;
597 /**** Initialise private data structure */
599 /* td->framesize = td->vob->im_v_width *
600 * MAX_PLANES * sizeof(char) * 2 * td->vob->im_v_height * 2;
602 // rgb24 = w*h*3 , yuv420p = w* h* 3/2
603 td->framesize_src = width*height*(pixelformat==mlt_image_rgb24 ? 3 : (3.0/2.0));
604 td->src = malloc(td->framesize_src); /* FIXME */
605 if (td->src == NULL) {
606 mlt_log_error(NULL,"tc_malloc failed\n");
610 td->width_src = width;
611 td->height_src = height;
613 /* Todo: in case we can scale the images, calc new size later */
614 td->width_dest = width;
615 td->height_dest = height;
616 td->framesize_dest = td->framesize_src;
620 td->trans_len = trans_len;
621 td->current_trans = 0;
622 td->warned_transform_end = 0;
625 // set from filter td->maxshift = -1;
626 // set from filter td->maxangle = -1;
629 // set from filter td->crop = 0;
630 // set from filter td->relative = 1;
631 // set from filter td->invert = 0;
632 // set from filter td->smoothing = 10;
634 td->rotation_threshhold = 0.25/(180/M_PI);
636 // set from filter td->zoom = 0;
637 // set from filter td->optzoom = 1;
638 // set from filter td->interpoltype = 2; // bi-linear
639 // set from filter td->sharpen = 0.8;
641 td->interpoltype = TC_MIN(td->interpoltype,4);
643 mlt_log_warning(NULL, "Image Transformation/Stabilization Settings:\n");
644 mlt_log_warning(NULL, " smoothing = %d\n", td->smoothing);
645 mlt_log_warning(NULL, " maxshift = %d\n", td->maxshift);
646 mlt_log_warning(NULL, " maxangle = %f\n", td->maxangle);
647 mlt_log_warning(NULL, " crop = %s\n",
648 td->crop ? "Black" : "Keep");
649 mlt_log_warning(NULL, " relative = %s\n",
650 td->relative ? "True": "False");
651 mlt_log_warning(NULL, " invert = %s\n",
652 td->invert ? "True" : "False");
653 mlt_log_warning(NULL, " zoom = %f\n", td->zoom);
654 mlt_log_warning(NULL, " optzoom = %s\n",
655 td->optzoom ? "On" : "Off");
656 mlt_log_warning(NULL, " interpol = %s\n",
657 interpoltypes[td->interpoltype]);
658 mlt_log_warning(NULL, " sharpen = %f\n", td->sharpen);
661 if (td->maxshift > td->width_dest/2
662 ) td->maxshift = td->width_dest/2;
663 if (td->maxshift > td->height_dest/2)
664 td->maxshift = td->height_dest/2;
666 if (!preprocess_transforms(td)) {
667 mlt_log_error(NULL,"error while preprocessing transforms!");
671 switch(td->interpoltype){
672 case 0: interpolate = &interpolateZero; break;
673 case 1: interpolate = &interpolateLin; break;
674 case 2: interpolate = &interpolateBiLin; break;
675 case 3: interpolate = &interpolateSqr; break;
676 case 4: interpolate = &interpolateBiCub; break;
677 default: interpolate = &interpolateBiLin;
685 * transform_filter_video: performs the transformation of frames
686 * See tcmodule-data.h for function details.
688 int transform_filter_video(TransformData *self,
689 unsigned char *frame,mlt_image_format pixelformat)
691 TransformData *td = self;
695 memcpy(td->src, frame, td->framesize_src);
696 if (td->current_trans >= td->trans_len) {
697 td->current_trans = td->trans_len-1;
698 if(!td->warned_transform_end)
699 mlt_log_warning(NULL,"not enough transforms found, use last transformation!\n");
700 td->warned_transform_end = 1;
703 if (pixelformat == mlt_image_rgb24 ) {
705 } else if (pixelformat == mlt_image_yuv420p) {
708 mlt_log_error(NULL,"unsupported Codec: %i\n", pixelformat);