2 * producer_slowmotion.c -- create subspeed frames
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include "filter_motion_est.h"
21 #include <framework/mlt.h>
30 #define ABS(a) ((a) >= 0 ? (a) : (-(a)))
32 // This is used to constrains pixel operations between two blocks to be within the image boundry
33 inline static int constrain( int *x
, int *y
, int *w
, int *h
,
34 const int dx
, const int dy
,
35 const int left
, const int right
,
36 const int top
, const int bottom
)
38 uint32_t penalty
= 1 << SHIFT
; // Retain a few extra bits of precision
44 // Origin of macroblock moves left of image boundy
45 if( *x
< left
|| x2
< left
) {
46 w_remains
= *w
- left
+ ((*x
< x2
) ?
*x
: x2
);
49 // Portion of macroblock moves right of image boundry
50 else if( *x
+ *w
> right
|| x2
+ *w
> right
)
51 w_remains
= right
- ((*x
> x2
) ?
*x
: x2
);
53 // Origin of macroblock moves above image boundy
54 if( *y
< top
|| y2
< top
) {
55 h_remains
= *h
- top
+ ((*y
< y2
) ?
*y
: y2
);
58 // Portion of macroblock moves bellow image boundry
59 else if( *y
+ *h
> bottom
|| y2
+ *h
> bottom
)
60 h_remains
= bottom
- ((*y
> y2
) ?
*y
: y2
);
62 if( w_remains
== *w
&& h_remains
== *h
) return penalty
;
63 if( w_remains
<= 0 || h_remains
<= 0) return 0; // Block is clipped out of existance
64 penalty
= (*w
* *h
* penalty
)
65 / ( w_remains
* h_remains
); // Recipricol of the fraction of the block that remains
67 *w
= w_remains
; // Update the width and height
73 static void motion_interpolate( uint8_t *first_image
, uint8_t *second_image
, uint8_t *output
,
74 int top_mb
, int bottom_mb
, int left_mb
, int right_mb
,
76 int width
, int height
,
77 int xstride
, int ystride
,
79 motion_vector
*vectors
)
81 assert ( scale
>= 0.0 && scale
<= 1.0 );
86 int scaled_dx
, scaled_dy
;
90 int mv_width
= width
/ mb_w
;
92 for( j
= top_mb
; j
<= bottom_mb
; j
++ ){
93 for( i
= left_mb
; i
<= right_mb
; i
++ ){
95 here
= vectors
+ j
*mv_width
+ i
;
96 scaled_dx
= (1.0 - scale
) * (double)here
->dx
;
97 scaled_dy
= (1.0 - scale
) * (double)here
->dy
;
101 x
= i
* w
; y
= j
* h
;
103 // Denoise function caused some blocks to be completely clipped, ignore them
104 if (constrain( &x
, &y
, &w
, &h
, dx
, dy
, 0, width
, 0, height
) == 0 )
107 for( ty
= y
; ty
< y
+ h
; ty
++ ){
108 for( tx
= x
; tx
< x
+ w
; tx
++ ){
110 f
= first_image
+ (tx
+ dx
)*xstride
+ (ty
+ dy
)*ystride
;
111 s
= second_image
+ (tx
)*xstride
+ (ty
)*ystride
;
112 r
= output
+ (tx
+scaled_dx
)*xstride
+ (ty
+scaled_dy
)*ystride
;
114 if( ABS(f[0] - s[0]) > 3 * here->msad / (mb_w * mb_h * 2) )
124 r
[0] = ( 1.0 - scale
) * (double)f
[0] + scale
* (double)s
[0];
128 if( scaled_dx
% 2 == 0 )
129 r
[1] = ( 1.0 - scale
) * (double)f
[1] + scale
* (double) s
[1];
131 *(r
-1) = ( 1.0 - scale
) * (double)f
[1] + scale
* (double) s
[1];
135 if( scaled_dx
%2 == 0 )
136 // FIXME: may exceed boundies
137 r
[1] = ( 1.0 - scale
) * ( (double)(*(f
-1) + (double)f
[3]) / 2.0 ) + scale
* (double) s
[1];
139 // FIXME: may exceed boundies
140 *(r
-1) = ( 1.0 - scale
) * ( (double)(*(f
-1) + (double)f
[3]) / 2.0 ) + scale
* (double) s
[1];
149 // Image stack(able) method
150 static int slowmotion_get_image( mlt_frame
this, uint8_t **image
, mlt_image_format
*format
, int *width
, int *height
, int writable
)
153 // Get the filter object and properties
154 mlt_producer producer
= mlt_frame_pop_service( this );
155 mlt_frame second_frame
= mlt_frame_pop_service( this );
156 mlt_frame first_frame
= mlt_frame_pop_service( this );
158 mlt_properties producer_properties
= MLT_PRODUCER_PROPERTIES( producer
);
160 // Frame properties objects
161 mlt_properties frame_properties
= MLT_FRAME_PROPERTIES( this );
162 mlt_properties first_frame_properties
= MLT_FRAME_PROPERTIES( first_frame
);
163 mlt_properties second_frame_properties
= MLT_FRAME_PROPERTIES( second_frame
);
166 int size
, xstride
, ystride
;
168 case mlt_image_yuv422
:
169 size
= *width
* *height
* 2;
171 ystride
= 2 * *width
;
174 fprintf(stderr
, "Unsupported image format\n");
178 uint8_t *output
= mlt_properties_get_data( producer_properties
, "output_buffer", 0 );
181 output
= mlt_pool_alloc( size
);
183 // Let someone else clean up
184 mlt_properties_set_data( producer_properties
, "output_buffer", output
, size
, mlt_pool_release
, NULL
);
187 uint8_t *first_image
= mlt_properties_get_data( first_frame_properties
, "image", NULL
);
188 uint8_t *second_image
= mlt_properties_get_data( second_frame_properties
, "image", NULL
);
190 // which frames are buffered?
194 if( first_image
== NULL
)
196 error
= mlt_frame_get_image( first_frame
, &first_image
, format
, width
, height
, writable
);
199 fprintf(stderr
, "first_image == NULL get image died\n");
204 if( second_image
== NULL
)
206 error
= mlt_frame_get_image( second_frame
, &second_image
, format
, width
, height
, writable
);
209 fprintf(stderr
, "second_image == NULL get image died\n");
214 // These need to passed onto the frame for other
215 mlt_properties_pass_list( frame_properties
, second_frame_properties
,
216 "motion_est.left_mb, motion_est.right_mb, \
217 motion_est.top_mb, motion_est.bottom_mb, \
218 motion_est.macroblock_width, motion_est.macroblock_height" );
220 // Pass the pointer to the vectors without serializing
221 mlt_properties_set_data( frame_properties
, "motion_est.vectors",
222 mlt_properties_get_data( second_frame_properties
, "motion_est.vectors", NULL
),
226 // Start with a base image
227 memcpy( output
, first_image
, size
);
229 if( mlt_properties_get_int( producer_properties
, "method" ) == 1 ) {
231 mlt_position first_position
= mlt_frame_get_position( first_frame
);
232 double actual_position
= mlt_producer_get_speed( producer
) * (double)mlt_frame_get_position( this );
233 double scale
= actual_position
- first_position
;
237 first_image
, second_image
, output
,
238 mlt_properties_get_int( second_frame_properties
, "motion_est.top_mb" ),
239 mlt_properties_get_int( second_frame_properties
, "motion_est.bottom_mb" ),
240 mlt_properties_get_int( second_frame_properties
, "motion_est.left_mb" ),
241 mlt_properties_get_int( second_frame_properties
, "motion_est.right_mb" ),
242 mlt_properties_get_int( second_frame_properties
, "motion_est.macroblock_width" ),
243 mlt_properties_get_int( second_frame_properties
, "motion_est.macroblock_height" ),
247 mlt_properties_get_data( second_frame_properties
, "motion_est.vectors", NULL
)
250 if( mlt_properties_get_int( producer_properties
, "debug" ) == 1 ) {
251 mlt_filter watermark
= mlt_properties_get_data( producer_properties
, "watermark", NULL
);
253 if( watermark
== NULL
) {
254 mlt_profile profile
= mlt_service_profile( MLT_PRODUCER_SERVICE( producer
) );
255 watermark
= mlt_factory_filter( profile
, "watermark", NULL
);
256 mlt_properties_set_data( producer_properties
, "watermark", watermark
, 0, (mlt_destructor
)mlt_filter_close
, NULL
);
257 mlt_producer_attach( producer
, watermark
);
260 mlt_properties wm_properties
= MLT_FILTER_PROPERTIES( watermark
);
263 sprintf(disp
, "+%10.2f.txt", actual_position
);
264 mlt_properties_set( wm_properties
, "resource", disp
);
271 mlt_properties_set_data( frame_properties
, "image", output
, size
, NULL
, NULL
);
273 // Make sure that no further scaling is done
274 mlt_properties_set( frame_properties
, "rescale.interps", "none" );
275 mlt_properties_set( frame_properties
, "scale", "off" );
277 mlt_frame_close( first_frame
);
278 mlt_frame_close( second_frame
);
283 static int slowmotion_get_frame( mlt_producer
this, mlt_frame_ptr frame
, int index
)
285 // Construct a new frame
286 *frame
= mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
288 mlt_properties properties
= MLT_PRODUCER_PROPERTIES(this);
294 mlt_frame first_frame
= mlt_properties_get_data( properties
, "first_frame", NULL
);
295 mlt_frame second_frame
= mlt_properties_get_data( properties
, "second_frame", NULL
);
297 mlt_position first_position
= (first_frame
!= NULL
) ?
mlt_frame_get_position( first_frame
) : -1;
298 mlt_position second_position
= (second_frame
!= NULL
) ?
mlt_frame_get_position( second_frame
) : -1;
300 // Get the real producer
301 mlt_producer real_producer
= mlt_properties_get_data( properties
, "producer", NULL
);
303 // Our "in" needs to be the same, keep it so
304 mlt_properties_pass_list( MLT_PRODUCER_PROPERTIES( real_producer
), properties
, "in" );
306 // Calculate our positions
307 double actual_position
= mlt_producer_get_speed( this ) * (double)mlt_producer_position( this );
308 mlt_position need_first
= floor( actual_position
);
309 mlt_position need_second
= need_first
+ 1;
311 if( need_first
!= first_position
)
313 mlt_frame_close( first_frame
);
318 if( need_second
!= second_position
)
320 mlt_frame_close( second_frame
);
321 second_position
= -1;
325 if( first_frame
== NULL
)
327 // Seek the producer to the correct place
328 mlt_producer_seek( real_producer
, need_first
);
331 mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer
), &first_frame
, index
);
334 if( second_frame
== NULL
)
336 // Seek the producer to the correct place
337 mlt_producer_seek( real_producer
, need_second
);
340 mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer
), &second_frame
, index
);
343 // Make sure things are in their place
344 mlt_properties_set_data( properties
, "first_frame", first_frame
, 0, NULL
, NULL
);
345 mlt_properties_set_data( properties
, "second_frame", second_frame
, 0, NULL
, NULL
);
347 mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame
), "test_image", 0 );
349 // Stack the producer and producer's get image
350 mlt_frame_push_service( *frame
, first_frame
);
351 mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( first_frame
) );
353 mlt_frame_push_service( *frame
, second_frame
);
354 mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( second_frame
) );
356 mlt_frame_push_service( *frame
, this );
357 mlt_frame_push_service( *frame
, slowmotion_get_image
);
359 // Give the returned frame temporal identity
360 mlt_frame_set_position( *frame
, mlt_producer_position( this ) );
366 mlt_producer
producer_slowmotion_init( mlt_profile profile
, mlt_service_type type
, const char *id
, char *arg
)
368 mlt_producer
this = mlt_producer_new( );
371 mlt_producer real_producer
= mlt_factory_producer( profile
, "fezzik", arg
);
373 // We need to apply the motion estimation filter manually
374 mlt_filter filter
= mlt_factory_filter( profile
, "motion_est", NULL
);
376 if ( this != NULL
&& real_producer
!= NULL
&& filter
!= NULL
)
378 // attach the motion_est filter to the real producer
379 mlt_producer_attach( real_producer
, filter
);
381 // Get the properties of this producer
382 mlt_properties properties
= MLT_PRODUCER_PROPERTIES( this );
384 // Fezzik normalised it for us already
385 mlt_properties_set_int( properties
, "fezzik_normalised", 1);
387 // Store the producer and fitler
388 mlt_properties_set_data( properties
, "producer", real_producer
, 0, ( mlt_destructor
)mlt_producer_close
, NULL
);
389 mlt_properties_set_data( properties
, "motion_est", filter
, 0, ( mlt_destructor
)mlt_filter_close
, NULL
);
390 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter
), "macroblock_width", 16 );
391 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter
), "macroblock_height", 16 );
392 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter
), "denoise", 0 );
394 // Grap some stuff from the real_producer
395 mlt_properties_pass_list( properties
, MLT_PRODUCER_PROPERTIES( real_producer
),
396 "in, out, length, resource" );
398 // Since we control the seeking, prevent it from seeking on its own
399 mlt_producer_set_speed( real_producer
, 0 );
401 //mlt_properties_set( properties, "method", "onefield" );
403 // Override the get_frame method
404 this->get_frame
= slowmotion_get_frame
;
410 mlt_producer_close( this );
412 mlt_producer_close( real_producer
);
414 mlt_filter_close( filter
);