no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / render.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
4  * Copyright (C) 2003-2016 Cinelerra CV contributors
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 "arender.h"
23 #include "asset.h"
24 #include "auto.h"
25 #include "awindow.h"
26 #include "awindowgui.h"
27 #include "batchrender.h"
28 #include "bcprogressbox.h"
29 #include "bcsignals.h"
30 #include "cache.h"
31 #include "clip.h"
32 #include "compresspopup.h"
33 #include "condition.h"
34 #include "confirmsave.h"
35 #include "cwindowgui.h"
36 #include "cwindow.h"
37 #include "bchash.h"
38 #include "edits.h"
39 #include "edl.h"
40 #include "edlsession.h"
41 #include "errorbox.h"
42 #include "file.h"
43 #include "filesystem.h"
44 #include "filexml.h"
45 #include "formatcheck.h"
46 #include "formatpopup.h"
47 #include "formattools.h"
48 #include "indexable.h"
49 #include "labels.h"
50 #include "language.h"
51 #include "loadmode.h"
52 #include "localsession.h"
53 #include "mainerror.h"
54 #include "mainprogress.h"
55 #include "mainsession.h"
56 #include "mainundo.h"
57 #include "module.h"
58 #include "mutex.h"
59 #include "mwindowgui.h"
60 #include "mwindow.h"
61 #include "packagedispatcher.h"
62 #include "packagerenderer.h"
63 #include "patchbay.h"
64 #include "playabletracks.h"
65 #include "preferences.h"
66 #include "preferencesthread.h"
67 #include "renderfarm.h"
68 #include "render.h"
69 #include "renderprofiles.h"
70 #include "statusbar.h"
71 #include "theme.h"
72 #include "timebar.h"
73 #include "tracks.h"
74 #include "transportque.h"
75 #include "vedit.h"
76 #include "vframe.h"
77 #include "videoconfig.h"
78 #include "vrender.h"
79
80 #include <ctype.h>
81 #include <string.h>
82
83
84
85 RenderItem::RenderItem(MWindow *mwindow)
86  : BC_MenuItem(_("Render..."), _("Shift-R"), 'R')
87 {
88         this->mwindow = mwindow;
89         set_shift(1);
90 }
91
92 int RenderItem::handle_event()
93 {
94         mwindow->gui->unlock_window();
95         mwindow->render->start_interactive();
96         mwindow->gui->lock_window("RenderItem::handle_event");
97         return 1;
98 }
99
100 RenderProgress::RenderProgress(MWindow *mwindow, Render *render)
101  : Thread(1, 0, 0)
102 {
103         this->mwindow = mwindow;
104         this->render = render;
105         last_value = 0;
106         Thread::set_synchronous(1);
107 }
108
109 RenderProgress::~RenderProgress()
110 {
111         Thread::cancel();
112         Thread::join();
113 }
114
115
116 void RenderProgress::run()
117 {
118         Thread::disable_cancel();
119         for( ;; ) {
120                 if( render->total_rendered != last_value ) {
121                         render->progress->update(render->total_rendered);
122                         last_value = render->total_rendered;
123
124                         if( mwindow ) mwindow->preferences_thread->update_rates();
125                 }
126
127                 Thread::enable_cancel();
128                 sleep(1);
129                 Thread::disable_cancel();
130         }
131 }
132
133
134
135 MainPackageRenderer::MainPackageRenderer(Render *render)
136  : PackageRenderer()
137 {
138         this->render = render;
139 }
140
141
142
143 MainPackageRenderer::~MainPackageRenderer()
144 {
145 }
146
147
148 int MainPackageRenderer::get_master()
149 {
150         return 1;
151 }
152
153 int MainPackageRenderer::get_result()
154 {
155         return render->result;
156 }
157
158 void MainPackageRenderer::set_result(int value)
159 {
160         if( value )
161                 render->result = value;
162 }
163
164 void MainPackageRenderer::set_progress(int64_t value)
165 {
166         render->counter_lock->lock("MainPackageRenderer::set_progress");
167 // Increase total rendered for all nodes
168         render->total_rendered += value;
169
170 // Update frames per second for master node
171         render->preferences->set_rate(frames_per_second, -1);
172
173 //printf("MainPackageRenderer::set_progress %d %ld %f\n", __LINE__, (long)value, frames_per_second);
174
175 // If non interactive, print progress out
176         if( !render->progress )
177                 render->show_progress();
178
179         render->counter_lock->unlock();
180
181         if( mwindow )
182                 mwindow->preferences->copy_rates_from(preferences);
183 }
184
185 int MainPackageRenderer::progress_cancelled()
186 {
187         return (render->progress && render->progress->is_cancelled()) ||
188                 render->batch_cancelled;
189 }
190
191 void RenderAssets::clear()
192 {
193         for( int i=size(); --i>=0; get(i)->remove_user() );
194         remove_all();
195 }
196 RenderAssets::RenderAssets()
197 {
198 }
199 RenderAssets::~RenderAssets()
200 {
201          clear();
202 }
203
204 Render::Render(MWindow *mwindow)
205  : BC_DialogThread()
206 {
207         this->mwindow = mwindow;
208         in_progress = 0;
209         progress = 0;
210         elapsed_time = 0.0;
211         package_lock = new Mutex("Render::package_lock");
212         counter_lock = new Mutex("Render::counter_lock");
213         completion = new Condition(0, "Render::completion");
214         progress_timer = new Timer;
215         range_type = RANGE_BACKCOMPAT;
216         preferences = new Preferences();
217         thread = new RenderThread(mwindow, this);
218         render_window = 0;
219         asset = 0;
220         result = 0;
221         beep = 0;
222 }
223
224 Render::~Render()
225 {
226         stop_operation();
227         close_window();
228         delete package_lock;
229         delete counter_lock;
230         delete completion;
231         delete preferences;
232         delete progress_timer;
233         if( asset ) asset->Garbage::remove_user();
234         delete thread;
235 }
236
237 void Render::start_interactive()
238 {
239         if( !thread->running() ) {
240                 mode = Render::INTERACTIVE;
241                 BC_DialogThread::start();
242         }
243         else if( in_progress ) {
244                 int cx, cy;
245                 mwindow->gui->get_abs_cursor(cx, cy, 1);
246                 ErrorBox error_box(_(PROGRAM_NAME ": Error"), cx, cy);
247                 error_box.create_objects(_("Already rendering"));
248                 error_box.raise_window();
249                 error_box.run_window();
250         }
251         else if( render_window ) {
252                 render_window->raise_window();
253         }
254 }
255
256
257 void Render::start_batches(ArrayList<BatchRenderJob*> *jobs)
258 {
259         if( !thread->running() ) {
260                 mode = Render::BATCH;
261                 batch_cancelled = 0;
262                 this->jobs = jobs;
263                 completion->reset();
264                 start_render();
265         }
266         else if( in_progress ) {
267                 int cx, cy;
268                 mwindow->gui->get_abs_cursor(cx, cy, 1);
269                 ErrorBox error_box(_(PROGRAM_NAME ": Error"), cx, cy);
270                 error_box.create_objects("Already rendering");
271                 error_box.raise_window();
272                 error_box.run_window();
273         }
274         // raise the window if rendering hasn't started yet
275         else if( render_window ) {
276                 render_window->raise_window();
277         }
278 }
279
280 void Render::start_batches(ArrayList<BatchRenderJob*> *jobs,
281         BC_Hash *boot_defaults, Preferences *batch_prefs)
282 {
283         mode = Render::BATCH;
284         batch_cancelled = 0;
285         preferences->copy_from(batch_prefs);
286         this->jobs = jobs;
287
288         completion->reset();
289         thread->run();
290 }
291
292
293 BC_Window* Render::new_gui()
294 {
295         this->jobs = 0;
296         batch_cancelled = 0;
297         result = 0;
298
299         if( mode == Render::INTERACTIVE ) {
300 // Fix the asset for rendering
301                 if( !asset ) asset = new Asset;
302                 load_defaults(asset);
303                 check_asset(mwindow->edl, *asset);
304                 int px = mwindow->gui->get_pop_cursor_x(1);
305                 int py = mwindow->gui->get_pop_cursor_y(1);
306 // Get format from user
307                 render_window = new RenderWindow(mwindow, this, asset, px, py);
308                 render_window->create_objects();
309         }
310
311         return render_window;
312 }
313
314 void Render::handle_done_event(int result)
315 {
316         if( !result ) {
317                 mwindow->edl->session->render_beep = beep;
318                 // add to recentlist only on OK
319                 render_window->render_format->path_recent->
320                         add_item(File::formattostr(asset->format), asset->path);
321                 setenv("CIN_RENDER", asset->path, 1);
322         }
323         render_window = 0;
324 }
325
326 void Render::handle_close_event(int result)
327 {
328         const int debug = 0;
329         double render_range = get_render_range();
330         const char *err_msg = 0;
331
332         if( !result && !render_range ) {
333                 err_msg = _("zero render range");
334                 result = 1;
335         }
336         if( !result && !asset->audio_data && !asset->video_data ) {
337                 err_msg = _("no audio or video in render asset format\n");
338                 result = 1;
339         }
340         EDL *edl = mwindow->edl;
341         if( !result && use_labels && !edl->labels->first ) {
342                 eprintf(_("Create new file at labels checked, but no labels\n"));
343                 result = 1;
344         }
345         if( !result && asset->video_data ) {
346                 double frame_rate = edl->session->frame_rate;
347                 if( frame_rate > 0 && render_range+1e-3 < 1./frame_rate ) {
348                         err_msg = _("Video data and range less than 1 frame");
349                         result = 1;
350                 }
351         }
352         if( !result && asset->audio_data ) {
353                 double sample_rate = edl->session->sample_rate;
354                 if( sample_rate > 0 && render_range+1e-6 < 1./sample_rate ) {
355                         err_msg = _("Audio data and range less than 1 sample");
356                         result = 1;
357                 }
358         }
359         if( !result && File::is_image_render(asset->format) ) {
360                 if( asset->video_data ) {
361                         double frames = render_range * edl->session->frame_rate;
362                         if( !EQUIV(frames, 1.) ) {
363                                 err_msg = _("Image format and not 1 frame");
364                                 result = 1;
365                         }
366                 }
367                 else {
368                         err_msg = _("Image format and no video data");
369                         result = 1;
370                 }
371         }
372
373         if( err_msg ) {
374                 int cx, cy;
375                 mwindow->gui->get_abs_cursor(cx, cy, 1);
376                 ErrorBox error_box(_(PROGRAM_NAME ": Error"),cx, cy);
377                 error_box.create_objects(err_msg);
378                 error_box.raise_window();
379                 error_box.run_window();
380         }
381
382         if( !result ) {
383 // Check the asset format for errors.
384                 FormatCheck format_check(asset);
385                 if( format_check.check_format() )
386                         result = 1;
387         }
388
389 //PRINT_TRACE
390
391         save_defaults(asset);
392 //PRINT_TRACE
393         mwindow->save_defaults();
394 //PRINT_TRACE
395
396         if( !result ) {
397                 if( debug ) printf("Render::handle_close_event %d\n", __LINE__);
398                 if( !result ) start_render();
399                 if( debug ) printf("Render::handle_close_event %d\n", __LINE__);
400         }
401 //PRINT_TRACE
402 }
403
404
405
406 void Render::stop_operation()
407 {
408         if( thread->Thread::running() ) {
409                 batch_cancelled = 1;
410 // Wait for completion
411                 completion->lock("Render::stop_operation");
412                 completion->reset();
413         }
414 }
415
416 int Render::check_asset(EDL *edl, Asset &asset)
417 {
418         if( asset.video_data &&
419                 edl->tracks->playable_video_tracks() &&
420                 File::renders_video(&asset) ) {
421                 asset.video_data = 1;
422                 asset.layers = 1;
423                 asset.width = edl->session->output_w;
424                 asset.height = edl->session->output_h;
425                 asset.interlace_mode = edl->session->interlace_mode;
426         }
427         else {
428                 asset.video_data = 0;
429                 asset.layers = 0;
430         }
431
432         if( asset.audio_data &&
433                 edl->tracks->playable_audio_tracks() &&
434                 File::renders_audio(&asset) ) {
435                 asset.audio_data = 1;
436                 asset.channels = edl->session->audio_channels;
437         }
438         else {
439                 asset.audio_data = 0;
440                 asset.channels = 0;
441         }
442
443         if( !asset.audio_data &&
444                 !asset.video_data ) {
445                 return 1;
446         }
447         return 0;
448 }
449
450 int Render::get_strategy(int use_renderfarm, int use_labels, int range_type)
451 {
452         return range_type == RANGE_1FRAME ? SINGLE_PASS :
453                  use_renderfarm ?
454                         (use_labels ? FILE_PER_LABEL_FARM : SINGLE_PASS_FARM) :
455                         (use_labels ? FILE_PER_LABEL      : SINGLE_PASS     ) ;
456 }
457 int Render::get_strategy()
458 {
459         return get_strategy(preferences->use_renderfarm, use_labels, range_type);
460 }
461
462 void Render::start_progress()
463 {
464         char filename[BCTEXTLEN];
465         char string[BCTEXTLEN];
466         progress_max = packages->get_progress_max();
467
468         progress_timer->update();
469         last_eta = 0;
470         if( mwindow ) {
471 // Generate the progress box
472                 FileSystem fs;
473                 fs.extract_name(filename, default_asset->path);
474                 sprintf(string, _("Rendering %s..."), filename);
475
476 // Don't bother with the filename since renderfarm defeats the meaning
477                 mwindow->gui->lock_window("Render::start_progress");
478                 progress = mwindow->mainprogress->start_progress(_("Rendering..."),
479                         progress_max);
480                 mwindow->gui->unlock_window();
481                 render_progress = new RenderProgress(mwindow, this);
482                 render_progress->start();
483         }
484 }
485
486 void Render::stop_progress()
487 {
488         if( progress ) {
489                 char string[BCTEXTLEN], string2[BCTEXTLEN];
490                 delete render_progress;
491                 progress->get_time(string);
492                 elapsed_time = progress->get_time();
493                 progress->stop_progress();
494                 delete progress;
495
496                 sprintf(string2, _("Rendering took %s"), string);
497                 mwindow->gui->lock_window("Render::stop_progress");
498                 mwindow->gui->show_message(string2);
499                 mwindow->gui->update_default_message();
500                 mwindow->gui->stop_hourglass();
501                 mwindow->gui->unlock_window();
502         }
503         progress = 0;
504 }
505
506 void Render::show_progress()
507 {
508         int64_t current_eta = progress_timer->get_scaled_difference(1000);
509         if( current_eta - last_eta < 1000  ) return;
510         double eta = !total_rendered ? 0 :
511                 current_eta / 1000. * (progress_max / (double)total_rendered - 1.);
512         char string[BCTEXTLEN];  Units::totext(string, eta, TIME_HMS2);
513         printf("\r%d%% %s: %s      ",
514                 (int)(100 * (float)total_rendered / progress_max), _("ETA"), string);
515         fflush(stdout);
516         last_eta = current_eta;
517 }
518
519
520
521 void Render::start_render()
522 {
523         in_progress = 0;
524         elapsed_time = 0.0;
525         result = 0;
526         completion->reset();
527         thread->start();
528 }
529
530
531 void Render::create_filename(char *path,
532         char *default_path,
533         int current_number,
534         int total_digits,
535         int number_start)
536 {
537         int i, j;
538         int len = strlen(default_path);
539         char printf_string[BCTEXTLEN];
540
541         for( i=0, j=0; i<number_start; ++i, ++j ) {
542                 printf_string[j] = default_path[i];
543         }
544
545 // Found the number
546         sprintf(&printf_string[j], "%%0%dd", total_digits);
547         j = strlen(printf_string);
548         i += total_digits;
549
550 // Copy remainder of string
551         for( ; i<len; ++i, ++j ) {
552                 printf_string[j] = default_path[i];
553         }
554         printf_string[j] = 0;
555 // Print the printf argument to the path
556         sprintf(path, printf_string, current_number);
557 }
558
559 void Render::get_starting_number(char *path,
560         int &current_number,
561         int &number_start,
562         int &total_digits,
563         int min_digits)
564 {
565         int len = strlen(path);
566         char number_text[BCTEXTLEN];
567         char *ptr = 0;
568         char *ptr2 = 0;
569
570         total_digits = 0;
571         number_start = 0;
572
573 // Search for last /
574         ptr2 = strrchr(path, '/');
575
576 // Search for first 0 after last /.
577         if( ptr2 )
578                 ptr = strchr(ptr2, '0');
579
580         if( ptr && isdigit(*ptr) ) {
581                 number_start = ptr - path;
582
583 // Store the first number
584                 char *ptr2 = number_text;
585                 while( isdigit(*ptr) ) *ptr2++ = *ptr++;
586                 *ptr2++ = 0;
587                 current_number = atol(number_text);
588                 total_digits = strlen(number_text);
589         }
590
591
592 // No number found or number not long enough
593         if( total_digits < min_digits ) {
594                 current_number = 1;
595                 number_start = len;
596                 total_digits = min_digits;
597         }
598 }
599
600
601 int Render::load_defaults(Asset *asset)
602 {
603         use_labels = mwindow->defaults->get("RENDER_FILE_PER_LABEL", 0);
604         load_mode = mwindow->defaults->get("RENDER_LOADMODE", LOADMODE_NEW_TRACKS);
605         range_type = mwindow->defaults->get("RENDER_RANGE_TYPE", RANGE_PROJECT);
606
607 // some defaults which work
608         asset->video_data = 1;
609         asset->audio_data = 1;
610         asset->format = FILE_FFMPEG;
611         strcpy(asset->acodec, "mp4.qt");
612         strcpy(asset->vcodec, "mp4.qt");
613
614         asset->load_defaults(mwindow->defaults,
615                 "RENDER_", 1, 1, 1, 1, 1);
616         return 0;
617 }
618
619 int Render::load_profile(int profile_slot, Asset *asset)
620 {
621         char string_name[100];
622         sprintf(string_name, "RENDER_%i_FILE_PER_LABEL", profile_slot);
623         use_labels = mwindow->defaults->get(string_name, 0);
624 // Load mode is not part of the profile
625 //      printf(string_name, "RENDER_%i_LOADMODE", profile_slot);
626 //      load_mode = mwindow->defaults->get(string_name, LOADMODE_NEW_TRACKS);
627         sprintf(string_name, "RENDER_%i_RANGE_TYPE", profile_slot);
628         range_type = mwindow->defaults->get(string_name, RANGE_PROJECT);
629
630         sprintf(string_name, "RENDER_%i_", profile_slot);
631         asset->load_defaults(mwindow->defaults,
632                 string_name, 1, 1, 1, 1, 1);
633         return 0;
634 }
635
636
637 int Render::save_defaults(Asset *asset)
638 {
639         mwindow->defaults->update("RENDER_FILE_PER_LABEL", use_labels);
640         mwindow->defaults->update("RENDER_LOADMODE", load_mode);
641         mwindow->defaults->update("RENDER_RANGE_TYPE", range_type);
642
643         asset->save_defaults(mwindow->defaults,
644                 "RENDER_", 1, 1, 1, 1, 1);
645         return 0;
646 }
647
648 void Render::update_assets()
649 {
650         if( packages )
651                 packages->get_asset_list(assets);
652 }
653
654 static void run_script(const char *script, const char *arg)
655 {
656         char *const argv[] = { (char*)script, (char*)arg, 0 };
657         execvp(script ,&argv[0]);
658         perror("execvp script failed");
659         exit(1);
660 }
661
662 RenderThread::RenderThread(MWindow *mwindow, Render *render)
663  : Thread(0, 0, 0)
664 {
665         this->mwindow = mwindow;
666         this->render = render;
667         render_frames = 0;
668 }
669
670 RenderThread::~RenderThread()
671 {
672 }
673
674
675 void RenderThread::render_single(int test_overwrite, Asset *asset, EDL *edl,
676         int strategy, int range_type)
677 {
678 // Total length in seconds
679         double total_length = 0;
680         RenderFarmServer *farm_server = 0;
681
682         render->in_progress = 1;
683         render->default_asset = asset;
684         render->progress = 0;
685         render->result = 0;
686
687 // Create rendering command
688         TransportCommand *command = new TransportCommand(render->preferences);
689         command->command = NORMAL_FWD;
690         command->get_edl()->copy_all(edl);
691         command->change_type = CHANGE_ALL;
692
693         switch( range_type ) {
694         case RANGE_BACKCOMPAT:
695 // Get highlighted playback range
696                 command->set_playback_range();
697 // Adjust playback range with in/out points
698                 command->playback_range_adjust_inout();
699                 break;
700         case RANGE_PROJECT:
701                 command->playback_range_project();
702                 break;
703         case RANGE_SELECTION:
704                 command->set_playback_range();
705                 break;
706         case RANGE_INOUT:
707                 command->playback_range_inout();
708                 break;
709         case RANGE_1FRAME:
710                 command->playback_range_1frame();
711                 break;
712         }
713
714         render->packages = new PackageDispatcher;
715
716 // Create caches
717         CICache *audio_cache = new CICache(render->preferences);
718         CICache *video_cache = new CICache(render->preferences);
719
720         render->default_asset->frame_rate = command->get_edl()->session->frame_rate;
721         render->default_asset->sample_rate = command->get_edl()->session->sample_rate;
722
723 // Conform asset to EDL.  Find out if any tracks are playable.
724         render->result = render->check_asset(command->get_edl(),
725                 *render->default_asset);
726
727         if( !render->result ) {
728 // Get total range to render
729                 render->total_start = command->start_position;
730 #if 0
731                 render->default_asset->timecode = command->start_position;
732                 printf("tc: %f \n", render->default_asset->timecode);
733                 render->default_asset->timecode += edl->session->timecode_offset;
734 #endif
735                 render->total_end = command->end_position;
736                 total_length = render->total_end - render->total_start;
737
738 // Nothing to render
739                 if( EQUIV(total_length, 0) ) {
740                         render->result = 1;
741                 }
742         }
743 // prevent single frame truncation to zero frames
744         render_frames = render->default_asset->frame_rate * total_length + 1e-4;
745
746 // Generate packages
747         if( !render->result ) {
748 // Stop background rendering
749                 if( mwindow ) mwindow->stop_brender();
750
751                 FileSystem fs;
752                 fs.complete_path(render->default_asset->path);
753
754                 render->result = render->packages->create_packages(mwindow, command->get_edl(),
755                         render->preferences, strategy, render->default_asset,
756                         render->total_start, render->total_end, test_overwrite);
757         }
758
759         render->total_rendered = 0;
760
761         if( !render->result &&
762             ( strategy == SINGLE_PASS_FARM || strategy == FILE_PER_LABEL_FARM ) ) {
763 // Start dispatching external jobs
764                 if( mwindow ) {
765                         mwindow->gui->lock_window("Render::render 1");
766                         mwindow->gui->show_message(_("Starting render farm"));
767                         mwindow->gui->start_hourglass();
768                         mwindow->gui->unlock_window();
769                 }
770                 else {
771                         printf("Render::render: starting render farm\n");
772                 }
773
774                 farm_server = new RenderFarmServer(mwindow, render->packages,
775                         render->preferences, 1, &render->result,
776                         &render->total_rendered, render->counter_lock,
777                         render->default_asset, command->get_edl(), 0);
778                 render->result = farm_server->start_clients();
779
780                 if( render->result ) {
781                         if( mwindow ) {
782                                 mwindow->gui->lock_window("Render::render 2");
783                                 mwindow->gui->show_message(_("Failed to start render farm"),
784                                         mwindow->theme->message_error);
785                                 mwindow->gui->stop_hourglass();
786                                 mwindow->gui->unlock_window();
787                         }
788                         else {
789                                 printf("Render::render: Failed to start render farm\n");
790                         }
791                 }
792         }
793
794 // Perform local rendering
795
796         render->assets.clear();
797         if( !render->result ) {
798                 render->start_progress();
799
800                 MainPackageRenderer package_renderer(render);
801                 render->result = package_renderer.initialize(mwindow,
802                                 command->get_edl(),   // Copy of master EDL
803                                 render->preferences,
804                                 render->default_asset);
805
806                 while( !render->result ) {
807                         int fps = strategy == SINGLE_PASS_FARM ?
808                                 package_renderer.frames_per_second : 0;
809 // Get unfinished job
810                         RenderPackage *package = render->packages->get_package(fps, -1, 1);
811 // Exit point
812                         if( !package ) break;
813
814                         if( package_renderer.render_package(package) )
815                                 render->result = 1;
816                 } // file_number
817
818 printf("Render::render_single: Session finished.\n");
819
820                 if( strategy == SINGLE_PASS_FARM ||
821                     strategy == FILE_PER_LABEL_FARM ) {
822                         if( !render->progress ) {
823                                 while( farm_server->active_clients() > 0 ) {
824                                         sleep(1);
825                                         render->show_progress();
826                                 }
827                         }
828                         farm_server->wait_clients();
829                         render->result |= render->packages->packages_are_done();
830                 }
831
832 // Notify of error
833                 if( render->result && !render->batch_cancelled &&
834                     (!render->progress || !render->progress->is_cancelled()) ) {
835                         if( mwindow ) {
836                                 int cx, cy;
837                                 mwindow->gui->get_abs_cursor(cx, cy, 1);
838                                 ErrorBox error_box(_(PROGRAM_NAME ": Error"), cx, cy);
839                                 error_box.create_objects(_("Error rendering data."));
840                                 error_box.raise_window();
841                                 error_box.run_window();
842                         }
843                         else {
844                                 printf("Render::render: Error rendering data\n");
845                         }
846                 }
847
848 // Delete the progress box
849                 render->stop_progress();
850                 render->update_assets();
851         }
852
853 // Paste all packages into timeline if desired
854
855         if( !render->result && mwindow &&
856             render->load_mode != LOADMODE_NOTHING &&
857             render->mode != Render::BATCH ) {
858                 mwindow->gui->lock_window("Render::render 3");
859
860                 mwindow->undo->update_undo_before();
861                 if( render->load_mode == LOADMODE_PASTE )
862                         mwindow->clear(0);
863                 mwindow->load_assets(&render->assets,
864                         -1, render->load_mode, 0, 0,
865                         mwindow->edl->session->labels_follow_edits,
866                         mwindow->edl->session->plugins_follow_edits,
867                         mwindow->edl->session->autos_follow_edits,
868                         0); // overwrite
869                 mwindow->save_backup();
870                 mwindow->undo->update_undo_after(_("render"), LOAD_ALL);
871                 mwindow->update_plugin_guis();
872                 mwindow->gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
873                 mwindow->sync_parameters(CHANGE_ALL);
874                 mwindow->gui->unlock_window();
875
876                 mwindow->awindow->gui->async_update_assets();
877         }
878
879 // Disable hourglass
880         if( mwindow ) {
881                 mwindow->gui->lock_window("Render::render 3");
882                 mwindow->gui->stop_hourglass();
883                 mwindow->gui->unlock_window();
884         }
885
886 //printf("Render::render 110\n");
887 // Need to restart because brender always stops before render.
888         if( mwindow )
889                 mwindow->restart_brender();
890         if( farm_server ) delete farm_server;
891         delete command;
892         audio_cache->remove_user();
893         video_cache->remove_user();
894 // Must delete packages after server
895         delete render->packages;
896
897         render->packages = 0;
898         render->in_progress = 0;
899 }
900
901 void RenderThread::run()
902 {
903         char *script = 0;
904         Timer render_timer;
905
906         if( mwindow )
907                 render->preferences->copy_from(mwindow->preferences);
908
909         if( render->mode == Render::INTERACTIVE ) {
910                 render_single(1, render->asset, mwindow->edl,
911                         render->get_strategy(), render->range_type);
912         }
913         else
914         if( render->mode == Render::BATCH ) {
915                 for( int i=0; i<render->jobs->total && !render->result; ++i ) {
916                         BatchRenderJob *job = render->jobs->values[i];
917                         if( !job->enabled ) continue;
918                         if( mwindow )
919                                 mwindow->batch_render->update_active(i);
920                         else
921                                 printf("Render::run: %s\n", job->edl_path);
922
923                         FileXML *file = new FileXML;
924                         EDL *edl = new EDL;
925                         edl->create_objects();
926                         file->read_from_file(job->edl_path);
927                         edl->load_xml(file, LOAD_ALL);
928                         delete file;
929
930                         render_single(0, job->asset, edl, job->get_strategy(), RANGE_BACKCOMPAT);
931                         if( !render->result ) {
932                                 if( !i )
933                                         script = job->create_script(edl, &render->assets);
934                                 if( mwindow )
935                                         mwindow->batch_render->update_done(i, 1, render->elapsed_time);
936                                 else {
937                                         char string[BCTEXTLEN];
938                                         render->elapsed_time =
939                                                 (double)render->progress_timer->get_scaled_difference(1);
940                                         Units::totext(string, render->elapsed_time, TIME_HMS2);
941                                         printf("Render::run: done in %s\n", string);
942                                 }
943                         }
944                         else {
945                                 if( mwindow )
946                                         mwindow->batch_render->update_active(-1);
947                                 else
948                                         printf("Render::run: failed\n");
949                         }
950                 }
951                 if( mwindow ) {
952                         mwindow->batch_render->update_active(-1);
953                         mwindow->batch_render->update_done(-1, 0, 0);
954                 }
955         }
956         render->completion->unlock();
957
958         if( !render->result ) {
959                 double render_time = render_timer.get_difference() / 1000.0;
960                 double render_rate = render_time > 0 ? render_frames / render_time : 0;
961                 printf("** rendered %jd frames in %0.3f secs, %0.3f fps\n",
962                         render_frames, render_time, render_rate);
963         }
964
965         float gain = render->beep;
966         if( render->mode == Render::INTERACTIVE && gain > 0 )
967                 mwindow->beep(3000., 1.5, gain);
968
969         if( script ) {
970                 if( !render->result )
971                         run_script(script, 0);
972                 delete [] script;
973         }
974 }
975
976
977 #define WIDTH xS(480)
978 #define HEIGHT yS(480)
979
980
981 RenderWindow::RenderWindow(MWindow *mwindow,
982         Render *render,
983         Asset *asset,
984         int x,
985         int y)
986  : BC_Window(_(PROGRAM_NAME ": Render"), x, y,
987         WIDTH, HEIGHT, WIDTH, HEIGHT, 0, 0, 1)
988 {
989         this->mwindow = mwindow;
990         this->render = render;
991         this->asset = asset;
992         render_format = 0;
993         loadmode = 0;
994         renderprofile = 0;
995         rangeproject = 0;
996         rangeselection = 0;
997         rangeinout = 0;
998         range1frame = 0;
999 // *** CONTEXT_HELP ***
1000         context_help_set_keyword("Single File Rendering");
1001 }
1002
1003 RenderWindow::~RenderWindow()
1004 {
1005         lock_window("RenderWindow::~RenderWindow");
1006         delete render_format;
1007         delete loadmode;
1008         delete renderprofile;
1009         unlock_window();
1010 }
1011
1012
1013 void RenderWindow::load_profile(int profile_slot)
1014 {
1015         render->load_profile(profile_slot, asset);
1016         update_range_type(render->range_type);
1017         render_format->update(asset, &render->use_labels);
1018 }
1019
1020
1021 void RenderWindow::create_objects()
1022 {
1023         int xs10 = xS(10), xs20 = xS(20);
1024         int ys10 = yS(10), ys20 = yS(20), ys25 = yS(25), ys30 = yS(30);
1025         int x = xs10, y = ys10;
1026         lock_window("RenderWindow::create_objects");
1027         add_subwindow(file_format = new BC_Title(x, y,
1028                 (render->use_labels ?
1029                         _("Select the first file to render to:") :
1030                         _("Select a file to render to:"))));
1031         y += ys25;
1032
1033         render_format = new RenderFormat(mwindow, this, asset);
1034         render_format->create_objects(x, y,
1035                 1, 1, 1, 1, 0, 1, 0, 0, &render->use_labels, 0);
1036
1037         BC_Title *title;
1038         add_subwindow(title = new BC_Title(x, y, _("Render range:")));
1039
1040         int is_image = File::is_image_render(asset->format);
1041         if( is_image )
1042                 render->range_type = RANGE_1FRAME;
1043
1044         int x1 = x + title->get_w() + xs20, y1 = y;
1045         add_subwindow(rangeproject = new RenderRangeProject(this,
1046                 render->range_type == RANGE_PROJECT, x1, y));
1047         int x2 = x1 + rangeproject->get_w();
1048         y += ys20;
1049         add_subwindow(rangeselection = new RenderRangeSelection(this,
1050                 render->range_type == RANGE_SELECTION, x1, y));
1051         int x3 = x1 + rangeselection->get_w();
1052         if( x2 < x3 ) x2 = x3;
1053         y += ys20;
1054         add_subwindow(rangeinout = new RenderRangeInOut(this,
1055                 render->range_type == RANGE_INOUT, x1, y));
1056         x3 = x1 + rangeinout->get_w();
1057         if( x2 < x3 ) x2 = x3;
1058         y += ys20;
1059         add_subwindow(range1frame = new RenderRange1Frame(this,
1060                 render->range_type == RANGE_1FRAME, x1, y));
1061         x3 = x1 + range1frame->get_w();
1062         if( x2 < x3 ) x2 = x3;
1063         y += ys30;
1064         if( is_image )
1065                 enable_render_range(0);
1066
1067         x1 = x2 + xs20;
1068         render->beep = mwindow->edl->session->render_beep;
1069         add_subwindow(beep_on_done = new RenderBeepOnDone(this, x1, y1));
1070         y1 += beep_on_done->get_h();
1071         add_subwindow(new BC_Title(x1, y1, _("Beep on done volume")));
1072
1073         renderprofile = new RenderProfile(mwindow, this, x, y, 1);
1074         renderprofile->create_objects();
1075         y += yS(70);
1076
1077         loadmode = new LoadMode(mwindow, this, x, y, &render->load_mode);
1078         loadmode->create_objects();
1079
1080         add_subwindow(new BC_OKButton(this));
1081         add_subwindow(new BC_CancelButton(this));
1082
1083         show_window();
1084         unlock_window();
1085 }
1086
1087 void RenderWindow::update_range_type(int range_type)
1088 {
1089         if( render->range_type == range_type ) return;
1090         render->range_type = range_type;
1091         rangeproject->update(range_type == RANGE_PROJECT);
1092         rangeselection->update(range_type == RANGE_SELECTION);
1093         rangeinout->update(range_type == RANGE_INOUT);
1094         range1frame->update(range_type == RANGE_1FRAME);
1095 }
1096
1097 void RenderWindow::enable_render_range(int v)
1098 {
1099         if( v ) {
1100                 rangeproject->enable();
1101                 rangeselection->enable();
1102                 rangeinout->enable();
1103                 range1frame->enable();
1104         }
1105         else {
1106                 rangeproject->disable();
1107                 rangeselection->disable();
1108                 rangeinout->disable();
1109                 range1frame->disable();
1110         }
1111 }
1112
1113
1114 RenderRangeProject::RenderRangeProject(RenderWindow *rwindow, int value, int x, int y)
1115  : BC_Radial(x, y, value, _("Project"))
1116 {
1117         this->rwindow = rwindow;
1118 }
1119 int RenderRangeProject::handle_event()
1120 {
1121         rwindow->update_range_type(RANGE_PROJECT);
1122         return 1;
1123 }
1124
1125 RenderRangeSelection::RenderRangeSelection(RenderWindow *rwindow, int value, int x, int y)
1126  : BC_Radial(x, y, value, _("Selection"))
1127 {
1128         this->rwindow = rwindow;
1129 }
1130 int RenderRangeSelection::handle_event()
1131 {
1132         rwindow->update_range_type(RANGE_SELECTION);
1133         return 1;
1134 }
1135
1136
1137 RenderRangeInOut::RenderRangeInOut(RenderWindow *rwindow, int value, int x, int y)
1138  : BC_Radial(x, y, value, _("In/Out Points"))
1139 {
1140         this->rwindow = rwindow;
1141 }
1142 int RenderRangeInOut::handle_event()
1143 {
1144         rwindow->update_range_type(RANGE_INOUT);
1145         return 1;
1146 }
1147
1148 RenderRange1Frame::RenderRange1Frame(RenderWindow *rwindow, int value, int x, int y)
1149  : BC_Radial(x, y, value, _("One Frame"))
1150 {
1151         this->rwindow = rwindow;
1152 }
1153 int RenderRange1Frame::handle_event()
1154 {
1155         rwindow->update_range_type(RANGE_1FRAME);
1156         return 1;
1157 }
1158
1159 double Render::get_render_range()
1160 {
1161         EDL *edl = mwindow->edl;
1162         double last = edl->tracks->total_playable_length();
1163         double framerate = edl->session->frame_rate;
1164         if( framerate <= 0 ) framerate = 1;
1165         double start = 0, end = last;
1166         switch( range_type ) {
1167         default:
1168         case RANGE_BACKCOMPAT:
1169                 start = edl->local_session->get_selectionstart(1);
1170                 end   = edl->local_session->get_selectionend(1);
1171                 if( EQUIV(start, end) ) end = last;
1172                 break;
1173         case RANGE_PROJECT:
1174                 break;
1175         case RANGE_SELECTION:
1176                 start = edl->local_session->get_selectionstart(1);
1177                 end   = edl->local_session->get_selectionend(1);
1178                 break;
1179         case RANGE_INOUT:
1180                 start = edl->local_session->inpoint_valid() ?
1181                         edl->local_session->get_inpoint() : 0;
1182                 end   = edl->local_session->outpoint_valid() ?
1183                         edl->local_session->get_outpoint() : last;
1184                 break;
1185         case RANGE_1FRAME:
1186                 start = end = edl->local_session->get_selectionstart(1);
1187                 if( edl->session->frame_rate > 0 ) end += 1./edl->session->frame_rate;
1188                 break;
1189         }
1190         if( start < 0 ) start = 0;
1191         if( end > last ) end = last;
1192         return end - start;
1193 }
1194
1195 RenderFormat::RenderFormat(MWindow *mwindow, BC_WindowBase *window, Asset *asset)
1196  : FormatTools(mwindow, window, asset)
1197 {
1198 }
1199 RenderFormat::~RenderFormat()
1200 {
1201 }
1202
1203 void RenderFormat::update_format()
1204 {
1205         FormatTools::update_format();
1206         RenderWindow *render_window = (RenderWindow *)window;
1207         if( render_window->is_hidden() ) return;
1208         int is_image = File::is_image_render(asset->format);
1209         if( is_image ) {
1210                 render_window->update_range_type(RANGE_1FRAME);
1211                 render_window->enable_render_range(0);
1212         }
1213         else
1214                 render_window->enable_render_range(1);
1215 }
1216 int RenderFormat::handle_event()
1217 {
1218         RenderWindow *render_window = (RenderWindow *)window;
1219         render_window->file_format->update(
1220                 (render_window->render->use_labels ?
1221                         _("Select the first file to render to:") :
1222                         _("Select a file to render to:")));
1223         return 1;
1224 }
1225
1226 RenderBeepOnDone::RenderBeepOnDone(RenderWindow *rwindow, int x, int y)
1227  : BC_FPot(x, y, rwindow->render->beep*100.f, 0.f, 100.f)
1228 {
1229         this->rwindow = rwindow;
1230 }
1231
1232 int RenderBeepOnDone::handle_event()
1233 {
1234         rwindow->render->beep = get_value()/100.f;
1235         return 1;
1236 }
1237
1238