4 * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "batchrender.h"
24 #include "bcdisplayinfo.h"
25 #include "bcsignals.h"
26 #include "confirmsave.h"
32 #include "edlsession.h"
34 #include "filesystem.h"
41 #include "mainerror.h"
43 #include "mainsession.h"
45 #include "mwindowgui.h"
46 #include "packagedispatcher.h"
47 #include "packagerenderer.h"
49 #include "pluginset.h"
50 #include "preferences.h"
54 #include "transportque.h"
58 static const char *list_titles[] =
66 static int list_widths[] =
74 BatchRenderMenuItem::BatchRenderMenuItem(MWindow *mwindow)
75 : BC_MenuItem(_("Batch Render..."), _("Shift-B"), 'B')
78 this->mwindow = mwindow;
81 int BatchRenderMenuItem::handle_event()
83 mwindow->batch_render->start();
94 BatchRenderJob::BatchRenderJob(Preferences *preferences)
96 this->preferences = preferences;
104 BatchRenderJob::~BatchRenderJob()
106 asset->Garbage::remove_user();
109 void BatchRenderJob::copy_from(BatchRenderJob *src)
111 asset->copy_from(src->asset, 0);
112 strcpy(edl_path, src->edl_path);
113 strategy = src->strategy;
114 enabled = src->enabled;
118 void BatchRenderJob::load(FileXML *file)
123 file->tag.get_property("EDL_PATH", edl_path);
124 strategy = file->tag.get_property("STRATEGY", strategy);
125 enabled = file->tag.get_property("ENABLED", enabled);
126 elapsed = file->tag.get_property("ELAPSED", elapsed);
129 result = file->read_tag();
132 if(file->tag.title_is("ASSET"))
134 file->tag.get_property("SRC", asset->path);
135 asset->read(file, 0);
136 // The compression parameters are stored in the defaults to reduce
137 // coding maintenance. The defaults must now be stuffed into the XML for
140 defaults.load_string(file->read_text());
141 asset->load_defaults(&defaults,
152 void BatchRenderJob::save(FileXML *file)
154 file->tag.set_property("EDL_PATH", edl_path);
155 file->tag.set_property("STRATEGY", strategy);
156 file->tag.set_property("ENABLED", enabled);
157 file->tag.set_property("ELAPSED", elapsed);
159 file->append_newline();
164 // The compression parameters are stored in the defaults to reduce
165 // coding maintenance. The defaults must now be stuffed into the XML for
168 asset->save_defaults(&defaults,
176 defaults.save_string(string);
177 file->append_text(string);
179 file->tag.set_title("/JOB");
181 file->append_newline();
184 void BatchRenderJob::fix_strategy()
186 strategy = Render::fix_strategy(strategy, preferences->use_renderfarm);
198 BatchRenderThread::BatchRenderThread(MWindow *mwindow)
201 this->mwindow = mwindow;
209 BatchRenderThread::BatchRenderThread()
220 BatchRenderThread::~BatchRenderThread()
225 void BatchRenderThread::handle_close_event(int result)
228 char path[BCTEXTLEN];
231 save_defaults(mwindow->defaults);
234 jobs.remove_all_objects();
237 file_entries->remove_all_objects();
243 BC_Window* BatchRenderThread::new_gui()
247 default_job = new BatchRenderJob(mwindow->preferences);
252 file_entries = new ArrayList<BC_ListBoxItem*>;
254 char string[BCTEXTLEN];
255 // Load current directory
256 fs.update(getcwd(string, BCTEXTLEN));
257 for(int i = 0; i < fs.total_files(); i++)
259 file_entries->append(
261 fs.get_entry(i)->get_name()));
265 char path[BCTEXTLEN];
267 load_jobs(path, mwindow->preferences);
268 load_defaults(mwindow->defaults);
269 this->gui = new BatchRenderGUI(mwindow,
271 mwindow->session->batchrender_x,
272 mwindow->session->batchrender_y,
273 mwindow->session->batchrender_w,
274 mwindow->session->batchrender_h);
275 this->gui->create_objects();
280 void BatchRenderThread::load_jobs(char *path, Preferences *preferences)
285 jobs.remove_all_objects();
287 file.read_from_file(path);
289 file.read_from_file(create_path(path));
293 if(!(result = file.read_tag()))
295 if(file.tag.title_is("JOB"))
298 jobs.append(job = new BatchRenderJob(preferences));
305 void BatchRenderThread::save_jobs(char *path)
309 for(int i = 0; i < jobs.total; i++)
311 file.tag.set_title("JOB");
312 jobs.values[i]->save(&file);
316 file.write_to_file(path);
318 file.write_to_file(create_path(path));
321 void BatchRenderThread::load_defaults(BC_Hash *defaults)
325 default_job->asset->load_defaults(defaults,
332 default_job->fix_strategy();
335 for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
337 char string[BCTEXTLEN];
338 sprintf(string, "BATCHRENDER_COLUMN%d", i);
339 column_width[i] = defaults->get(string, list_widths[i]);
343 void BatchRenderThread::save_defaults(BC_Hash *defaults)
347 default_job->asset->save_defaults(defaults,
354 defaults->update("BATCHRENDER_STRATEGY", default_job->strategy);
356 for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
358 char string[BCTEXTLEN];
359 sprintf(string, "BATCHRENDER_COLUMN%d", i);
360 defaults->update(string, column_width[i]);
362 // defaults->update("BATCHRENDER_JOB", current_job);
364 mwindow->save_defaults();
369 char* BatchRenderThread::create_path(char *string)
372 sprintf(string, "%s", BCASTDIR);
373 fs.complete_path(string);
374 strcat(string, BATCH_PATH);
378 void BatchRenderThread::new_job()
380 BatchRenderJob *result = new BatchRenderJob(mwindow->preferences);
381 result->copy_from(get_current_job());
383 current_job = jobs.total - 1;
388 void BatchRenderThread::delete_job()
390 if(current_job < jobs.total && current_job >= 0)
392 jobs.remove_object_number(current_job);
393 if(current_job > 0) current_job--;
399 void BatchRenderThread::use_current_edl()
401 // printf("BatchRenderThread::use_current_edl %d %p %s\n",
403 // mwindow->edl->path,
404 // mwindow->edl->path);
406 strcpy(get_current_edl(), mwindow->edl->path);
408 gui->edl_path_text->update(get_current_edl());
411 void BatchRenderThread::update_selected_edl()
414 char *path = get_current_edl();
415 EDL *edl = mwindow->edl;
416 edl->save_xml(&xml_file, path, 0, 0);
417 xml_file.terminate_string();
418 if( xml_file.write_to_file(path) ) {
420 sprintf(msg, _("Unable to save: %s"), path);
421 MainError::show_error(msg);
425 BatchRenderJob* BatchRenderThread::get_current_job()
427 BatchRenderJob *result;
428 if(current_job >= jobs.total || current_job < 0)
430 result = default_job;
434 result = jobs.values[current_job];
440 Asset* BatchRenderThread::get_current_asset()
442 return get_current_job()->asset;
445 char* BatchRenderThread::get_current_edl()
447 return get_current_job()->edl_path;
451 // Test EDL files for existence
452 int BatchRenderThread::test_edl_files()
454 for(int i = 0; i < jobs.total; i++)
456 if(jobs.values[i]->enabled)
458 const char *path = jobs.values[i]->edl_path;
459 if( *path == '@' ) ++path;
460 FILE *fd = fopen(path, "r");
463 char string[BCTEXTLEN];
464 sprintf(string, _("EDL %s not found.\n"), jobs.values[i]->edl_path);
467 ErrorBox error_box(_(PROGRAM_NAME ": Error"),
468 mwindow->gui->get_abs_cursor_x(1),
469 mwindow->gui->get_abs_cursor_y(1));
470 error_box.create_objects(string);
471 error_box.run_window();
472 gui->button_enable();
493 void BatchRenderThread::calculate_dest_paths(ArrayList<char*> *paths,
494 Preferences *preferences)
496 for(int i = 0; i < jobs.total; i++)
498 BatchRenderJob *job = jobs.values[i];
499 if(job->enabled && *job->edl_path != '@')
501 PackageDispatcher *packages = new PackageDispatcher;
504 TransportCommand *command = new TransportCommand;
505 FileXML *file = new FileXML;
506 file->read_from_file(job->edl_path);
508 // Use command to calculate range.
509 command->command = NORMAL_FWD;
510 command->get_edl()->load_xml(file,
512 command->change_type = CHANGE_ALL;
513 command->set_playback_range();
514 command->adjust_playback_range();
516 // Create test packages
517 packages->create_packages(mwindow,
522 command->start_position,
523 command->end_position,
526 // Append output paths allocated to total
527 for(int j = 0; j < packages->get_total_packages(); j++)
529 RenderPackage *package = packages->get_package(j);
530 paths->append(cstrdup(package->path));
533 // Delete package harness
542 void BatchRenderThread::start_rendering(char *config_path,
545 BC_Hash *boot_defaults;
546 Preferences *preferences;
548 BC_Signals *signals = new BC_Signals;
549 // XXX the above stuff is leaked,
551 // Initialize stuff which MWindow does.
552 signals->initialize();
553 MWindow::init_defaults(boot_defaults, config_path);
554 load_defaults(boot_defaults);
555 preferences = new Preferences;
556 preferences->load_defaults(boot_defaults);
557 MWindow::init_plugins(0, preferences);
558 char font_path[BCTEXTLEN];
559 strcpy(font_path, preferences->plugin_dir);
560 strcat(font_path, "/fonts");
561 BC_Resources::init_fontconfig(font_path);
562 BC_WindowBase::get_resources()->vframe_shm = 1;
563 MWindow::init_fileserver(preferences);
566 load_jobs(batch_path, preferences);
567 save_jobs(batch_path);
568 save_defaults(boot_defaults);
571 // Test EDL files for existence
572 if(test_edl_files()) return;
576 // Predict all destination paths
577 ArrayList<char*> paths;
578 paths.set_array_delete();
579 calculate_dest_paths(&paths, preferences);
582 int result = ConfirmSave::test_files(0, &paths);
583 paths.remove_all_objects();
584 // Abort on any existing file because it's so hard to set this up.
588 render = new Render(0);
590 render->start_batches(&jobs,
596 void BatchRenderThread::start_rendering()
598 if(is_rendering) return;
601 char path[BCTEXTLEN];
604 save_defaults(mwindow->defaults);
605 gui->button_disable();
607 // Test EDL files for existence
608 if(test_edl_files()) return;
610 // Predict all destination paths
611 ArrayList<char*> paths;
612 calculate_dest_paths(&paths,
613 mwindow->preferences);
615 // Test destination files for overwrite
616 int result = ConfirmSave::test_files(mwindow, &paths);
617 paths.remove_all_objects();
623 gui->button_enable();
627 mwindow->render->start_batches(&jobs);
630 void BatchRenderThread::stop_rendering()
632 if(!is_rendering) return;
633 mwindow->render->stop_operation();
637 void BatchRenderThread::update_active(int number)
639 gui->lock_window("BatchRenderThread::update_active");
642 current_job = number;
643 rendering_job = number;
651 gui->unlock_window();
654 void BatchRenderThread::update_done(int number,
658 gui->lock_window("BatchRenderThread::update_done");
661 gui->button_enable();
665 jobs.values[number]->enabled = 0;
666 jobs.values[number]->elapsed = elapsed_time;
667 if(create_list) gui->create_list(1);
669 gui->unlock_window();
672 void BatchRenderThread::move_batch(int src, int dst)
674 BatchRenderJob *src_job = jobs.values[src];
675 if(dst < 0) dst = jobs.total - 1;
679 for(int i = src; i < jobs.total - 1; i++)
680 jobs.values[i] = jobs.values[i + 1];
681 // if(dst > src) dst--;
682 for(int i = jobs.total - 1; i > dst; i--)
683 jobs.values[i] = jobs.values[i - 1];
684 jobs.values[dst] = src_job;
695 BatchRenderGUI::BatchRenderGUI(MWindow *mwindow,
696 BatchRenderThread *thread,
701 : BC_Window(_(PROGRAM_NAME ": Batch Render"),
712 this->mwindow = mwindow;
713 this->thread = thread;
716 BatchRenderGUI::~BatchRenderGUI()
718 lock_window("BatchRenderGUI::~BatchRenderGUI");
724 void BatchRenderGUI::create_objects()
726 lock_window("BatchRenderGUI::create_objects");
727 mwindow->theme->get_batchrender_sizes(this, get_w(), get_h());
730 int x = mwindow->theme->batchrender_x1;
732 int x1 = mwindow->theme->batchrender_x1;
733 int x2 = mwindow->theme->batchrender_x2;
734 //int x3 = mwindow->theme->batchrender_x3;
739 add_subwindow(output_path_title = new BC_Title(x1, y, _("Output path:")));
741 format_tools = new BatchFormat(mwindow,
743 thread->get_current_asset());
744 format_tools->set_w(get_w() / 2);
745 format_tools->create_objects(x,
755 &thread->get_current_job()->strategy,
760 x += format_tools->get_w();
767 add_subwindow(edl_path_title = new BC_Title(x, y, _("EDL Path:")));
769 add_subwindow(edl_path_text = new BatchRenderEDLPath(
774 thread->get_current_edl()));
776 x += edl_path_text->get_w();
777 add_subwindow(edl_path_browse = new BrowseButton(
783 thread->get_current_edl(),
785 _("Select an EDL to load:"),
791 add_subwindow(update_selected_edl = new BatchRenderUpdateEDL(thread,
794 y += update_selected_edl->get_h() + mwindow->theme->widget_border;
796 add_subwindow(new_batch = new BatchRenderNew(thread,
799 x += new_batch->get_w() + 10;
801 add_subwindow(delete_batch = new BatchRenderDelete(thread,
804 x = new_batch->get_x();
805 y += new_batch->get_h() + mwindow->theme->widget_border;
806 add_subwindow(use_current_edl = new BatchRenderCurrentEDL(thread,
809 if( !mwindow->edl || !mwindow->edl->path[0] ) use_current_edl->disable();
813 add_subwindow(list_title = new BC_Title(x, y, _("Batches to render:")));
815 add_subwindow(batch_list = new BatchRenderList(thread,
819 get_h() - y - BC_GenericButton::calculate_h() - 15));
821 y += batch_list->get_h() + 10;
822 add_subwindow(start_button = new BatchRenderStart(thread,
826 BC_GenericButton::calculate_w(this, _("Stop")) / 2;
827 add_subwindow(stop_button = new BatchRenderStop(thread,
831 BC_GenericButton::calculate_w(this, _("Close")) -
833 add_subwindow(cancel_button = new BatchRenderCancel(thread,
841 void BatchRenderGUI::button_disable()
843 new_batch->disable();
844 delete_batch->disable();
845 use_current_edl->disable();
846 update_selected_edl->disable();
849 void BatchRenderGUI::button_enable()
852 delete_batch->enable();
853 if( mwindow->edl && mwindow->edl->path[0] )
854 use_current_edl->enable();
855 update_selected_edl->enable();
858 int BatchRenderGUI::resize_event(int w, int h)
860 mwindow->session->batchrender_w = w;
861 mwindow->session->batchrender_h = h;
862 mwindow->theme->get_batchrender_sizes(this, w, h);
864 int x = mwindow->theme->batchrender_x1;
866 int x1 = mwindow->theme->batchrender_x1;
867 int x2 = mwindow->theme->batchrender_x2;
868 //int x3 = mwindow->theme->batchrender_x3;
872 output_path_title->reposition_window(x1, y);
874 format_tools->reposition_window(x, y);
878 x += format_tools->get_w();
883 edl_path_title->reposition_window(x, y);
885 edl_path_text->reposition_window(x, y, w - x - 40);
886 x += edl_path_text->get_w();
887 edl_path_browse->reposition_window(x, y);
891 // status_title->reposition_window(x, y);
893 // status_text->reposition_window(x, y);
896 // progress_bar->reposition_window(x, y, w - x - 10);
899 update_selected_edl->reposition_window(x, y);
900 y += update_selected_edl->get_h() + mwindow->theme->widget_border;
901 new_batch->reposition_window(x, y);
902 x += new_batch->get_w() + 10;
903 delete_batch->reposition_window(x, y);
904 x = new_batch->get_x();
905 y += new_batch->get_h() + mwindow->theme->widget_border;
906 use_current_edl->reposition_window(x, y);
910 int y_margin = get_h() - batch_list->get_h();
911 list_title->reposition_window(x, y);
913 batch_list->reposition_window(x, y, w - x - 10, h - y_margin);
915 y += batch_list->get_h() + 10;
916 start_button->reposition_window(x, y);
918 stop_button->get_w() / 2;
919 stop_button->reposition_window(x, y);
921 cancel_button->get_w() -
923 cancel_button->reposition_window(x, y);
927 int BatchRenderGUI::translation_event()
929 mwindow->session->batchrender_x = get_x();
930 mwindow->session->batchrender_y = get_y();
934 int BatchRenderGUI::close_event()
936 // Stop batch rendering
938 thread->stop_rendering();
939 lock_window("BatchRenderGUI::close_event");
944 void BatchRenderGUI::create_list(int update_widget)
946 for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
948 list_columns[i].remove_all_objects();
951 for(int i = 0; i < thread->jobs.total; i++)
953 BatchRenderJob *job = thread->jobs.values[i];
954 char string[BCTEXTLEN];
955 BC_ListBoxItem *enabled = new BC_ListBoxItem(job->enabled ?
958 BC_ListBoxItem *item1 = new BC_ListBoxItem(job->asset->path);
959 BC_ListBoxItem *item2 = new BC_ListBoxItem(job->edl_path);
960 BC_ListBoxItem *item3;
962 item3 = new BC_ListBoxItem(
963 Units::totext(string,
967 item3 = new BC_ListBoxItem(_("Unknown"));
968 list_columns[0].append(enabled);
969 list_columns[1].append(item1);
970 list_columns[2].append(item2);
971 list_columns[3].append(item3);
972 if(i == thread->current_job)
974 enabled->set_selected(1);
975 item1->set_selected(1);
976 item2->set_selected(1);
977 item3->set_selected(1);
979 if(i == thread->rendering_job)
981 enabled->set_color(RED);
982 item1->set_color(RED);
983 item2->set_color(RED);
984 item3->set_color(RED);
990 batch_list->update(list_columns,
992 thread->column_width,
994 batch_list->get_xposition(),
995 batch_list->get_yposition(),
996 batch_list->get_highlighted_item(), // Flat index of item cursor is over
997 1, // set all autoplace flags to 1
1002 void BatchRenderGUI::change_job()
1004 BatchRenderJob *job = thread->get_current_job();
1005 format_tools->update(job->asset, &job->strategy);
1006 edl_path_text->update(job->edl_path);
1016 BatchFormat::BatchFormat(MWindow *mwindow,
1017 BatchRenderGUI *gui,
1019 : FormatTools(mwindow, gui, asset)
1022 this->mwindow = mwindow;
1025 BatchFormat::~BatchFormat()
1030 int BatchFormat::handle_event()
1032 gui->create_list(1);
1046 BatchRenderEDLPath::BatchRenderEDLPath(BatchRenderThread *thread,
1057 this->thread = thread;
1061 int BatchRenderEDLPath::handle_event()
1064 calculate_suggestions(thread->file_entries);
1066 strcpy(thread->get_current_edl(), get_text());
1067 thread->gui->create_list(1);
1076 BatchRenderNew::BatchRenderNew(BatchRenderThread *thread,
1079 : BC_GenericButton(x, y, _("New"))
1081 this->thread = thread;
1084 int BatchRenderNew::handle_event()
1090 BatchRenderDelete::BatchRenderDelete(BatchRenderThread *thread,
1093 : BC_GenericButton(x, y, _("Delete"))
1095 this->thread = thread;
1098 int BatchRenderDelete::handle_event()
1100 thread->delete_job();
1109 BatchRenderCurrentEDL::BatchRenderCurrentEDL(BatchRenderThread *thread,
1112 : BC_GenericButton(x, y, _("Use Current EDL"))
1114 this->thread = thread;
1117 int BatchRenderCurrentEDL::handle_event()
1119 thread->use_current_edl();
1123 BatchRenderUpdateEDL::BatchRenderUpdateEDL(BatchRenderThread *thread,
1126 : BC_GenericButton(x, y, _("Save to EDL Path"))
1128 this->thread = thread;
1131 int BatchRenderUpdateEDL::handle_event()
1133 thread->update_selected_edl();
1140 BatchRenderList::BatchRenderList(BatchRenderThread *thread,
1150 thread->gui->list_columns,
1152 thread->column_width,
1153 BATCHRENDER_COLUMNS,
1160 this->thread = thread;
1162 set_process_drag(0);
1165 int BatchRenderList::handle_event()
1170 int BatchRenderList::selection_changed()
1172 thread->current_job = get_selection_number(0, 0);
1173 thread->gui->change_job();
1174 if(get_cursor_x() < thread->column_width[0])
1176 BatchRenderJob *job = thread->get_current_job();
1177 job->enabled = !job->enabled;
1178 thread->gui->create_list(1);
1183 int BatchRenderList::column_resize_event()
1185 for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
1187 thread->column_width[i] = get_column_width(i);
1192 int BatchRenderList::drag_start_event()
1194 if(BC_ListBox::drag_start_event())
1203 int BatchRenderList::drag_motion_event()
1205 if(BC_ListBox::drag_motion_event())
1212 int BatchRenderList::drag_stop_event()
1216 int src = get_selection_number(0, 0);
1217 int dst = get_highlighted_item();
1220 thread->move_batch(src, dst);
1222 BC_ListBox::drag_stop_event();
1239 BatchRenderStart::BatchRenderStart(BatchRenderThread *thread,
1242 : BC_GenericButton(x,
1246 this->thread = thread;
1249 int BatchRenderStart::handle_event()
1251 thread->start_rendering();
1255 BatchRenderStop::BatchRenderStop(BatchRenderThread *thread,
1258 : BC_GenericButton(x,
1262 this->thread = thread;
1265 int BatchRenderStop::handle_event()
1268 thread->stop_rendering();
1269 lock_window("BatchRenderStop::handle_event");
1274 BatchRenderCancel::BatchRenderCancel(BatchRenderThread *thread,
1277 : BC_GenericButton(x,
1281 this->thread = thread;
1284 int BatchRenderCancel::handle_event()
1287 thread->stop_rendering();
1288 lock_window("BatchRenderCancel::handle_event");
1289 thread->gui->set_done(1);
1293 int BatchRenderCancel::keypress_event()
1295 if(get_keypress() == ESC)
1298 thread->stop_rendering();
1299 lock_window("BatchRenderCancel::keypress_event");
1300 thread->gui->set_done(1);