bunch-o compiler bitch'n
[goodguy/history.git] / cinelerra-5.1 / plugins / motion / motion.C
index 447ed2d6a55ba29a5b5177bf6f180fd6df48647b..db9235eccb4ecf2ddf6a973ca90b3e33fb1cf438 100644 (file)
@@ -27,6 +27,7 @@
 #include "filexml.h"
 #include "keyframe.h"
 #include "language.h"
+#include "mainerror.h"
 #include "motion.h"
 #include "motionscan.h"
 #include "motionwindow.h"
 REGISTER_PLUGIN(MotionMain)
 
 
-#undef DEBUG
-
-// #ifndef DEBUG
-// #define DEBUG
-// #endif
-
-
+//#define DEBUG
 
 MotionConfig::MotionConfig()
 {
-       global_range_w = 5;
-       global_range_h = 5;
-       rotation_range = 5;
+       global_range_w = 25; //5;
+       global_range_h = 25; //5;
+       rotation_range = 8; //5;
        rotation_center = 0;
        block_count = 1;
-       global_block_w = MIN_BLOCK;
-       global_block_h = MIN_BLOCK;
-//     rotation_block_w = MIN_BLOCK;
-//     rotation_block_h = MIN_BLOCK;
+       global_block_w = 33; //MIN_BLOCK;
+       global_block_h = 33; //MIN_BLOCK;
        block_x = 50;
        block_y = 50;
        global_positions = 256;
-       rotate_positions = 4;
+       rotate_positions = 8; // 4;
        magnitude = 100;
-       rotate_magnitude = 90;
-       return_speed = 0;
-       rotate_return_speed = 0;
+       rotate_magnitude = 30;
+       return_speed = 5; //0;
+       rotate_return_speed = 5; //0;
        action_type = MotionScan::STABILIZE;
        global = 1;
        rotate = 1;
        addtrackedframeoffset = 0;
-       tracking_type = MotionScan::NO_CALCULATE;
-       draw_vectors = 1;
-       tracking_object = MotionScan::TRACK_SINGLE;
+       strcpy(tracking_file, TRACKING_FILE);
+       tracking_type = MotionScan::SAVE; //MotionScan::NO_CALCULATE;
+       tracking_object = MotionScan::TRACK_PREVIOUS; //TRACK_SINGLE;
+       draw_vectors = 1; //0;
        track_frame = 0;
        bottom_is_master = 1;
        horizontal_only = 0;
        vertical_only = 0;
 }
 
+
 void MotionConfig::boundaries()
 {
        CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
@@ -91,8 +86,6 @@ void MotionConfig::boundaries()
        CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
        CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
        CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
-//     CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
-//     CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
 }
 
 int MotionConfig::equivalent(MotionConfig &that)
@@ -102,15 +95,12 @@ int MotionConfig::equivalent(MotionConfig &that)
                rotation_range == that.rotation_range &&
                rotation_center == that.rotation_center &&
                action_type == that.action_type &&
-               global == that.global &&
-               rotate == that.rotate &&
+               global == that.global && rotate == that.rotate &&
                addtrackedframeoffset == that.addtrackedframeoffset &&
                draw_vectors == that.draw_vectors &&
                block_count == that.block_count &&
                global_block_w == that.global_block_w &&
                global_block_h == that.global_block_h &&
-//             rotation_block_w == that.rotation_block_w &&
-//             rotation_block_h == that.rotation_block_h &&
                EQUIV(block_x, that.block_x) &&
                EQUIV(block_y, that.block_y) &&
                global_positions == that.global_positions &&
@@ -145,8 +135,6 @@ void MotionConfig::copy_from(MotionConfig &that)
        rotate_positions = that.rotate_positions;
        global_block_w = that.global_block_w;
        global_block_h = that.global_block_h;
-//     rotation_block_w = that.rotation_block_w;
-//     rotation_block_h = that.rotation_block_h;
        magnitude = that.magnitude;
        return_speed = that.return_speed;
        rotate_magnitude = that.rotate_magnitude;
@@ -158,62 +146,13 @@ void MotionConfig::copy_from(MotionConfig &that)
        vertical_only = that.vertical_only;
 }
 
-void MotionConfig::interpolate(MotionConfig &prev,
-       MotionConfig &next,
-       int64_t prev_frame,
-       int64_t next_frame,
-       int64_t current_frame)
+void MotionConfig::interpolate(MotionConfig &prev, MotionConfig &next,
+       int64_t prev_frame, int64_t next_frame, int64_t current_frame)
 {
-       //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
-       //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
-       this->block_x = prev.block_x;
-       this->block_y = prev.block_y;
-       global_range_w = prev.global_range_w;
-       global_range_h = prev.global_range_h;
-       rotation_range = prev.rotation_range;
-       rotation_center = prev.rotation_center;
-       action_type = prev.action_type;
-       global = prev.global;
-       rotate = prev.rotate;
-       addtrackedframeoffset = prev.addtrackedframeoffset;
-       tracking_type = prev.tracking_type;
-       draw_vectors = prev.draw_vectors;
-       block_count = prev.block_count;
-       global_positions = prev.global_positions;
-       rotate_positions = prev.rotate_positions;
-       global_block_w = prev.global_block_w;
-       global_block_h = prev.global_block_h;
-//     rotation_block_w = prev.rotation_block_w;
-//     rotation_block_h = prev.rotation_block_h;
-       magnitude = prev.magnitude;
-       return_speed = prev.return_speed;
-       rotate_magnitude = prev.rotate_magnitude;
-       rotate_return_speed = prev.rotate_return_speed;
-       tracking_object = prev.tracking_object;
-       track_frame = prev.track_frame;
-       bottom_is_master = prev.bottom_is_master;
-       horizontal_only = prev.horizontal_only;
-       vertical_only = prev.vertical_only;
+       copy_from(prev);
 }
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 MotionMain::MotionMain(PluginServer *server)
  : PluginVClient(server)
 {
@@ -234,6 +173,16 @@ MotionMain::MotionMain(PluginServer *server)
        global_target_src = 0;
        global_target_dst = 0;
 
+       cache_file[0] = 0;
+       cache_fp = active_fp = 0;
+       cache_line[0] = 0;
+       cache_key = active_key = -1;
+       dx_offset = dy_offset = 0;
+       load_ok = 0;
+       save_dx = load_dx = 0;
+       save_dy = load_dy = 0;
+       save_dt = load_dt = 0;
+       tracking_frame = -1;
        prev_rotate_ref = 0;
        current_rotate_ref = 0;
        rotate_target_src = 0;
@@ -250,12 +199,13 @@ MotionMain::~MotionMain()
        delete rotate_engine;
        delete motion_rotate;
 
-
        delete prev_global_ref;
        delete current_global_ref;
        delete global_target_src;
        delete global_target_dst;
 
+       reset_cache_file();
+
        delete prev_rotate_ref;
        delete current_rotate_ref;
        delete rotate_target_src;
@@ -275,57 +225,51 @@ LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig)
 
 void MotionMain::update_gui()
 {
-       if(thread)
-       {
-               if(load_configuration())
-               {
-                       thread->window->lock_window("MotionMain::update_gui");
-
-                       char string[BCTEXTLEN];
-                       sprintf(string, "%d", config.global_positions);
-                       ((MotionWindow*)thread->window)->global_search_positions->set_text(string);
-                       sprintf(string, "%d", config.rotate_positions);
-                       ((MotionWindow*)thread->window)->rotation_search_positions->set_text(string);
-
-                       ((MotionWindow*)thread->window)->global_block_w->update(config.global_block_w);
-                       ((MotionWindow*)thread->window)->global_block_h->update(config.global_block_h);
-//                     ((MotionWindow*)thread->window)->rotation_block_w->update(config.rotation_block_w);
-//                     ((MotionWindow*)thread->window)->rotation_block_h->update(config.rotation_block_h);
-                       ((MotionWindow*)thread->window)->block_x->update(config.block_x);
-                       ((MotionWindow*)thread->window)->block_y->update(config.block_y);
-                       ((MotionWindow*)thread->window)->block_x_text->update((float)config.block_x);
-                       ((MotionWindow*)thread->window)->block_y_text->update((float)config.block_y);
-                       ((MotionWindow*)thread->window)->magnitude->update(config.magnitude);
-                       ((MotionWindow*)thread->window)->return_speed->update(config.return_speed);
-                       ((MotionWindow*)thread->window)->rotate_magnitude->update(config.rotate_magnitude);
-                       ((MotionWindow*)thread->window)->rotate_return_speed->update(config.rotate_return_speed);
-                       ((MotionWindow*)thread->window)->rotation_range->update(config.rotation_range);
-                       ((MotionWindow*)thread->window)->rotation_center->update(config.rotation_center);
-
-
-                       ((MotionWindow*)thread->window)->track_single->update(config.tracking_object == MotionScan::TRACK_SINGLE);
-                       ((MotionWindow*)thread->window)->track_frame_number->update(config.track_frame);
-                       ((MotionWindow*)thread->window)->track_previous->update(config.tracking_object == MotionScan::TRACK_PREVIOUS);
-                       ((MotionWindow*)thread->window)->previous_same->update(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK);
-                       if(config.tracking_object != MotionScan::TRACK_SINGLE)
-                               ((MotionWindow*)thread->window)->track_frame_number->disable();
-                       else
-                               ((MotionWindow*)thread->window)->track_frame_number->enable();
-
-                       ((MotionWindow*)thread->window)->action_type->set_text(
-                               ActionType::to_text(config.action_type));
-                       ((MotionWindow*)thread->window)->tracking_type->set_text(
-                               TrackingType::to_text(config.tracking_type));
-                       ((MotionWindow*)thread->window)->track_direction->set_text(
-                               TrackDirection::to_text(config.horizontal_only, config.vertical_only));
-                       ((MotionWindow*)thread->window)->master_layer->set_text(
-                               MasterLayer::to_text(config.bottom_is_master));
-
-
-                       ((MotionWindow*)thread->window)->update_mode();
-                       thread->window->unlock_window();
-               }
-       }
+       if( !thread ) return;
+       if( !load_configuration() ) return;
+       thread->window->lock_window("MotionMain::update_gui");
+       MotionWindow *window = (MotionWindow*)thread->window;
+
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", config.global_positions);
+       window->global_search_positions->set_text(string);
+       sprintf(string, "%d", config.rotate_positions);
+       window->rotation_search_positions->set_text(string);
+
+       window->global_block_w->update(config.global_block_w);
+       window->global_block_h->update(config.global_block_h);
+       window->block_x->update(config.block_x);
+       window->block_y->update(config.block_y);
+       window->block_x_text->update((float)config.block_x);
+       window->block_y_text->update((float)config.block_y);
+       window->magnitude->update(config.magnitude);
+       window->return_speed->update(config.return_speed);
+       window->rotate_magnitude->update(config.rotate_magnitude);
+       window->rotate_return_speed->update(config.rotate_return_speed);
+       window->rotation_range->update(config.rotation_range);
+       window->rotation_center->update(config.rotation_center);
+
+
+       window->track_single->update(config.tracking_object == MotionScan::TRACK_SINGLE);
+       window->track_frame_number->update(config.track_frame);
+       window->track_previous->update(config.tracking_object == MotionScan::TRACK_PREVIOUS);
+       window->previous_same->update(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK);
+       if( config.tracking_object != MotionScan::TRACK_SINGLE )
+               window->track_frame_number->disable();
+       else
+               window->track_frame_number->enable();
+
+       window->action_type->set_text(
+               ActionType::to_text(config.action_type));
+       window->tracking_type->set_text(
+               TrackingType::to_text(config.tracking_type));
+       window->track_direction->set_text(
+               TrackDirection::to_text(config.horizontal_only, config.vertical_only));
+       window->master_layer->set_text(
+               MasterLayer::to_text(config.bottom_is_master));
+
+       window->update_mode();
+       thread->window->unlock_window();
 }
 
 
@@ -344,8 +288,6 @@ void MotionMain::save_data(KeyFrame *keyframe)
        output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
        output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
        output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
-//     output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
-//     output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
        output.tag.set_property("BLOCK_X", config.block_x);
        output.tag.set_property("BLOCK_Y", config.block_y);
        output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
@@ -360,6 +302,7 @@ void MotionMain::save_data(KeyFrame *keyframe)
        output.tag.set_property("GLOBAL", config.global);
        output.tag.set_property("ROTATE", config.rotate);
        output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
+       output.tag.set_property("TRACKING_FILE", config.tracking_file);
        output.tag.set_property("TRACKING_TYPE", config.tracking_type);
        output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
        output.tag.set_property("TRACKING_OBJECT", config.tracking_object);
@@ -376,153 +319,107 @@ void MotionMain::save_data(KeyFrame *keyframe)
 void MotionMain::read_data(KeyFrame *keyframe)
 {
        FileXML input;
-
        input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
-
        int result = 0;
 
-       while(!result)
-       {
-               result = input.read_tag();
-
-               if(!result)
-               {
-                       if(input.tag.title_is("MOTION"))
-                       {
-                               config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
-                               config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
-                               config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
-                               config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
-                               config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
-//                             config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
-//                             config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
-                               config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
-                               config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
-                               config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
-                               config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
-                               config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
-                               config.rotation_center = input.tag.get_property("ROTATION_CENTER", config.rotation_center);
-                               config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
-                               config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
-                               config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
-                               config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
-                               config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type);
-                               config.global = input.tag.get_property("GLOBAL", config.global);
-                               config.rotate = input.tag.get_property("ROTATE", config.rotate);
-                               config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
-                               config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type);
-                               config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
-                               config.tracking_object = input.tag.get_property("TRACKING_OBJECT", config.tracking_object);
-                               config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
-                               config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
-                               config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
-                               config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
-                       }
+       while( !(result = input.read_tag()) ) {
+               if( input.tag.title_is("MOTION") ) {
+                       config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
+                       config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
+                       config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
+                       config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
+                       config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
+                       config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
+                       config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
+                       config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
+                       config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
+                       config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
+                       config.rotation_center = input.tag.get_property("ROTATION_CENTER", config.rotation_center);
+                       config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
+                       config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
+                       config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
+                       config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
+                       config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type);
+                       config.global = input.tag.get_property("GLOBAL", config.global);
+                       config.rotate = input.tag.get_property("ROTATE", config.rotate);
+                       config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
+                       input.tag.get_property("TRACKING_FILE", config.tracking_file);
+                       config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type);
+                       config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
+                       config.tracking_object = input.tag.get_property("TRACKING_OBJECT", config.tracking_object);
+                       config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
+                       config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
+                       config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
+                       config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
                }
        }
        config.boundaries();
 }
 
-
-
-
-
-
-
-
-
 void MotionMain::allocate_temp(int w, int h, int color_model)
 {
-       if(temp_frame &&
-               (temp_frame->get_w() != w ||
-               temp_frame->get_h() != h))
-       {
+       if( temp_frame &&
+           ( temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
                delete temp_frame;
                temp_frame = 0;
        }
-       if(!temp_frame)
+       if( !temp_frame )
                temp_frame = new VFrame(w, h, color_model);
 }
 
-
 void MotionMain::process_global()
 {
 
-       if(!engine) engine = new MotionScan(PluginClient::get_project_smp() + 1,
+       if( !engine ) engine = new MotionScan(PluginClient::get_project_smp() + 1,
                PluginClient::get_project_smp() + 1);
 
 // Determine if frames changed
-       engine->scan_frame(current_global_ref,
-               prev_global_ref,
-               config.global_range_w,
-               config.global_range_h,
-               config.global_block_w,
-               config.global_block_h,
-               config.block_x,
-               config.block_y,
-               config.tracking_object,
-               config.tracking_type,
-               config.action_type,
-               config.horizontal_only,
-               config.vertical_only,
-               get_source_position(),
-               config.global_positions,
-               total_dx,
-               total_dy,
-               0,
-               0);
-       current_dx = engine->dx_result;
-       current_dy = engine->dy_result;
+       engine->scan_frame(current_global_ref, prev_global_ref,
+               config.global_range_w, config.global_range_h,
+               config.global_block_w, config.global_block_h,
+               config.block_x, config.block_y,
+               config.tracking_object, config.tracking_type,
+               config.action_type, config.horizontal_only,
+               config.vertical_only, get_source_position(),
+               config.global_positions, total_dx, total_dy,
+               0, 0, load_ok, load_dx, load_dy);
+       current_dx = (engine->dx_result += dx_offset);
+       current_dy = (engine->dy_result += dy_offset);
+
+// Write results
+       if( config.tracking_type == MotionScan::SAVE ) {
+               save_dx = engine->dx_result;
+               save_dy = engine->dy_result;
+       }
 
 // Add current motion vector to accumulation vector.
-       if(config.tracking_object != MotionScan::TRACK_SINGLE)
-       {
+       if( config.tracking_object != MotionScan::TRACK_SINGLE ) {
 // Retract over time
                total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
                total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
                total_dx += engine->dx_result;
                total_dy += engine->dy_result;
 // printf("MotionMain::process_global total_dx=%d engine->dx_result=%d\n",
-// total_dx,
-// engine->dx_result);
+// total_dx, engine->dx_result);
        }
-       else
+       else {
 // Make accumulation vector current
-       {
                total_dx = engine->dx_result;
                total_dy = engine->dy_result;
        }
 
 // Clamp accumulation vector
-       if(config.magnitude < 100)
-       {
-               //int block_w = (int64_t)config.global_block_w *
-               //              current_global_ref->get_w() / 100;
-               //int block_h = (int64_t)config.global_block_h *
-               //              current_global_ref->get_h() / 100;
-               int block_x_orig = (int64_t)(config.block_x *
-                       current_global_ref->get_w() /
-                       100);
-               int block_y_orig = (int64_t)(config.block_y *
-                       current_global_ref->get_h() /
-                       100);
-
-               int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
-                       OVERSAMPLE *
-                       config.magnitude /
-                       100;
-               int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
-                       OVERSAMPLE *
-                       config.magnitude /
-                       100;
-               int min_block_x = (int64_t)-block_x_orig *
-                       OVERSAMPLE *
-                       config.magnitude /
-                       100;
-               int min_block_y = (int64_t)-block_y_orig *
-                       OVERSAMPLE *
-                       config.magnitude /
-                       100;
+       if( config.magnitude < 100 ) {
+               int block_x_orig = (int64_t)(config.block_x * current_global_ref->get_w() / 100);
+               int block_y_orig = (int64_t)(config.block_y * current_global_ref->get_h() / 100);
+               int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig)
+                       * OVERSAMPLE * config.magnitude / 100;
+               int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig)
+                       * OVERSAMPLE * config.magnitude / 100;
+               int min_block_x = (int64_t)-block_x_orig
+                       * OVERSAMPLE * config.magnitude / 100;
+               int min_block_y = (int64_t)-block_y_orig
+                       * OVERSAMPLE * config.magnitude / 100;
 
                CLAMP(total_dx, min_block_x, max_block_x);
                CLAMP(total_dy, min_block_y, max_block_y);
@@ -530,12 +427,10 @@ void MotionMain::process_global()
 
 #ifdef DEBUG
 printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
-(float)total_dx / OVERSAMPLE,
-(float)total_dy / OVERSAMPLE);
+  (float)total_dx / OVERSAMPLE, (float)total_dy / OVERSAMPLE);
 #endif
 
-       if(config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate)
-       {
+       if( config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate ) {
 // Transfer current reference frame to previous reference frame and update
 // counter.  Must wait for rotate to compare.
                prev_global_ref->copy_from(current_global_ref);
@@ -544,55 +439,44 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
 
 // Decide what to do with target based on requested operation
        int interpolation = NEAREST_NEIGHBOR;
-       float dx = 0.;
-       float dy = 0.;
-       switch(config.action_type)
-       {
-               case MotionScan::NOTHING:
-                       global_target_dst->copy_from(global_target_src);
-                       break;
-               case MotionScan::TRACK_PIXEL:
-                       interpolation = NEAREST_NEIGHBOR;
-                       dx = (int)(total_dx / OVERSAMPLE);
-                       dy = (int)(total_dy / OVERSAMPLE);
-                       break;
-               case MotionScan::STABILIZE_PIXEL:
-                       interpolation = NEAREST_NEIGHBOR;
-                       dx = -(int)(total_dx / OVERSAMPLE);
-                       dy = -(int)(total_dy / OVERSAMPLE);
-                       break;
-                       break;
-               case MotionScan::TRACK:
-                       interpolation = CUBIC_LINEAR;
-                       dx = (float)total_dx / OVERSAMPLE;
-                       dy = (float)total_dy / OVERSAMPLE;
-                       break;
-               case MotionScan::STABILIZE:
-                       interpolation = CUBIC_LINEAR;
-                       dx = -(float)total_dx / OVERSAMPLE;
-                       dy = -(float)total_dy / OVERSAMPLE;
-                       break;
+       float dx = 0., dy = 0.;
+       switch(config.action_type) {
+       case MotionScan::NOTHING:
+               global_target_dst->copy_from(global_target_src);
+               break;
+       case MotionScan::TRACK_PIXEL:
+               interpolation = NEAREST_NEIGHBOR;
+               dx = (int)(total_dx / OVERSAMPLE);
+               dy = (int)(total_dy / OVERSAMPLE);
+               break;
+       case MotionScan::STABILIZE_PIXEL:
+               interpolation = NEAREST_NEIGHBOR;
+               dx = -(int)(total_dx / OVERSAMPLE);
+               dy = -(int)(total_dy / OVERSAMPLE);
+               break;
+       case MotionScan::TRACK:
+               interpolation = CUBIC_LINEAR;
+               dx = (float)total_dx / OVERSAMPLE;
+               dy = (float)total_dy / OVERSAMPLE;
+               break;
+       case MotionScan::STABILIZE:
+               interpolation = CUBIC_LINEAR;
+               dx = -(float)total_dx / OVERSAMPLE;
+               dy = -(float)total_dy / OVERSAMPLE;
+               break;
        }
 
 
-       if(config.action_type != MotionScan::NOTHING)
-       {
-               if(!overlayer)
+       if( config.action_type != MotionScan::NOTHING ) {
+               if( !overlayer )
                        overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
                global_target_dst->clear_frame();
-               overlayer->overlay(global_target_dst,
-                       global_target_src,
-                       0,
-                       0,
-                       global_target_src->get_w(),
-                       global_target_src->get_h(),
-                       dx,
-                       dy,
+               overlayer->overlay(global_target_dst, global_target_src,
+                       0, 0, global_target_src->get_w(), global_target_src->get_h(),
+                       dx, dy,
                        (float)global_target_src->get_w() + dx,
                        (float)global_target_src->get_h() + dy,
-                       1,
-                       TRANSFER_REPLACE,
-                       interpolation);
+                       1, TRANSFER_REPLACE, interpolation);
        }
 }
 
@@ -600,112 +484,81 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
 
 void MotionMain::process_rotation()
 {
-       int block_x;
-       int block_y;
+       int block_x, block_y;
 
 // Convert the previous global reference into the previous rotation reference.
 // Convert global target destination into rotation target source.
-       if(config.global)
-       {
-               if(!overlayer)
+       if( config.global ) {
+               if( !overlayer )
                        overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
-               float dx;
-               float dy;
-               if(config.tracking_object == MotionScan::TRACK_SINGLE)
-               {
+               float dx, dy;
+               if( config.tracking_object == MotionScan::TRACK_SINGLE ) {
                        dx = (float)total_dx / OVERSAMPLE;
                        dy = (float)total_dy / OVERSAMPLE;
                }
-               else
-               {
+               else {
                        dx = (float)current_dx / OVERSAMPLE;
                        dy = (float)current_dy / OVERSAMPLE;
                }
 
                prev_rotate_ref->clear_frame();
-               overlayer->overlay(prev_rotate_ref,
-                       prev_global_ref,
-                       0,
-                       0,
-                       prev_global_ref->get_w(),
-                       prev_global_ref->get_h(),
-                       dx,
-                       dy,
+               overlayer->overlay(prev_rotate_ref, prev_global_ref,
+                       0, 0, prev_global_ref->get_w(), prev_global_ref->get_h(),
+                       dx, dy,
                        (float)prev_global_ref->get_w() + dx,
                        (float)prev_global_ref->get_h() + dy,
-                       1,
-                       TRANSFER_REPLACE,
-                       CUBIC_LINEAR);
+                       1, TRANSFER_REPLACE, CUBIC_LINEAR);
 // Pivot is destination global position
                block_x = (int)(prev_rotate_ref->get_w() *
-                       config.block_x /
-                       100 +
-                       (float)total_dx /
-                       OVERSAMPLE);
+                       config.block_x / 100 + (float)total_dx / OVERSAMPLE);
                block_y = (int)(prev_rotate_ref->get_h() *
-                       config.block_y /
-                       100 +
-                       (float)total_dy /
-                       OVERSAMPLE);
+                       config.block_y / 100 + (float)total_dy / OVERSAMPLE);
 // Use the global target output as the rotation target input
                rotate_target_src->copy_from(global_target_dst);
 // Transfer current reference frame to previous reference frame for global.
-               if(config.tracking_object != MotionScan::TRACK_SINGLE)
-               {
+               if( config.tracking_object != MotionScan::TRACK_SINGLE ) {
                        prev_global_ref->copy_from(current_global_ref);
                        previous_frame_number = get_source_position();
                }
        }
-       else
-       {
+       else {
 // Pivot is fixed
-               block_x = (int)(prev_rotate_ref->get_w() *
-                       config.block_x /
-                       100);
-               block_y = (int)(prev_rotate_ref->get_h() *
-                       config.block_y /
-                       100);
+               block_x = (int)(prev_rotate_ref->get_w() * config.block_x / 100);
+               block_y = (int)(prev_rotate_ref->get_h() * config.block_y / 100);
        }
 
-
-
 // Get rotation
-       if(!motion_rotate)
+       if( !motion_rotate )
                motion_rotate = new RotateScan(this,
-                       get_project_smp() + 1,
-                       get_project_smp() + 1);
-
-       current_angle = motion_rotate->scan_frame(prev_rotate_ref,
-               current_rotate_ref,
-               block_x,
-               block_y);
+                       get_project_smp() + 1, get_project_smp() + 1);
 
+       current_angle = motion_rotate->
+               scan_frame(prev_rotate_ref, current_rotate_ref, block_x, block_y);
 
+// Write results
+       if( config.tracking_type == MotionScan::SAVE ) {
+               save_dt = current_angle;
+       }
 
 // Add current rotation to accumulation
-       if(config.tracking_object != MotionScan::TRACK_SINGLE)
-       {
+       if( config.tracking_object != MotionScan::TRACK_SINGLE ) {
 // Retract over time
                total_angle = total_angle * (100 - config.rotate_return_speed) / 100;
 // Accumulate current rotation
                total_angle += current_angle;
 
 // Clamp rotation accumulation
-               if(config.rotate_magnitude < 90)
-               {
+               if( config.rotate_magnitude < 90 ) {
                        CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude);
                }
 
-               if(!config.global)
-               {
-// Transfer current reference frame to previous reference frame and update
-// counter.
+               if( !config.global ) {
+// Transfer current reference frame to previous reference frame and update counter.
                        prev_rotate_ref->copy_from(current_rotate_ref);
                        previous_frame_number = get_source_position();
                }
        }
-       else
-       {
+       else {
                total_angle = current_angle;
        }
 
@@ -716,288 +569,202 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
 
 // Calculate rotation parameters based on requested operation
        float angle = 0.;
-       switch(config.action_type)
-       {
-               case MotionScan::NOTHING:
-                       rotate_target_dst->copy_from(rotate_target_src);
-                       break;
-               case MotionScan::TRACK:
-               case MotionScan::TRACK_PIXEL:
-                       angle = total_angle;
-                       break;
-               case MotionScan::STABILIZE:
-               case MotionScan::STABILIZE_PIXEL:
-                       angle = -total_angle;
-                       break;
+       switch(config.action_type) {
+       case MotionScan::NOTHING:
+               rotate_target_dst->copy_from(rotate_target_src);
+               break;
+       case MotionScan::TRACK:
+       case MotionScan::TRACK_PIXEL:
+               angle = total_angle;
+               break;
+       case MotionScan::STABILIZE:
+       case MotionScan::STABILIZE_PIXEL:
+               angle = -total_angle;
+               break;
        }
 
-
-
-       if(config.action_type != MotionScan::NOTHING)
-       {
-               if(!rotate_engine)
-                       rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
+       if( config.action_type != MotionScan::NOTHING ) {
+               if( !rotate_engine )
+                       rotate_engine = new AffineEngine(
+                               PluginClient::get_project_smp() + 1,
                                PluginClient::get_project_smp() + 1);
 
                rotate_target_dst->clear_frame();
 
 // Determine pivot based on a number of factors.
-               switch(config.action_type)
-               {
-                       case MotionScan::TRACK:
-                       case MotionScan::TRACK_PIXEL:
+               switch(config.action_type) {
+               case MotionScan::TRACK:
+               case MotionScan::TRACK_PIXEL:
 // Use destination of global tracking.
-//                             rotate_engine->set_pivot(block_x, block_y);
-                               rotate_engine->set_in_pivot(block_x, block_y);
-                               rotate_engine->set_out_pivot(block_x, block_y);
-                               break;
+                       rotate_engine->set_in_pivot(block_x, block_y);
+                       rotate_engine->set_out_pivot(block_x, block_y);
+                       break;
 
-                       case MotionScan::STABILIZE:
-                       case MotionScan::STABILIZE_PIXEL:
-                               if(config.global)
-                               {
+               case MotionScan::STABILIZE:
+               case MotionScan::STABILIZE_PIXEL:
+                       if( config.global ) {
 // Use origin of global stabilize operation
-//                                     rotate_engine->set_pivot((int)(rotate_target_dst->get_w() *
-//                                                     config.block_x /
-//                                                     100),
-//                                             (int)(rotate_target_dst->get_h() *
-//                                                     config.block_y /
-//                                                     100));
-                                       rotate_engine->set_in_pivot((int)(rotate_target_dst->get_w() *
-                                                       config.block_x /
-                                                       100),
-                                               (int)(rotate_target_dst->get_h() *
-                                                       config.block_y /
-                                                       100));
-                                       rotate_engine->set_out_pivot((int)(rotate_target_dst->get_w() *
-                                                       config.block_x /
-                                                       100),
-                                               (int)(rotate_target_dst->get_h() *
-                                                       config.block_y /
-                                                       100));
-
+                               rotate_engine->set_in_pivot(
+                                       (int)(rotate_target_dst->get_w() * config.block_x / 100),
+                                       (int)(rotate_target_dst->get_h() * config.block_y / 100));
+                               rotate_engine->set_out_pivot(
+                                       (int)(rotate_target_dst->get_w() * config.block_x / 100),
+                                       (int)(rotate_target_dst->get_h() * config.block_y / 100));
                                }
-                               else
-                               {
+                               else {
 // Use origin
-//                                     rotate_engine->set_pivot(block_x, block_y);
                                        rotate_engine->set_in_pivot(block_x, block_y);
                                        rotate_engine->set_out_pivot(block_x, block_y);
                                }
                                break;
                }
 
-
                rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
-// overlayer->overlay(rotate_target_dst,
-//     prev_rotate_ref,
-//     0,
-//     0,
-//     prev_rotate_ref->get_w(),
-//     prev_rotate_ref->get_h(),
-//     0,
-//     0,
-//     prev_rotate_ref->get_w(),
-//     prev_rotate_ref->get_h(),
-//     1,
-//     TRANSFER_NORMAL,
-//     CUBIC_LINEAR);
-// overlayer->overlay(rotate_target_dst,
-//     current_rotate_ref,
-//     0,
-//     0,
-//     prev_rotate_ref->get_w(),
-//     prev_rotate_ref->get_h(),
-//     0,
-//     0,
-//     prev_rotate_ref->get_w(),
-//     prev_rotate_ref->get_h(),
-//     1,
-//     TRANSFER_NORMAL,
-//     CUBIC_LINEAR);
-
-
+// overlayer->overlay(rotate_target_dst, prev_rotate_ref,
+//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
+//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
+//     1, TRANSFER_NORMAL, CUBIC_LINEAR);
+// overlayer->overlay(rotate_target_dst, current_rotate_ref,
+//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
+//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
+//     1, TRANSFER_NORMAL, //  CUBIC_LINEAR);
        }
-
-
 }
 
 
-
-
-
-
-
-
-
-int MotionMain::process_buffer(VFrame **frame,
-       int64_t start_position,
-       double frame_rate)
+int MotionMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
 {
+       int prev_config_tracking_type = config.tracking_type;
        int need_reconfigure = load_configuration();
        int color_model = frame[0]->get_color_model();
        w = frame[0]->get_w();
        h = frame[0]->get_h();
 
-
 #ifdef DEBUG
-printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_position);
+printf("MotionMain::process_buffer %d start_position=%jd\n", __LINE__, start_position);
 #endif
 
-
 // Calculate the source and destination pointers for each of the operations.
 // Get the layer to track motion in.
-       reference_layer = config.bottom_is_master ?
-               PluginClient::total_in_buffers - 1 :
-               0;
 // Get the layer to apply motion in.
+       reference_layer = config.bottom_is_master ?
+               PluginClient::total_in_buffers - 1 : 0;
        target_layer = config.bottom_is_master ?
-               0 :
-               PluginClient::total_in_buffers - 1;
-
+               0 : PluginClient::total_in_buffers - 1;
 
        output_frame = frame[target_layer];
-
-
 // Get the position of previous reference frame.
        int64_t actual_previous_number;
 // Skip if match frame not available
        int skip_current = 0;
 
-
-       if(config.tracking_object == MotionScan::TRACK_SINGLE)
-       {
+       if( config.tracking_object == MotionScan::TRACK_SINGLE ) {
                actual_previous_number = config.track_frame;
-               if(get_direction() == PLAY_REVERSE)
+               if( get_direction() == PLAY_REVERSE )
                        actual_previous_number++;
-               if(actual_previous_number == start_position)
+               if( actual_previous_number == start_position )
                        skip_current = 1;
        }
-       else
-       {
+       else {
                actual_previous_number = start_position;
-               if(get_direction() == PLAY_FORWARD)
-               {
+               if( get_direction() == PLAY_FORWARD ) {
                        actual_previous_number--;
-                       if(actual_previous_number < get_source_start())
+                       if( actual_previous_number < get_source_start() )
                                skip_current = 1;
-                       else
-                       {
+                       else {
                                KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
-                               if(keyframe->position > 0 &&
-                                       actual_previous_number < keyframe->position)
+                               if( keyframe->position > 0 &&
+                                   actual_previous_number < keyframe->position )
                                        skip_current = 1;
                        }
                }
-               else
-               {
+               else {
                        actual_previous_number++;
-                       if(actual_previous_number >= get_source_start() + get_total_len())
+                       if( actual_previous_number >= get_source_start() + get_total_len() )
                                skip_current = 1;
-                       else
-                       {
+                       else {
                                KeyFrame *keyframe = get_next_keyframe(start_position, 1);
-                               if(keyframe->position > 0 &&
-                                       actual_previous_number >= keyframe->position)
+                               if( keyframe->position > 0 &&
+                                   actual_previous_number >= keyframe->position )
                                        skip_current = 1;
                        }
                }
-
 // Only count motion since last keyframe
-
-
        }
 
+       if( !config.global && !config.rotate )
+               skip_current = 1;
 
-       if(!config.global && !config.rotate) skip_current = 1;
-
-
-
+//printf("process_realtime: %jd %d %jd %jd\n", start_position,
+// skip_current, previous_frame_number, actual_previous_number);
+       if( prev_config_tracking_type != MotionScan::SAVE &&
+           config.tracking_type == MotionScan::SAVE ) {
+               reset_cache_file();
+               char save_file[BCTEXTLEN];
+               snprintf(save_file, sizeof(save_file), "%s.bak", config.tracking_file);
+#ifdef DEBUG
+printf("MotionMain::process_buffer 2 rename tracking file: %s to %s\n",
+ config.tracking_file, save_file);
+#endif
+               ::rename(config.tracking_file, save_file);
+       }
+       else if( !cache_file[0] || active_key > start_position )
+               reset_cache_file();
 
-// printf("process_realtime %d %lld %lld\n",
-// skip_current,
-// previous_frame_number,
-// actual_previous_number);
 // Load match frame and reset vectors
        int need_reload = !skip_current &&
                (previous_frame_number != actual_previous_number ||
                need_reconfigure);
-       if(need_reload)
-       {
-               total_dx = 0;
-               total_dy = 0;
-               total_angle = 0;
+       if( need_reload ) {
+               total_dx = total_dy = 0; total_angle = 0;
                previous_frame_number = actual_previous_number;
        }
 
-
-       if(skip_current)
-       {
-               total_dx = 0;
-               total_dy = 0;
-               current_dx = 0;
-               current_dy = 0;
-               total_angle = 0;
-               current_angle = 0;
+       if( skip_current ) {
+               total_dx = total_dy = 0;
+               current_dx = current_dy = 0;
+               total_angle = current_angle = 0;
        }
 
-
-
-
 // Get the global pointers.  Here we walk through the sequence of events.
-       if(config.global)
-       {
+       if( config.global ) {
 // Assume global only.  Global reads previous frame and compares
 // with current frame to get the current translation.
 // The center of the search area is fixed in compensate mode or
 // the user value + the accumulation vector in track mode.
-               if(!prev_global_ref)
+               if( !prev_global_ref )
                        prev_global_ref = new VFrame(w, h, color_model);
-               if(!current_global_ref)
+               if( !current_global_ref )
                        current_global_ref = new VFrame(w, h, color_model);
 
 // Global loads the current target frame into the src and
 // writes it to the dst frame with desired translation.
-               if(!global_target_src)
+               if( !global_target_src )
                        global_target_src = new VFrame(w, h, color_model);
-               if(!global_target_dst)
+               if( !global_target_dst )
                        global_target_dst = new VFrame(w, h, color_model);
 
-
 // Load the global frames
-               if(need_reload)
-               {
-                       read_frame(prev_global_ref,
-                               reference_layer,
-                               previous_frame_number,
-                               frame_rate,
-                               0);
+               if( need_reload ) {
+                       read_frame(prev_global_ref, reference_layer,
+                               previous_frame_number, frame_rate, 0);
                }
 
-               read_frame(current_global_ref,
-                       reference_layer,
-                       start_position,
-                       frame_rate,
-                       0);
-               read_frame(global_target_src,
-                       target_layer,
-                       start_position,
-                       frame_rate,
-                       0);
-
-
+               read_frame(current_global_ref, reference_layer,
+                       start_position, frame_rate, 0);
+               read_frame(global_target_src, target_layer,
+                       start_position, frame_rate, 0);
 
 // Global followed by rotate
-               if(config.rotate)
-               {
+               if( config.rotate ) {
 // Must translate the previous global reference by the current global
 // accumulation vector to match the current global reference.
 // The center of the search area is always the user value + the accumulation
 // vector.
-                       if(!prev_rotate_ref)
+                       if( !prev_rotate_ref )
                                prev_rotate_ref = new VFrame(w, h, color_model);
 // The current global reference is the current rotation reference.
-                       if(!current_rotate_ref)
+                       if( !current_rotate_ref )
                                current_rotate_ref = new VFrame(w, h, color_model);
                        current_rotate_ref->copy_from(current_global_ref);
 
@@ -1006,100 +773,109 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po
 // The pivot for the rotation is the center of the search area
 // if we're tracking.
 // The pivot is fixed to the user position if we're compensating.
-                       if(!rotate_target_src)
+                       if( !rotate_target_src )
                                rotate_target_src = new VFrame(w, h, color_model);
-                       if(!rotate_target_dst)
+                       if( !rotate_target_dst )
                                rotate_target_dst = new VFrame(w, h, color_model);
                }
        }
-       else
 // Rotation only
-       if(config.rotate)
-       {
+       else if( config.rotate ) {
 // Rotation reads the previous reference frame and compares it with current
 // reference frame.
-               if(!prev_rotate_ref)
+               if( !prev_rotate_ref )
                        prev_rotate_ref = new VFrame(w, h, color_model);
-               if(!current_rotate_ref)
+               if( !current_rotate_ref )
                        current_rotate_ref = new VFrame(w, h, color_model);
 
 // Rotation loads target frame to temporary, rotates it, and writes it to the
 // target frame.  The pivot is always fixed.
-               if(!rotate_target_src)
+               if( !rotate_target_src )
                        rotate_target_src = new VFrame(w, h, color_model);
-               if(!rotate_target_dst)
+               if( !rotate_target_dst )
                        rotate_target_dst = new VFrame(w, h, color_model);
 
 
 // Load the rotate frames
-               if(need_reload)
-               {
-                       read_frame(prev_rotate_ref,
-                               reference_layer,
-                               previous_frame_number,
-                               frame_rate,
-                               0);
+               if( need_reload ) {
+                       read_frame(prev_rotate_ref, reference_layer,
+                               previous_frame_number, frame_rate, 0);
                }
-               read_frame(current_rotate_ref,
-                       reference_layer,
-                       start_position,
-                       frame_rate,
-                       0);
-               read_frame(rotate_target_src,
-                       target_layer,
-                       start_position,
-                       frame_rate,
-                       0);
+               read_frame(current_rotate_ref, reference_layer,
+                       start_position, frame_rate, 0);
+               read_frame(rotate_target_src, target_layer,
+                       start_position, frame_rate, 0);
        }
 
+       dx_offset = 0; dy_offset = 0;
+       if( config.tracking_type == MotionScan::LOAD ) {
+               if( config.addtrackedframeoffset ) {
+                       if( config.track_frame != tracking_frame ) {
+                               tracking_frame = config.track_frame;
+                               int64_t no;  int dx, dy;  float dt;
+                               if( !get_cache_line(tracking_frame) &&
+                                   sscanf(cache_line, "%jd %d %d %f", &no, &dx, &dy, &dt) == 4 ) {
+                                       dx_offset = dx; dy_offset = dy;
+                               }
+                               else {
+                                       eprintf("no offset data frame %jd\n", tracking_frame);
+                               }
+                       }
+               }
+               else
+                       tracking_frame = -1;
+       }
 
+       if( !skip_current ) {
+               load_ok = 0;
+               if( config.tracking_type == MotionScan::LOAD ||
+                   config.tracking_type == MotionScan::SAVE ) {
+                       int64_t no;  int dx, dy;  float dt;
+                       int64_t frame_no = get_source_position();
+// Load result from disk
+                       if( !get_cache_line(frame_no) &&
+                           sscanf(cache_line, "%jd %d %d %f", &no, &dx, &dy, &dt) == 4 ) {
+                               load_ok = 1;  load_dx = dx;  load_dy = dy;  load_dt = dt;
+                       }
+                       else {
+#ifdef DEBUG
+printf("MotionMain::process_buffer: no tracking data frame %jd\n", frame_no);
+#endif
+                       }
+               }
 
-
-
-
-
-
-
-
-       if(!skip_current)
-       {
 // Get position change from previous frame to current frame
-               if(config.global) process_global();
+               if( config.global )
+                       process_global();
 // Get rotation change from previous frame to current frame
-               if(config.rotate) process_rotation();
+               if( config.rotate )
+                       process_rotation();
 //frame[target_layer]->copy_from(prev_rotate_ref);
 //frame[target_layer]->copy_from(current_rotate_ref);
-       }
-
-
-
-
-
 
+// write results to disk
+               if( config.tracking_type == MotionScan::SAVE ) {
+                       char line[BCSTRLEN];
+                       int64_t frame_no = get_source_position();
+                       snprintf(line, sizeof(line), "%jd %d %d %f\n",
+                               frame_no, save_dx, save_dy, save_dt);
+                       put_cache_line(line);
+               }
 // Transfer the relevant target frame to the output
-       if(!skip_current)
-       {
-               if(config.rotate)
-               {
+               if( config.rotate ) {
                        frame[target_layer]->copy_from(rotate_target_dst);
                }
-               else
-               {
+               else {
                        frame[target_layer]->copy_from(global_target_dst);
                }
        }
-       else
 // Read the target destination directly
-       {
+       else {
                read_frame(frame[target_layer],
-                       target_layer,
-                       start_position,
-                       frame_rate,
-                       0);
+                       target_layer, start_position, frame_rate, 0);
        }
 
-       if(config.draw_vectors)
-       {
+       if( config.draw_vectors ) {
                draw_vectors(frame[target_layer]);
        }
 
@@ -1113,74 +889,43 @@ printf("MotionMain::process_buffer %d\n", __LINE__);
 
 void MotionMain::draw_vectors(VFrame *frame)
 {
-       int w = frame->get_w();
-       int h = frame->get_h();
-       int global_x1, global_y1;
-       int global_x2, global_y2;
-       int block_x, block_y;
-       int block_w, block_h;
-       int block_x1, block_y1;
-       int block_x2, block_y2;
-       int block_x3, block_y3;
-       int block_x4, block_y4;
+       int w = frame->get_w(), h = frame->get_h();
+       int global_x1, global_y1, global_x2, global_y2;
+       int block_x, block_y, block_w, block_h;
+       int block_x1, block_y1, block_x2, block_y2;
+       int block_x3, block_y3, block_x4, block_y4;
+       int search_x1, search_y1, search_x2, search_y2;
        int search_w, search_h;
-       int search_x1, search_y1;
-       int search_x2, search_y2;
 
 
-       if(config.global)
-       {
+       if( config.global ) {
 // Get vector
 // Start of vector is center of previous block.
 // End of vector is total accumulation.
-               if(config.tracking_object == MotionScan::TRACK_SINGLE)
-               {
-                       global_x1 = (int64_t)(config.block_x *
-                               w /
-                               100);
-                       global_y1 = (int64_t)(config.block_y *
-                               h /
-                               100);
+               if( config.tracking_object == MotionScan::TRACK_SINGLE ) {
+                       global_x1 = (int64_t)(config.block_x * w / 100);
+                       global_y1 = (int64_t)(config.block_y * h / 100);
                        global_x2 = global_x1 + total_dx / OVERSAMPLE;
                        global_y2 = global_y1 + total_dy / OVERSAMPLE;
 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
                }
-               else
 // Start of vector is center of previous block.
 // End of vector is current change.
-               if(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK)
-               {
-                       global_x1 = (int64_t)(config.block_x *
-                               w /
-                               100);
-                       global_y1 = (int64_t)(config.block_y *
-                               h /
-                               100);
+               else if( config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK ) {
+                       global_x1 = (int64_t)(config.block_x * w / 100);
+                       global_y1 = (int64_t)(config.block_y * h / 100);
                        global_x2 = global_x1 + current_dx / OVERSAMPLE;
                        global_y2 = global_y1 + current_dy / OVERSAMPLE;
                }
-               else
-               {
-                       global_x1 = (int64_t)(config.block_x *
-                               w /
-                               100 +
-                               (total_dx - current_dx) /
-                               OVERSAMPLE);
-                       global_y1 = (int64_t)(config.block_y *
-                               h /
-                               100 +
-                               (total_dy - current_dy) /
-                               OVERSAMPLE);
-                       global_x2 = (int64_t)(config.block_x *
-                               w /
-                               100 +
-                               total_dx /
-                               OVERSAMPLE);
-                       global_y2 = (int64_t)(config.block_y *
-                               h /
-                               100 +
-                               total_dy /
-                               OVERSAMPLE);
+               else {
+                       global_x1 = (int64_t)(config.block_x * w / 100
+                               + (total_dx - current_dx) / OVERSAMPLE);
+                       global_y1 = (int64_t)(config.block_y * h / 100
+                               + (total_dy - current_dy) / OVERSAMPLE);
+                       global_x2 = (int64_t)(config.block_x * w / 100
+                               + total_dx / OVERSAMPLE);
+                       global_y2 = (int64_t)(config.block_y * h / 100
+                               + total_dy / OVERSAMPLE);
                }
 
                block_x = global_x1;
@@ -1198,31 +943,13 @@ void MotionMain::draw_vectors(VFrame *frame)
                search_x2 = block_x2 + search_w / 2;
                search_y2 = block_y2 + search_h / 2;
 
-// printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
-// global_x1,
-// global_y1,
-// block_w,
-// block_h,
-// block_x1,
-// block_y1,
-// block_x2,
-// block_y2,
-// search_x1,
-// search_y1,
-// search_x2,
-// search_y2);
-
-               MotionScan::clamp_scan(w,
-                       h,
-                       &block_x1,
-                       &block_y1,
-                       &block_x2,
-                       &block_y2,
-                       &search_x1,
-                       &search_y1,
-                       &search_x2,
-                       &search_y2,
-                       1);
+//printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
+// global_x1, global_y1, block_w, block_h, block_x1, block_y1,
+// block_x2, block_y2, search_x1, search_y1, search_x2, search_y2);
+
+               MotionScan::clamp_scan(w, h,
+                       &block_x1, &block_y1, &block_x2, &block_y2,
+                       &search_x1, &search_y1, &search_x2, &search_y2, 1);
 
 // Vector
                draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
@@ -1233,7 +960,6 @@ void MotionMain::draw_vectors(VFrame *frame)
                draw_line(frame, block_x2, block_y2, block_x1, block_y2);
                draw_line(frame, block_x1, block_y2, block_x1, block_y1);
 
-
 // Search area
                draw_line(frame, search_x1, search_y1, search_x2, search_y1);
                draw_line(frame, search_x2, search_y1, search_x2, search_y2);
@@ -1241,22 +967,19 @@ void MotionMain::draw_vectors(VFrame *frame)
                draw_line(frame, search_x1, search_y2, search_x1, search_y1);
 
 // Block should be endpoint of motion
-               if(config.rotate)
-               {
+               if( config.rotate ) {
                        block_x = global_x2;
                        block_y = global_y2;
                }
        }
-       else
-       {
+       else {
                block_x = (int64_t)(config.block_x * w / 100);
                block_y = (int64_t)(config.block_y * h / 100);
        }
 
        block_w = config.global_block_w * w / 100;
        block_h = config.global_block_h * h / 100;
-       if(config.rotate)
-       {
+       if( config.rotate ) {
                float angle = total_angle * 2 * M_PI / 360;
                double base_angle1 = atan((float)block_h / block_w);
                double base_angle2 = atan((float)block_w / block_h);
@@ -1279,8 +1002,7 @@ void MotionMain::draw_vectors(VFrame *frame)
 
 
 // Center
-               if(!config.global)
-               {
+               if( !config.global ) {
                        draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
                        draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
                }
@@ -1288,62 +1010,37 @@ void MotionMain::draw_vectors(VFrame *frame)
 }
 
 
-
 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
 {
-       if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
+       if( !(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h()) ) return;
 
-#define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
-{ \
+#define DRAW_PIXEL(model, x, y, components, do_yuv, max, type) \
+ case model: { \
        type **rows = (type**)frame->get_rows(); \
        rows[y][x * components] = max - rows[y][x * components]; \
-       if(!do_yuv) \
-       { \
+       if( !do_yuv ) { \
                rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
                rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
        } \
-       else \
-       { \
+       else { \
                rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
                rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
        } \
-       if(components == 4) \
+       if( components == 4 ) \
                rows[y][x * components + 3] = max; \
-}
-
-
-       switch(frame->get_color_model())
-       {
-               case BC_RGB888:
-                       DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
-                       break;
-               case BC_RGBA8888:
-                       DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
-                       break;
-               case BC_RGB_FLOAT:
-                       DRAW_PIXEL(x, y, 3, 0, 1.0, float);
-                       break;
-               case BC_RGBA_FLOAT:
-                       DRAW_PIXEL(x, y, 4, 0, 1.0, float);
-                       break;
-               case BC_YUV888:
-                       DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
-                       break;
-               case BC_YUVA8888:
-                       DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
-                       break;
-               case BC_RGB161616:
-                       DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
-                       break;
-               case BC_YUV161616:
-                       DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
-                       break;
-               case BC_RGBA16161616:
-                       DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
-                       break;
-               case BC_YUVA16161616:
-                       DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
-                       break;
+} break
+
+       switch(frame->get_color_model()) {
+       DRAW_PIXEL(BC_RGB888, x, y, 3, 0, 0xff, unsigned char);
+       DRAW_PIXEL(BC_RGBA8888, x, y, 4, 0, 0xff, unsigned char);
+       DRAW_PIXEL(BC_RGB_FLOAT, x, y, 3, 0, 1.0, float);
+       DRAW_PIXEL(BC_RGBA_FLOAT, x, y, 4, 0, 1.0, float);
+       DRAW_PIXEL(BC_YUV888, x, y, 3, 1, 0xff, unsigned char);
+       DRAW_PIXEL(BC_YUVA8888, x, y, 4, 1, 0xff, unsigned char);
+       DRAW_PIXEL(BC_RGB161616, x, y, 3, 0, 0xffff, uint16_t);
+       DRAW_PIXEL(BC_YUV161616, x, y, 3, 1, 0xffff, uint16_t);
+       DRAW_PIXEL(BC_RGBA16161616, x, y, 4, 0, 0xffff, uint16_t);
+       DRAW_PIXEL(BC_YUVA16161616, x, y, 4, 1, 0xffff, uint16_t);
        }
 }
 
@@ -1354,47 +1051,31 @@ void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
        int h = labs(y2 - y1);
 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
 
-       if(!w && !h)
-       {
+       if( !w && !h ) {
                draw_pixel(frame, x1, y1);
        }
-       else
-       if(w > h)
-       {
+       else if( w > h ) {
 // Flip coordinates so x1 < x2
-               if(x2 < x1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+               if( x2 < x1 ) {
+                       y2 ^= y1; y1 ^= y2; y2 ^= y1;
+                       x1 ^= x2; x2 ^= x1; x1 ^= x2;
                }
                int numerator = y2 - y1;
                int denominator = x2 - x1;
-               for(int i = x1; i < x2; i++)
-               {
+               for( int i = x1; i < x2; i++ ) {
                        int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
                        draw_pixel(frame, i, y);
                }
        }
-       else
-       {
+       else {
 // Flip coordinates so y1 < y2
-               if(y2 < y1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+               if( y2 < y1 ) {
+                       y2 ^= y1; y1 ^= y2; y2 ^= y1;
+                       x1 ^= x2; x2 ^= x1; x1 ^= x2;
                }
                int numerator = x2 - x1;
                int denominator = y2 - y1;
-               for(int i = y1; i < y2; i++)
-               {
+               for( int i = y1; i < y2; i++ ) {
                        int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
                        draw_pixel(frame, x, i);
                }
@@ -1408,19 +1089,14 @@ void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
        double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
        double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
        double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
-       int x3;
-       int y3;
-       int x4;
-       int y4;
-       if(x2 < x1)
-       {
+       int x3, y3, x4, y4;
+       if( x2 < x1 ) {
                x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
                y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
                x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
                y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
        }
-       else
-       {
+       else {
                x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
                y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
                x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
@@ -1432,33 +1108,125 @@ void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
 //     draw_line(frame, x1, y1 + 1, x2, y2 + 1);
 
 // Arrow line
-       if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
+       if( abs(y2 - y1) || abs(x2 - x1) ) draw_line(frame, x2, y2, x3, y3);
 //     draw_line(frame, x2, y2 + 1, x3, y3 + 1);
 // Arrow line
-       if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
+       if( abs(y2 - y1) || abs(x2 - x1) ) draw_line(frame, x2, y2, x4, y4);
 //     draw_line(frame, x2, y2 + 1, x4, y4 + 1);
 }
 
+int MotionMain::open_cache_file()
+{
+       if( cache_fp ) return 0;
+       if( !cache_file[0] ) return 1;
+       if( !(cache_fp = fopen(cache_file, "r")) ) return 1;
+       return 0;
+}
 
+void MotionMain::close_cache_file()
+{
+       if( !cache_fp ) return;
+       fclose(cache_fp);
+       cache_fp = 0; cache_key = -1; tracking_frame = -1;
+}
 
+int MotionMain::load_cache_line()
+{
+       cache_key = -1;
+       if( open_cache_file() ) return 1;
+       if( !fgets(cache_line, sizeof(cache_line), cache_fp) ) return 1;
+       cache_key = strtol(cache_line, 0, 0);
+       return 0;
+}
 
+int MotionMain::get_cache_line(int64_t key)
+{
+       if( cache_key == key ) return 0;
+       if( open_cache_file() ) return 1;
+       if( cache_key >= 0 && key > cache_key ) {
+               if( load_cache_line() ) return 1;
+               if( cache_key == key ) return 0;
+               if( cache_key > key ) return 1;
+       }
+// binary search file
+       fseek(cache_fp, 0, SEEK_END);
+       int64_t l = -1, r = ftell(cache_fp);
+       while( (r - l) > 1 ) {
+               int64_t m = (l + r) / 2;
+               fseek(cache_fp, m, SEEK_SET);
+               if( m > 0 && !fgets(cache_line, sizeof(cache_line), cache_fp) )
+                       return -1;
+               if( !load_cache_line() ) {
+                       if( cache_key == key )
+                               return 0;
+                       if( cache_key < key ) { l = m; continue; }
+               }
+               r = m;
+       }
+       return 1;
+}
 
+int MotionMain::locate_cache_line(int64_t key)
+{
+       int ret = 1;
+       if( key < 0 || !(ret=get_cache_line(key)) ||
+           ( cache_key >= 0 && cache_key < key ) )
+               ret = load_cache_line();
+       return ret;
+}
 
+int MotionMain::put_cache_line(const char *line)
+{
+       int64_t key = strtol(line, 0, 0);
+       if( key == active_key ) return 1;
+       if( !active_fp ) {
+               close_cache_file();
+               snprintf(cache_file, sizeof(cache_file), "%s.bak", config.tracking_file);
+               ::rename(config.tracking_file, cache_file);
+               if( !(active_fp = fopen(config.tracking_file, "w")) ) {
+                       perror(config.tracking_file);
+                       fprintf(stderr, "err writing key %jd\n", key);
+                       return -1;
+               }
+               active_key = -1;
+       }
 
+       if( active_key < key ) {
+               locate_cache_line(active_key);
+               while( cache_key >= 0 && key >= cache_key ) {
+                       if( key > cache_key )
+                               fputs(cache_line, active_fp);
+                       load_cache_line();
+               }
+       }
 
+       active_key = key;
+       fputs(line, active_fp);
+       fflush(active_fp);
+       return 0;
+}
 
-
-
-
-
-
+void MotionMain::reset_cache_file()
+{
+       if( active_fp ) {
+               locate_cache_line(active_key);
+               while( cache_key >= 0 ) {
+                       fputs(cache_line, active_fp);
+                       load_cache_line();
+               }
+               close_cache_file();  ::remove(cache_file);
+               fclose(active_fp); active_fp = 0; active_key = -1;
+       }
+       else
+               close_cache_file();
+       strcpy(cache_file, config.tracking_file);
+}
 
 
 RotateScanPackage::RotateScanPackage()
 {
 }
 
-
 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
  : LoadClient(server)
 {
@@ -1476,19 +1244,18 @@ RotateScanUnit::~RotateScanUnit()
 
 void RotateScanUnit::process_package(LoadPackage *package)
 {
-       if(server->skip) return;
+       if( server->skip ) return;
        RotateScanPackage *pkg = (RotateScanPackage*)package;
 
-       if((pkg->difference = server->get_cache(pkg->angle)) < 0)
-       {
+       if( (pkg->difference = server->get_cache(pkg->angle)) < 0 ) {
 //printf("RotateScanUnit::process_package %d\n", __LINE__);
                int color_model = server->previous_frame->get_color_model();
                int pixel_size = BC_CModels::calculate_pixelsize(color_model);
                int row_bytes = server->previous_frame->get_bytes_per_line();
 
-               if(!rotater)
+               if( !rotater )
                        rotater = new AffineEngine(1, 1);
-               if(!temp) temp = new VFrame(0,
+               if( !temp ) temp = new VFrame(0,
                        -1,
                        server->previous_frame->get_w(),
                        server->previous_frame->get_h(),
@@ -1498,35 +1265,23 @@ void RotateScanUnit::process_package(LoadPackage *package)
 
 
 // Rotate original block size
-//             rotater->set_viewport(server->block_x1,
-//                     server->block_y1,
-//                     server->block_x2 - server->block_x1,
-//                     server->block_y2 - server->block_y1);
-               rotater->set_in_viewport(server->block_x1,
-                       server->block_y1,
-                       server->block_x2 - server->block_x1,
-                       server->block_y2 - server->block_y1);
-               rotater->set_out_viewport(server->block_x1,
-                       server->block_y1,
-                       server->block_x2 - server->block_x1,
-                       server->block_y2 - server->block_y1);
+//             rotater->set_viewport(server->block_x1, server->block_y1,
+//                     server->block_x2 - server->block_x1, server->block_y2 - server->block_y1);
+               rotater->set_in_viewport(server->block_x1, server->block_y1,
+                       server->block_x2 - server->block_x1, server->block_y2 - server->block_y1);
+               rotater->set_out_viewport(server->block_x1, server->block_y1,
+                       server->block_x2 - server->block_x1, server->block_y2 - server->block_y1);
 //             rotater->set_pivot(server->block_x, server->block_y);
                rotater->set_in_pivot(server->block_x, server->block_y);
                rotater->set_out_pivot(server->block_x, server->block_y);
 //printf("RotateScanUnit::process_package %d\n", __LINE__);
-               rotater->rotate(temp,
-                       server->previous_frame,
-                       pkg->angle);
+               rotater->rotate(temp, server->previous_frame, pkg->angle);
 
 // Scan reduced block size
 //plugin->output_frame->copy_from(server->current_frame);
 //plugin->output_frame->copy_from(temp);
-// printf("RotateScanUnit::process_package %d %d %d %d %d\n",
-// __LINE__,
-// server->scan_x,
-// server->scan_y,
-// server->scan_w,
-// server->scan_h);
+//printf("RotateScanUnit::process_package %d %d %d %d %d\n",
+// __LINE__, server->scan_x, server->scan_y, server->scan_w, server->scan_h);
 // Clamp coordinates
                int x1 = server->scan_x;
                int y1 = server->scan_y;
@@ -1536,64 +1291,36 @@ void RotateScanUnit::process_package(LoadPackage *package)
                y2 = MIN(temp->get_h(), y2);
                x2 = MIN(server->current_frame->get_w(), x2);
                y2 = MIN(server->current_frame->get_h(), y2);
-               x1 = MAX(0, x1);
-               y1 = MAX(0, y1);
+               x1 = MAX(0, x1);  y1 = MAX(0, y1);
 
-               if(x2 > x1 && y2 > y1)
-               {
+               if( x2 > x1 && y2 > y1 ) {
                        pkg->difference = MotionScan::abs_diff(
                                temp->get_rows()[y1] + x1 * pixel_size,
                                server->current_frame->get_rows()[y1] + x1 * pixel_size,
-                               row_bytes,
-                               x2 - x1,
-                               y2 - y1,
-                               color_model);
+                               row_bytes, x2 - x1, y2 - y1, color_model);
 //printf("RotateScanUnit::process_package %d\n", __LINE__);
                        server->put_cache(pkg->angle, pkg->difference);
                }
-
-// printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n",
-// server->block_x1,
-// server->block_y1,
-// server->block_x2 - server->block_x1,
-// server->block_y2 - server->block_y1,
-// server->block_x,
-// server->block_y,
-// pkg->angle,
-// server->scan_w,
-// server->scan_h,
-// pkg->difference);
+#if 0
+       VFrame png(x2-x1, y2-y1, BC_RGB888, -1);
+       png.transfer_from(temp, 0, x1, y1, x2-x1, y2-y1);
+       char fn[64];
+       sprintf(fn,"%s%f.png","/tmp/temp",pkg->angle); png.write_png(fn);
+       png.transfer_from(server->current_frame, 0, x1, y1, x2-x1, y2-y1);
+       sprintf(fn,"%s%f.png","/tmp/curr",pkg->angle); png.write_png(fn);
+printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%jd\n",
+ server->block_x1, server->block_y1, server->block_x2 - server->block_x1, server->block_y2 - server->block_y1,
+ server->block_x,  server->block_y,  pkg->angle,  server->scan_w, server->scan_h, pkg->difference);
+#endif
        }
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 
 RotateScan::RotateScan(MotionMain *plugin,
        int total_clients,
        int total_packages)
- : LoadServer(
-//1, 1
-total_clients, total_packages
-)
+ : LoadServer( //1, 1)
+               total_clients, total_packages)
 {
        this->plugin = plugin;
        cache_lock = new Mutex("RotateScan::cache_lock");
@@ -1607,13 +1334,10 @@ RotateScan::~RotateScan()
 
 void RotateScan::init_packages()
 {
-       for(int i = 0; i < get_total_packages(); i++)
-       {
+       for( int i = 0; i < get_total_packages(); i++ ) {
                RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
-               pkg->angle = i *
-                       (scan_angle2 - scan_angle1) /
-                       (total_steps - 1) +
-                       scan_angle1;
+               pkg->angle = scan_angle1 +
+                       i * (scan_angle2 - scan_angle1) / (total_steps - 1);
        }
 }
 
@@ -1628,50 +1352,29 @@ LoadPackage* RotateScan::new_package()
 }
 
 
-float RotateScan::scan_frame(VFrame *previous_frame,
-       VFrame *current_frame,
-       int block_x,
-       int block_y)
+float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
+       int block_x, int block_y)
 {
        skip = 0;
        this->block_x = block_x;
        this->block_y = block_y;
 
 //printf("RotateScan::scan_frame %d\n", __LINE__);
-       switch(plugin->config.tracking_type)
-       {
-               case MotionScan::NO_CALCULATE:
-                       result = plugin->config.rotation_center;
-                       skip = 1;
-                       break;
+       switch(plugin->config.tracking_type) {
+       case MotionScan::NO_CALCULATE:
+               result = plugin->config.rotation_center;
+               skip = 1;
+               break;
 
-               case MotionScan::LOAD:
-               {
-                       char string[BCTEXTLEN];
-                       sprintf(string, "%s%06jd",
-                                ROTATION_FILE, plugin->get_source_position());
-                       FILE *input = fopen(string, "r");
-                       if(input)
-                       {
-                               fscanf(input, "%f", &result);
-                               fclose(input);
-                               skip = 1;
-                       }
-                       else
-                       {
-                               perror("RotateScan::scan_frame LOAD");
-                       }
-                       break;
+       case MotionScan::LOAD:
+       case MotionScan::SAVE:
+               if( plugin->load_ok ) {
+                       result = plugin->load_dt;
+                       skip = 1;
                }
+               break;
        }
 
-
-
-
-
-
-
-
        this->previous_frame = previous_frame;
        this->current_frame = current_frame;
        int w = current_frame->get_w();
@@ -1679,10 +1382,10 @@ float RotateScan::scan_frame(VFrame *previous_frame,
        int block_w = w * plugin->config.global_block_w / 100;
        int block_h = h * plugin->config.global_block_h / 100;
 
-       if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
-       if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
-       if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
-       if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
+       if( this->block_x - block_w / 2 < 0 ) block_w = this->block_x * 2;
+       if( this->block_y - block_h / 2 < 0 ) block_h = this->block_y * 2;
+       if( this->block_x + block_w / 2 > w ) block_w = (w - this->block_x) * 2;
+       if( this->block_y + block_h / 2 > h ) block_h = (h - this->block_y) * 2;
 
        block_x1 = this->block_x - block_w / 2;
        block_x2 = this->block_x + block_w / 2;
@@ -1711,14 +1414,11 @@ float RotateScan::scan_frame(VFrame *previous_frame,
        double max_area1 = 0;
        //double max_x1 = 0;
        double max_y1 = 0;
-       for(double x = x1; x < x2; x++)
-       {
+       for( double x = x1; x < x2; x++ ) {
                double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
-               if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
-               {
+               if( x >= center_x && x < block_x2 && y >= block_y1 && y < center_y ) {
                        double area = fabs(x - center_x) * fabs(y - center_y);
-                       if(area > max_area1)
-                       {
+                       if( area > max_area1 ) {
                                max_area1 = area;
                                //max_x1 = x;
                                max_y1 = y;
@@ -1730,14 +1430,11 @@ float RotateScan::scan_frame(VFrame *previous_frame,
        double max_area2 = 0;
        double max_x2 = 0;
        //double max_y2 = 0;
-       for(double y = y1; y < y3; y++)
-       {
+       for( double y = y1; y < y3; y++ ) {
                double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
-               if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
-               {
+               if( x >= block_x1 && x < center_x && y >= block_y1 && y < center_y ) {
                        double area = fabs(x - center_x) * fabs(y - center_y);
-                       if(area > max_area2)
-                       {
+                       if( area > max_area2 ) {
                                max_area2 = area;
                                max_x2 = x;
                                //max_y2 = y;
@@ -1769,40 +1466,33 @@ float RotateScan::scan_frame(VFrame *previous_frame,
        cache.remove_all_objects();
 
 
-       if(!skip)
-       {
-               if(previous_frame->data_matches(current_frame))
-               {
+       if( !skip ) {
+               if( previous_frame->data_matches(current_frame) ) {
 //printf("RotateScan::scan_frame: frames match.  Skipping.\n");
                        result = plugin->config.rotation_center;
                        skip = 1;
                }
        }
 
-       if(!skip)
-       {
+       if( !skip ) {
 // Initial search range
                float angle_range = max_angle;
                result = plugin->config.rotation_center;
                total_steps = plugin->config.rotate_positions;
 
 
-               while(angle_range >= min_angle * total_steps)
-               {
+               while( angle_range >= min_angle * total_steps ) {
                        scan_angle1 = result - angle_range;
                        scan_angle2 = result + angle_range;
 
-
                        set_package_count(total_steps);
 //set_package_count(1);
                        process_packages();
 
                        int64_t min_difference = -1;
-                       for(int i = 0; i < get_total_packages(); i++)
-                       {
+                       for( int i = 0; i < get_total_packages(); i++ ) {
                                RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
-                               if(pkg->difference < min_difference || min_difference == -1)
-                               {
+                               if( pkg->difference < min_difference || min_difference == -1 ) {
                                        min_difference = pkg->difference;
                                        result = pkg->angle;
                                }
@@ -1815,29 +1505,10 @@ float RotateScan::scan_frame(VFrame *previous_frame,
                }
        }
 
-//printf("RotateScan::scan_frame %d\n", __LINE__);
-
-       if(!skip && plugin->config.tracking_type == MotionScan::SAVE)
-       {
-               char string[BCTEXTLEN];
-               sprintf(string, "%s%06jd",
-                       ROTATION_FILE, plugin->get_source_position());
-               FILE *output = fopen(string, "w");
-               if(output)
-               {
-                       fprintf(output, "%f\n", result);
-                       fclose(output);
-               }
-               else
-               {
-                       perror("RotateScan::scan_frame SAVE");
-               }
+       if( plugin->config.tracking_type == MotionScan::SAVE ) {
+               plugin->save_dt = result;
        }
-
 //printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result);
-
-
-
        return result;
 }
 
@@ -1845,11 +1516,9 @@ int64_t RotateScan::get_cache(float angle)
 {
        int64_t result = -1;
        cache_lock->lock("RotateScan::get_cache");
-       for(int i = 0; i < cache.total; i++)
-       {
+       for( int i = 0; i < cache.total; i++ ) {
                RotateScanCache *ptr = cache.values[i];
-               if(fabs(ptr->angle - angle) <= MIN_ANGLE)
-               {
+               if( fabs(ptr->angle - angle) <= MIN_ANGLE ) {
                        result = ptr->difference;
                        break;
                }
@@ -1867,13 +1536,6 @@ void RotateScan::put_cache(float angle, int64_t difference)
 }
 
 
-
-
-
-
-
-
-
 RotateScanCache::RotateScanCache(float angle, int64_t difference)
 {
        this->angle = angle;