]> git.sesse.net Git - mlt/commitdiff
Updates to vid.stab module.
authorBrian Matherly <pez4brian@yahoo.com>
Tue, 14 Jan 2014 13:44:34 +0000 (07:44 -0600)
committerBrian Matherly <pez4brian@yahoo.com>
Thu, 16 Jan 2014 18:36:41 +0000 (12:36 -0600)
* Clean up serialization/deserialization
* results are not published until the analysis step is complete
* results are stored in "results" property
* Misc changes for MLT conventions and consistency

src/modules/vid.stab/filter_vidstab.cpp

index 32e06679779b3a99c38f070926fb8b66b2c553b8..4b4fe46ff3333b6f6eafd457f51341d4c68c3c17 100644 (file)
@@ -30,142 +30,154 @@ extern "C"
 #include <assert.h>
 #include "common.h"
 
-
-typedef struct _stab_data
+typedef struct
 {
        VSMotionDetect md;
-       mlt_animation animation;
-} StabData;
+       VSManyLocalMotions mlms;
+} vs_analyze;
 
 typedef struct
 {
        VSTransformData td;
        VSTransformations trans;
-} TransformData;
-
+} vs_apply;
 
-char* vectors_serializer(mlt_animation animation, int length)
+typedef struct
 {
-       return mlt_animation_serialize(animation);
-}
+       vs_analyze* analyze_data;
+       vs_apply* apply_data;
+} vs_data;
 
-char* lm_serializer(LocalMotions *lms, int length)
-{
-       std::ostringstream oss;
-       int size = vs_vector_size(lms);
-       for (int i = 0; i < size; ++i)
-       {
-               LocalMotion* lm = (LocalMotion*) vs_vector_get(lms, i);
-               oss << lm->v.x << ' ';
-               oss << lm->v.y << ' ';
-               oss << lm->f.x << ' ';
-               oss << lm->f.y << ' ';
-               oss << lm->f.size << ' ';
-               oss << lm->contrast << ' ';
-               oss << lm->match << ' ';
-       }
-       return strdup(oss.str().c_str());
-}
+/** Free all data used by a VSManyLocalMotions instance.
+ */
 
-int lm_deserialize(LocalMotions *lms, mlt_property property)
+static void free_manylocalmotions( VSManyLocalMotions* mlms )
 {
-       std::istringstream iss(mlt_property_get_string(property));
-       vs_vector_init(lms, 0);
-
-       while (iss.good())
+       for( int i = 0; i < vs_vector_size( mlms ); i++ )
        {
-               LocalMotion lm;
-               iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
-               if (iss.fail())
-               {
-                       break;
-               }
-               vs_vector_append_dup(lms, &lm, sizeof(lm));
+               LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
+               vs_vector_del( lms );
        }
-       return 0;
+       vs_vector_del( mlms );
 }
 
-void lm_destructor(void *lms)
-{
-       vs_vector_del(static_cast<VSVector*>(lms));
-}
+/** Serialize a VSManyLocalMotions instance and store it in the properties.
+ *
+ * Each LocalMotions instance is converted to a string and stored in an animation.
+ * Then, the entire animation is serialized and stored in the properties.
+ * \param properties the filter properties
+ * \param mlms an initialized VSManyLocalMotions instance
+ */
 
-static void serialize_localmotions(StabData* data, LocalMotions &vectors, mlt_position pos)
+static void publish_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
 {
+       mlt_animation animation = mlt_animation_new();
        mlt_animation_item_s item;
 
-       // Initialize animation item
+       // Initialize animation item.
        item.is_key = 1;
-       item.frame = data->md.frameNum;
        item.keyframe_type = mlt_keyframe_discrete;
        item.property = mlt_property_init();
 
-       mlt_property_set_data(item.property, &vectors, 1, lm_destructor, (mlt_serialiser) lm_serializer);
-       mlt_animation_insert(data->animation, &item);
-       mlt_property_close(item.property);
+       // Convert each LocalMotions instance to a string and add it to the animation.
+       for( int i = 0; i < vs_vector_size( mlms ); i++ )
+       {
+               LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
+               item.frame = i;
+               int size = vs_vector_size( lms );
+
+               std::ostringstream oss;
+               for ( int j = 0; j < size; ++j )
+               {
+                       LocalMotion* lm = (LocalMotion*)vs_vector_get( lms, j );
+                       oss << lm->v.x << ' ';
+                       oss << lm->v.y << ' ';
+                       oss << lm->f.x << ' ';
+                       oss << lm->f.y << ' ';
+                       oss << lm->f.size << ' ';
+                       oss << lm->contrast << ' ';
+                       oss << lm->match << ' ';
+               }
+               mlt_property_set_string( item.property, oss.str().c_str() );
+               mlt_animation_insert( animation, &item );
+       }
+
+       // Serialize and store the animation.
+       char* motion_str = mlt_animation_serialize( animation );
+       mlt_properties_set( properties, "results", motion_str );
+
+       mlt_property_close( item.property );
+       mlt_animation_close( animation );
+       free( motion_str );
 }
 
-int vectors_deserialize(mlt_animation anim, VSManyLocalMotions *mlms)
+/** Get the motions data from the properties and convert it to a VSManyLocalMotions.
+ *
+ * Each LocalMotions instance is converted to a string and stored in an animation.
+ * Then, the entire animation is serialized and stored in the properties.
+ * \param properties the filter properties
+ * \param mlms an initialized (but empty) VSManyLocalMotions instance
+ */
+
+static void read_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
 {
-       int error = 0;
        mlt_animation_item_s item;
        item.property = mlt_property_init();
+       mlt_animation animation = mlt_animation_new();
+       // Get the results property which represents the VSManyLocalMotions
+       char* motion_str = mlt_properties_get( properties, "results" );
 
-       vs_vector_init(mlms, 1024); // initial number of frames, but it will be increased
+       mlt_animation_parse( animation, motion_str, 0, 0, NULL );
 
-       int length = mlt_animation_get_length(anim);
-       for (int i = 0; i < length; ++i)
+       int length = mlt_animation_get_length( animation );
+
+       for ( int i = 0; i <= length; ++i )
        {
                LocalMotions lms;
+               vs_vector_init( &lms, 0 );
+
+               // Get the animation item that represents the LocalMotions
+               mlt_animation_get_item( animation, &item, i );
 
-               // read lms
-               mlt_animation_get_item(anim, &item, i + 1);
-               if ((error = lm_deserialize(&lms, item.property)))
+               // Convert the property to a real LocalMotions
+               std::istringstream iss( mlt_property_get_string( item.property ) );
+               while ( iss.good() )
                {
-                       break;
+                       LocalMotion lm;
+                       iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
+                       if ( !iss.fail() )
+                       {
+                               vs_vector_append_dup( &lms, &lm, sizeof(lm) );
+                       }
                }
 
-               vs_vector_set_dup(mlms, i, &lms, sizeof(LocalMotions));
+               // Add the LocalMotions to the ManyLocalMotions
+               vs_vector_set_dup( mlms, i, &lms, sizeof(LocalMotions) );
        }
 
-       mlt_property_close(item.property);
-       return error;
-}
-
-void destroy_transforms(TransformData *data)
-{
-       if (data)
-       {
-               vsTransformDataCleanup(&data->td);
-               vsTransformationsCleanup(&data->trans);
-               delete data;
-       }
+       mlt_property_close( item.property );
+       mlt_animation_close( animation );
 }
 
-TransformData* initialize_transforms(int *width, int *height, mlt_image_format *format,
-               mlt_properties properties, const char* interps)
+static vs_apply* init_apply_data( mlt_filter filter, mlt_frame frame, int width, int height, mlt_image_format format )
 {
-       TransformData *data = new TransformData;
-       memset(data, 0, sizeof(TransformData));
-
-       VSPixelFormat pf = convertImageFormat(*format);
-       VSFrameInfo fi_src, fi_dst;
-       vsFrameInfoInit(&fi_src, *width, *height, pf);
-       vsFrameInfoInit(&fi_dst, *width, *height, pf);
-
-       const char* filterName = mlt_properties_get(properties, "mlt_service");
-
-       VSTransformConfig conf = vsTransformGetDefaultConfig(filterName);
-       conf.smoothing = mlt_properties_get_int(properties, "smoothing");
-       conf.maxShift = mlt_properties_get_int(properties, "maxshift");
-       conf.maxAngle = mlt_properties_get_double(properties, "maxangle");
-       conf.crop = (VSBorderType) mlt_properties_get_int(properties, "crop");
-       conf.zoom = mlt_properties_get_int(properties, "zoom");
-       conf.optZoom = mlt_properties_get_int(properties, "optzoom");
-       conf.zoomSpeed = mlt_properties_get_double(properties, "zoomspeed");
-       conf.relative = mlt_properties_get_int(properties, "relative");
-       conf.invert = mlt_properties_get_int(properties, "invert");
-       if (mlt_properties_get_int(properties, "tripod") != 0)
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       vs_apply* apply_data = (vs_apply*)calloc( 1, sizeof(vs_apply) );
+       memset( apply_data, 0, sizeof( vs_apply ) );
+       VSPixelFormat pf = convertImageFormat( format );
+
+       const char* filterName = mlt_properties_get( properties, "mlt_service" );
+       VSTransformConfig conf = vsTransformGetDefaultConfig( filterName );
+       conf.smoothing = mlt_properties_get_int( properties, "smoothing" );
+       conf.maxShift = mlt_properties_get_int( properties, "maxshift" );
+       conf.maxAngle = mlt_properties_get_double( properties, "maxangle" );
+       conf.crop = (VSBorderType)mlt_properties_get_int( properties, "crop" );
+       conf.zoom = mlt_properties_get_int( properties, "zoom" );
+       conf.optZoom = mlt_properties_get_int( properties, "optzoom" );
+       conf.zoomSpeed = mlt_properties_get_double( properties, "zoomspeed" );
+       conf.relative = mlt_properties_get_int( properties, "relative" );
+       conf.invert = mlt_properties_get_int( properties, "invert" );
+       if ( mlt_properties_get_int( properties, "tripod" ) != 0 )
        {
                // Virtual tripod mode: relative=False, smoothing=0
                conf.relative = 0;
@@ -173,216 +185,228 @@ TransformData* initialize_transforms(int *width, int *height, mlt_image_format *
        }
 
        // by default a bilinear interpolation is selected
+       const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
        conf.interpolType = VS_BiLinear;
        if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
                conf.interpolType = VS_Zero;
        else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
                conf.interpolType = VS_Linear;
 
-       vsTransformDataInit(&data->td, &conf, &fi_src, &fi_dst);
-       vsTransformationsInit(&data->trans);
+       // load motions
+       VSManyLocalMotions mlms;
+       vs_vector_init( &mlms, mlt_filter_get_length2( filter, frame ) );
+       read_manylocalmotions( properties, &mlms );
+
+       // Convert motions to VSTransformations
+       VSTransformData* td = &apply_data->td;
+       VSTransformations* trans = &apply_data->trans;
+       VSFrameInfo fi_src, fi_dst;
+       vsFrameInfoInit( &fi_src, width, height, pf );
+       vsFrameInfoInit( &fi_dst, width, height, pf );
+       vsTransformDataInit( td, &conf, &fi_src, &fi_dst );
+       vsTransformationsInit( trans );
+       vsLocalmotions2Transforms( td, &mlms, trans );
+       vsPreprocessTransforms( td, trans );
 
-       // load transformations
-       mlt_animation animation = mlt_animation_new();
-       char* strAnim = mlt_properties_get(properties, "vectors");
-       if (mlt_animation_parse(animation, strAnim, 0, 0, NULL))
-       {
-               mlt_log_warning(NULL, "parse failed\n");
-               mlt_animation_close(animation);
-               destroy_transforms(data);
-               return NULL;
-       }
+       free_manylocalmotions( &mlms );
 
-       VSManyLocalMotions mlms;
-       if (vectors_deserialize(animation, &mlms))
+       return apply_data;
+}
+
+static void destory_apply_data( vs_apply* apply_data )
+{
+       if ( apply_data )
        {
-               mlt_animation_close(animation);
-               destroy_transforms(data);
-               return NULL;
+               vsTransformDataCleanup( &apply_data->td );
+               vsTransformationsCleanup( &apply_data->trans );
+               free( apply_data );
        }
+}
 
-       mlt_animation_close(animation);
+static vs_analyze* init_analyze_data( mlt_filter filter, mlt_frame frame, mlt_image_format format, int width, int height )
+{
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       vs_analyze* analyze_data = (vs_analyze*)calloc( 1, sizeof(vs_analyze) );
+       memset( analyze_data, 0, sizeof(vs_analyze) );
+
+       // Initialize the VSManyLocalMotions vector where motion data will be
+       // stored for each frame.
+       vs_vector_init( &analyze_data->mlms, mlt_filter_get_length2( filter, frame ) );
 
-       vsLocalmotions2Transforms(&data->td, &mlms, &data->trans);
-       vsPreprocessTransforms(&data->td, &data->trans);
-       return data;
+       // Initialize a VSFrameInfo to be used below
+       VSPixelFormat pf = convertImageFormat( format );
+       VSFrameInfo fi;
+       vsFrameInfoInit( &fi, width, height, pf );
+
+       // Initialize a VSMotionDetect
+       const char* filterName = mlt_properties_get( properties, "mlt_service" );
+       VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig( filterName );
+       conf.shakiness = mlt_properties_get_int( properties, "shakiness" );
+       conf.accuracy = mlt_properties_get_int( properties, "accuracy" );
+       conf.stepSize = mlt_properties_get_int( properties, "stepsize" );
+       conf.algo = mlt_properties_get_int( properties, "algo" );
+       conf.contrastThreshold = mlt_properties_get_double( properties, "mincontrast" );
+       conf.show = mlt_properties_get_int( properties, "show" );
+       conf.virtualTripod = mlt_properties_get_int( properties, "tripod" );
+       vsMotionDetectInit( &analyze_data->md, &conf, &fi );
+
+       return analyze_data;
 }
 
-int get_image_and_transform(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
+void destory_analyze_data( vs_analyze* analyze_data )
+{
+       if ( analyze_data )
+       {
+               vsMotionDetectionCleanup( &analyze_data->md );
+               free_manylocalmotions( &analyze_data->mlms );
+               free( analyze_data );
+       }
+}
+
+static int get_image_and_apply( mlt_filter filter, mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
 {
        int error = 0;
-       mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
-       mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       vs_data* data = (vs_data*)filter->child;
 
        *format = mlt_image_yuv420p;
 
-       error = mlt_frame_get_image(frame, image, format, width, height, 1);
-       if (!error)
+       error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+       if ( !error )
        {
-               // Service locks are for concurrency control
-               mlt_service_lock(MLT_FILTER_SERVICE(filter));
-
-               TransformData *data = static_cast<TransformData*>(mlt_properties_get_data(properties, "_transform_data", NULL));
+               mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
 
                // Handle signal from app to re-init data
-               if (mlt_properties_get_int(properties, "refresh"))
+               if ( mlt_properties_get_int(properties, "refresh") )
                {
                        mlt_properties_set(properties, "refresh", NULL);
-                       destroy_transforms(data);
-                       data = NULL;
+                       destory_apply_data( data->apply_data );
+                       data->apply_data = NULL;
                }
 
-               if (!data)
+               // Init transform data if necessary (first time)
+               if ( !data->apply_data )
                {
-                       const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
-                       data = initialize_transforms(width, height, format, properties, interps);
-                       if(!data) {
-                               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
-                               return 1; // return error code
-                       }
-                       mlt_properties_set_data(properties, "_transform_data", data, 0, (mlt_destructor) destroy_transforms, NULL);
+                       data->apply_data = init_apply_data( filter, frame, *width, *height, *format );
                }
 
-               VSTransformData* td = &data->td;
+               // Apply transformations to this image
+               VSTransformData* td = &data->apply_data->td;
+               VSTransformations* trans = &data->apply_data->trans;
                VSFrame vsFrame;
-               vsFrameFillFromBuffer(&vsFrame, *image, vsTransformGetSrcFrameInfo(td));
-
-               // transform frame
-               data->trans.current = mlt_filter_get_position(filter, frame);
-               vsTransformPrepare(td, &vsFrame, &vsFrame);
-               VSTransform t = vsGetNextTransform(td, &data->trans);
-               vsDoTransform(td, t);
-               vsTransformFinish(td);
-
-               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
+               vsFrameFillFromBuffer( &vsFrame, *image, vsTransformGetSrcFrameInfo( td ) );
+               trans->current = mlt_filter_get_position( filter, frame );
+               vsTransformPrepare( td, &vsFrame, &vsFrame );
+               VSTransform t = vsGetNextTransform( td, trans );
+               vsDoTransform( td, t );
+               vsTransformFinish( td );
+
+               mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
        }
 
        return error;
 }
 
-static StabData* init_detect(mlt_properties properties, mlt_image_format *format, int *width, int *height)
-{
-       StabData *data = new StabData;
-       memset(data, 0, sizeof(StabData));
-       data->animation = mlt_animation_new();
-
-       VSPixelFormat pf = convertImageFormat(*format);
-       VSFrameInfo fi;
-       vsFrameInfoInit(&fi, *width, *height, pf);
-
-       const char* filterName = mlt_properties_get(properties, "mlt_service");
-
-       VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(filterName);
-       conf.shakiness = mlt_properties_get_int(properties, "shakiness");
-       conf.accuracy = mlt_properties_get_int(properties, "accuracy");
-       conf.stepSize = mlt_properties_get_int(properties, "stepsize");
-       conf.algo = mlt_properties_get_int(properties, "algo");
-       conf.contrastThreshold = mlt_properties_get_double(properties, "mincontrast");
-       conf.show = mlt_properties_get_int(properties, "show");
-       conf.virtualTripod = mlt_properties_get_int(properties, "tripod");
-       vsMotionDetectInit(&data->md, &conf, &fi);
-
-       // add vectors to properties
-       mlt_properties_set_data(properties, "vectors", data->animation, 1, (mlt_destructor) mlt_animation_close,
-                                       (mlt_serialiser) vectors_serializer);
-       return data;
-}
 
-void destroy_detect(StabData *data)
+static int get_image_and_analyze( mlt_filter filter, mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
 {
-       if (data)
-       {
-               vsMotionDetectionCleanup(&data->md);
-               delete data;
-       }
-}
-
-int get_image_and_detect(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
-{
-       mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
-       mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       vs_data* data = (vs_data*)filter->child;
+       mlt_position pos = mlt_filter_get_position( filter, frame );
 
        *format = mlt_image_yuv420p;
 
-       writable = writable || mlt_properties_get_int(properties, "show") ? 1 : 0;
+       writable = writable || mlt_properties_get_int( properties, "show" ) ? 1 : 0;
 
-       int error = mlt_frame_get_image(frame, image, format, width, height, writable);
-       if (!error)
+       int error = mlt_frame_get_image( frame, image, format, width, height, writable );
+       if ( !error )
        {
                // Service locks are for concurrency control
-               mlt_service_lock(MLT_FILTER_SERVICE(filter));
+               mlt_service_lock( MLT_FILTER_SERVICE(filter) );
 
-               StabData *data = static_cast<StabData*>(mlt_properties_get_data(properties, "_stab_data", NULL));
-               if (!data)
+               if ( !data->analyze_data )
                {
-                       data = init_detect(properties, format, width, height);
-                       mlt_properties_set_data(properties, "_stab_data", data, 0, (mlt_destructor) destroy_detect, NULL);
+                       data->analyze_data = init_analyze_data( filter, frame, *format, *width, *height );
                }
 
-               VSMotionDetect* md = &data->md;
+               // Initialize the VSFrame to be analyzed.
+               VSMotionDetect* md = &data->analyze_data->md;
                LocalMotions localmotions;
                VSFrame vsFrame;
-               vsFrameFillFromBuffer(&vsFrame, *image, &md->fi);
+               vsFrameFillFromBuffer( &vsFrame, *image, &md->fi );
 
-               // detect and save motions
-               vsMotionDetection(md, &localmotions, &vsFrame);
-               mlt_position pos = mlt_filter_get_position( filter, frame );
-               serialize_localmotions(data, localmotions, pos);
+               // Detect and save motions.
+               vsMotionDetection( md, &localmotions, &vsFrame );
+               vs_vector_set_dup( &data->analyze_data->mlms, pos, &localmotions, sizeof(LocalMotions) );
 
-               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
-       }
+               // Publish the motions if this is the last frame.
+               if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
+               {
+                       publish_manylocalmotions( properties, &data->analyze_data->mlms );
+               }
 
+               mlt_service_unlock( MLT_FILTER_SERVICE(filter) );
+       }
        return error;
 }
 
-static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
+static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
 {
-       mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
-       mlt_get_image vidstab_get_image = (mlt_get_image) mlt_properties_get_data( properties, "_vidstab_get_image", NULL );
+       mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame );
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
 
-#if 1
-       mlt_position pos = mlt_filter_get_position(filter, frame);
-       mlt_position length = mlt_filter_get_length2(filter, frame) - 1;
-       if(pos >= length)
+       if( mlt_properties_get( properties, "results" ) )
        {
-               mlt_properties_set_data(properties, "_vidstab_get_image", NULL, 0, NULL, NULL);
+               return get_image_and_apply( filter, frame, image, format, width, height, writable );
        }
-#endif
-
-       if(vidstab_get_image == NULL)
+       else
        {
-               if(mlt_properties_get(properties, "vectors") == NULL)
-               {
-                       // vectors are NULL, so use a detect filter
-                       vidstab_get_image = get_image_and_detect;
-               } else {
-                       // found vectors, so use a transform filter
-                       vidstab_get_image = get_image_and_transform;
-               }
-
-               mlt_properties_set_data( properties, "_vidstab_get_image", (void*)vidstab_get_image, 0, NULL, NULL );
+               return get_image_and_analyze( filter, frame, image, format, width, height, writable );
        }
+}
 
-       mlt_frame_push_service(frame, filter);
-       mlt_frame_push_get_image(frame, vidstab_get_image);
+static mlt_frame process_filter( mlt_filter filter, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
+static void filter_close( mlt_filter filter )
+{
+       vs_data* data = (vs_data*)filter->child;
+       if ( data )
+       {
+               if ( data->analyze_data ) destory_analyze_data( data->analyze_data );
+               if ( data->apply_data ) destory_apply_data( data->apply_data );
+               free( data );
+       }
+       filter->close = NULL;
+       filter->child = NULL;
+       filter->parent.close = NULL;
+       mlt_service_close( &filter->parent );
+}
+
 extern "C"
 {
 
-mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type,
-               const char *id, char *arg)
+mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
 {
        mlt_filter filter = mlt_filter_new();
+       vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) );
 
-       if( filter )
+       if ( filter && data )
        {
+               data->analyze_data = NULL;
+               data->apply_data = NULL;
+
+               filter->close = filter_close;
+               filter->child = data;
                filter->process = process_filter;
 
                mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
 
-               //properties for stabilize
+               //properties for analyze
                mlt_properties_set(properties, "shakiness", "4");
                mlt_properties_set(properties, "accuracy", "4");
                mlt_properties_set(properties, "stepsize", "6");
@@ -391,7 +415,7 @@ mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type,
                mlt_properties_set(properties, "show", "0");
                mlt_properties_set(properties, "tripod", "0");
 
-               // properties for transform
+               // properties for apply
                mlt_properties_set(properties, "smoothing", "15");
                mlt_properties_set(properties, "maxshift", "-1");
                mlt_properties_set(properties, "maxangle", "-1");
@@ -404,7 +428,20 @@ mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type,
 
                mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
        }
+       else
+       {
+               if( filter )
+               {
+                       mlt_filter_close( filter );
+               }
+
+               if( data )
+               {
+                       free( data );
+               }
 
+               filter = NULL;
+       }
        return filter;
 }