From: Good Guy Date: Sun, 22 Aug 2021 19:00:31 +0000 (-0600) Subject: Contributed Speed PerCent video plugin X-Git-Tag: 2021-08~4 X-Git-Url: https://git.cinelerra-gg.org/git/?a=commitdiff_plain;h=ed0d23fa63102c890bfd22e30abb1a0a40b4390b;p=goodguy%2Fcinelerra.git Contributed Speed PerCent video plugin --- diff --git a/cinelerra-5.1/expanders.txt b/cinelerra-5.1/expanders.txt index 9bda7186..af915744 100644 --- a/cinelerra-5.1/expanders.txt +++ b/cinelerra-5.1/expanders.txt @@ -161,6 +161,7 @@ Video Effects Interpolate Video ReframeRT Reverse video + Speed PerCent Time Average TimeFront Timelapse Helper diff --git a/cinelerra-5.1/picon/cinfinity/speed_pc.png b/cinelerra-5.1/picon/cinfinity/speed_pc.png new file mode 100644 index 00000000..a98ec84d Binary files /dev/null and b/cinelerra-5.1/picon/cinfinity/speed_pc.png differ diff --git a/cinelerra-5.1/picon/cinfinity2/speed_pc.png b/cinelerra-5.1/picon/cinfinity2/speed_pc.png new file mode 100644 index 00000000..22d6d1bc Binary files /dev/null and b/cinelerra-5.1/picon/cinfinity2/speed_pc.png differ diff --git a/cinelerra-5.1/plugin_defs b/cinelerra-5.1/plugin_defs index 137c49f4..9f7d5396 100644 --- a/cinelerra-5.1/plugin_defs +++ b/cinelerra-5.1/plugin_defs @@ -106,6 +106,7 @@ video := \ sharpen \ shiftinterlace \ slide \ + speed_pc \ spherecam \ svg \ swapchannels \ diff --git a/cinelerra-5.1/plugins/Makefile b/cinelerra-5.1/plugins/Makefile index ff00d785..c4a5fd43 100644 --- a/cinelerra-5.1/plugins/Makefile +++ b/cinelerra-5.1/plugins/Makefile @@ -134,6 +134,7 @@ DIRS = $(OPENCV_OBJS) \ shiftinterlace \ slide \ spectrogram \ + speed_pc \ spherecam \ svg \ swapchannels \ diff --git a/cinelerra-5.1/plugins/speed_pc/Makefile b/cinelerra-5.1/plugins/speed_pc/Makefile new file mode 100644 index 00000000..d4c5eb09 --- /dev/null +++ b/cinelerra-5.1/plugins/speed_pc/Makefile @@ -0,0 +1,13 @@ +include ../../plugin_defs + +OBJS = $(OBJDIR)/speed_pc.o + +PLUGIN = speed_pc + +include ../../plugin_config + + +$(OBJDIR)/speed_pc.o: speed_pc.C + + + diff --git a/cinelerra-5.1/plugins/speed_pc/speed_pc.C b/cinelerra-5.1/plugins/speed_pc/speed_pc.C new file mode 100644 index 00000000..de8d1e1b --- /dev/null +++ b/cinelerra-5.1/plugins/speed_pc/speed_pc.C @@ -0,0 +1,564 @@ + +/* + * CINELERRA + * Copyright (C) 2008-2016 Adam Williams + * + * 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 + * + */ + +/* + * 2020. Derivative by ReframeRT plugin for a more easy use. + * It uses percentage value of the speed referred to originl speed (=100%). + * Some old ReframeRT parameters (Stretch and denom) have not been deleted, + * for future development, if any. + * Stretch and denom variables are set to a constant value: + * Stretch= 1; denom= 100.00. + * Speed_MIN= 1.00%; Speed_MAX= 1000.00% + */ + +#include "bcdisplayinfo.h" +#include "clip.h" +#include "bchash.h" +#include "filexml.h" +#include "speed_pc.h" +#include "guicast.h" +#include "language.h" +#include "pluginvclient.h" +#include "theme.h" +#include "transportque.h" + +#include + + + + +REGISTER_PLUGIN(SpeedPc); + + + +SpeedPcConfig::SpeedPcConfig() +{ + reset(RESET_DEFAULT_SETTINGS); +} + +void SpeedPcConfig::reset(int clear) +{ + switch(clear) { + case RESET_SPEED : + num = 100.00; + denom = 100.0; + stretch = 1; + break; + case RESET_ALL : + case RESET_DEFAULT_SETTINGS : + default: + num = 100.00; + denom = 100.0; + stretch = 1; + interp = 0; + optic_flow = 1; + break; + } +} + +int SpeedPcConfig::equivalent(SpeedPcConfig &src) +{ + return fabs(num - src.num) < 0.0001 && + fabs(denom - src.denom) < 0.0001 && + stretch == src.stretch && + interp == src.interp; +} + +void SpeedPcConfig::copy_from(SpeedPcConfig &src) +{ + this->num = src.num; + this->denom = src.denom; + this->stretch = src.stretch; + this->interp = src.interp; +} + +void SpeedPcConfig::interpolate(SpeedPcConfig &prev, + SpeedPcConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame) +{ + this->interp = prev.interp; + this->stretch = prev.stretch; + this->denom = prev.denom; + + if (this->interp && prev_frame != next_frame) + { + double next_weight = (double)(current_frame - prev_frame) / (next_frame - prev_frame); + double prev_weight = (double)(next_frame - current_frame) / (next_frame - prev_frame); + double prev_slope = prev.num / prev.denom, next_slope = next.num / next.denom; + // for interpolation, this is (for now) a simple linear slope to the next keyframe. + double scale = prev_slope * prev_weight + next_slope * next_weight; + this->num = this->denom * scale; + } + else + { + this->num = prev.num; + } +} + +void SpeedPcConfig::boundaries() +{ + if(num < 0.0001) num = 0.0001; + if(denom < 0.0001) denom = 0.0001; +} + + + + + + + + +SpeedPcWindow::SpeedPcWindow(SpeedPc *plugin) + : PluginClientWindow(plugin, xS(420), yS(150), xS(420), yS(150), 0) // Note: with "Stretch" and "Downsample" gui yS was yS(210) +{ + this->plugin = plugin; +} + +SpeedPcWindow::~SpeedPcWindow() +{ +} + +void SpeedPcWindow::create_objects() +{ + int xs10 = xS(10), xs64 = xS(64), xs200 = xS(200); + int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40); + int x2 = xS(80), x3 = xS(180); + int x = xs10, y = ys10; + int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22 + + BC_Bar *bar; + + add_subwindow(new BC_Title(x, y, _("Preset:"))); + x = x + x2; + add_subwindow(toggle25pc = new SpeedPcToggle(plugin, this, + plugin->config.num == 25, x, y, 25, "25%")); + x += xs64; + add_subwindow(toggle50pc = new SpeedPcToggle(plugin, this, + plugin->config.num == 50, x, y, 50, "50%")); + x += xs64; + add_subwindow(toggle100pc = new SpeedPcToggle(plugin, this, + plugin->config.num == 100, x, y, 100, "100%")); + x += xs64; + add_subwindow(toggle200pc = new SpeedPcToggle(plugin, this, + plugin->config.num == 200, x, y, 200, "200%")); + x += xs64; + add_subwindow(toggle400pc = new SpeedPcToggle(plugin, this, + plugin->config.num == 400, x, y, 400, "400%")); + x = xs10; y += ys30; + + add_tool(new BC_Title(x, y, _("Speed:"))); + add_tool(new BC_Title((x2-x), y, _("%"))); + speed_pc_text = new SpeedPcText(plugin, this, (x + x2), y); + speed_pc_text->create_objects(); + speed_pc_slider = new SpeedPcSlider(plugin, this, x3, y, xs200); + add_subwindow(speed_pc_slider); + clr_x = x3 + speed_pc_slider->get_w() + x; + add_subwindow(speed_pc_clr = new SpeedPcClr(plugin, this, + clr_x, y, RESET_SPEED)); + y += ys30; + +// REM 2020-06-23 +/* + add_subwindow(stretch = new SpeedPcStretch(plugin, this, x, y)); + y += yS(30); + add_subwindow(downsample = new SpeedPcDownsample(plugin, this, x, y)); + y += yS(30); +*/ + + add_subwindow(interpolate = new SpeedPcInterpolate(plugin, this, x, y)); + y += ys40; + +// Reset section + add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x)); + y += ys10; + add_subwindow(reset = new SpeedPcReset(plugin, this, x, y)); + + update(RESET_ALL); + show_window(); +} + +void SpeedPcWindow::update(int clear) +{ + switch(clear) { + case RESET_SPEED : + speed_pc_text->update((float)plugin->config.num); + speed_pc_slider->update((float)plugin->config.num); + update_toggles(); + break; + case RESET_ALL : + case RESET_DEFAULT_SETTINGS : + default: + speed_pc_text->update((float)plugin->config.num); + speed_pc_slider->update((float)plugin->config.num); + update_toggles(); + +// OLD ReframeRT code +/* + stretch->update(plugin->config.stretch); + downsample->update(!plugin->config.stretch); +*/ + interpolate->update(plugin->config.interp); + break; + } +} + + +int SpeedPcWindow::update_toggles() +{ + toggle25pc->update(EQUIV(plugin->config.num, 25)); + toggle50pc->update(EQUIV(plugin->config.num, 50)); + toggle100pc->update(EQUIV(plugin->config.num, 100)); + toggle200pc->update(EQUIV(plugin->config.num, 200)); + toggle400pc->update(EQUIV(plugin->config.num, 400)); + return 0; +} + + +SpeedPcToggle::SpeedPcToggle(SpeedPc *plugin, SpeedPcWindow *gui, + int init_value, + int x, + int y, + int value, + const char *string) + : BC_Radial(x, y, init_value, string) +{ + this->value = value; + this->plugin = plugin; + this->gui = gui; +} + +int SpeedPcToggle::handle_event() +{ + plugin->config.num = (float)value; + gui->update(RESET_SPEED); + plugin->send_configure_change(); + return 1; +} + + + +/* *********************************** */ +/* **** SPEED ******************** */ +SpeedPcText::SpeedPcText(SpeedPc *plugin, SpeedPcWindow *gui, + int x, + int y) + : BC_TumbleTextBox(gui, (float)plugin->config.num, + (float)1.00, (float)1000.00, x, y, xS(60), 2) +{ + this->plugin = plugin; + this->gui = gui; +} + +SpeedPcText::~SpeedPcText() +{ +} + +int SpeedPcText::handle_event() +{ + plugin->config.num = atof(get_text()); + plugin->config.denom = 100.00; + plugin->config.stretch = 1; + plugin->config.boundaries(); + gui->update(RESET_SPEED); + plugin->send_configure_change(); + return 1; +} + +SpeedPcSlider::SpeedPcSlider(SpeedPc *plugin, SpeedPcWindow *gui, + int x, int y, int w) + : BC_FSlider(x, y, 0, w, w, 1.00, 1000.00, plugin->config.num) +{ + this->plugin = plugin; + this->gui = gui; + enable_show_value(0); // Hide caption + set_precision(1.00); +} + +SpeedPcSlider::~SpeedPcSlider() +{ +} + +int SpeedPcSlider::handle_event() +{ + plugin->config.num = get_value(); + plugin->config.denom = 100.00; + plugin->config.stretch = 1; + gui->update(RESET_SPEED); + plugin->send_configure_change(); + return 1; +} +/* *********************************** */ + + +SpeedPcStretch::SpeedPcStretch(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, + int y) + : BC_Radial(x, y, plugin->config.stretch, _("Stretch")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int SpeedPcStretch::handle_event() +{ + plugin->config.stretch = get_value(); + gui->downsample->update(!get_value()); + plugin->send_configure_change(); + return 1; +} + + +SpeedPcDownsample::SpeedPcDownsample(SpeedPc *plugin, SpeedPcWindow *gui, + int x, + int y) + : BC_Radial(x, y, !plugin->config.stretch, _("Downsample")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int SpeedPcDownsample::handle_event() +{ + plugin->config.stretch = !get_value(); + gui->stretch->update(!get_value()); + plugin->send_configure_change(); + return 1; +} + +SpeedPcInterpolate::SpeedPcInterpolate(SpeedPc *plugin, SpeedPcWindow *gui, + int x, + int y) + : BC_CheckBox(x, y, 0, _("Interpolate")) +{ + this->plugin = plugin; + this->gui = gui; + set_tooltip(_("Interpolate between keyframes")); +} + +int SpeedPcInterpolate::handle_event() +{ + plugin->config.interp = get_value(); + gui->interpolate->update(get_value()); + plugin->send_configure_change(); + return 1; +} + + +SpeedPcClr::SpeedPcClr(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y, int clear) + : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button")) +{ + this->plugin = plugin; + this->gui = gui; + this->clear = clear; +} +SpeedPcClr::~SpeedPcClr() +{ +} +int SpeedPcClr::handle_event() +{ + plugin->config.reset(clear); + gui->update(clear); + plugin->send_configure_change(); + return 1; +} + + +SpeedPcReset::SpeedPcReset(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y) + : BC_GenericButton(x, y, _("Reset")) +{ + this->plugin = plugin; + this->gui = gui; +} +SpeedPcReset::~SpeedPcReset() +{ +} +int SpeedPcReset::handle_event() +{ + plugin->config.reset(RESET_ALL); + gui->update(RESET_ALL); + plugin->send_configure_change(); + return 1; +} + + + +SpeedPc::SpeedPc(PluginServer *server) + : PluginVClient(server) +{ +} + +SpeedPc::~SpeedPc() +{ + +} + +const char* SpeedPc::plugin_title() { return N_("Speed PerCent"); } +int SpeedPc::is_realtime() { return 1; } +int SpeedPc::is_synthesis() { return 1; } + + +NEW_WINDOW_MACRO(SpeedPc, SpeedPcWindow) +LOAD_CONFIGURATION_MACRO(SpeedPc, SpeedPcConfig) + +int SpeedPc::process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate) +{ + int64_t input_frame = get_source_start(); + SpeedPcConfig prev_config, next_config; + KeyFrame *tmp_keyframe, *next_keyframe = get_prev_keyframe(get_source_start()); + int64_t tmp_position, next_position; + int64_t segment_len; + double input_rate = frame_rate; + int is_current_keyframe; + +// if there are no keyframes, the default keyframe is used, and its position is always 0; +// if there are keyframes, the first keyframe can be after the effect start (and it controls settings before it) +// so let's calculate using a fake keyframe with the same settings but position == effect start + KeyFrame *fake_keyframe = new KeyFrame(); + fake_keyframe->copy_from(next_keyframe); + fake_keyframe->position = local_to_edl(get_source_start()); + next_keyframe = fake_keyframe; + + // calculate input_frame accounting for all previous keyframes + do + { + tmp_keyframe = next_keyframe; + next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0); + + tmp_position = edl_to_local(tmp_keyframe->position); + next_position = edl_to_local(next_keyframe->position); + + is_current_keyframe = + next_position > start_position // the next keyframe is after the current position + || next_keyframe->position == tmp_keyframe->position // there are no more keyframes + || !next_keyframe->position; // there are no keyframes at all + + if (is_current_keyframe) + segment_len = start_position - tmp_position; + else + segment_len = next_position - tmp_position; + + read_data(next_keyframe); + next_config.copy_from(config); + read_data(tmp_keyframe); + prev_config.copy_from(config); + config.interpolate(prev_config, next_config, tmp_position, next_position, tmp_position + segment_len); + + // the area under the curve is the number of frames to advance + // as long as interpolate() uses a linear slope we can use geometry to determine this + // if interpolate() changes to use a curve then this needs use (possibly) the definite integral + double prev_scale = prev_config.num / 100.00; + double config_scale = config.num / 100.00; + input_frame += (int64_t)(segment_len * ((prev_scale + config_scale) / 2)); + } while (!is_current_keyframe); + +// Change rate + if (!config.stretch) + { + input_rate *= config.num / 100.00; + + } + +// printf("SpeedPc::process_buffer %d %lld %f %lld %f\n", +// __LINE__, +// start_position, +// frame_rate, +// input_frame, +// input_rate); + + read_frame(frame, + 0, + input_frame, + input_rate, + 0); + + delete fake_keyframe; + + return 0; +} + + + +void SpeedPc::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->xbuf); + output.tag.set_title("SPEED_PC"); + output.tag.set_property("SPEED", config.num); + output.tag.set_property("DENOM", config.denom); + output.tag.set_property("STRETCH", config.stretch); + output.tag.set_property("INTERPOLATE", config.interp); + output.append_tag(); + output.tag.set_title("/SPEED_PC"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void SpeedPc::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->xbuf); + + while(!input.read_tag()) + { + if(input.tag.title_is("SPEED_PC")) + { + config.num = input.tag.get_property("SPEED", config.num); + config.denom = input.tag.get_property("DENOM", config.denom); + config.stretch = input.tag.get_property("STRETCH", config.stretch); + config.interp = input.tag.get_property("INTERPOLATE", config.interp); + } + } +} + +void SpeedPc::update_gui() +{ + if(thread) + { + int changed = load_configuration(); + + if(changed) + { + SpeedPcWindow* window = (SpeedPcWindow*)thread->window; + window->lock_window("SpeedPc::update_gui"); + window->update(RESET_ALL); +// OLD ReframeRT code +/* + window->stretch->update(config.stretch); + window->downsample->update(!config.stretch); +*/ + window->unlock_window(); + } + } +} + + + + + diff --git a/cinelerra-5.1/plugins/speed_pc/speed_pc.h b/cinelerra-5.1/plugins/speed_pc/speed_pc.h new file mode 100644 index 00000000..58fb6939 --- /dev/null +++ b/cinelerra-5.1/plugins/speed_pc/speed_pc.h @@ -0,0 +1,234 @@ + +/* + * CINELERRA + * Copyright (C) 2008-2016 Adam Williams + * + * 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 + * + */ + +/* + * 2020. Derivative by ReframeRT plugin for a more easy use. + * It uses percentage value of the speed referred to originl speed (=100%). + * Some old ReframeRT parameters (Stretch and denom) have not been deleted, + * for future development, if any. + * Stretch and denom variables are set to a constant value: + * Stretch= 1; denom= 100.00. + * Speed_MIN= 1.00%; Speed_MAX= 1000.00% + */ + +#ifndef SPEED_PC_H +#define SPEED_PC_H + + +#include "bcdisplayinfo.h" +#include "clip.h" +#include "bchash.h" +#include "filexml.h" +#include "guicast.h" +#include "language.h" +#include "pluginvclient.h" +#include "theme.h" +#include "transportque.h" + + +#define RESET_DEFAULT_SETTINGS 10 +#define RESET_ALL 0 +#define RESET_SPEED 1 + +class SpeedPcConfig; +class SpeedPcText; +class SpeedPcSlider; +class SpeedPcStretch; +class SpeedPcDownsample; +class SpeedPcInterpolate; +class SpeedPcClr; +class SpeedPc; +class SpeedPcWindow; +class SpeedPcReset; + + + +class SpeedPcConfig +{ +public: + SpeedPcConfig(); + void boundaries(); + int equivalent(SpeedPcConfig &src); + void reset(int clear); + void copy_from(SpeedPcConfig &src); + void interpolate(SpeedPcConfig &prev, + SpeedPcConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame); + + double num; + double denom; + int stretch; + int interp; + int optic_flow; +}; + + + +class SpeedPcToggle : public BC_Radial +{ +public: + SpeedPcToggle(SpeedPc *plugin, + SpeedPcWindow *gui, + int init_value, + int x, + int y, + int value, + const char *string); + int handle_event(); + + SpeedPc *plugin; + SpeedPcWindow *gui; + int value; +}; + +class SpeedPcText : public BC_TumbleTextBox +{ +public: + SpeedPcText(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, + int y); + ~SpeedPcText(); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcSlider : public BC_FSlider +{ +public: + SpeedPcSlider(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, int y, int w); + ~SpeedPcSlider(); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcClr : public BC_Button +{ +public: + SpeedPcClr(SpeedPc *plugin, SpeedPcWindow *gui, + int x, int y, int clear); + ~SpeedPcClr(); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; + int clear; +}; + +class SpeedPcStretch : public BC_Radial +{ +public: + SpeedPcStretch(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, + int y); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcDownsample : public BC_Radial +{ +public: + SpeedPcDownsample(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, + int y); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcInterpolate : public BC_CheckBox +{ +public: + SpeedPcInterpolate(SpeedPc *plugin, + SpeedPcWindow *gui, + int x, + int y); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcReset : public BC_GenericButton +{ +public: + SpeedPcReset(SpeedPc *plugin, SpeedPcWindow *gui, int x, int y); + ~SpeedPcReset(); + int handle_event(); + SpeedPc *plugin; + SpeedPcWindow *gui; +}; + +class SpeedPcWindow : public PluginClientWindow +{ +public: + SpeedPcWindow(SpeedPc *plugin); + ~SpeedPcWindow(); + void create_objects(); + void update(int clear); + + int update_toggles(); + + SpeedPc *plugin; + + SpeedPcToggle *toggle25pc; + SpeedPcToggle *toggle50pc; + SpeedPcToggle *toggle100pc; + SpeedPcToggle *toggle200pc; + SpeedPcToggle *toggle400pc; + + SpeedPcText *speed_pc_text; + SpeedPcSlider *speed_pc_slider; + SpeedPcClr *speed_pc_clr; + SpeedPcStretch *stretch; + SpeedPcDownsample *downsample; + SpeedPcInterpolate *interpolate; + + SpeedPcReset *reset; +}; + + +class SpeedPc : public PluginVClient +{ +public: + SpeedPc(PluginServer *server); + ~SpeedPc(); + + PLUGIN_CLASS_MEMBERS(SpeedPcConfig) + + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + void update_gui(); + int is_realtime(); + int is_synthesis(); + int process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate); +}; + +#endif \ No newline at end of file