#include <framework/mlt_frame.h>
#include <framework/mlt_log.h>
#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/stat.h>
-#define MIN(a,b) (a<b?a:b)
-#define MAX(a,b) (a>b?a:b)
+#include <string.h>
#include "stab/vector.h"
#include "stab/utils.h"
#include "stab/estimate.h"
-#include "stab/resample.h"
+#include "stab/resample.h"
typedef struct {
mlt_filter parent;
int initialized;
+ int* lanc_kernels;
+ es_ctx *es;
+ vc *pos_i;
vc *pos_h;
vc *pos_y;
rs_ctx *rs;
} *videostab;
-int load_vc_from_file(videostab self, const char* filename ,vc* pos,int maxlength){
-
- struct stat stat_buff;
- stat (filename,&stat_buff);
- if (S_ISREG(stat_buff.st_mode) ){
- //load file
- mlt_log_verbose(NULL,"loading file %s\n",filename);
- FILE *infile;
- infile=fopen(filename,"r");
- if (infile){
- int i;
- for (i=0;i< maxlength;i++){
- float x,y;
- fscanf(infile,"%f%f",&x,&y);
- self->pos_h[i].x=x;
- self->pos_h[i].y=y;
- //pos_h[i]=vc_set(x,y);
- }
- fclose(infile);
- return 1;
+static void serialize_vectors( videostab self, mlt_position length )
+{
+ mlt_geometry g = mlt_geometry_init();
+
+ if ( g )
+ {
+ struct mlt_geometry_item_s item;
+ int i;
+
+ // Initialize geometry item
+ item.key = item.f[0] = item.f[1] = 1;
+ item.f[2] = item.f[3] = item.f[4] = 0;
+
+ for ( i = 0; i < length; i++ )
+ {
+ // Set the geometry item
+ item.frame = i;
+ item.x = self->pos_h[i].x;
+ item.y = self->pos_h[i].y;
+
+ // Add the geometry item
+ mlt_geometry_insert( g, &item );
}
+
+ // Put the analysis results in a property
+ mlt_geometry_set_length( g, length );
+ mlt_properties_set_data( MLT_FILTER_PROPERTIES( self->parent ), "vectors", g, 0,
+ (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise );
}
- return 0;
}
-int save_vc_to_file(videostab self, const char* filename ,vc* pos,int length){
+static void deserialize_vectors( videostab self, char *vectors, mlt_position length )
+{
+ mlt_geometry g = mlt_geometry_init();
- FILE *outfile;
- int i=0;
- outfile=fopen(filename,"w+");
- if (!outfile){
- mlt_log_error(NULL,"could not save shakefile %s\n",filename);
- return -1;
+ // Parse the property as a geometry
+ if ( g && !mlt_geometry_parse( g, vectors, length, -1, -1 ) )
+ {
+ struct mlt_geometry_item_s item;
+ int i;
+
+ // Copy the geometry items to a vc array for interp()
+ for ( i = 0; i < length; i++ )
+ {
+ mlt_geometry_fetch( g, &item, i );
+ self->pos_h[i].x = item.x;
+ self->pos_h[i].y = item.y;
+ }
}
- for (i=0;i< length ;i++){
- fprintf(outfile,"%f %f\n",pos[i].x,pos[i].y);
- //mlt_log_verbose(NULL,"writing %d/%d %f %f\n",i,length,pos[i].x,pos[i].y);
+ else
+ {
+ mlt_log_warning( MLT_FILTER_SERVICE(self->parent), "failed to parse vectors\n" );
}
- fclose(outfile);
- return 0;
-}
-
-int load_or_generate_pos_h(videostab self, mlt_frame this,int *h,int *w,int tfs, int fps){
- int i=0;
- char shakefile[2048];
- mlt_producer producer = mlt_frame_get_original_producer(this) ;
- mlt_properties prod_props= MLT_PRODUCER_PROPERTIES ( producer );
-
- mlt_producer parent_prod;
- if (mlt_properties_get_int(prod_props,"_cut") == 1 ){
- parent_prod=mlt_producer_cut_parent( producer);
- }else{
- // had no such case, but the is a fallback
- parent_prod = producer;
- }
- sprintf(shakefile,"%s%s", mlt_properties_get (MLT_PRODUCER_PROPERTIES(parent_prod), "resource"),".deshake");
- if (!load_vc_from_file( self, shakefile, self->pos_h, tfs)){
-
- mlt_log_verbose(this,"calculating deshake, please wait\n");
- mlt_image_format format = mlt_image_rgb24;
- vc* pos_i = (vc *)malloc(tfs * sizeof(vc));
- es_ctx *es1=es_init(*w,*h);
- for (i=0;i< tfs;i++){
- mlt_producer_seek(producer,i);
- mlt_frame frame;
- mlt_service_get_frame( mlt_producer_service(producer), &frame, 0 );
- uint8_t *buffer= NULL;
- int error = mlt_frame_get_image( frame, &buffer, &format, w, h, 1 );
-
- pos_i[i] = vc_add( i > 0 ? pos_i[i - 1] : vc_set(0.0, 0.0), es_estimate(es1, buffer));
- }
- hipass(pos_i, self->pos_h, tfs, fps);
- free (pos_i);
- free(es1);
- save_vc_to_file( self, shakefile, self->pos_h,tfs);
- }
- return 0;
+ // We are done with this mlt_geometry
+ if ( g ) mlt_geometry_close( g );
}
-static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
{
-
- mlt_filter filter = mlt_frame_pop_service( this );
- videostab self = filter->child;
+ mlt_filter filter = mlt_frame_pop_service( frame );
*format = mlt_image_rgb24;
- int error = mlt_frame_get_image( this, image, format, width, height, 1 );
- int in,out,length;
- mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
- mlt_producer producer = mlt_frame_get_original_producer(this);
-
- mlt_properties prod_props= MLT_PRODUCER_PROPERTIES ( producer );
- mlt_position pos = mlt_filter_get_position( filter, this );
- int opt_shutter_angle=mlt_properties_get_int ( MLT_FRAME_PROPERTIES (this) , "shutterangle") ;
-
+ mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 );
+ int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
- if ( error == 0 && *image )
+ if ( !error && *image )
{
+ videostab self = filter->child;
+ mlt_position length = mlt_filter_get_length2( filter, frame );
int h = *height;
int w = *width;
- //double position = mlt_filter_get_progress( filter, this );
-
- mlt_properties pro=prod_props;
- in=mlt_properties_get_int( pro, "in" );
- out=mlt_properties_get_int( pro, "out" );
- length=mlt_properties_get_int( pro, "length" );
- mlt_log_verbose(filter,"deshaking for in=%d out=%d length=%d\n",in,out,length);
- if (!self->initialized){
- int fps = mlt_profile_fps( profile );
-
- self->pos_h = (vc *)malloc(length * sizeof(vc));
-
- self->pos_y = (vc *)malloc(h * sizeof(vc));
- self->rs = rs_init(w, h);
- self->initialized=1;
-
- load_or_generate_pos_h(self, this,&h,&w,length,fps/2);
-
+ // Service locks are for concurrency control
+ mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
+ if ( !self->initialized )
+ {
+ // Initialize our context
+ self->initialized = 1;
+ self->es = es_init( w, h );
+ self->pos_i = (vc*) malloc( length * sizeof(vc) );
+ self->pos_h = (vc*) malloc( length * sizeof(vc) );
+ self->pos_y = (vc*) malloc( h * sizeof(vc) );
+ self->rs = rs_init( w, h );
}
- if (self->initialized>=1){
- int i;
- for (i = 0; i < h; i ++) {
- self->pos_y[i] = interp( self->pos_h, length, pos + (i - h / 2.0) * opt_shutter_angle / (h * 360.0));
+ char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" );
+ if ( !vectors )
+ {
+ // Analyse
+ int pos = (int) mlt_filter_get_position( filter, frame );
+ self->pos_i[pos] = vc_add( pos == 0 ? vc_zero() : self->pos_i[pos - 1], es_estimate( self->es, *image ) );
+
+ // On last frame
+ if ( pos == length - 1 )
+ {
+ mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) );
+ double fps = mlt_profile_fps( profile );
+
+ // Filter and store the results
+ hipass( self->pos_i, self->pos_h, length, fps );
+ serialize_vectors( self, length );
+ }
+ } else {
+ // Apply
+ if ( self->initialized != 2 )
+ {
+ // Load analysis results from property
+ self->initialized = 2;
+ deserialize_vectors( self, vectors, length );
+ }
+ if ( self->initialized == 2 )
+ {
+ // Stabilize
+ float shutter_angle = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame) , "shutterangle" );
+ float pos = mlt_filter_get_position( filter, frame );
+ int i;
+
+ for (i = 0; i < h; i ++)
+ self->pos_y[i] = interp( self->lanc_kernels,self->pos_h, length, pos + (i - h / 2.0) * shutter_angle / (h * 360.0) );
+ rs_resample( self->lanc_kernels,self->rs, *image, self->pos_y );
}
- rs_resample( self->rs, *image, self->pos_y );
- self->initialized=2;
}
-
+ mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
}
return error;
}
-static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
{
- mlt_frame_push_service( frame, this );
+ mlt_frame_push_service( frame, filter );
mlt_frame_push_get_image( frame, filter_get_image );
return frame;
}
void filter_close( mlt_filter parent )
{
- mlt_service service = MLT_FILTER_SERVICE( parent );
videostab self = parent->child;
- if ( self->pos_h ) free(self->pos_h);
- if ( self->pos_y ) free(self->pos_y);
- if ( self->rs ) rs_free(self->rs);
- free_lanc_kernels();
- service->close = NULL;
- mlt_service_close( service );
+ if ( self->es ) es_free( self->es );
+ if ( self->pos_i ) free( self->pos_i );
+ if ( self->pos_h ) free( self->pos_h );
+ if ( self->pos_y ) free( self->pos_y );
+ if ( self->rs ) rs_free( self->rs );
+ if ( self->lanc_kernels) free_lanc_kernels(self->lanc_kernels);
free( self );
+ parent->close = NULL;
+ parent->child = NULL;
}
mlt_filter filter_videostab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
if ( self )
{
mlt_filter parent = mlt_filter_new();
+ if ( !parent )
+ {
+ free( self );
+ return NULL;
+ }
parent->child = self;
parent->close = filter_close;
parent->process = filter_process;
+ self->parent = parent;
mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shutterangle", "0" ); // 0 - 180 , default 0
- prepare_lanc_kernels();
+ self->lanc_kernels=prepare_lanc_kernels();
return parent;
}
return NULL;