initial commit
[goodguy/history.git] / cinelerra-5.0 / cinelerra / batchrender.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  * 
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.
10  * 
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.
15  * 
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
19  * 
20  */
21
22 #include "asset.h"
23 #include "batchrender.h"
24 #include "bcdisplayinfo.h"
25 #include "bcsignals.h"
26 #include "confirmsave.h"
27 #include "cstrdup.h"
28 #include "bchash.h"
29 #include "edits.h"
30 #include "edit.h"
31 #include "edl.h"
32 #include "edlsession.h"
33 #include "errorbox.h"
34 #include "filesystem.h"
35 #include "filexml.h"
36 #include "format.inc"
37 #include "keyframe.h"
38 #include "keys.h"
39 #include "labels.h"
40 #include "language.h"
41 #include "mainerror.h"
42 #include "mainundo.h"
43 #include "mainsession.h"
44 #include "mwindow.h"
45 #include "mwindowgui.h"
46 #include "packagedispatcher.h"
47 #include "packagerenderer.h"
48 #include "plugin.h"
49 #include "pluginset.h"
50 #include "preferences.h"
51 #include "render.h"
52 #include "theme.h"
53 #include "tracks.h"
54 #include "transportque.h"
55 #include "vframe.h"
56
57 #include <unistd.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <sys/stat.h>
61 #include <sys/statfs.h>
62
63
64
65 static const char *list_titles[] = 
66 {
67         "Enabled", 
68         "Output",
69         "EDL",
70         "Elapsed"
71 };
72
73 static int list_widths[] =
74 {
75         50,
76         100,
77         200,
78         100
79 };
80
81 BatchRenderMenuItem::BatchRenderMenuItem(MWindow *mwindow)
82  : BC_MenuItem(_("Batch Render..."), "Shift-B", 'B')
83 {
84         set_shift(1); 
85         this->mwindow = mwindow;
86 }
87
88 int BatchRenderMenuItem::handle_event()
89 {
90         mwindow->batch_render->start();
91         return 1;
92 }
93
94
95
96
97
98
99
100
101 BatchRenderJob::BatchRenderJob(Preferences *preferences)
102 {
103         this->preferences = preferences;
104         asset = new Asset;
105         edl_path[0] = 0;
106         strategy = 0;
107         enabled = 1;
108         elapsed = 0;
109 }
110
111 BatchRenderJob::~BatchRenderJob()
112 {
113         asset->Garbage::remove_user();
114 }
115
116 void BatchRenderJob::copy_from(BatchRenderJob *src)
117 {
118         asset->copy_from(src->asset, 0);
119         strcpy(edl_path, src->edl_path);
120         strategy = src->strategy;
121         enabled = src->enabled;
122         elapsed = 0;
123 }
124
125 void BatchRenderJob::load(FileXML *file)
126 {
127         int result = 0;
128
129         edl_path[0] = 0;
130         file->tag.get_property("EDL_PATH", edl_path);
131         strategy = file->tag.get_property("STRATEGY", strategy);
132         enabled = file->tag.get_property("ENABLED", enabled);
133         elapsed = file->tag.get_property("ELAPSED", elapsed);
134         fix_strategy();
135
136         result = file->read_tag();
137         if(!result)
138         {
139                 if(file->tag.title_is("ASSET"))
140                 {
141                         file->tag.get_property("SRC", asset->path);
142                         asset->read(file, 0);
143 // The compression parameters are stored in the defaults to reduce
144 // coding maintenance.  The defaults must now be stuffed into the XML for
145 // unique storage.
146                         BC_Hash defaults;
147                         defaults.load_string(file->read_text());
148                         asset->load_defaults(&defaults,
149                                 "",
150                                 0,
151                                 1,
152                                 0,
153                                 0,
154                                 0);
155                 }
156         }
157 }
158
159 void BatchRenderJob::save(FileXML *file)
160 {
161         file->tag.set_property("EDL_PATH", edl_path);
162         file->tag.set_property("STRATEGY", strategy);
163         file->tag.set_property("ENABLED", enabled);
164         file->tag.set_property("ELAPSED", elapsed);
165         file->append_tag();
166         file->append_newline();
167         asset->write(file,
168                 0,
169                 "");
170
171 // The compression parameters are stored in the defaults to reduce
172 // coding maintenance.  The defaults must now be stuffed into the XML for
173 // unique storage.
174         BC_Hash defaults;
175         asset->save_defaults(&defaults, 
176                 "",
177                 0,
178                 1,
179                 0,
180                 0,
181                 0);
182         char *string;
183         defaults.save_string(string);
184         file->append_text(string);
185         delete [] string;
186         file->tag.set_title("/JOB");
187         file->append_tag();
188         file->append_newline();
189 }
190
191 void BatchRenderJob::fix_strategy()
192 {
193         strategy = Render::fix_strategy(strategy, preferences->use_renderfarm);
194 }
195
196
197
198
199
200
201
202
203
204
205 BatchRenderThread::BatchRenderThread(MWindow *mwindow)
206  : BC_DialogThread()
207 {
208         this->mwindow = mwindow;
209         current_job = 0;
210         rendering_job = -1;
211         is_rendering = 0;
212         default_job = 0;
213         file_entries = 0;
214 }
215
216 BatchRenderThread::BatchRenderThread()
217  : BC_DialogThread()
218 {
219         mwindow = 0;
220         current_job = 0;
221         rendering_job = -1;
222         is_rendering = 0;
223         default_job = 0;
224         file_entries = 0;
225 }
226
227 void BatchRenderThread::handle_close_event(int result)
228 {
229 // Save settings
230         char path[BCTEXTLEN];
231         path[0] = 0;
232         save_jobs(path);
233         save_defaults(mwindow->defaults);
234         delete default_job;
235         default_job = 0;
236         jobs.remove_all_objects();
237         if(file_entries)
238         {
239                 file_entries->remove_all_objects();
240                 delete file_entries;
241                 file_entries = 0;
242         }
243 }
244
245 BC_Window* BatchRenderThread::new_gui()
246 {
247         current_start = 0.0;
248         current_end = 0.0;
249         default_job = new BatchRenderJob(mwindow->preferences);
250         
251         
252         if(!file_entries)
253         {
254                 file_entries = new ArrayList<BC_ListBoxItem*>;
255                 FileSystem fs;
256                 char string[BCTEXTLEN];
257         // Load current directory
258                 fs.update(getcwd(string, BCTEXTLEN));
259                 for(int i = 0; i < fs.total_files(); i++)
260                 {
261                         file_entries->append(
262                                 new BC_ListBoxItem(
263                                         fs.get_entry(i)->get_name()));
264                 }
265         }
266
267         char path[BCTEXTLEN];
268         path[0] = 0;
269         load_jobs(path, mwindow->preferences);
270         load_defaults(mwindow->defaults);
271         this->gui = new BatchRenderGUI(mwindow, 
272                 this,
273                 mwindow->session->batchrender_x,
274                 mwindow->session->batchrender_y,
275                 mwindow->session->batchrender_w,
276                 mwindow->session->batchrender_h);
277         this->gui->create_objects();
278         return this->gui;
279 }
280
281
282 void BatchRenderThread::load_jobs(char *path, Preferences *preferences)
283 {
284         FileXML file;
285         int result = 0;
286
287         jobs.remove_all_objects();
288         if(path[0])
289                 file.read_from_file(path);
290         else
291                 file.read_from_file(create_path(path));
292
293         while(!result)
294         {
295                 if(!(result = file.read_tag()))
296                 {
297                         if(file.tag.title_is("JOB"))
298                         {
299                                 BatchRenderJob *job;
300                                 jobs.append(job = new BatchRenderJob(preferences));
301                                 job->load(&file);
302                         }
303                 }
304         }
305 }
306
307 void BatchRenderThread::save_jobs(char *path)
308 {
309         FileXML file;
310
311         for(int i = 0; i < jobs.total; i++)
312         {
313                 file.tag.set_title("JOB");
314                 jobs.values[i]->save(&file);
315         }
316
317         if(path[0])
318                 file.write_to_file(path);
319         else
320                 file.write_to_file(create_path(path));
321 }
322
323 void BatchRenderThread::load_defaults(BC_Hash *defaults)
324 {
325         if(default_job)
326         {
327                 default_job->asset->load_defaults(defaults,
328                         "BATCHRENDER_",
329                         1,
330                         1,
331                         1,
332                         1,
333                         1);
334                 default_job->fix_strategy();
335         }
336
337         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
338         {
339                 char string[BCTEXTLEN];
340                 sprintf(string, "BATCHRENDER_COLUMN%d", i);
341                 column_width[i] = defaults->get(string, list_widths[i]);
342         }
343 }
344
345 void BatchRenderThread::save_defaults(BC_Hash *defaults)
346 {
347         if(default_job)
348         {
349                 default_job->asset->save_defaults(defaults,
350                         "BATCHRENDER_",
351                         1,
352                         1,
353                         1,
354                         1,
355                         1);
356                 defaults->update("BATCHRENDER_STRATEGY", default_job->strategy);
357         }
358         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
359         {
360                 char string[BCTEXTLEN];
361                 sprintf(string, "BATCHRENDER_COLUMN%d", i);
362                 defaults->update(string, column_width[i]);
363         }
364 //      defaults->update("BATCHRENDER_JOB", current_job);
365         if(mwindow)
366                 mwindow->save_defaults();
367         else
368                 defaults->save();
369 }
370
371 char* BatchRenderThread::create_path(char *string)
372 {
373         FileSystem fs;
374         sprintf(string, "%s", BCASTDIR);
375         fs.complete_path(string);
376         strcat(string, BATCH_PATH);
377         return string;
378 }
379
380 void BatchRenderThread::new_job()
381 {
382         BatchRenderJob *result = new BatchRenderJob(mwindow->preferences);
383         result->copy_from(get_current_job());
384         jobs.append(result);
385         current_job = jobs.total - 1;
386         gui->create_list(1);
387         gui->change_job();
388 }
389
390 void BatchRenderThread::delete_job()
391 {
392         if(current_job < jobs.total && current_job >= 0)
393         {
394                 jobs.remove_object_number(current_job);
395                 if(current_job > 0) current_job--;
396                 gui->create_list(1);
397                 gui->change_job();
398         }
399 }
400
401 void BatchRenderThread::use_current_edl()
402 {
403 // printf("BatchRenderThread::use_current_edl %d %p %s\n", 
404 // __LINE__, 
405 // mwindow->edl->path, 
406 // mwindow->edl->path);
407
408         strcpy(get_current_edl(), mwindow->edl->path);
409         gui->create_list(1);
410         gui->edl_path_text->update(get_current_edl());
411 }
412
413 void BatchRenderThread::update_selected_edl()
414 {
415         FileXML xml_file;
416         char *path = get_current_edl();
417         EDL *edl = mwindow->edl;
418         edl->save_xml(&xml_file, path, 0, 0);
419         xml_file.terminate_string();
420         if( xml_file.write_to_file(path) ) {
421                 char msg[BCTEXTLEN];
422                 sprintf(msg, "Unable to save: %s", path);
423                 MainError::show_error(msg);
424         }
425 }
426
427 BatchRenderJob* BatchRenderThread::get_current_job()
428 {
429         BatchRenderJob *result;
430         if(current_job >= jobs.total || current_job < 0)
431         {
432                 result = default_job;
433         }
434         else
435         {
436                 result = jobs.values[current_job];
437         }
438         return result;
439 }
440
441
442 Asset* BatchRenderThread::get_current_asset()
443 {
444         return get_current_job()->asset;
445 }
446
447 char* BatchRenderThread::get_current_edl()
448 {
449         return get_current_job()->edl_path;
450 }
451
452
453 // Test EDL files for existence
454 int BatchRenderThread::test_edl_files()
455 {
456         for(int i = 0; i < jobs.total; i++)
457         {
458                 if(jobs.values[i]->enabled)
459                 {
460                         const char *path = jobs.values[i]->edl_path;
461                         if( *path == '@' ) ++path;
462                         FILE *fd = fopen(path, "r");
463                         if(!fd)
464                         {
465                                 char string[BCTEXTLEN];
466                                 sprintf(string, _("EDL %s not found.\n"), jobs.values[i]->edl_path);
467                                 if(mwindow)
468                                 {
469                                         ErrorBox error_box(PROGRAM_NAME ": Error",
470                                                 mwindow->gui->get_abs_cursor_x(1),
471                                                 mwindow->gui->get_abs_cursor_y(1));
472                                         error_box.create_objects(string);
473                                         error_box.run_window();
474                                         gui->button_enable();
475                                 }
476                                 else
477                                 {
478                                         fprintf(stderr, 
479                                                 "%s",
480                                                 string);
481                                 }
482
483                                 is_rendering = 0;
484                                 return 1;
485                         }
486                         else
487                         {
488                                 fclose(fd);
489                         }
490                 }
491         }
492         return 0;
493 }
494
495 void BatchRenderThread::calculate_dest_paths(ArrayList<char*> *paths,
496         Preferences *preferences)
497 {
498         for(int i = 0; i < jobs.total; i++)
499         {
500                 BatchRenderJob *job = jobs.values[i];
501                 if(job->enabled && *job->edl_path != '@')
502                 {
503                         PackageDispatcher *packages = new PackageDispatcher;
504
505 // Load EDL
506                         TransportCommand *command = new TransportCommand;
507                         FileXML *file = new FileXML;
508                         file->read_from_file(job->edl_path);
509
510 // Use command to calculate range.
511                         command->command = NORMAL_FWD;
512                         command->get_edl()->load_xml(file, 
513                                 LOAD_ALL);
514                         command->change_type = CHANGE_ALL;
515                         command->set_playback_range();
516                         command->adjust_playback_range();
517
518 // Create test packages
519                         packages->create_packages(mwindow,
520                                 command->get_edl(),
521                                 preferences,
522                                 job->strategy, 
523                                 job->asset, 
524                                 command->start_position, 
525                                 command->end_position,
526                                 0);
527
528 // Append output paths allocated to total
529                         for(int j = 0; j < packages->get_total_packages(); j++)
530                         {
531                                 RenderPackage *package = packages->get_package(j);
532                                 paths->append(cstrdup(package->path));
533                         }
534
535 // Delete package harness
536                         delete packages;
537                         delete command;
538                         delete file;
539                 }
540         }
541 }
542
543
544 void BatchRenderThread::start_rendering(char *config_path,
545         char *batch_path)
546 {
547         BC_Hash *boot_defaults;
548         Preferences *preferences;
549         Render *render;
550         BC_Signals *signals = new BC_Signals;
551         // XXX the above stuff is leaked,
552 //PRINT_TRACE
553 // Initialize stuff which MWindow does.
554         signals->initialize();
555         MWindow::init_defaults(boot_defaults, config_path);
556         load_defaults(boot_defaults);
557         preferences = new Preferences;
558         preferences->load_defaults(boot_defaults);
559         MWindow::init_plugins(0, preferences);
560         char font_path[BCTEXTLEN];
561         strcpy(font_path, preferences->plugin_dir);
562         strcat(font_path, "/fonts");
563         BC_Resources::init_fontconfig(font_path);
564         BC_WindowBase::get_resources()->vframe_shm = 1;
565         MWindow::init_fileserver(preferences);
566
567 //PRINT_TRACE
568         load_jobs(batch_path, preferences);
569         save_jobs(batch_path);
570         save_defaults(boot_defaults);
571
572 //PRINT_TRACE
573 // Test EDL files for existence
574         if(test_edl_files()) return;
575
576 //PRINT_TRACE
577
578 // Predict all destination paths
579         ArrayList<char*> paths;
580         paths.set_array_delete();
581         calculate_dest_paths(&paths, preferences);
582
583 //PRINT_TRACE
584         int result = ConfirmSave::test_files(0, &paths);
585         paths.remove_all_objects();
586 // Abort on any existing file because it's so hard to set this up.
587         if(result) return;
588
589 //PRINT_TRACE
590         render = new Render(0);
591 //PRINT_TRACE
592         render->start_batches(&jobs, 
593                 boot_defaults,
594                 preferences);
595 //PRINT_TRACE
596 }
597
598 void BatchRenderThread::start_rendering()
599 {
600         if(is_rendering) return;
601
602         is_rendering = 1;
603         char path[BCTEXTLEN];
604         path[0] = 0;
605         save_jobs(path);
606         save_defaults(mwindow->defaults);
607         gui->button_disable();
608
609 // Test EDL files for existence
610         if(test_edl_files()) return;
611
612 // Predict all destination paths
613         ArrayList<char*> paths;
614         calculate_dest_paths(&paths,
615                 mwindow->preferences);
616
617 // Test destination files for overwrite
618         int result = ConfirmSave::test_files(mwindow, &paths);
619         paths.remove_all_objects();
620
621 // User cancelled
622         if(result)
623         {
624                 is_rendering = 0;
625                 gui->button_enable();
626                 return;
627         }
628
629         mwindow->render->start_batches(&jobs);
630 }
631
632 void BatchRenderThread::stop_rendering()
633 {
634         if(!is_rendering) return;
635         mwindow->render->stop_operation();
636         is_rendering = 0;
637 }
638
639 void BatchRenderThread::update_active(int number)
640 {
641         gui->lock_window("BatchRenderThread::update_active");
642         if(number >= 0)
643         {
644                 current_job = number;
645                 rendering_job = number;
646         }
647         else
648         {
649                 rendering_job = -1;
650                 is_rendering = 0;
651         }
652         gui->create_list(1);
653         gui->unlock_window();
654 }
655
656 void BatchRenderThread::update_done(int number, 
657         int create_list, 
658         double elapsed_time)
659 {
660         gui->lock_window("BatchRenderThread::update_done");
661         if(number < 0)
662         {
663                 gui->button_enable();
664         }
665         else
666         {
667                 jobs.values[number]->enabled = 0;
668                 jobs.values[number]->elapsed = elapsed_time;
669                 if(create_list) gui->create_list(1);
670         }
671         gui->unlock_window();
672 }
673
674 void BatchRenderThread::move_batch(int src, int dst)
675 {
676         BatchRenderJob *src_job = jobs.values[src];
677         if(dst < 0) dst = jobs.total - 1;
678
679         if(dst != src)
680         {
681                 for(int i = src; i < jobs.total - 1; i++)
682                         jobs.values[i] = jobs.values[i + 1];
683 //              if(dst > src) dst--;
684                 for(int i = jobs.total - 1; i > dst; i--)
685                         jobs.values[i] = jobs.values[i - 1];
686                 jobs.values[dst] = src_job;
687                 gui->create_list(1);
688         }
689 }
690
691
692
693
694
695
696
697 BatchRenderGUI::BatchRenderGUI(MWindow *mwindow, 
698         BatchRenderThread *thread,
699         int x,
700         int y,
701         int w,
702         int h)
703  : BC_Window(PROGRAM_NAME ": Batch Render", 
704         x,
705         y,
706         w, 
707         h, 
708         50, 
709         50, 
710         1,
711         0, 
712         1)
713 {
714         this->mwindow = mwindow;
715         this->thread = thread;
716 }
717
718 BatchRenderGUI::~BatchRenderGUI()
719 {
720         lock_window("BatchRenderGUI::~BatchRenderGUI");
721         delete format_tools;
722         unlock_window();
723 }
724
725
726 void BatchRenderGUI::create_objects()
727 {
728         lock_window("BatchRenderGUI::create_objects");
729         mwindow->theme->get_batchrender_sizes(this, get_w(), get_h());
730         create_list(0);
731
732         int x = mwindow->theme->batchrender_x1;
733         int y = 5;
734         int x1 = mwindow->theme->batchrender_x1;
735         int x2 = mwindow->theme->batchrender_x2;
736         //int x3 = mwindow->theme->batchrender_x3;
737         int y1 = y;
738         int y2;
739
740 // output file
741         add_subwindow(output_path_title = new BC_Title(x1, y, _("Output path:")));
742         y += 20;
743         format_tools = new BatchFormat(mwindow,
744                                         this, 
745                                         thread->get_current_asset());
746         format_tools->set_w(get_w() / 2);
747         format_tools->create_objects(x, 
748                                                 y, 
749                                                 1, 
750                                                 1, 
751                                                 1, 
752                                                 1, 
753                                                 0, 
754                                                 1, 
755                                                 0, 
756                                                 0, 
757                                                 &thread->get_current_job()->strategy, 
758                                                 0);
759
760         x2 = x;
761         y2 = y + 10;
762         x += format_tools->get_w();
763         y = y1;
764         x1 = x;
765         //x3 = x + 80;
766
767 // input EDL
768         x = x1;
769         add_subwindow(edl_path_title = new BC_Title(x, y, _("EDL Path:")));
770         y += 20;
771         add_subwindow(edl_path_text = new BatchRenderEDLPath(
772                 thread, 
773                 x, 
774                 y, 
775                 get_w() - x - 40, 
776                 thread->get_current_edl()));
777
778         x += edl_path_text->get_w();
779         add_subwindow(edl_path_browse = new BrowseButton(
780                 mwindow,
781                 this,
782                 edl_path_text, 
783                 x, 
784                 y, 
785                 thread->get_current_edl(),
786                 _("Input EDL"),
787                 _("Select an EDL to load:"),
788                 0));
789
790         x = x1;
791
792         y += 30;
793         add_subwindow(update_selected_edl = new BatchRenderUpdateEDL(thread,
794                 x,
795                 y));
796         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
797
798         add_subwindow(new_batch = new BatchRenderNew(thread, 
799                 x, 
800                 y));
801         x += new_batch->get_w() + 10;
802
803         add_subwindow(delete_batch = new BatchRenderDelete(thread, 
804                 x, 
805                 y));
806         x = new_batch->get_x();
807         y += new_batch->get_h() + mwindow->theme->widget_border;
808         add_subwindow(use_current_edl = new BatchRenderCurrentEDL(thread,
809                 x,
810                 y));
811         if( !mwindow->edl || !mwindow->edl->path[0] ) use_current_edl->disable();
812
813         x = x2;
814         y = y2;
815         add_subwindow(list_title = new BC_Title(x, y, _("Batches to render:")));
816         y += 20;
817         add_subwindow(batch_list = new BatchRenderList(thread, 
818                 x, 
819                 y,
820                 get_w() - x - 10,
821                 get_h() - y - BC_GenericButton::calculate_h() - 15));
822
823         y += batch_list->get_h() + 10;
824         add_subwindow(start_button = new BatchRenderStart(thread, 
825             x, 
826             y));
827         x = get_w() / 2 -
828                 BC_GenericButton::calculate_w(this, _("Stop")) / 2;
829         add_subwindow(stop_button = new BatchRenderStop(thread, 
830                 x, 
831                 y));
832         x = get_w() - 
833                 BC_GenericButton::calculate_w(this, _("Close")) - 
834                 10;
835         add_subwindow(cancel_button = new BatchRenderCancel(thread, 
836                 x, 
837                 y));
838
839         show_window(1);
840         unlock_window();
841 }
842
843 void BatchRenderGUI::button_disable()
844 {
845         new_batch->disable();
846         delete_batch->disable();
847         use_current_edl->disable();
848         update_selected_edl->disable();
849 }
850
851 void BatchRenderGUI::button_enable()
852 {
853         new_batch->enable();
854         delete_batch->enable();
855         if( mwindow->edl && mwindow->edl->path[0] )
856                 use_current_edl->enable();
857         update_selected_edl->enable();
858 }
859
860 int BatchRenderGUI::resize_event(int w, int h)
861 {
862         mwindow->session->batchrender_w = w;
863         mwindow->session->batchrender_h = h;
864         mwindow->theme->get_batchrender_sizes(this, w, h);
865
866         int x = mwindow->theme->batchrender_x1;
867         int y = 5;
868         int x1 = mwindow->theme->batchrender_x1;
869         int x2 = mwindow->theme->batchrender_x2;
870         //int x3 = mwindow->theme->batchrender_x3;
871         int y1 = y;
872         int y2;
873
874         output_path_title->reposition_window(x1, y);
875         y += 20;
876         format_tools->reposition_window(x, y);
877         x2 = x;
878         y2 = y + 10;
879         y = y1;
880         x += format_tools->get_w();
881         x1 = x;
882         //x3 = x + 80;
883
884         x = x1;
885         edl_path_title->reposition_window(x, y);
886         y += 20;
887         edl_path_text->reposition_window(x, y, w - x - 40);
888         x += edl_path_text->get_w();
889         edl_path_browse->reposition_window(x, y);
890
891         x = x1;
892 //      y += 30;
893 //      status_title->reposition_window(x, y);
894 //      x = x3;
895 //      status_text->reposition_window(x, y);
896 //      x = x1;
897 //      y += 30;
898 //      progress_bar->reposition_window(x, y, w - x - 10);
899
900         y += 30;
901         update_selected_edl->reposition_window(x, y);
902         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
903         new_batch->reposition_window(x, y);
904         x += new_batch->get_w() + 10;
905         delete_batch->reposition_window(x, y);
906         x = new_batch->get_x();
907         y += new_batch->get_h() + mwindow->theme->widget_border;
908         use_current_edl->reposition_window(x, y);
909
910         x = x2;
911         y = y2;
912         int y_margin = get_h() - batch_list->get_h();
913         list_title->reposition_window(x, y);
914         y += 20;
915         batch_list->reposition_window(x, y, w - x - 10, h - y_margin);
916
917         y += batch_list->get_h() + 10;
918         start_button->reposition_window(x, y);
919         x = w / 2 - 
920                 stop_button->get_w() / 2;
921         stop_button->reposition_window(x, y);
922         x = w -
923                 cancel_button->get_w() - 
924                 10;
925         cancel_button->reposition_window(x, y);
926         return 1;
927 }
928
929 int BatchRenderGUI::translation_event()
930 {
931         mwindow->session->batchrender_x = get_x();
932         mwindow->session->batchrender_y = get_y();
933         return 1;
934 }
935
936 int BatchRenderGUI::close_event()
937 {
938 // Stop batch rendering
939         unlock_window();
940         thread->stop_rendering();
941         lock_window("BatchRenderGUI::close_event");
942         set_done(1);
943         return 1;
944 }
945
946 void BatchRenderGUI::create_list(int update_widget)
947 {
948         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
949         {
950                 list_columns[i].remove_all_objects();
951         }
952
953         for(int i = 0; i < thread->jobs.total; i++)
954         {
955                 BatchRenderJob *job = thread->jobs.values[i];
956                 char string[BCTEXTLEN];
957                 BC_ListBoxItem *enabled = new BC_ListBoxItem(job->enabled ? 
958                         (char*)"X" : 
959                         (char*)" ");
960                 BC_ListBoxItem *item1 = new BC_ListBoxItem(job->asset->path);
961                 BC_ListBoxItem *item2 = new BC_ListBoxItem(job->edl_path);
962                 BC_ListBoxItem *item3;
963                 if(job->elapsed)
964                         item3 = new BC_ListBoxItem(
965                                 Units::totext(string,
966                                         job->elapsed,
967                                         TIME_HMS2));
968                 else
969                         item3 = new BC_ListBoxItem(_("Unknown"));
970                 list_columns[0].append(enabled);
971                 list_columns[1].append(item1);
972                 list_columns[2].append(item2);
973                 list_columns[3].append(item3);
974                 if(i == thread->current_job)
975                 {
976                         enabled->set_selected(1);
977                         item1->set_selected(1);
978                         item2->set_selected(1);
979                         item3->set_selected(1);
980                 }
981                 if(i == thread->rendering_job)
982                 {
983                         enabled->set_color(RED);
984                         item1->set_color(RED);
985                         item2->set_color(RED);
986                         item3->set_color(RED);
987                 }
988         }
989
990         if(update_widget)
991         {
992                 batch_list->update(list_columns,
993                                                 list_titles,
994                                                 thread->column_width,
995                                                 BATCHRENDER_COLUMNS,
996                                                 batch_list->get_xposition(),
997                                                 batch_list->get_yposition(), 
998                                                 batch_list->get_highlighted_item(),  // Flat index of item cursor is over
999                                                 1,     // set all autoplace flags to 1
1000                                                 1);
1001         }
1002 }
1003
1004 void BatchRenderGUI::change_job()
1005 {
1006         BatchRenderJob *job = thread->get_current_job();
1007         format_tools->update(job->asset, &job->strategy);
1008         edl_path_text->update(job->edl_path);
1009 }
1010
1011
1012
1013
1014
1015
1016
1017
1018 BatchFormat::BatchFormat(MWindow *mwindow,
1019                         BatchRenderGUI *gui,
1020                         Asset *asset)
1021  : FormatTools(mwindow, gui, asset)
1022 {
1023         this->gui = gui;
1024         this->mwindow = mwindow;
1025 }
1026
1027 BatchFormat::~BatchFormat()
1028 {
1029 }
1030
1031
1032 int BatchFormat::handle_event()
1033 {
1034         gui->create_list(1);
1035         return 1;
1036 }
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048 BatchRenderEDLPath::BatchRenderEDLPath(BatchRenderThread *thread, 
1049         int x, 
1050         int y, 
1051         int w, 
1052         char *text)
1053  : BC_TextBox(x, 
1054                 y, 
1055                 w, 
1056                 1,
1057                 text)
1058 {
1059         this->thread = thread;
1060 }
1061
1062
1063 int BatchRenderEDLPath::handle_event()
1064 {
1065 // Suggestions
1066         calculate_suggestions(thread->file_entries);
1067
1068         strcpy(thread->get_current_edl(), get_text());
1069         thread->gui->create_list(1);
1070         return 1;
1071 }
1072
1073
1074
1075
1076
1077
1078 BatchRenderNew::BatchRenderNew(BatchRenderThread *thread, 
1079         int x, 
1080         int y)
1081  : BC_GenericButton(x, y, _("New"))
1082 {
1083         this->thread = thread;
1084 }
1085
1086 int BatchRenderNew::handle_event()
1087 {
1088         thread->new_job();
1089         return 1;
1090 }
1091
1092 BatchRenderDelete::BatchRenderDelete(BatchRenderThread *thread, 
1093         int x, 
1094         int y)
1095  : BC_GenericButton(x, y, _("Delete"))
1096 {
1097         this->thread = thread;
1098 }
1099
1100 int BatchRenderDelete::handle_event()
1101 {
1102         thread->delete_job();
1103         return 1;
1104 }
1105
1106
1107
1108
1109
1110
1111 BatchRenderCurrentEDL::BatchRenderCurrentEDL(BatchRenderThread *thread, 
1112         int x, 
1113         int y)
1114  : BC_GenericButton(x, y, _("Use Current EDL"))
1115 {
1116         this->thread = thread;
1117 }
1118
1119 int BatchRenderCurrentEDL::handle_event()
1120 {
1121         thread->use_current_edl();
1122         return 1;
1123 }
1124
1125 BatchRenderUpdateEDL::BatchRenderUpdateEDL(BatchRenderThread *thread, 
1126         int x, 
1127         int y)
1128  : BC_GenericButton(x, y, _("Save to EDL Path"))
1129 {
1130         this->thread = thread;
1131 }
1132
1133 int BatchRenderUpdateEDL::handle_event()
1134 {
1135         thread->update_selected_edl();
1136         return 1;
1137 }
1138
1139
1140
1141
1142 BatchRenderList::BatchRenderList(BatchRenderThread *thread, 
1143         int x, 
1144         int y,
1145         int w,
1146         int h)
1147  : BC_ListBox(x, 
1148         y, 
1149         w, 
1150         h, 
1151         LISTBOX_TEXT,
1152         thread->gui->list_columns,
1153         list_titles,
1154         thread->column_width,
1155         BATCHRENDER_COLUMNS,
1156         0,
1157         0,
1158         LISTBOX_SINGLE,
1159         ICON_LEFT,
1160         1)
1161 {
1162         this->thread = thread;
1163         dragging_item = 0;
1164         set_process_drag(0);
1165 }
1166
1167 int BatchRenderList::handle_event()
1168 {
1169         return 1;
1170 }
1171
1172 int BatchRenderList::selection_changed()
1173 {
1174         thread->current_job = get_selection_number(0, 0);
1175         thread->gui->change_job();
1176         if(get_cursor_x() < thread->column_width[0])
1177         {
1178                 BatchRenderJob *job = thread->get_current_job();
1179                 job->enabled = !job->enabled;
1180                 thread->gui->create_list(1);
1181         }
1182         return 1;
1183 }
1184
1185 int BatchRenderList::column_resize_event()
1186 {
1187         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
1188         {
1189                 thread->column_width[i] = get_column_width(i);
1190         }
1191         return 1;
1192 }
1193
1194 int BatchRenderList::drag_start_event()
1195 {
1196         if(BC_ListBox::drag_start_event())
1197         {
1198                 dragging_item = 1;
1199                 return 1;
1200         }
1201
1202         return 0;
1203 }
1204
1205 int BatchRenderList::drag_motion_event()
1206 {
1207         if(BC_ListBox::drag_motion_event())
1208         {
1209                 return 1;
1210         }
1211         return 0;
1212 }
1213
1214 int BatchRenderList::drag_stop_event()
1215 {
1216         if(dragging_item)
1217         {
1218                 int src = get_selection_number(0, 0);
1219                 int dst = get_highlighted_item();
1220                 if(src != dst)
1221                 {
1222                         thread->move_batch(src, dst);
1223                 }
1224                 BC_ListBox::drag_stop_event();
1225         }
1226         return 0;
1227 }
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241 BatchRenderStart::BatchRenderStart(BatchRenderThread *thread, 
1242         int x, 
1243         int y)
1244  : BC_GenericButton(x, 
1245         y, 
1246         _("Start"))
1247 {
1248         this->thread = thread;
1249 }
1250
1251 int BatchRenderStart::handle_event()
1252 {
1253         thread->start_rendering();
1254         return 1;
1255 }
1256
1257 BatchRenderStop::BatchRenderStop(BatchRenderThread *thread, 
1258         int x, 
1259         int y)
1260  : BC_GenericButton(x, 
1261         y, 
1262         _("Stop"))
1263 {
1264         this->thread = thread;
1265 }
1266
1267 int BatchRenderStop::handle_event()
1268 {
1269         unlock_window();
1270         thread->stop_rendering();
1271         lock_window("BatchRenderStop::handle_event");
1272         return 1;
1273 }
1274
1275
1276 BatchRenderCancel::BatchRenderCancel(BatchRenderThread *thread, 
1277         int x, 
1278         int y)
1279  : BC_GenericButton(x, 
1280         y, 
1281         _("Close"))
1282 {
1283         this->thread = thread;
1284 }
1285
1286 int BatchRenderCancel::handle_event()
1287 {
1288         unlock_window();
1289         thread->stop_rendering();
1290         lock_window("BatchRenderCancel::handle_event");
1291         thread->gui->set_done(1);
1292         return 1;
1293 }
1294
1295 int BatchRenderCancel::keypress_event()
1296 {
1297         if(get_keypress() == ESC) 
1298         {
1299                 unlock_window();
1300                 thread->stop_rendering();
1301                 lock_window("BatchRenderCancel::keypress_event");
1302                 thread->gui->set_done(1);
1303                 return 1;
1304         }
1305         return 0;
1306 }
1307
1308
1309
1310
1311
1312 // DVD Creation
1313
1314 const int64_t CreateDVD_Thread::DVD_SIZE = 4700000000;
1315 const int CreateDVD_Thread::DVD_STREAMS = 1;
1316 const int CreateDVD_Thread::DVD_WIDTH = 720;
1317 const int CreateDVD_Thread::DVD_HEIGHT = 480;
1318 const double CreateDVD_Thread::DVD_ASPECT_WIDTH = 4.;
1319 const double CreateDVD_Thread::DVD_ASPECT_HEIGHT = 3.;
1320 const double CreateDVD_Thread::DVD_WIDE_ASPECT_WIDTH = 16.;
1321 const double CreateDVD_Thread::DVD_WIDE_ASPECT_HEIGHT = 9.;
1322 const double CreateDVD_Thread::DVD_FRAMERATE = 30000. / 1001.;
1323 const int CreateDVD_Thread::DVD_MAX_BITRATE = 8000000;
1324 const int CreateDVD_Thread::DVD_CHANNELS = 2;
1325 const int CreateDVD_Thread::DVD_WIDE_CHANNELS = 6;
1326 const double CreateDVD_Thread::DVD_SAMPLERATE = 48000;
1327 const double CreateDVD_Thread::DVD_KAUDIO_RATE = 224;
1328
1329
1330 CreateDVD_MenuItem::CreateDVD_MenuItem(MWindow *mwindow)
1331  : BC_MenuItem(_("DVD Render..."), "Shift-D", 'D')
1332 {
1333         set_shift(1); 
1334         this->mwindow = mwindow;
1335 }
1336
1337 int CreateDVD_MenuItem::handle_event()
1338 {
1339         mwindow->create_dvd->start();
1340         return 1;
1341 }
1342
1343
1344 CreateDVD_Thread::CreateDVD_Thread(MWindow *mwindow)
1345  : BC_DialogThread()
1346 {
1347         this->mwindow = mwindow;
1348         this->gui = 0;
1349         this->use_deinterlace = 0;
1350         this->use_inverse_telecine = 0;
1351         this->use_scale = 0;
1352         this->use_resize_tracks = 0;
1353         this->use_histogram = 0;
1354         this->use_wide_audio = 0;
1355         this->use_wide_aspect = 0;
1356         this->use_label_chapters = 0;
1357 }
1358
1359 CreateDVD_Thread::~CreateDVD_Thread()
1360 {
1361 }
1362
1363 int CreateDVD_Thread::create_dvd_jobs(ArrayList<BatchRenderJob*> *jobs,
1364         const char *tmp_path, const char *asset_title)
1365 {
1366         EDL *edl = mwindow->edl;
1367         if( !edl || !edl->session ) {
1368                 char msg[BCTEXTLEN];
1369                 sprintf(msg, "No EDL/Session");
1370                 MainError::show_error(msg);
1371                 return 1;
1372         }
1373         EDLSession *session = edl->session;
1374
1375         double total_length = edl->tracks->total_length();
1376         if( total_length <= 0 ) {
1377                 char msg[BCTEXTLEN];
1378                 sprintf(msg, "No content: %s", asset_title);
1379                 MainError::show_error(msg);
1380                 return 1;
1381         }
1382
1383         char asset_dir[BCTEXTLEN];
1384         sprintf(asset_dir, "%s/%s", tmp_path, asset_title);
1385
1386         if( mkdir(asset_dir, 0777) ) {
1387                 char err[BCTEXTLEN], msg[BCTEXTLEN];
1388                 strerror_r(errno, err, sizeof(err));
1389                 sprintf(msg, "Unable to create directory: %s\n-- %s", asset_dir, err);
1390                 MainError::show_error(msg);
1391                 return 1;
1392         }
1393
1394         double old_samplerate = session->sample_rate;
1395         double old_framerate = session->frame_rate;
1396
1397         session->video_channels = DVD_STREAMS;
1398         session->video_tracks = DVD_STREAMS;
1399         session->frame_rate = DVD_FRAMERATE;
1400         session->output_w = DVD_WIDTH;
1401         session->output_h = DVD_HEIGHT;
1402         session->aspect_w = use_wide_aspect ? DVD_WIDE_ASPECT_WIDTH : DVD_ASPECT_WIDTH;
1403         session->aspect_h = use_wide_aspect ? DVD_WIDE_ASPECT_HEIGHT : DVD_ASPECT_HEIGHT;
1404         session->sample_rate = DVD_SAMPLERATE;
1405         session->audio_channels = session->audio_tracks =
1406                 use_wide_audio ? DVD_WIDE_CHANNELS : DVD_CHANNELS;
1407
1408         char script_filename[BCTEXTLEN];
1409         sprintf(script_filename, "%s/dvd.sh", asset_dir);
1410         int fd = open(script_filename, O_WRONLY+O_CREAT+O_TRUNC, 0755);
1411         FILE *fp = fdopen(fd, "w");
1412         if( !fp ) {
1413                 char err[BCTEXTLEN], msg[BCTEXTLEN];
1414                 strerror_r(errno, err, sizeof(err));
1415                 sprintf(msg, "Unable to save: %s\n-- %s", script_filename, err);
1416                 MainError::show_error(msg);
1417                 return 1;
1418         }
1419         fprintf(fp,"#!/bin/bash\n");
1420         fprintf(fp,"echo \"running %s\" $# $*\n", script_filename);
1421         fprintf(fp,"\n");
1422         fprintf(fp,"mplex -f 8 -o $1/dvd.mpg $1/dvd.m2v $1/dvd.ac3\n");
1423         fprintf(fp,"\n");
1424         fprintf(fp,"rm -rf $1/iso\n");
1425         fprintf(fp,"mkdir -p $1/iso\n");
1426         fprintf(fp,"\n");
1427         fprintf(fp,"dvdauthor -x - <<eof\n");
1428         fprintf(fp,"<dvdauthor dest=\"$1/iso\">\n");
1429         fprintf(fp,"  <vmgm>\n");
1430         fprintf(fp,"    <fpc> jump title 1; </fpc>\n");
1431         fprintf(fp,"  </vmgm>\n");
1432         fprintf(fp,"  <titleset>\n");
1433         fprintf(fp,"    <titles>\n");
1434         fprintf(fp,"    <video format=\"ntsc\" aspect=\"%d:%d\" resolution=\"%dx%d\"/>\n",
1435                 (int)session->aspect_w, (int)session->aspect_h,
1436                 session->output_w, session->output_h);
1437         fprintf(fp,"    <audio format=\"ac3\" lang=\"en\"/>\n");
1438         fprintf(fp,"    <pgc>\n");
1439         fprintf(fp,"      <vob file=\"$1/dvd.mpg\" chapters=\"");
1440         if( use_label_chapters && edl->labels ) {
1441                 Label *label = edl->labels->first;
1442                 while( label ) {
1443                         int secs = label->position;
1444                         int mins = secs / 60;
1445                         int frms = (label->position-secs) * session->frame_rate;
1446                         fprintf(fp,"%d:%02d:%02d.%d", mins/60, mins%60, secs%60, frms);
1447                         if( (label=label->next) != 0 ) fprintf(fp, ",");
1448                 }
1449         }
1450         else {
1451                 int mins = 0;
1452                 for( int secs=0 ; secs<total_length; secs+=10*60 ) {
1453                         mins = secs / 60;
1454                         fprintf(fp,"%d:%02d:00,", mins/60, mins%60);
1455                 }
1456                 fprintf(fp,"%d:%02d:00", mins/60, mins%60);
1457         }
1458         fprintf(fp,"\"/>\n");
1459         fprintf(fp,"    </pgc>\n");
1460         fprintf(fp,"    </titles>\n");
1461         fprintf(fp,"  </titleset>\n");
1462         fprintf(fp,"</dvdauthor>\n");
1463         fprintf(fp,"eof\n");
1464         fprintf(fp,"\n");
1465         fprintf(fp,"echo To burn dvd, load blank media and run:\n");
1466         fprintf(fp,"echo growisofs -dvd-compat -Z /dev/dvd -dvd-video $1/iso\n");
1467         fprintf(fp,"\n");
1468         fclose(fp);
1469
1470         if( use_wide_audio ) {
1471                 session->audio_channels = session->audio_tracks = DVD_WIDE_CHANNELS;
1472                 session->achannel_positions[0] = 90;
1473                 session->achannel_positions[1] = 150;
1474                 session->achannel_positions[2] = 30;
1475                 session->achannel_positions[3] = 210;
1476                 session->achannel_positions[4] = 330;
1477                 session->achannel_positions[5] = 270;
1478                 if( edl->tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
1479                         mwindow->remap_audio(MWindow::AUDIO_1_TO_1);
1480         }
1481         else {
1482                 session->audio_channels = session->audio_tracks = DVD_CHANNELS;
1483                 session->achannel_positions[0] = 180;
1484                 session->achannel_positions[1] = 0;
1485                 if( edl->tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
1486                         mwindow->remap_audio(MWindow::AUDIO_5_1_TO_2);
1487         }
1488
1489         double new_samplerate = session->sample_rate;
1490         double new_framerate = session->frame_rate;
1491         edl->rechannel();
1492         edl->resample(old_samplerate, new_samplerate, TRACK_AUDIO);
1493         edl->resample(old_framerate, new_framerate, TRACK_VIDEO);
1494
1495         int64_t aud_size = ((DVD_KAUDIO_RATE * total_length)/8 + 1000-1) * 1000;
1496         int64_t vid_size = DVD_SIZE*0.96 - aud_size;
1497         int vid_bitrate = (vid_size * 8) / total_length;
1498         vid_bitrate /= 1000;  vid_bitrate *= 1000;
1499         if( vid_bitrate > DVD_MAX_BITRATE ) vid_bitrate = DVD_MAX_BITRATE;
1500
1501         char xml_filename[BCTEXTLEN];
1502         sprintf(xml_filename, "%s/dvd.xml", asset_dir);
1503         FileXML xml_file;
1504         edl->save_xml(&xml_file, xml_filename, 0, 0);
1505         xml_file.terminate_string();
1506         if( xml_file.write_to_file(xml_filename) ) {
1507                 char msg[BCTEXTLEN];
1508                 sprintf(msg, "Unable to save: %s", xml_filename);
1509                 MainError::show_error(msg);
1510                 return 1;
1511         }
1512
1513         BatchRenderJob *job = new BatchRenderJob(mwindow->preferences);
1514         jobs->append(job);
1515         strcpy(&job->edl_path[0], xml_filename);
1516         Asset *asset = job->asset;
1517
1518         sprintf(&asset->path[0],"%s/dvd.m2v", asset_dir);
1519         asset->video_data = 1;
1520         asset->format = FILE_VMPEG;
1521         asset->layers = DVD_STREAMS;
1522         asset->frame_rate = session->frame_rate;
1523         asset->width = session->output_w;
1524         asset->height = session->output_h;
1525         asset->aspect_ratio = session->aspect_w / session->aspect_h;
1526         asset->vmpeg_cmodel = BC_YUV420P;
1527         asset->vmpeg_fix_bitrate = 1;
1528         asset->vmpeg_bitrate = vid_bitrate;
1529         asset->vmpeg_quantization = 15;
1530         asset->vmpeg_iframe_distance = 15;
1531         asset->vmpeg_progressive = 0;
1532         asset->vmpeg_denoise = 0;
1533         asset->vmpeg_seq_codes = 0;
1534         asset->vmpeg_derivative = 2;
1535         asset->vmpeg_preset = 8;
1536         asset->vmpeg_field_order = 0;
1537         asset->vmpeg_pframe_distance = 0;
1538
1539         job = new BatchRenderJob(mwindow->preferences);
1540         jobs->append(job);
1541         strcpy(&job->edl_path[0], xml_filename);
1542         asset = job->asset;
1543
1544         sprintf(&asset->path[0],"%s/dvd.ac3", asset_dir);
1545         asset->audio_data = 1;
1546         asset->format = FILE_AC3;
1547         asset->channels = session->audio_channels;
1548         asset->sample_rate = session->sample_rate;
1549         asset->bits = 16;
1550         asset->byte_order = 0;
1551         asset->signed_ = 1;
1552         asset->header = 0;
1553         asset->dither = 0;
1554         asset->ac3_bitrate = DVD_KAUDIO_RATE;
1555
1556         job = new BatchRenderJob(mwindow->preferences);
1557         jobs->append(job);
1558         job->edl_path[0] = '@';
1559         strcpy(&job->edl_path[1], script_filename);
1560         strcpy(&job->asset->path[0], asset_dir);
1561
1562         return 0;
1563 }
1564
1565 void CreateDVD_Thread::handle_close_event(int result)
1566 {
1567         if( result ) return;
1568         mwindow->batch_render->load_defaults(mwindow->defaults);
1569         mwindow->undo->update_undo_before();
1570         KeyFrame keyframe;  char data[BCTEXTLEN];
1571         if( use_deinterlace ) {
1572                 sprintf(data,"<DEINTERLACE MODE=1>");
1573                 keyframe.set_data(data);
1574                 insert_video_plugin("Deinterlace", &keyframe);
1575         }
1576         if( use_inverse_telecine ) {
1577                 sprintf(data,"<IVTC FRAME_OFFSET=0 FIRST_FIELD=0 "
1578                         "AUTOMATIC=1 AUTO_THRESHOLD=2.0e+00 PATTERN=2>");
1579                 keyframe.set_data(data);
1580                 insert_video_plugin("Inverse Telecine", &keyframe);
1581         }
1582         if( use_scale ) {
1583                 sprintf(data,"<SCALE TYPE=1 X_FACTOR=1 Y_FACTOR=1 "
1584                         "WIDTH=%d HEIGHT=%d CONSTRAIN=0>", DVD_WIDTH, DVD_HEIGHT);
1585                 keyframe.set_data(data);
1586                 insert_video_plugin("Scale", &keyframe);
1587         }
1588         if( use_resize_tracks )
1589                 resize_tracks();
1590         if( use_histogram ) {
1591 #if 0
1592                 sprintf(data, "<HISTOGRAM OUTPUT_MIN_0=0 OUTPUT_MAX_0=1 "
1593                         "OUTPUT_MIN_1=0 OUTPUT_MAX_1=1 "
1594                         "OUTPUT_MIN_2=0 OUTPUT_MAX_2=1 "
1595                         "OUTPUT_MIN_3=0 OUTPUT_MAX_3=1 "
1596                         "AUTOMATIC=0 THRESHOLD=9.0-01 PLOT=0 SPLIT=0>"
1597                         "<POINTS></POINTS><POINTS></POINTS><POINTS></POINTS>"
1598                         "<POINTS><POINT X=6.0e-02 Y=0>"
1599                                 "<POINT X=9.4e-01 Y=1></POINTS>");
1600 #else
1601                 sprintf(data, "<HISTOGRAM AUTOMATIC=0 THRESHOLD=1.0e-01 "
1602                         "PLOT=0 SPLIT=0 W=440 H=500 PARADE=0 MODE=3 "
1603                         "LOW_OUTPUT_0=0 HIGH_OUTPUT_0=1 LOW_INPUT_0=0 HIGH_INPUT_0=1 GAMMA_0=1 "
1604                         "LOW_OUTPUT_1=0 HIGH_OUTPUT_1=1 LOW_INPUT_1=0 HIGH_INPUT_1=1 GAMMA_1=1 "
1605                         "LOW_OUTPUT_2=0 HIGH_OUTPUT_2=1 LOW_INPUT_2=0 HIGH_INPUT_2=1 GAMMA_2=1 "
1606                         "LOW_OUTPUT_3=0 HIGH_OUTPUT_3=1 LOW_INPUT_3=0.06 HIGH_INPUT_3=0.94 "
1607                         "GAMMA_3=1>");
1608 #endif
1609                 keyframe.set_data(data);
1610                 insert_video_plugin("Histogram", &keyframe);
1611         }
1612         create_dvd_jobs(&mwindow->batch_render->jobs, tmp_path, asset_title);
1613         mwindow->save_backup();
1614         mwindow->undo->update_undo_after(_("create dvd"), LOAD_ALL);
1615         mwindow->resync_guis();
1616         mwindow->batch_render->handle_close_event(0);
1617         mwindow->batch_render->start();
1618 }
1619
1620 BC_Window* CreateDVD_Thread::new_gui()
1621 {
1622         memset(tmp_path,0,sizeof(tmp_path));
1623         strcpy(tmp_path,"/tmp");
1624         memset(asset_title,0,sizeof(asset_title));
1625         time_t dt;      time(&dt);
1626         struct tm dtm;  localtime_r(&dt, &dtm);
1627         sprintf(asset_title, "dvd_%02d%02d%02d-%02d%02d%02d",
1628                 dtm.tm_year+1900, dtm.tm_mon+1, dtm.tm_mday,
1629                 dtm.tm_hour, dtm.tm_min, dtm.tm_sec);
1630         use_deinterlace = 0;
1631         use_inverse_telecine = 0;
1632         use_scale = 0;
1633         use_resize_tracks = 0;
1634         use_histogram = 0;
1635         use_wide_audio = 0;
1636         use_wide_aspect = 0;
1637         use_label_chapters = 0;
1638         option_presets();
1639         int scr_x = mwindow->gui->get_screen_x(0, -1);
1640         int scr_w = mwindow->gui->get_screen_w(0, -1);
1641         int scr_h = mwindow->gui->get_screen_h(0, -1);
1642         int w = 500, h = 250;
1643         int x = scr_x + scr_w/2 - w/2, y = scr_h/2 - h/2;
1644
1645         gui = new CreateDVD_GUI(this, x, y, w, h);
1646         gui->create_objects();
1647         return gui;
1648 }
1649
1650
1651 CreateDVD_OK::CreateDVD_OK(CreateDVD_GUI *gui, int x, int y)
1652  : BC_OKButton(x, y)
1653 {
1654         this->gui = gui;
1655         set_tooltip("end setup, start batch render");
1656 }
1657
1658 CreateDVD_OK::~CreateDVD_OK()
1659 {
1660 }
1661
1662 int CreateDVD_OK::button_press_event()
1663 {
1664         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
1665                 gui->set_done(0);
1666                 return 1;
1667         }
1668         return 0;
1669 }
1670
1671 int CreateDVD_OK::keypress_event()
1672 {
1673         return 0;
1674 }
1675
1676
1677 CreateDVD_Cancel::CreateDVD_Cancel(CreateDVD_GUI *gui, int x, int y)
1678  : BC_CancelButton(x, y)
1679 {
1680         this->gui = gui;
1681 }
1682
1683 CreateDVD_Cancel::~CreateDVD_Cancel()
1684 {
1685 }
1686
1687 int CreateDVD_Cancel::button_press_event()
1688 {
1689         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
1690                 gui->set_done(1);
1691                 return 1;
1692         }
1693         return 0;
1694 }
1695
1696
1697 CreateDVD_DiskSpace::CreateDVD_DiskSpace(CreateDVD_GUI *gui, int x, int y)
1698  : BC_Title(x, y, "", MEDIUMFONT, GREEN)
1699 {
1700         this->gui = gui;
1701 }
1702
1703 CreateDVD_DiskSpace::~CreateDVD_DiskSpace()
1704 {
1705 }
1706
1707 int64_t CreateDVD_DiskSpace::tmp_path_space()
1708 {
1709         const char *path = gui->tmp_path->get_text();
1710         if( access(path,R_OK+W_OK) ) return 0;
1711         struct statfs sfs;
1712         if( statfs(path, &sfs) ) return 0;
1713         return (int64_t)sfs.f_bsize * sfs.f_bfree;
1714 }
1715
1716 void CreateDVD_DiskSpace::update()
1717 {
1718 //      gui->disk_space->set_color(get_bg_color());
1719         int64_t disk_space = tmp_path_space();
1720         int color = disk_space<gui->needed_disk_space ? RED : GREEN;
1721         static const char *suffix[] = { "", "KB", "MB", "GB", "TB", "PB" };
1722         int i = 0;
1723         for( int64_t space=disk_space; i<5 && (space/=1000)>0; disk_space=space, ++i );
1724         char text[BCTEXTLEN];
1725         sprintf(text, "disk space: " _LDv(3) "%s", disk_space, suffix[i]);
1726         gui->disk_space->BC_Title::update(text);
1727         gui->disk_space->set_color(color);
1728 }
1729
1730 CreateDVD_TmpPath::CreateDVD_TmpPath(CreateDVD_GUI *gui, int x, int y, int w)
1731  : BC_TextBox(x, y, w, 1, -sizeof(gui->thread->tmp_path),
1732                 gui->thread->tmp_path, 1, MEDIUMFONT)
1733 {
1734         this->gui = gui;
1735 }
1736
1737 CreateDVD_TmpPath::~CreateDVD_TmpPath()
1738 {
1739 }
1740
1741 int CreateDVD_TmpPath::handle_event()
1742 {
1743         gui->disk_space->update();
1744         return 1;
1745 }
1746
1747
1748 CreateDVD_AssetTitle::CreateDVD_AssetTitle(CreateDVD_GUI *gui, int x, int y, int w)
1749  : BC_TextBox(x, y, w, 1, 0, gui->thread->asset_title, 1, MEDIUMFONT)
1750 {
1751         this->gui = gui;
1752 }
1753
1754 CreateDVD_AssetTitle::~CreateDVD_AssetTitle()
1755 {
1756 }
1757
1758
1759 CreateDVD_Deinterlace::CreateDVD_Deinterlace(CreateDVD_GUI *gui, int x, int y)
1760  : BC_CheckBox(x, y, &gui->thread->use_deinterlace, "Deinterlace")
1761 {
1762         this->gui = gui;
1763 }
1764
1765 CreateDVD_Deinterlace::~CreateDVD_Deinterlace()
1766 {
1767 }
1768
1769 int CreateDVD_Deinterlace::handle_event()
1770 {
1771         if( get_value() ) {
1772                 gui->need_inverse_telecine->set_value(0);
1773                 gui->thread->use_inverse_telecine = 0;
1774         }
1775         return BC_CheckBox::handle_event();
1776 }
1777
1778
1779 CreateDVD_InverseTelecine::CreateDVD_InverseTelecine(CreateDVD_GUI *gui, int x, int y)
1780  : BC_CheckBox(x, y, &gui->thread->use_inverse_telecine, "Inverse Telecine")
1781 {
1782         this->gui = gui;
1783 }
1784
1785 CreateDVD_InverseTelecine::~CreateDVD_InverseTelecine()
1786 {
1787 }
1788
1789 int CreateDVD_InverseTelecine::handle_event()
1790 {
1791         if( get_value() ) {
1792                 gui->need_deinterlace->set_value(0);
1793                 gui->thread->use_deinterlace = 0;
1794         }
1795         return BC_CheckBox::handle_event();
1796 }
1797
1798
1799 CreateDVD_Scale::CreateDVD_Scale(CreateDVD_GUI *gui, int x, int y)
1800  : BC_CheckBox(x, y, &gui->thread->use_scale, "Scale")
1801 {
1802         this->gui = gui;
1803 }
1804
1805 CreateDVD_Scale::~CreateDVD_Scale()
1806 {
1807 }
1808
1809
1810 CreateDVD_ResizeTracks::CreateDVD_ResizeTracks(CreateDVD_GUI *gui, int x, int y)
1811  : BC_CheckBox(x, y, &gui->thread->use_resize_tracks, "Resize Tracks")
1812 {
1813         this->gui = gui;
1814 }
1815
1816 CreateDVD_ResizeTracks::~CreateDVD_ResizeTracks()
1817 {
1818 }
1819
1820
1821 CreateDVD_Histogram::CreateDVD_Histogram(CreateDVD_GUI *gui, int x, int y)
1822  : BC_CheckBox(x, y, &gui->thread->use_histogram, "Histogram")
1823 {
1824         this->gui = gui;
1825 }
1826
1827 CreateDVD_Histogram::~CreateDVD_Histogram()
1828 {
1829 }
1830
1831 CreateDVD_LabelChapters::CreateDVD_LabelChapters(CreateDVD_GUI *gui, int x, int y)
1832  : BC_CheckBox(x, y, &gui->thread->use_label_chapters, "Chapters at Labels")
1833 {
1834         this->gui = gui;
1835 }
1836
1837 CreateDVD_LabelChapters::~CreateDVD_LabelChapters()
1838 {
1839 }
1840
1841 CreateDVD_WideAudio::CreateDVD_WideAudio(CreateDVD_GUI *gui, int x, int y)
1842  : BC_CheckBox(x, y, &gui->thread->use_wide_audio, "Audio 5.1")
1843 {
1844         this->gui = gui;
1845 }
1846
1847 CreateDVD_WideAudio::~CreateDVD_WideAudio()
1848 {
1849 }
1850
1851 CreateDVD_WideAspect::CreateDVD_WideAspect(CreateDVD_GUI *gui, int x, int y)
1852  : BC_CheckBox(x, y, &gui->thread->use_wide_aspect, "Aspect 16x9")
1853 {
1854         this->gui = gui;
1855 }
1856
1857 CreateDVD_WideAspect::~CreateDVD_WideAspect()
1858 {
1859 }
1860
1861
1862
1863
1864 CreateDVD_GUI::CreateDVD_GUI(CreateDVD_Thread *thread, int x, int y, int w, int h)
1865  : BC_Window(PROGRAM_NAME ": Create DVD", x, y, w, h, 50, 50, 1, 0, 1)
1866 {
1867         this->thread = thread;
1868         at_x = at_y = tmp_x = tmp_y = 0;
1869         ok_x = ok_y = ok_w = ok_h = 0;
1870         cancel_x = cancel_y = cancel_w = cancel_h = 0;
1871         asset_title = 0;
1872         tmp_path = 0;
1873         disk_space = 0;
1874         needed_disk_space = 15e9;
1875         need_deinterlace = 0;
1876         need_inverse_telecine = 0;
1877         need_scale = 0;
1878         need_resize_tracks = 0;
1879         need_histogram = 0;
1880         need_wide_audio = 0;
1881         need_wide_aspect = 0;
1882         need_label_chapters = 0;
1883         ok = 0;
1884         cancel = 0;
1885 }
1886
1887 CreateDVD_GUI::~CreateDVD_GUI()
1888 {
1889 }
1890
1891 void CreateDVD_GUI::create_objects()
1892 {
1893         lock_window("CreateDVD_GUI::create_objects");
1894         int pady = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1) + 5;
1895         int padx = BC_Title::calculate_w(this, (char*)"X", MEDIUMFONT);
1896         int x = padx/2, y = pady/2;
1897         BC_Title *title = new BC_Title(x, y, "Title:", MEDIUMFONT, YELLOW);
1898         add_subwindow(title);
1899         at_x = x + title->get_w();  at_y = y;
1900         asset_title = new CreateDVD_AssetTitle(this, at_x, at_y, get_w()-at_x-10);
1901         add_subwindow(asset_title);
1902         y += title->get_h() + pady/2;
1903         title = new BC_Title(x, y, "tmp path:", MEDIUMFONT, YELLOW);
1904         add_subwindow(title);
1905         tmp_x = x + title->get_w();  tmp_y = y;
1906         tmp_path = new CreateDVD_TmpPath(this, tmp_x, tmp_y,  get_w()-tmp_x-10);
1907         add_subwindow(tmp_path);
1908         y += title->get_h() + pady/2;
1909         disk_space = new CreateDVD_DiskSpace(this, x, y);
1910         add_subwindow(disk_space);
1911         disk_space->update();
1912         y += disk_space->get_h() + pady/2;
1913         need_deinterlace = new CreateDVD_Deinterlace(this, x, y);
1914         add_subwindow(need_deinterlace);
1915         int x1 = x + 150, x2 = x1 + 150;
1916         need_inverse_telecine = new CreateDVD_InverseTelecine(this, x1, y);
1917         add_subwindow(need_inverse_telecine);
1918         y += need_deinterlace->get_h() + pady/2;
1919         need_scale = new CreateDVD_Scale(this, x, y);
1920         add_subwindow(need_scale);
1921         need_wide_audio = new CreateDVD_WideAudio(this, x1, y);
1922         add_subwindow(need_wide_audio);
1923         need_resize_tracks = new CreateDVD_ResizeTracks(this, x2, y);
1924         add_subwindow(need_resize_tracks);
1925         y += need_scale->get_h() + pady/2;
1926         need_histogram = new CreateDVD_Histogram(this, x, y);
1927         add_subwindow(need_histogram);
1928         need_wide_aspect = new CreateDVD_WideAspect(this, x1, y);
1929         add_subwindow(need_wide_aspect);
1930         need_label_chapters = new CreateDVD_LabelChapters(this, x2, y);
1931         add_subwindow(need_label_chapters);
1932         ok_w = BC_OKButton::calculate_w();
1933         ok_h = BC_OKButton::calculate_h();
1934         ok_x = 10;
1935         ok_y = get_h() - ok_h - 10;
1936         ok = new CreateDVD_OK(this, ok_x, ok_y);
1937         add_subwindow(ok);
1938         cancel_w = BC_CancelButton::calculate_w();
1939         cancel_h = BC_CancelButton::calculate_h();
1940         cancel_x = get_w() - cancel_w - 10,
1941         cancel_y = get_h() - cancel_h - 10;
1942         cancel = new CreateDVD_Cancel(this, cancel_x, cancel_y);
1943         add_subwindow(cancel);
1944         show_window();
1945         unlock_window();
1946 }
1947
1948 int CreateDVD_GUI::resize_event(int w, int h)
1949 {
1950         asset_title->reposition_window(at_x, at_y, get_w()-at_x-10);
1951         tmp_path->reposition_window(tmp_x, tmp_y,  get_w()-tmp_x-10);
1952         ok_y = h - ok_h - 10;
1953         ok->reposition_window(ok_x, ok_y);
1954         cancel_x = w - cancel_w - 10,
1955         cancel_y = h - cancel_h - 10;
1956         cancel->reposition_window(cancel_x, cancel_y);
1957         return 0;
1958 }
1959
1960 int CreateDVD_GUI::translation_event()
1961 {
1962         return 1;
1963 }
1964
1965 int CreateDVD_GUI::close_event()
1966 {
1967         set_done(1);
1968         return 1;
1969 }
1970
1971 int CreateDVD_Thread::
1972 insert_video_plugin(const char *title, KeyFrame *default_keyframe)
1973 {
1974         Tracks *tracks = mwindow->edl->tracks;
1975         for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
1976                 if( vtrk->data_type != TRACK_VIDEO ) continue;
1977                 if( !vtrk->record ) continue;
1978                 vtrk->expand_view = 1;
1979                 PluginSet *plugin_set = new PluginSet(mwindow->edl, vtrk);
1980                 vtrk->plugin_set.append(plugin_set);
1981                 Edits *edits = vtrk->edits;
1982                 for( Edit *edit=edits->first; edit; edit=edit->next ) {
1983                         plugin_set->insert_plugin(title,
1984                                 edit->startproject, edit->length,
1985                                 PLUGIN_STANDALONE, 0, default_keyframe, 0);
1986                 }
1987                 vtrk->optimize();
1988         }
1989         return 0;
1990 }
1991
1992 int CreateDVD_Thread::
1993 resize_tracks()
1994 {
1995         Tracks *tracks = mwindow->edl->tracks;
1996         int max_w = 0, max_h = 0;
1997         for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
1998                 if( vtrk->data_type != TRACK_VIDEO ) continue;
1999                 if( !vtrk->record ) continue;
2000                 Edits *edits = vtrk->edits;
2001                 for( Edit *edit=edits->first; edit; edit=edit->next ) {
2002                         Indexable *indexable = edit->get_source();
2003                         int w = indexable->get_w();
2004                         if( w > max_w ) max_w = w;
2005                         int h = indexable->get_h();
2006                         if( h > max_h ) max_h = h;
2007                 }
2008         }
2009         for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
2010                 if( vtrk->data_type != TRACK_VIDEO ) continue;
2011                 if( !vtrk->record ) continue;
2012                 vtrk->track_w = max_w;
2013                 vtrk->track_h = max_h;
2014         }
2015         return 0;
2016 }
2017
2018 int CreateDVD_Thread::
2019 option_presets()
2020 {
2021         if( !mwindow->edl ) return 1;
2022         Tracks *tracks = mwindow->edl->tracks;
2023         int max_w = 0, max_h = 0;
2024         int has_deinterlace = 0, has_scale = 0;
2025         for( Track *trk=tracks->first; trk; trk=trk->next ) {
2026                 if( !trk->record ) continue;
2027                 Edits *edits = trk->edits;
2028                 switch( trk->data_type ) {
2029                 case TRACK_VIDEO:
2030                         for( Edit *edit=edits->first; edit; edit=edit->next ) {
2031                                 Indexable *indexable = edit->get_source();
2032                                 int w = indexable->get_w();
2033                                 if( w > max_w ) max_w = w;
2034                                 if( w != DVD_WIDTH ) use_scale = 1;
2035                                 int h = indexable->get_h();
2036                                 if( h > max_h ) max_h = h;
2037                                 if( h != DVD_HEIGHT ) use_scale = 1;
2038                         }
2039                         for( int i=0; i<trk->plugin_set.size(); ++i ) {
2040                                 for(Plugin *plugin = (Plugin*)trk->plugin_set[i]->first;
2041                                                 plugin;
2042                                                 plugin = (Plugin*)plugin->next) {
2043                                         if( !strcmp(plugin->title, "Deinterlace") )
2044                                                 has_deinterlace = 1;
2045                                         if( !strcmp(plugin->title, "Scale") )
2046                                                 has_scale = 1;
2047                                 }
2048                         }
2049                         break;
2050                 }
2051         }
2052         if( has_scale ) use_scale = 0;
2053         for( Track *trk=tracks->first; trk && !use_resize_tracks; trk=trk->next ) {
2054                 if( !trk->record ) continue;
2055                 switch( trk->data_type ) {
2056                 case TRACK_VIDEO:
2057                         if( trk->track_w != max_w ) use_resize_tracks = 1;
2058                         if( trk->track_h != max_h ) use_resize_tracks = 1;
2059                         break;
2060                 }
2061         }
2062         if( !has_deinterlace && max_h > 2*DVD_HEIGHT ) use_deinterlace = 1;
2063         Labels *labels = mwindow->edl->labels;
2064         use_label_chapters = labels && labels->first ? 1 : 0;
2065         float w, h;
2066         MWindow::create_aspect_ratio(w, h, max_w, max_h);
2067         if( w == DVD_WIDE_ASPECT_WIDTH && h == DVD_WIDE_ASPECT_HEIGHT )
2068                 use_wide_aspect = 1;
2069         if( tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
2070                 use_wide_audio = 1;
2071         return 0;
2072 }
2073