add proxy
authorGood Guy <good1.2guy@gmail.com>
Tue, 19 Sep 2017 17:19:31 +0000 (11:19 -0600)
committerGood Guy <good1.2guy@gmail.com>
Tue, 19 Sep 2017 17:19:31 +0000 (11:19 -0600)
17 files changed:
cinelerra-5.1/cinelerra/Makefile
cinelerra-5.1/cinelerra/edl.C
cinelerra-5.1/cinelerra/edlsession.C
cinelerra-5.1/cinelerra/edlsession.h
cinelerra-5.1/cinelerra/floatauto.h
cinelerra-5.1/cinelerra/floatautos.C
cinelerra-5.1/cinelerra/floatautos.h
cinelerra-5.1/cinelerra/indexable.C
cinelerra-5.1/cinelerra/indexable.h
cinelerra-5.1/cinelerra/mainmenu.C
cinelerra-5.1/cinelerra/maskautos.C
cinelerra-5.1/cinelerra/maskautos.h
cinelerra-5.1/cinelerra/mwindow.h
cinelerra-5.1/cinelerra/mwindow.inc
cinelerra-5.1/cinelerra/mwindowedit.C
cinelerra-5.1/cinelerra/proxy.C [new file with mode: 0644]
cinelerra-5.1/cinelerra/proxy.h [new file with mode: 0644]

index 240a8c7e2c96bbcc845aced0a7225c292a067b80..6fe883550bb4c6be5333ac7f1d1337499dbf7dc0 100644 (file)
@@ -231,6 +231,7 @@ OBJS = \
        $(OBJDIR)/preferences.o \
        $(OBJDIR)/preferencesthread.o \
        $(OBJDIR)/probeprefs.o \
+       $(OBJDIR)/proxy.o \
        $(OBJDIR)/question.o \
        $(OBJDIR)/quit.o \
        $(OBJDIR)/recconfirmdelete.o \
index 5d9ff496238e1f15727fe154c333bf04ae015786..591b4ca93141a08a4e9e8a499abb0cb8afcaffe5 100644 (file)
@@ -1061,7 +1061,8 @@ int EDL::dump(FILE *fp)
                        "  output_h: %d\n"
                        "  aspect_w: %f\n"
                        "  aspect_h: %f\n"
-                       "  color_model: %d\n",
+                       "  color_model: %d\n"
+                       "  proxy_scale: %d\n",
                                session->video_channels,
                                session->video_tracks,
                                session->frame_rate,
@@ -1070,7 +1071,8 @@ int EDL::dump(FILE *fp)
                                session->output_h,
                                session->aspect_w,
                                session->aspect_h,
-                               session->color_model);
+                               session->color_model,
+                               session->proxy_scale);
 
                fprintf(fp," CLIPS\n");
                fprintf(fp,"  total: %d\n", clips.total);
index 1a83545e063cccf443973b9c7f4583074cd922e4..c65e53f1ba977f0041cb1ed067e1e1f417862f76 100644 (file)
@@ -98,6 +98,7 @@ EDLSession::EDLSession(EDL *edl)
        playback_buffer = 4096;
        playback_cursor_visible = 0;
        playback_preload = 0;
+       proxy_scale = 1;
        decode_subtitles = 0;
        subtitle_number = 0;
        label_cells = 0;
@@ -165,21 +166,23 @@ int EDLSession::need_rerender(EDLSession *ptr)
                (decode_subtitles != ptr->decode_subtitles) ||
                (subtitle_number != ptr->subtitle_number) ||
                (interpolate_raw != ptr->interpolate_raw) ||
-               (white_balance_raw != ptr->white_balance_raw));
+               (white_balance_raw != ptr->white_balance_raw) ||
+               (proxy_scale != ptr->proxy_scale));
 }
 
 void EDLSession::equivalent_output(EDLSession *session, double *result)
 {
-       if(session->output_w != output_w ||
-               session->output_h != output_h ||
-               !EQUIV(session->frame_rate, frame_rate) ||
-               session->color_model != color_model ||
-               session->interpolation_type != interpolation_type ||
-               session->interpolate_raw != interpolate_raw ||
-               session->white_balance_raw != white_balance_raw ||
-               session->mpeg4_deblock != mpeg4_deblock ||
-               session->decode_subtitles != decode_subtitles ||
-               session->subtitle_number != subtitle_number)
+       if( session->output_w != output_w ||
+           session->output_h != output_h ||
+           !EQUIV(session->frame_rate, frame_rate) ||
+           session->color_model != color_model ||
+           session->interpolation_type != interpolation_type ||
+           session->interpolate_raw != interpolate_raw ||
+           session->white_balance_raw != white_balance_raw ||
+           session->mpeg4_deblock != mpeg4_deblock ||
+           session->decode_subtitles != decode_subtitles ||
+           session->subtitle_number != subtitle_number ||
+           session->proxy_scale != proxy_scale )
                *result = 0;
 
 // If it's before the current brender_start, render extra data.
@@ -281,6 +284,7 @@ int EDLSession::load_defaults(BC_Hash *defaults)
        delete playback_config;
        playback_config = new PlaybackConfig;
        playback_config->load_defaults(defaults);
+//     proxy_scale = defaults->get("PROXY_SCALE", 1);
        real_time_playback = defaults->get("PLAYBACK_REALTIME", 0);
        real_time_record = defaults->get("REALTIME_RECORD", 0);
        record_positioning = defaults->get("RECORD_POSITIONING", 1);
@@ -422,6 +426,7 @@ int EDLSession::save_defaults(BC_Hash *defaults)
        defaults->update("PLAYBACK_SOFTWARE_POSITION", playback_software_position);
        playback_config->save_defaults(defaults);
        defaults->update("PLAYBACK_REALTIME", real_time_playback);
+//     defaults->update("PROXY_SCALE", proxy_scale);
        defaults->update("REALTIME_RECORD", real_time_record);
        defaults->update("RECORD_POSITIONING", record_positioning);
        defaults->update("RECORD_RAW_STREAM", record_raw_stream);
@@ -489,6 +494,7 @@ void EDLSession::boundaries()
        Workarounds::clamp(min_meter_db, -80, -20);
        Workarounds::clamp(max_meter_db, 0, 10);
        Workarounds::clamp(frames_per_foot, 1, 32);
+       Workarounds::clamp(proxy_scale, 1, 32);
        Workarounds::clamp(output_w, 16, (int)BC_INFINITY);
        Workarounds::clamp(output_h, 16, (int)BC_INFINITY);
        Workarounds::clamp(video_write_length, 1, 1000);
@@ -546,6 +552,7 @@ int EDLSession::load_video_config(FileXML *file, int append_mode, uint32_t load_
        output_h = file->tag.get_property("OUTPUTH", output_h);
        aspect_w = file->tag.get_property("ASPECTW", aspect_w);
        aspect_h = file->tag.get_property("ASPECTH", aspect_h);
+       proxy_scale = file->tag.get_property("PROXY_SCALE", proxy_scale);
        return 0;
 }
 
@@ -735,6 +742,7 @@ int EDLSession::save_video_config(FileXML *file)
        file->tag.set_property("OUTPUTH", output_h);
        file->tag.set_property("ASPECTW", aspect_w);
        file->tag.set_property("ASPECTH", aspect_h);
+       file->tag.set_property("PROXY_SCALE", proxy_scale);
        file->append_tag();
        file->tag.set_title("/VIDEO");
        file->append_tag();
@@ -873,6 +881,7 @@ int EDLSession::copy(EDLSession *session)
        view_follows_playback = session->view_follows_playback;
        vwindow_meter = session->vwindow_meter;
        vwindow_zoom = session->vwindow_zoom;
+       proxy_scale = session->proxy_scale;
 
        subtitle_number = session->subtitle_number;
        decode_subtitles = session->decode_subtitles;
@@ -887,9 +896,11 @@ void EDLSession::dump()
        printf("EDLSession::dump\n");
        printf("    audio_tracks=%d audio_channels=%d sample_rate=%jd\n"
                "    video_tracks=%d frame_rate=%f output_w=%d output_h=%d aspect_w=%f aspect_h=%f\n"
-               "    decode subtitles=%d subtitle_number=%d label_cells=%d program_no=%d\n",
+               "    decode subtitles=%d subtitle_number=%d label_cells=%d program_no=%d\n"
+               "    proxy_scale=%d\n",
                audio_tracks, audio_channels, sample_rate, video_tracks,
                frame_rate, output_w, output_h, aspect_w, aspect_h,
-               decode_subtitles, subtitle_number, label_cells, program_no);
+               decode_subtitles, subtitle_number, label_cells, program_no,
+               proxy_scale);
 }
 
index 44838fa86521f2651f067fb848c043cdcad819f3..eddced8e0a6973a52d53a2776cd4ee306992ba0d 100644 (file)
@@ -159,6 +159,8 @@ public:
        int label_cells;
        int program_no;
        int playback_software_position;
+// current settings are scaled this much from the original settings
+        int proxy_scale;
 //     int playback_strategy;
 // Play audio in realtime priority
        int real_time_playback;
index 7049c59e0d4cb31fdb854662fc4890b2dbf34d5b..be0b304e720f529c6aaafe81bae7b64127ccc442 100644 (file)
@@ -32,6 +32,7 @@ class FloatAuto;
 
 class FloatAuto : public Auto
 {
+       friend class FloatAutos;
 public:
        FloatAuto() {};
        FloatAuto(EDL *edl, FloatAutos *autos);
index fde4a155eca44c0e571658541c714d295796bd58..4ee1d4070702542362fcf360577d4729f00d91c2 100644 (file)
@@ -409,6 +409,22 @@ void FloatAutos::get_extents(float *min,
        }
 }
 
+void FloatAutos::set_proxy(int orig_scale, int new_scale)
+{
+       float orig_value;
+       orig_value = ((FloatAuto*)default_auto)->value * orig_scale;
+       ((FloatAuto*)default_auto)->value = orig_value / new_scale;
+
+       for( FloatAuto *current= (FloatAuto*)first; current; current=(FloatAuto*)NEXT ) {
+               orig_value = current->value * orig_scale;
+               current->value = orig_value / new_scale;
+               orig_value = current->control_in_value * orig_scale;
+               current->control_in_value = orig_value / new_scale;
+               orig_value = current->control_out_value * orig_scale;
+               current->control_out_value = orig_value / new_scale;
+       }
+}
+
 void FloatAutos::dump()
 {
        printf("        FloatAutos::dump %p\n", this);
index d83f150936890da5349b3d7839f3d30b2c94d78d..96d06d6390c582a127005dc4f91a9e507b3d84d3 100644 (file)
@@ -63,6 +63,7 @@ public:
                int64_t unit_end);
 
        void set_automation_mode(int64_t start, int64_t end, int mode);
+       void set_proxy(int orig_scale, int new_scale);
        double automation_integral(int64_t start, int64_t length, int direction);
 
        void dump();
index e7f090c483565f1cc5c5f878fb5103c35e0762db..a038b3308c0188a089597fb3d23fea165f741d95 100644 (file)
@@ -54,7 +54,7 @@ int64_t Indexable::get_audio_samples()
        return 0;
 }
 
-void Indexable::update_path(char *new_path)
+void Indexable::update_path(const char *new_path)
 {
        strcpy(path, new_path);
 }
index a9b13277c955d7a1fe5004599be65d7bb9ab7cf2..fa9db6aea21d570d62416df0de83f3a2c445de2f 100644 (file)
@@ -49,7 +49,7 @@ public:
        virtual int64_t get_video_frames();
 
         void copy_indexable(Indexable *src);
-       void update_path(char *new_path);
+       void update_path(const char *new_path);
        void update_index(Indexable *src);
 
        IndexState *index_state;
index a3fb40385fab857918ea6a0f5c68a81f9b670a5e..a36efccec6a2cb0bcb11a329e8cec5d5e129c666 100644 (file)
@@ -60,6 +60,7 @@
 #include "patchbay.h"
 #include "playbackengine.h"
 #include "preferences.h"
+#include "proxy.h"
 #include "preferencesthread.h"
 #include "quit.h"
 #include "record.h"
@@ -203,6 +204,9 @@ void MainMenu::create_objects()
 
        settingsmenu->add_item(new SetFormat(mwindow));
        settingsmenu->add_item(preferences = new PreferencesMenuitem(mwindow));
+       ProxyMenuItem *proxy;
+       settingsmenu->add_item(proxy = new ProxyMenuItem(mwindow));
+       proxy->create_objects();
        mwindow->preferences_thread = preferences->thread;
        settingsmenu->add_item(labels_follow_edits = new LabelsFollowEdits(mwindow));
        settingsmenu->add_item(plugins_follow_edits = new PluginsFollowEdits(mwindow));
index e67c4ea2342a34f0e54b34519913c86c51e5f664..843d76ee3cd75da64e75a38c2adc67287b3acb1f 100644 (file)
@@ -294,4 +294,11 @@ void MaskAutos::translate_masks(float translate_x, float translate_y)
        }
 }
 
+void MaskAutos::set_proxy(int orig_scale, int new_scale)
+{
+       ((MaskAuto *)default_auto)->scale_submasks(orig_scale, new_scale);
+       for( MaskAuto* current=(MaskAuto*)first; current; current=(MaskAuto*)NEXT ) {
+               current->scale_submasks(orig_scale, new_scale);
+       }
+}
 
index e28c023305a9d175eabc95501b342cbebee735cf..fcac125af29ea00767fd98c80ae2abb4fa0d2f14 100644 (file)
@@ -60,9 +60,7 @@ public:
        int total_submasks(int64_t position, int direction);
 // Translates all mask points
        void translate_masks(float translate_x, float translate_y);
+       void set_proxy(int orig_scale, int new_scale);
 };
 
-
-
-
 #endif
index 0bbcfe2c285cc797474d21b26157180a16c24d80..22f43e54b74a2038784a086e8e3db3adb34ed6c6 100644 (file)
@@ -492,6 +492,8 @@ public:
        int modify_edithandles();
        int modify_pluginhandles();
        void finish_modify_handles();
+        void set_proxy(int new_scale,
+                ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets);
 
        void dump_plugins(FILE *fp=stdout);
        void dump_edl(FILE *fp=stdout);
index f685d018c5f0c9a6fd5361cee54a51c12b0912f2..48d2648479ae85f6e3e0146f9ba0086009adc337 100644 (file)
@@ -106,6 +106,7 @@ N_("Cinelerra: Preferences")
 N_("Cinelerra: Probes")
 N_("Cinelerra: Program")
 N_("Cinelerra: Projector")
+N_("Cinelerra: Proxy settings")
 N_("Cinelerra: Question")
 N_("Cinelerra: Record")
 N_("Cinelerra: Recording")
index 7710d4ada162471c62cb6c0d81b1fae6fd5156ed..a0e9b955f510417dadc2bcd999bb0681b15b6938 100644 (file)
@@ -2274,6 +2274,53 @@ void MWindow::remap_audio(int pattern)
        }
 }
 
+void MWindow::set_proxy(int new_scale,
+        ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
+{
+       int orig_scale = edl->session->proxy_scale;
+       edl->session->proxy_scale = new_scale;
+
+// project size
+       float orig_w = (float)edl->session->output_w * orig_scale;
+       float orig_h = (float)edl->session->output_h * orig_scale;
+       edl->session->output_w = Units::round(orig_w / new_scale);
+       edl->session->output_h = Units::round(orig_h / new_scale);
+
+// track sizes
+       for( Track *track=edl->tracks->first; track; track=track->next ) {
+               if( track->data_type != TRACK_VIDEO ) continue;
+               orig_w = (float)track->track_w * orig_scale;
+               orig_h = (float)track->track_h * orig_scale;
+               track->track_w = Units::round(orig_w / new_scale);
+               track->track_h = Units::round(orig_h / new_scale);
+               ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_X])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_Y])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_X])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_Y])->
+                       set_proxy(orig_scale, new_scale);
+       }
+
+// assets
+       for( int i=0; i<proxy_assets->size(); i++ ) {
+               Asset *proxy_asset = edl->assets->update((Asset*)proxy_assets->get(i));
+// replace track contents
+               for( Track *track = edl->tracks->first; track; track = track->next ) {
+                       if( track->data_type != TRACK_VIDEO ) continue;
+                       for( Edit *edit = track->edits->first; edit; edit = edit->next ) {
+                               if( !edit->asset ) continue;
+                               if( !strcmp(edit->asset->path, orig_assets->get(i)->path) ) {
+                                       edit->asset = proxy_asset;
+                               }
+                       }
+               }
+       }
+}
+
 void MWindow::cut_commercials()
 {
        undo->update_undo_before();
diff --git a/cinelerra-5.1/cinelerra/proxy.C b/cinelerra-5.1/cinelerra/proxy.C
new file mode 100644 (file)
index 0000000..b9a5ef5
--- /dev/null
@@ -0,0 +1,715 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2015 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 "assets.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "confirmsave.h"
+#include "cstrdup.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "errorbox.h"
+#include "file.h"
+#include "filesystem.h"
+#include "formattools.h"
+#include "language.h"
+#include "mainprogress.h"
+#include "mainundo.h"
+#include "mutex.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "overlayframe.h"
+#include "preferences.h"
+#include "proxy.h"
+#include "theme.h"
+
+#define WIDTH 400
+#define HEIGHT 320
+#define MAX_SCALE 16
+
+ProxyMenuItem::ProxyMenuItem(MWindow *mwindow)
+ : BC_MenuItem(_("Proxy settings..."),  _("Alt-P"), 'p')
+{
+       this->mwindow = mwindow;
+       set_alt();
+}
+
+void ProxyMenuItem::create_objects()
+{
+       thread = new ProxyThread(mwindow);
+}
+
+int ProxyMenuItem::handle_event()
+{
+       mwindow->gui->unlock_window();
+       thread->start();
+       mwindow->gui->lock_window("ProxyMenuItem::handle_event");
+
+       return 1;
+}
+
+ProxyThread::ProxyThread(MWindow *mwindow)
+{
+       this->mwindow = mwindow;
+       gui = 0;
+       asset = new Asset;
+       progress = 0;
+       counter_lock = new Mutex("ProxyThread::counter_lock");
+       bzero(size_text, sizeof(char*) * MAX_SIZES);
+       bzero(size_factors, sizeof(int) * MAX_SIZES);
+       total_sizes = 0;
+}
+ProxyThread::~ProxyThread()
+{
+       for( int i=0; i<MAX_SIZES; ++i )
+               if( size_text[i] ) delete [] size_text[i];
+}
+
+BC_Window* ProxyThread::new_gui()
+{
+       asset->format = FILE_FFMPEG;
+       asset->load_defaults(mwindow->defaults, "PROXY_", 1, 1, 0, 0, 0);
+       mwindow->gui->lock_window("ProxyThread::new_gui");
+       int x = mwindow->gui->get_abs_cursor_x(0) - WIDTH / 2;
+       int y = mwindow->gui->get_abs_cursor_y(0) - HEIGHT / 2;
+
+       gui = new ProxyWindow(mwindow, this, x, y);
+       gui->create_objects();
+       mwindow->gui->unlock_window();
+       return gui;
+}
+
+void ProxyThread::scale_to_text(char *string, int scale)
+{
+       calculate_sizes();
+       strcpy(string, size_text[0]);
+       for( int i = 0; i < total_sizes; i++ ) {
+               if( scale == size_factors[i] ) {
+                       strcpy(string, size_text[i]);
+                       break;
+               }
+       }
+}
+
+
+void ProxyThread::calculate_sizes()
+{
+// delete old values
+       for( int i = 0; i < MAX_SIZES; i++ ) {
+               if( size_text[i] ) delete [] size_text[i];
+       }
+       bzero(size_text, sizeof(char*) * MAX_SIZES);
+       bzero(size_factors, sizeof(int) * MAX_SIZES);
+
+       int orig_w = mwindow->edl->session->output_w * orig_scale;
+       int orig_h = mwindow->edl->session->output_h * orig_scale;
+       total_sizes = 0;
+       
+       size_text[0] = cstrdup(_("Original size"));
+       size_factors[0] = 1;
+       
+       int current_factor = 2;
+       total_sizes = 1;
+       for( current_factor = 2; current_factor < MAX_SCALE; current_factor++ ) {
+               if( current_factor * (orig_w / current_factor) == orig_w &&
+                       current_factor * (orig_h / current_factor) == orig_h ) {
+//printf("ProxyThread::calculate_sizes %d\n", current_factor);
+                       char string[BCTEXTLEN];
+                       sprintf(string, "1/%d", current_factor);
+                       size_text[total_sizes] = cstrdup(string);
+                       size_factors[total_sizes] = current_factor;
+                       total_sizes++;
+               }
+               
+               current_factor++;
+       }
+}
+
+void ProxyThread::handle_close_event(int result)
+{
+       asset->save_defaults(mwindow->defaults, "PROXY_", 1, 1, 0, 0, 0); 
+
+       if( !result ) {
+               to_proxy();
+       }
+}
+
+void ProxyThread::to_proxy()
+{
+// test for new files
+       ArrayList<char *> confirm_paths;
+       confirm_paths.set_array_delete();
+// all proxy assets
+       ArrayList<Indexable*> proxy_assets;
+// assets which must be created
+       ArrayList<Asset*> needed_assets;
+// original assets
+       ArrayList<Indexable*> orig_assets;
+// original assets which match the needed_assets
+       ArrayList<Asset*> needed_orig_assets;
+       Asset *proxy_asset = 0;
+       Asset *orig_asset = 0;
+
+       mwindow->edl->Garbage::add_user();
+       mwindow->save_backup();
+       mwindow->undo->update_undo_before(_("proxy"), this);
+
+// revert project to original size from current size
+       if( mwindow->edl->session->proxy_scale > 1 ) {
+               for( orig_asset = mwindow->edl->assets->first;
+                       orig_asset;
+                       orig_asset = orig_asset->next ) {
+                       char new_path[BCTEXTLEN];
+                       to_proxy_path(new_path, orig_asset, mwindow->edl->session->proxy_scale);
+
+// test if proxy asset was already added to proxy assets
+                       int got_it = 0;
+                       proxy_asset = 0;
+                       for( int i = 0; i < proxy_assets.size(); i++ ) {
+                               if( !strcmp(proxy_assets.get(i)->path, new_path) ) {
+                                       got_it = 1;
+                                       break;
+                               }
+                       }
+
+// add pointer to existing EDL asset if it exists
+// EDL won't delete it unless it's the same pointer.
+                       if( !got_it ) {
+                               proxy_asset = mwindow->edl->assets->get_asset(new_path);
+
+                               if( proxy_asset ) {
+                                       proxy_assets.append(proxy_asset);
+                                       proxy_asset->Garbage::add_user();
+
+                                       orig_assets.append(orig_asset);
+                                       orig_asset->Garbage::add_user();
+                               }
+
+                       }
+               }
+
+// convert from the proxy assets to the original assets
+               mwindow->set_proxy(1, &proxy_assets, &orig_assets);
+
+// remove the proxy assets
+               mwindow->remove_assets_from_project(0, 0, &proxy_assets, NULL);
+
+
+               for( int i = 0; i < proxy_assets.size(); i++ ) {
+                       proxy_assets.get(i)->Garbage::remove_user();
+               }
+               proxy_assets.remove_all();
+
+               for( int i = 0; i < orig_assets.size(); i++ ) {
+                       orig_assets.get(i)->Garbage::remove_user();
+               }
+               orig_assets.remove_all();
+       }
+
+// convert to new size if not original size
+       if( new_scale != 1 ) {
+               for( orig_asset = mwindow->edl->assets->first;
+                       orig_asset;
+                       orig_asset = orig_asset->next ) {
+                       if( orig_asset->video_data ) {
+                               char new_path[BCTEXTLEN];
+                               to_proxy_path(new_path, orig_asset, new_scale);
+// add to proxy_assets & orig_assets if it isn't already there.
+                               int got_it = 0;
+                               proxy_asset = 0;
+                               for( int i = 0; i < proxy_assets.size(); i++ ) {
+                                       if( !strcmp(proxy_assets.get(i)->path, new_path) ) {
+                                               got_it = 1;
+                                               proxy_asset = (Asset*)proxy_assets.get(i);
+                                               break;
+                                       }
+                               }
+
+                               if( !got_it ) {
+                                       proxy_asset = new Asset;
+// new compression parameters
+                                       proxy_asset->copy_format(asset, 0);
+                                       proxy_asset->update_path(new_path);
+                                       proxy_asset->audio_data = 0;
+                                       proxy_asset->video_data = 1;
+                                       proxy_asset->layers = 1;
+                                       proxy_asset->width = orig_asset->width / new_scale;
+                                       proxy_asset->height = orig_asset->height / new_scale;
+                                       proxy_asset->frame_rate = orig_asset->frame_rate;
+                                       proxy_asset->video_length = orig_asset->video_length;
+                                       
+                                       proxy_assets.append(proxy_asset);
+                                       orig_asset->add_user();
+                                       orig_assets.append(orig_asset);
+                               }
+
+// test if proxy file exists.
+                               int exists = 0;
+                               FILE *fd = fopen(new_path, "r");
+                               if( fd ) {
+                                       got_it = 1;
+                                       exists = 1;
+                                       fclose(fd);
+
+                                       FileSystem fs;
+// test if proxy file is newer than original.
+                                       if( fs.get_date(new_path) < fs.get_date(asset->path) ) {
+                                               got_it = 0;
+                                       }
+                               }
+                               else {
+// proxy doesn't exist
+                                       got_it = 0;
+                               }
+
+                               if( !got_it ) {
+// prompt user to overwrite
+                                       if( exists ) {
+                                               confirm_paths.append(cstrdup(new_path));
+                                       }
+
+                                       needed_assets.append(proxy_asset);
+                                       proxy_asset->add_user();
+                                       needed_orig_assets.append(orig_asset);
+                                       orig_asset->add_user();
+                               }
+//printf("ProxyThread::handle_close_event %d %s\n", __LINE__, new_path);
+                       }
+               }
+
+// test for existing files
+               int result = 0;
+               if( confirm_paths.size() ) {
+                       result = ConfirmSave::test_files(mwindow, &confirm_paths);
+                       confirm_paths.remove_all_objects();
+               }
+
+               if( !result ) {
+                       int canceled = 0;
+                       failed = 0;
+
+// create proxy assets which don't already exist
+                       if( needed_orig_assets.size() > 0 ) {
+                               int64_t total_len = 0;
+                               for( int i = 0; i < needed_orig_assets.size(); i++ )
+                                       total_len += needed_orig_assets.get(i)->video_length;
+// start progress bar.  MWindow is locked inside this
+                               progress = mwindow->mainprogress->
+                                       start_progress(_("Creating proxy files..."), total_len);
+                               total_rendered = 0;
+
+                               ProxyFarm engine(mwindow, 
+                                       this, 
+                                       &needed_assets,
+                                       &needed_orig_assets);
+                               engine.process_packages();
+
+printf("failed=%d canceled=%d\n", failed, progress->is_cancelled());
+
+       // stop progress bar
+                               canceled = progress->is_cancelled();
+                               progress->stop_progress();
+                               delete progress;
+                               progress = 0;
+
+                               if( failed && !canceled ) {
+                                       ErrorBox error_box(PROGRAM_NAME ": Error",
+                                               mwindow->gui->get_abs_cursor_x(1),
+                                               mwindow->gui->get_abs_cursor_y(1));
+                                       error_box.create_objects(_("Error making proxy."));
+                                       error_box.raise_window();
+                                       error_box.run_window();
+                               }
+                       }
+
+// resize project
+                       if( !failed && !canceled ) {
+                               mwindow->set_proxy(new_scale, &orig_assets, &proxy_assets);
+                       }
+               }
+
+               for( int i = 0; i < proxy_assets.size(); i++ ) {
+                       proxy_assets.get(i)->Garbage::remove_user();
+               }
+
+               for( int i = 0; i < orig_assets.size(); i++ ) {
+                       orig_assets.get(i)->Garbage::remove_user();
+               }
+
+               for( int i = 0; i < needed_assets.size(); i++ ) {
+                       needed_assets.get(i)->Garbage::remove_user();
+               }
+
+               for( int i = 0; i < needed_orig_assets.size(); i++ ) {
+                       needed_orig_assets.get(i)->Garbage::remove_user();
+               }
+       }
+
+       mwindow->undo->update_undo_after(_("proxy"), LOAD_ALL);
+
+       mwindow->edl->Garbage::remove_user();
+
+       mwindow->restart_brender();
+
+       mwindow->gui->lock_window("ProxyThread::to_proxy");
+       mwindow->update_project(LOAD_ALL);
+       mwindow->gui->unlock_window();
+}
+
+
+void ProxyThread::to_proxy_path(char *new_path, Asset *asset, int scale)
+{
+       strcpy(new_path, asset->path);
+       char prxy[BCTEXTLEN];
+       sprintf(prxy, ".proxy%d", scale);
+// path is already a proxy
+       if( strstr(new_path, prxy) ) return;
+       int len = strlen(new_path);
+// insert proxy prxy
+       char *ptr = strrchr(new_path, '.');
+       if( ptr ) {
+               char *cp = new_path + len;
+               int n = strlen(prxy);
+               char *bp = cp + n;
+               for( *bp=0; cp>ptr; ) *--bp = *--cp;
+               for( cp= prxy+n; bp>ptr; ) *--bp = *--cp;
+//printf("ProxyThread::to_proxy_path %d %s %s\n", __LINE__, new_path), asset->path);
+       }
+       else
+               strcpy(new_path+len, prxy);
+}
+
+void ProxyThread::from_proxy_path(char *new_path, Asset *asset, int scale)
+{
+       char prxy[BCTEXTLEN];
+       sprintf(prxy, ".proxy%d", scale);
+       strcpy(new_path, asset->path);
+       char *ptr = strstr(asset->path, prxy);
+       if( !ptr ) return;
+       int n = strlen(prxy);
+       for( char *cp=ptr+n; --n>=0; ++ptr,++cp ) *ptr = *cp;
+       *ptr = 0;
+}
+
+void ProxyThread::update_progress()
+{
+       counter_lock->lock();
+       total_rendered++;
+       counter_lock->unlock();
+       progress->update(total_rendered);
+}
+
+int ProxyThread::is_canceled()
+{
+       return progress->is_cancelled();
+}
+
+
+
+ProxyWindow::ProxyWindow(MWindow *mwindow, ProxyThread *thread, int x, int y)
+ : BC_Window(_(PROGRAM_NAME ": Proxy settings"), x, y, WIDTH, HEIGHT,
+               -1, -1, 0, 0, 1)
+{
+       this->mwindow = mwindow;
+       this->thread = thread;
+       format_tools = 0;
+}
+
+ProxyWindow::~ProxyWindow()
+{
+       lock_window("ProxyWindow::~ProxyWindow");
+       delete format_tools;
+       unlock_window();
+}
+
+
+void ProxyWindow::create_objects()
+{
+       lock_window("ProxyWindow::create_objects");
+       
+       int margin = mwindow->theme->widget_border;
+       int x = margin;
+       int y = margin;
+       thread->orig_scale = 
+               thread->new_scale = 
+               mwindow->edl->session->proxy_scale;
+       
+       BC_Title *text;
+       add_subwindow(text = new BC_Title(x, y, 
+               _("What size should the project\n"
+                 "be scaled to for editing?")));
+       y += text->get_h() * 2 + margin;
+       
+       
+       add_subwindow(text = new BC_Title(x, y, _("Scale factor:")));
+       x += text->get_w() + margin;
+
+
+       thread->calculate_sizes();
+       int popupmenu_w = BC_PopupMenu::calculate_w(get_text_width(MEDIUMFONT, thread->size_text[0]));
+       add_subwindow(scale_factor = new ProxyMenu(mwindow, this, x, y, popupmenu_w, ""));
+       for( int i = 0; i < thread->total_sizes; i++ ) {
+               scale_factor->add_item(new BC_MenuItem(thread->size_text[i]));
+       }
+       x += scale_factor->get_w() + margin;
+       
+       ProxyTumbler *tumbler;
+       add_subwindow(tumbler = new ProxyTumbler(mwindow, this, x, y));
+
+       x = margin;
+       y += tumbler->get_h() + margin;
+       ProxyReset *reset;
+       add_subwindow(reset = new ProxyReset(mwindow, this, x, y));
+       
+       y += reset->get_h() * 2 + margin;
+       x = margin;
+       add_subwindow(text = new BC_Title(x, y, _("New project dimensions: ")));
+       x += text->get_w() + margin;
+       add_subwindow(new_dimensions = new BC_Title(x, y, ""));
+
+       x = margin;
+       y += new_dimensions->get_h() * 2 + margin;
+
+
+       format_tools = new FormatTools(mwindow, this, thread->asset);
+       format_tools->create_objects(x, y, 0, 1, 0, 0, 0, 1, 0, 1, // skip the path
+               0, 0);
+
+       update();
+               
+       add_subwindow(new BC_OKButton(this));
+       add_subwindow(new BC_CancelButton(this));
+       show_window(1);
+       unlock_window();
+}
+
+void ProxyWindow::update()
+{
+// preview the new size
+       char string[BCTEXTLEN];
+//printf("ProxyWindow::update %d %d %d %d %d\n", 
+// __LINE__, mwindow->edl->session->output_w, mwindow->edl->session->output_h,
+// thread->orig_scale, thread->new_scale);
+       int orig_w = mwindow->edl->session->output_w * thread->orig_scale;
+       int orig_h = mwindow->edl->session->output_h * thread->orig_scale;
+       sprintf(string, "%dx%d", 
+               orig_w / thread->new_scale, orig_h / thread->new_scale);
+       new_dimensions->update(string);
+       thread->scale_to_text(string, thread->new_scale);
+       scale_factor->set_text(string);
+}
+
+
+ProxyReset::ProxyReset(MWindow *mwindow, ProxyWindow *pwindow, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+       this->mwindow = mwindow;
+       this->pwindow = pwindow;
+}
+
+int ProxyReset::handle_event()
+{
+       pwindow->thread->new_scale = pwindow->thread->orig_scale;
+       pwindow->update();
+       return 1;
+}
+
+
+ProxyMenu::ProxyMenu(MWindow *mwindow, ProxyWindow *pwindow,
+               int x, int y, int w, const char *text)
+ : BC_PopupMenu(x, y, w, text, 1)
+{
+       this->mwindow = mwindow;
+       this->pwindow = pwindow;
+}
+
+int ProxyMenu::handle_event()
+{
+       for( int i = 0; i < pwindow->thread->total_sizes; i++ ) {
+               if( !strcmp(get_text(), pwindow->thread->size_text[i]) ) {
+                       pwindow->thread->new_scale = pwindow->thread->size_factors[i];
+                       pwindow->update();
+                       break;
+               }
+       }
+       return 1;
+}
+
+
+ProxyTumbler::ProxyTumbler(MWindow *mwindow, ProxyWindow *pwindow, int x, int y)
+ : BC_Tumbler(x, y, 0)
+{
+       this->mwindow = mwindow;
+       this->pwindow = pwindow;
+}
+
+int ProxyTumbler::handle_up_event()
+{
+       if( pwindow->thread->new_scale > 1 ) {
+               int i;
+               for( i = 0; i < pwindow->thread->total_sizes; i++ ) {
+                       if( pwindow->thread->new_scale == pwindow->thread->size_factors[i] ) {
+                               i--;
+                               pwindow->thread->new_scale = pwindow->thread->size_factors[i];
+                               pwindow->update();
+                               return 1;
+                       }
+               }               
+       }
+
+       return 0;
+}
+
+int ProxyTumbler::handle_down_event()
+{
+       int i;
+       for( i = 0; i < pwindow->thread->total_sizes - 1; i++ ) {
+               if( pwindow->thread->new_scale == pwindow->thread->size_factors[i] ) {
+                       i++;
+                       pwindow->thread->new_scale = pwindow->thread->size_factors[i];
+                       pwindow->update();
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+ProxyPackage::ProxyPackage()
+ : LoadPackage()
+{
+}
+
+ProxyClient::ProxyClient(MWindow *mwindow, ProxyThread *thread, ProxyFarm *server)
+ : LoadClient(server)
+{
+       this->mwindow = mwindow;
+       this->thread = thread;
+}
+
+void ProxyClient::process_package(LoadPackage *ptr)
+{
+       ProxyPackage *package = (ProxyPackage*)ptr;
+       if( thread->failed ) return;
+
+       File src_file;
+       File dst_file;
+       EDL *edl = mwindow->edl;
+       Preferences *preferences = mwindow->preferences;
+       int processors = 1;
+
+       int result;
+       src_file.set_processors(processors);
+       src_file.set_preload(edl->session->playback_preload);
+       src_file.set_subtitle(edl->session->decode_subtitles ? 
+               edl->session->subtitle_number : -1);
+       src_file.set_interpolate_raw(edl->session->interpolate_raw);
+       src_file.set_white_balance_raw(edl->session->white_balance_raw);
+
+//printf("%s %s\n", package->orig_asset->path, package->proxy_asset->path);
+
+
+       result = src_file.open_file(preferences, package->orig_asset, 1, 0);
+       if( result ) {
+// go to the next asset if the reader fails
+//             thread->failed = 1;
+               return;
+       }
+       
+       dst_file.set_processors(processors);
+       result = dst_file.open_file(preferences, package->proxy_asset, 0, 1);
+       if( result ) {
+               thread->failed = 1;
+               return;
+       }
+       
+       dst_file.start_video_thread(1, edl->session->color_model,
+                       processors > 1 ? 2 : 1, 0);
+       
+       VFrame src_frame(0, -1,
+               package->orig_asset->width, package->orig_asset->height, 
+               edl->session->color_model, -1);
+       
+       OverlayFrame scaler(processors);
+
+       for( int64_t i = 0; i < package->orig_asset->video_length &&
+            !thread->failed && !thread->is_canceled(); i++ ) {
+               src_file.set_video_position(i, 0);
+               result = src_file.read_frame(&src_frame);
+//printf("result=%d\n", result);
+
+               if( result ) {
+// go to the next asset if the reader fails
+//                     thread->failed = 1;
+                       break;
+               }
+
+// have to write after getting the video buffer or it locks up
+               VFrame ***dst_frames = dst_file.get_video_buffer();
+               VFrame *dst_frame = dst_frames[0][0];
+               scaler.overlay(dst_frame, &src_frame,
+                       0, 0, src_frame.get_w(), src_frame.get_h(),
+                       0, 0, dst_frame->get_w(), dst_frame->get_h(),
+                       1.0, TRANSFER_REPLACE, NEAREST_NEIGHBOR);
+               result = dst_file.write_video_buffer(1);
+               if( result ) {
+// only fail if the writer fails
+                       thread->failed = 1;
+                       break;
+               }
+               else {
+                       thread->update_progress();
+               }
+       }
+}
+
+
+ProxyFarm::ProxyFarm(MWindow *mwindow, ProxyThread *thread,
+       ArrayList<Asset*> *proxy_assets, ArrayList<Asset*> *orig_assets)
+ : LoadServer(MIN(mwindow->preferences->processors, proxy_assets->size()), 
+       proxy_assets->size())
+{
+       this->mwindow = mwindow;
+       this->thread = thread;
+       this->proxy_assets = proxy_assets;
+       this->orig_assets = orig_assets;
+}
+
+void ProxyFarm::init_packages()
+{
+       for( int i = 0; i < get_total_packages(); i++ ) {
+       ProxyPackage *package = (ProxyPackage*)get_package(i);
+       package->proxy_asset = proxy_assets->get(i);
+               package->orig_asset = orig_assets->get(i);
+       }
+}
+
+LoadClient* ProxyFarm::new_client()
+{
+       return new ProxyClient(mwindow, thread, this);
+}
+
+LoadPackage* ProxyFarm::new_package()
+{
+       return new ProxyPackage;
+}
+
diff --git a/cinelerra-5.1/cinelerra/proxy.h b/cinelerra-5.1/cinelerra/proxy.h
new file mode 100644 (file)
index 0000000..5436daf
--- /dev/null
@@ -0,0 +1,183 @@
+#ifndef __PROXY_H__
+#define __PROXY_H__
+
+/*
+ * CINELERRA
+ * Copyright (C) 2015 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
+ * 
+ */
+
+// functions for handling proxies
+
+#include "arraylist.h"
+#include "asset.h"
+#include "bcdialog.h"
+#include "formattools.inc"
+#include "loadbalance.h"
+#include "mutex.inc"
+#include "mwindow.inc"
+
+class ProxyThread;
+class ProxyWindow;
+
+#define MAX_SIZES 16
+
+class ProxyMenuItem : public BC_MenuItem
+{
+public:
+       ProxyMenuItem(MWindow *mwindow);
+
+       int handle_event();
+       void create_objects();
+
+       MWindow *mwindow;
+       ProxyThread *thread;
+};
+
+class FromProxyMenuItem : public BC_MenuItem
+{
+public:
+       FromProxyMenuItem(MWindow *mwindow);
+
+       int handle_event();
+       MWindow *mwindow;
+};
+
+class ProxyThread : public BC_DialogThread
+{
+public:
+       ProxyThread(MWindow *mwindow);
+       ~ProxyThread();
+       BC_Window* new_gui();
+       void handle_close_event(int result);
+       static void to_proxy_path(char *new_path, Asset *asset, int scale);
+       static void from_proxy_path(char *new_path, Asset *asset, int scale);
+       void from_proxy();
+       void to_proxy();
+// increment the frame count by 1
+       void update_progress();
+// if user canceled progress bar
+       int is_canceled();
+// calculate possible sizes based on the original size
+       void calculate_sizes();
+       void scale_to_text(char *string, int scale);
+
+       MWindow *mwindow;
+       ProxyWindow *gui;
+       MainProgressBar *progress;
+       Mutex *counter_lock;
+       Asset *asset;
+       int new_scale;
+       int orig_scale;
+       int total_rendered;
+       int failed;
+       char *size_text[MAX_SIZES];
+       int size_factors[MAX_SIZES];
+       int total_sizes;
+};
+
+class ProxyReset : public BC_GenericButton
+{
+public:
+       ProxyReset(MWindow *mwindow, ProxyWindow *pwindow,
+               int x, int y);
+       int handle_event();
+       MWindow *mwindow;
+       ProxyWindow *pwindow;
+};
+
+class ProxyMenu : public BC_PopupMenu
+{
+public:
+       ProxyMenu(MWindow *mwindow, ProxyWindow *pwindow,
+               int x, int y, int w, const char *text);
+       int handle_event();
+       MWindow *mwindow;
+       ProxyWindow *pwindow;
+};
+
+
+class ProxyTumbler : public BC_Tumbler
+{
+public:
+       ProxyTumbler(MWindow *mwindow, ProxyWindow *pwindow, int x, int y);
+
+       int handle_up_event();
+       int handle_down_event();
+       
+       ProxyWindow *pwindow;
+       MWindow *mwindow;
+};
+
+
+class ProxyWindow : public BC_Window
+{
+public:
+       ProxyWindow(MWindow *mwindow, ProxyThread *thread,
+               int x, int y);
+       ~ProxyWindow();
+
+       void create_objects();
+       void update();
+
+       MWindow *mwindow;
+       ProxyThread *thread;
+       FormatTools *format_tools;
+       BC_Title *new_dimensions;
+       BC_PopupMenu *scale_factor;
+       ProxyReset *reset;
+};
+
+class ProxyFarm;
+
+class ProxyPackage : public LoadPackage
+{
+public:
+       ProxyPackage();
+       Asset *orig_asset;
+       Asset *proxy_asset;
+};
+
+class ProxyClient : public LoadClient
+{
+public:
+       ProxyClient(MWindow *mwindow, ProxyThread *thread,
+               ProxyFarm *server);
+       void process_package(LoadPackage *package);
+
+       MWindow *mwindow;
+       ProxyThread *thread;
+};
+
+
+class ProxyFarm : public LoadServer
+{
+public:
+       ProxyFarm(MWindow *mwindow, ProxyThread *thread, 
+               ArrayList<Asset*> *proxy_assets, ArrayList<Asset*> *orig_assets);
+       
+       void init_packages();
+       LoadClient* new_client();
+       LoadPackage* new_package();
+       
+       MWindow *mwindow;
+       ProxyThread *thread;
+       ArrayList<Asset*> *proxy_assets;
+       ArrayList<Asset*> *orig_assets;
+};
+
+#endif