version update master 2021-03
authorGood Guy <good1.2guy@gmail.com>
Wed, 31 Mar 2021 18:21:28 +0000 (12:21 -0600)
committerGood Guy <good1.2guy@gmail.com>
Wed, 31 Mar 2021 18:21:28 +0000 (12:21 -0600)
cinelerra-5.1/cinelerra/fileffmpeg.C
cinelerra-5.1/cinelerra/mwindow.C
cinelerra-5.1/expanders.txt
cinelerra-5.1/msg/txt
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/posterize/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/posterize/posterize.C [new file with mode: 0644]
cinelerra-5.1/plugins/posterize/posterize.h [new file with mode: 0644]
cinelerra-5.1/plugins/timelapsehelper/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/timelapsehelper/timelapsehelper.C [new file with mode: 0644]

index 58125d691c8b82856759326f7891e864de0d6cb7..a71d692aad1e3584e54b350cd248b98387be5807 100644 (file)
@@ -345,7 +345,7 @@ int FileFFMPEG::open_file(int rd, int wr)
                                asset->aspect_ratio = ff->ff_aspect_ratio(0);
                                if (!asset->interlace_mode) asset->interlace_mode = ff->ff_interlace(0);
                                if ( ff->ff_video_frames(0) > 1 ) {
-                               ff->video_probe(1);
+//                             ff->video_probe(1);
                                 if (!asset->interlace_mode && (ff->interlace_from_codec) ) asset->interlace_mode = ff->video_probe(1); 
                                }
                                if( !asset->layers ) asset->layers = video_layers;
index e63b7c54a8c629284fe682ad1822aa3831eddf0f..2918e23e63e59e4035bb86e2b07059ea0536fd2e 100644 (file)
@@ -4325,7 +4325,7 @@ void MWindow::get_backup_path(char *path, int len)
 
 void MWindow::create_timestamped_copy_from_previous_backup(char *previouspath)
 {
-  if (previouspath == nullptr) return;
+  if (previouspath == NULL) return;
   char backup_path[BCTEXTLEN];
   backup_path[0] = 0;
   time_t now = time(NULL);
index 0ef0fd232a541b75886a6600c583c6d05ad2d974..9bda7186a340d300610d89a1292f2d2838efb7ec 100644 (file)
@@ -14,6 +14,7 @@ Video Effects
                Hue saturation
                Interpolate Bayer
                Invert Video
+               Posterize
                RGBShift
                RGB - 601
                Reroute
@@ -162,6 +163,7 @@ Video Effects
                Reverse video
                Time Average
                TimeFront
+               Timelapse Helper
                F_amplify
                F_deflicker
                F_framerate
index 5515ddc7456e954fdc3308bb0690f730dcacbedb..344935d18365e3642ede196708c42ba8b129647e 100644 (file)
@@ -3,62 +3,48 @@ For usage help, refer to the following:
   https://cinelerra-gg.org/download/CinelerraGG_Manual.pdf
   http://g-raffa.eu/Cinelerra/HOWTO/basics.html
 .
-October 2020 New Features of note:
+2021 March New Features of note:
+  AppImage is the method of dibrusging new releases;
+   Newer distros use: CinGG-20210331-x86_64.AppImage 
+   Older distros use: CinGG-20210331-x86_64-older_distros.AppImage
+  There are 2 new plugins: Posterize and Timelapse Helper.
+.
+2021 1st Quarter New Features of note:
+  The manual can now be used in HTML format besides PDF.
+  Autosave backups optional feature in Preferences.
+  Additional FFmpeg video/audio render formats available.
+  Aspect ratio and Interlace improvements are in.
+  Batch Render menu now has hidden feature to prevent mistakes.
+  Openjpeg upgraded from 2.3.1 to 2.4.0.
+.
+2020 New Features of note:
   Drag select and deselect edits was added on the timeline.
-  Gang mode for patchbay options is now available.
-  Proxy scale factor of 1:1 to change bitrate works.
   Tile mixers allows for designating a rectangular area.
-September 2020 New Features of note:
   Motion plugin improvements by SGE for better results.
-  Camera and Projector menus now include easy range change.
   Faster transition playing with cache + drag capable.
-  Histogram new feature of average frame calculation.
-August 2020 New Features of note:
   Bump Autos new feature; especially useful for speed.
   Language preference now in Settings->Preferences.
-  Align Timecodes time-reference added as an improvement.
-  Ctrl-s and Ctrl-z more standard usage implemented.
   Added drag speed highlighting for easier usage of auto.
   Option and shortcut to select multiple edits.
-July 2020 New Features of note:
   Align Timecodes for various timecodes/stamps is available.
-  FFmpeg plugins will have changes take effect when use Enter.
   Subtitles can be generated in either SRT, SUB, or uDVD form.
   Mix masters make it easy to use multicam mode on timeline.
-  Auto Rotate in the metadata will now automatically rotate.
-June 2020 New Features of note:
   Ganging modes added for DAW-like editing of channels.
   Rotate plugin has been modernized and improved.
-  Debug assist added of BC_TRACE_XERROR environment variable.
-  Perspective plugin minor addition of numbered corners.
-May 2020 New Features of note:
   Audio track height button added, separate from video height.
   New patchbay toggle to vary individual vertical track height.
-  "Refresh on Release" additional setting in Videoscope.
-  Rotate plugin improvement for visibility and scaling.
-April 2020 New Features of note:
   Potential Performance improvements added.
   New way of creating and using a stack of Effects.
   Additional ShapeWipe transitions are now available.
   BoxBlur plugin allows forjust blurring an area.
-  The Drag box for plugins automatically turns on/off.  
-March 2020 New Features of note:
   Videoscope is now on the Compositor and Viewer.
-  Additional enhancements made for the Videoscope.
   Several new Shape Wipe transitions are included.
-  Slider bars added to Projector and Camera menus.
-  OpenCV and OpenExr have been upgraded on some O/S.
-February 2020 New Features of note:
   New ColorSpace plugin to convert color space/range.
   Updated 17 thirdparty libraries to keep up to date.
   Rewrote and replaced OGG/Theora/Vorbis file codec.
-  Shortcut for "Delete last track" has changed.
   PulseAudio option is now built in as audio choice.
-January 2020 New Features of note:
   Updated reference Manual using LaTeX now complete.
   File by Reference capability now available.
-  Hauppauge Remote Control added for OTA TV.
-  Pulse Audio added as a sound driver choice.
 .
 2019 New Features of note:
   Edit EDLs feature added for easily editing nested EDLs.
index 39230250687fadfa422a58c8e029dc1994c45e1f..ff00d7851ba44eecc7bff4038546aa059c99b8da 100644 (file)
@@ -110,6 +110,7 @@ DIRS = $(OPENCV_OBJS) \
        parametric \
        perspective \
        photoscale \
+       posterize \
        pitch \
        polar \
        radialblur \
@@ -142,6 +143,7 @@ DIRS = $(OPENCV_OBJS) \
        timeavg \
        timeblur \
        timefront \
+       timelapsehelper \
        timestretch \
        timestretchrt \
        titler \
diff --git a/cinelerra-5.1/plugins/posterize/Makefile b/cinelerra-5.1/plugins/posterize/Makefile
new file mode 100644 (file)
index 0000000..3e62f96
--- /dev/null
@@ -0,0 +1,9 @@
+include ../../plugin_defs
+
+OBJS = $(OBJDIR)/posterize.o
+
+PLUGIN = posterize
+
+include ../../plugin_config
+
+$(OBJDIR)/posterize.o: posterize.C
diff --git a/cinelerra-5.1/plugins/posterize/posterize.C b/cinelerra-5.1/plugins/posterize/posterize.C
new file mode 100644 (file)
index 0000000..2966ef2
--- /dev/null
@@ -0,0 +1,339 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2021 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "clip.h"
+#include "filexml.h"
+#include "language.h"
+#include "posterize.h"
+#include "theme.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+REGISTER_PLUGIN(PosterizeMain)
+
+
+#define MIN_STEPS 1
+#define MAX_STEPS 255
+
+PosterizeConfig::PosterizeConfig()
+{
+       steps = 255;
+}
+
+int PosterizeConfig::equivalent(PosterizeConfig &that)
+{
+    return steps == that.steps;
+}
+
+void PosterizeConfig::copy_from(PosterizeConfig &that)
+{
+    steps = that.steps;
+}
+
+void PosterizeConfig::boundaries()
+{
+    CLAMP(steps, MIN_STEPS, MAX_STEPS);
+}
+
+
+void PosterizeConfig::interpolate(PosterizeConfig &prev, 
+       PosterizeConfig &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->steps = (int)(prev.steps * prev_scale + next.steps * next_scale);
+       boundaries();
+}
+
+PosterizeMain::PosterizeMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+}
+
+PosterizeMain::~PosterizeMain()
+{
+       
+}
+
+const char* PosterizeMain::plugin_title() { return N_("Posterize"); }
+int PosterizeMain::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(PosterizeMain, PosterizeWindow)
+LOAD_CONFIGURATION_MACRO(PosterizeMain, PosterizeConfig)
+
+
+void PosterizeMain::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration())
+        {
+               thread->window->lock_window();
+            ((PosterizeWindow*)thread->window)->update(1, 1);
+               thread->window->unlock_window();
+        }
+       }
+}
+
+
+
+void PosterizeMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->xbuf);
+       output.tag.set_title("POSTERIZE");
+       output.tag.set_property("STEPS", config.steps);
+       output.append_tag();
+       output.tag.set_title("/POSTERIZE");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void PosterizeMain::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->xbuf);
+
+       int result = 0;
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("POSTERIZE"))
+                       {
+                               config.steps = input.tag.get_property("STEPS", config.steps);
+                       }
+               }
+       }
+    
+       config.boundaries();
+}
+
+
+#define PROCESS(type, components, yuv) \
+{ \
+       for(int i = 0; i < h; i++) \
+       { \
+               type *in_row = (type*)frame->get_rows()[i]; \
+               type *out_row = (type*)frame->get_rows()[i]; \
+ \
+        for(int j = 0; j < w; j++) \
+        { \
+            if(sizeof(type) == 4) \
+            { \
+                out_row[j * components + 0] = (int)(in_row[j * components + 0] / division) * division; \
+                out_row[j * components + 1] = (int)(in_row[j * components + 1] / division) * division; \
+                out_row[j * components + 2] = (int)(in_row[j * components + 2] / division) * division; \
+            } \
+            else \
+            { \
+                out_row[j * components + 0] = table_r[(int)in_row[j * components + 0]]; \
+                out_row[j * components + 1] = table_g[(int)in_row[j * components + 1]]; \
+                out_row[j * components + 2] = table_b[(int)in_row[j * components + 2]]; \
+            } \
+        } \
+       } \
+}
+
+
+int PosterizeMain::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       load_configuration();
+
+       read_frame(frame, 
+               0, 
+               start_position, 
+               frame_rate,
+               0);
+
+       int w = frame->get_w();
+       int h = frame->get_h();
+
+    int table_r[256];
+    int table_g[256];
+    int table_b[256];
+    float division = (float)255 / config.steps;
+    for(int i = 0; i < 256; i++)
+    {
+// luma/red
+        table_r[i] = (int)(i / division) * division;
+//printf("PosterizeMain::process_buffer %d i=%d %d\n", __LINE__, i, table_r[i]);
+//         if(BC_CModels::is_yuv(frame->get_color_model()))
+//         {
+//             table_g[i] = (int)(i / division) * division;
+//             table_b[i] = (int)(i / division) * division;
+//         }
+//         else
+//         {
+            table_g[i] = table_r[i];
+            table_b[i] = table_r[i];
+//        }
+    }
+
+
+
+       switch(frame->get_color_model())
+       {
+               case BC_YUV888:
+                       PROCESS(unsigned char, 3, 1);
+                       break;
+               case BC_YUVA8888:
+                       PROCESS(unsigned char, 4, 1);
+                       break;
+               case BC_RGB888:
+                       PROCESS(unsigned char, 3, 0);
+                       break;
+               case BC_RGBA8888:
+                       PROCESS(unsigned char, 4, 0);
+                       break;
+               case BC_RGB_FLOAT:
+            division = (float)1 / config.steps;
+                       PROCESS(float, 3, 0);
+                       break;
+               case BC_RGBA_FLOAT:
+            division = (float)1 / config.steps;
+                       PROCESS(float, 4, 0);
+                       break;
+       }
+
+       return 0;
+}
+
+
+
+PosterizeText::PosterizeText(PosterizeMain *plugin,
+    PosterizeWindow *gui,
+    int x,
+    int y,
+    int w,
+    int *output)
+ : BC_TextBox(x, y, w, 1, (int64_t)*output, 1, MEDIUMFONT)
+{
+    this->plugin = plugin;
+    this->gui = gui;
+       this->output = output;
+}
+
+int PosterizeText::handle_event()
+{
+    *output = atoi(get_text());
+    gui->update(1, 0);
+    plugin->send_configure_change();
+    return 1;
+}
+
+
+
+
+PosterizeSlider::PosterizeSlider(PosterizeMain *plugin,
+    PosterizeWindow *gui,
+       int x,
+       int y,
+    int w,
+       int *output,
+       int min,
+       int max)
+ : BC_ISlider(x, y, 0, w, w, min, max, *output)
+{
+       this->plugin = plugin;
+    this->gui = gui;
+       this->output = output;
+}
+int PosterizeSlider::handle_event()
+{
+       *output = get_value();
+    gui->update(0, 1);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+PosterizeWindow::PosterizeWindow(PosterizeMain *plugin)
+ : PluginClientWindow(plugin,
+       xS(300),
+       yS(100),
+       xS(300),
+       yS(100),
+       0)
+{ 
+       this->plugin = plugin; 
+}
+
+PosterizeWindow::~PosterizeWindow()
+{
+}
+
+void PosterizeWindow::create_objects()
+{
+    int text_w = xS(100);
+       int margin = client->get_theme()->widget_border;
+       int x = margin, y = margin;
+    BC_Title *title;
+       add_tool(title = new BC_Title(x, y, _("Steps per channel:")));
+    y += title->get_h() + margin;
+    add_tool(slider = new PosterizeSlider(plugin,
+        this,
+           x,
+           y,
+        get_w() - text_w - margin - margin - margin,
+           &plugin->config.steps,
+           MIN_STEPS,
+           MAX_STEPS));
+    x += slider->get_w() + margin;
+    add_tool(text = new PosterizeText(plugin,
+        this,
+        x,
+        y,
+        text_w,
+        &plugin->config.steps));
+
+       show_window();
+       flush();
+}
+
+void PosterizeWindow::update(int do_slider, int do_text)
+{
+       if(do_text) text->update((int64_t)plugin->config.steps);
+       if(do_slider) slider->update(plugin->config.steps);
+}
+
+
+
+
+
diff --git a/cinelerra-5.1/plugins/posterize/posterize.h b/cinelerra-5.1/plugins/posterize/posterize.h
new file mode 100644 (file)
index 0000000..aae6466
--- /dev/null
@@ -0,0 +1,117 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2020 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#ifndef POSTERIZE_H
+#define POSTERIZE_H
+
+class PosterizeMain;
+class PosterizeWindow;
+
+#include "bchash.h"
+#include "mutex.h"
+#include "pluginvclient.h"
+#include <sys/types.h>
+
+class PosterizeConfig
+{
+public:
+       PosterizeConfig();
+       int equivalent(PosterizeConfig &that);
+       void copy_from(PosterizeConfig &that);
+       void interpolate(PosterizeConfig &prev, 
+               PosterizeConfig &next, 
+               int64_t prev_frame, 
+               int64_t next_frame, 
+               int64_t current_frame);
+       void boundaries();
+       int steps;
+};
+
+class PosterizeMain : public PluginVClient
+{
+public:
+       PosterizeMain(PluginServer *server);
+       ~PosterizeMain();
+
+// required for all realtime plugins
+       PLUGIN_CLASS_MEMBERS(PosterizeConfig);
+       int process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate);
+       int is_realtime();
+       void update_gui();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+};
+
+
+
+class PosterizeSlider : public BC_ISlider
+{
+public:
+       PosterizeSlider(PosterizeMain *plugin, 
+        PosterizeWindow *gui,
+               int x, 
+               int y, 
+        int w,
+               int *output,
+               int min,
+               int max);
+       int handle_event();
+       PosterizeMain *plugin;
+    PosterizeWindow *gui;
+       int *output;
+};
+
+class PosterizeText : public BC_TextBox
+{
+public:
+    PosterizeText(PosterizeMain *plugin, 
+        PosterizeWindow *gui,
+        int x, 
+        int y, 
+        int w, 
+        int *output);
+    int handle_event();
+       PosterizeMain *plugin;
+    PosterizeWindow *gui;
+       int *output;
+};
+
+
+
+class PosterizeWindow : public PluginClientWindow
+{
+public:
+       PosterizeWindow(PosterizeMain *plugin);
+       ~PosterizeWindow();
+       
+       void create_objects();
+
+       void update(int do_slider, int do_text);
+
+       PosterizeMain *plugin;
+       PosterizeText *text;
+    PosterizeSlider *slider;
+};
+
+
+#endif
diff --git a/cinelerra-5.1/plugins/timelapsehelper/Makefile b/cinelerra-5.1/plugins/timelapsehelper/Makefile
new file mode 100644 (file)
index 0000000..030abcc
--- /dev/null
@@ -0,0 +1,9 @@
+include ../../plugin_defs
+
+OBJS = $(OBJDIR)/timelapsehelper.o
+
+PLUGIN = timelapsehelper
+
+include ../../plugin_config
+
+$(OBJDIR)/timelapsehelper.o: timelapsehelper.C
diff --git a/cinelerra-5.1/plugins/timelapsehelper/timelapsehelper.C b/cinelerra-5.1/plugins/timelapsehelper/timelapsehelper.C
new file mode 100644 (file)
index 0000000..b3831ed
--- /dev/null
@@ -0,0 +1,447 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2021 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+// output 1 frame for each block of frames
+// take the most similar frame in the current block of frames to the previous frame
+// this is for 3D printers where you want the bed in the same position
+
+
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "keyframe.h"
+#include "language.h"
+#include "transportque.inc"
+#include "pluginvclient.h"
+#include "theme.h"
+#include "vframe.h"
+
+#include <string.h>
+#include <stdint.h>
+
+
+class TimelapseHelper;
+class TimelapseHelperWindow;
+
+
+class TimelapseHelperConfig
+{
+public:
+       TimelapseHelperConfig();
+       void copy_from(TimelapseHelperConfig &config);
+       int equivalent(TimelapseHelperConfig &config);
+    void interpolate(TimelapseHelperConfig &prev, 
+           TimelapseHelperConfig &next, 
+           int64_t prev_frame, 
+           int64_t next_frame, 
+           int64_t current_frame);
+
+       int block_size;
+};
+
+
+
+
+class TimelapseHelperSize : public BC_TumbleTextBox
+{
+public:
+       TimelapseHelperSize(TimelapseHelper *plugin,
+               TimelapseHelperWindow *gui, 
+               int x,
+               int y,
+        int w);
+       int handle_event();
+       TimelapseHelper *plugin;
+       TimelapseHelperWindow *gui;
+};
+
+
+class TimelapseHelperWindow : public PluginClientWindow
+{
+public:
+       TimelapseHelperWindow(TimelapseHelper *plugin);
+       ~TimelapseHelperWindow();
+
+       void create_objects();
+
+       TimelapseHelper *plugin;
+       TimelapseHelperSize *size;
+};
+
+
+
+
+
+class TimelapseHelper : public PluginVClient
+{
+public:
+       TimelapseHelper(PluginServer *server);
+       ~TimelapseHelper();
+
+       PLUGIN_CLASS_MEMBERS(TimelapseHelperConfig)
+
+       int process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate);
+       int is_realtime();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+
+       int64_t calculate_difference(VFrame *frame1, VFrame *frame2);
+
+
+// frame used from the last block
+       VFrame *ref;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+TimelapseHelperConfig::TimelapseHelperConfig()
+{
+       block_size = 10;
+}
+
+void TimelapseHelperConfig::copy_from(TimelapseHelperConfig &config)
+{
+       this->block_size = config.block_size;
+}
+
+int TimelapseHelperConfig::equivalent(TimelapseHelperConfig &config)
+{
+       return this->block_size == config.block_size;
+}
+
+void TimelapseHelperConfig::interpolate(TimelapseHelperConfig &prev, 
+       TimelapseHelperConfig &next, 
+       int64_t prev_frame, 
+       int64_t next_frame, 
+       int64_t current_frame)
+{
+       this->block_size = next.block_size;
+}
+
+
+
+
+
+
+
+
+TimelapseHelperWindow::TimelapseHelperWindow(TimelapseHelper *plugin)
+ : PluginClientWindow(plugin,
+       xS(230),
+       yS(60),
+       xS(230),
+       yS(60),
+       0)
+{
+       this->plugin = plugin;
+}
+
+TimelapseHelperWindow::~TimelapseHelperWindow()
+{
+}
+
+void TimelapseHelperWindow::create_objects()
+{
+    int margin = client->get_theme()->widget_border;
+       int x = margin, y = margin;
+
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(x, y, _("Number of frames per block:")));
+       y += title->get_h() + margin;
+       size = new TimelapseHelperSize(plugin,
+               this,
+               x,
+               y,
+        get_w() - x - margin - xS(20) );
+    size->create_objects();
+       show_window();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+TimelapseHelperSize::TimelapseHelperSize(TimelapseHelper *plugin,
+       TimelapseHelperWindow *gui, 
+       int x,
+       int y,
+    int w)
+ : BC_TumbleTextBox(gui,
+    plugin->config.block_size, 
+    1,
+    1000,
+    x,
+       y,
+       w)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int TimelapseHelperSize::handle_event()
+{
+       plugin->config.block_size = atoi(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+REGISTER_PLUGIN(TimelapseHelper)
+
+
+
+
+
+
+TimelapseHelper::TimelapseHelper(PluginServer *server)
+ : PluginVClient(server)
+{
+       ref = 0;
+}
+
+
+TimelapseHelper::~TimelapseHelper()
+{
+       if(ref)
+    {
+        delete ref;
+    }
+}
+
+#define DIFFERENCE_MACRO(type, temp_type, components) \
+{ \
+       temp_type result2 = 0; \
+       for(int i = 0; i < h; i++) \
+       { \
+               type *row1 = (type*)frame1->get_rows()[i]; \
+               type *row2 = (type*)frame2->get_rows()[i]; \
+               for(int j = 0; j < w * components; j++) \
+               { \
+                       temp_type temp = *row1 - *row2; \
+                       result2 += (temp > 0 ? temp : -temp); \
+                       row1++; \
+                       row2++; \
+               } \
+       } \
+       result = (int64_t)result2; \
+}
+
+int64_t TimelapseHelper::calculate_difference(VFrame *frame1, VFrame *frame2)
+{
+       int w = frame1->get_w();
+       int h = frame1->get_h();
+       int64_t result = 0;
+       switch(frame1->get_color_model())
+       {
+               case BC_RGB888:
+               case BC_YUV888:
+                       DIFFERENCE_MACRO(unsigned char, int64_t, 3);
+                       break;
+               case BC_RGB_FLOAT:
+                       DIFFERENCE_MACRO(float, double, 3);
+                       break;
+               case BC_RGBA8888:
+               case BC_YUVA8888:
+                       DIFFERENCE_MACRO(unsigned char, int64_t, 4);
+                       break;
+               case BC_RGBA_FLOAT:
+                       DIFFERENCE_MACRO(float, double, 4);
+                       break;
+               case BC_RGB161616:
+               case BC_YUV161616:
+                       DIFFERENCE_MACRO(uint16_t, int64_t, 3);
+                       break;
+               case BC_RGBA16161616:
+               case BC_YUVA16161616:
+                       DIFFERENCE_MACRO(uint16_t, int64_t, 4);
+                       break;
+       }
+       return result;
+}
+
+
+
+int TimelapseHelper::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       load_configuration();
+
+// calculate frame positions
+    int64_t ref_position = get_source_start();
+    int64_t block_start;
+    int64_t block_end;
+    block_start = ref_position + (start_position - ref_position) * config.block_size;
+    block_end = block_start + config.block_size;
+
+// printf("TimelapseHelper::process_buffer %d current_position=%ld ref_position=%ld block_start=%ld block_end=%ld\n",
+// __LINE__, 
+//start_position,
+//ref_position,
+//block_start,
+//block_end);
+
+// load initial reference frame from plugin start
+       if(!ref)
+       {
+               ref = new VFrame(0,
+                       -1,
+                       frame->get_w(),
+                       frame->get_h(),
+                       frame->get_color_model(),
+                       -1);
+        read_frame(ref, 
+                       0, 
+                       ref_position, 
+                       frame_rate,
+                       0);
+        frame->copy_from(ref);
+       }
+    else
+// compare next block of frames to reference frame
+    {
+        VFrame *temp = new_temp(frame->get_w(),
+                       frame->get_h(),
+                       frame->get_color_model());
+        int64_t best = 0x7fffffffffffffffLL;
+        for(int64_t i = block_start; i < block_end; i++)
+        {
+
+            if(get_direction() == PLAY_FORWARD)
+            {
+                read_frame(temp, 
+                               0, 
+                               i, 
+                               frame_rate,
+                               0);
+            }
+            else
+            {
+                read_frame(temp, 
+                               0, 
+                               i + 1, 
+                               frame_rate,
+                               0);
+            }
+            
+            int64_t diff = calculate_difference(temp, 
+                                       ref);
+            if(diff < best)
+            {
+                best = diff;
+                frame->copy_from(temp);
+            }
+        }
+
+// replace reference frame with best match
+        ref->copy_from(frame);
+    }
+
+       return 0;
+}
+
+
+
+const char* TimelapseHelper::plugin_title() { return N_("Timelapse Helper"); }
+int TimelapseHelper::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(TimelapseHelper, TimelapseHelperWindow)
+
+LOAD_CONFIGURATION_MACRO(TimelapseHelper, TimelapseHelperConfig)
+
+
+void TimelapseHelper::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->xbuf);
+       output.tag.set_title("TIMELAPSEHELPER");
+       output.tag.set_property("BLOCK_SIZE", config.block_size);
+       output.append_tag();
+        output.tag.set_title("/TIMELAPSEHELPER");
+        output.append_tag();
+        output.append_newline();
+       output.terminate_string();
+}
+
+void TimelapseHelper::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->xbuf);
+
+       while(!input.read_tag())
+       {
+               if(input.tag.title_is("TIMELAPSEHELPER"))
+               {
+                       config.block_size = input.tag.get_property("BLOCK_SIZE", config.block_size);
+        }
+       }
+}
+
+void TimelapseHelper::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration())
+               {
+                       ((TimelapseHelperWindow*)thread->window)->lock_window("TimelapseHelper::update_gui");
+                       ((TimelapseHelperWindow*)thread->window)->size->update((int64_t)config.block_size);
+                       ((TimelapseHelperWindow*)thread->window)->unlock_window();
+               }
+       }
+}
+
+
+
+