asset drag/drop to viewers, bluebanana bug, listbox fontlist highlight
[goodguy/history.git] / cinelerra-5.1 / cinelerra / renderengine.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 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 "amodule.h"
23 #include "arender.h"
24 #include "asset.h"
25 #include "audiodevice.h"
26 #include "bcsignals.h"
27 #include "condition.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "mutex.h"
31 #include "mwindow.h"
32 #include "playbackengine.h"
33 #include "preferences.h"
34 #include "preferencesthread.h"
35 #include "renderengine.h"
36 #include "mainsession.h"
37 #include "tracks.h"
38 #include "transportque.h"
39 #include "videodevice.h"
40 #include "vrender.h"
41 #include "workarounds.h"
42
43
44
45 RenderEngine::RenderEngine(PlaybackEngine *playback_engine,
46         Preferences *preferences,
47         Canvas *output,
48         int is_nested)
49  : Thread(1, 0, 0)
50 {
51         this->playback_engine = playback_engine;
52         this->output = output;
53         this->is_nested = is_nested;
54         audio = 0;
55         video = 0;
56         config = new PlaybackConfig;
57         arender = 0;
58         vrender = 0;
59         do_audio = 0;
60         do_video = 0;
61         interrupted = 0;
62         this->preferences = new Preferences;
63         this->command = new TransportCommand;
64         this->preferences->copy_from(preferences);
65         edl = 0;
66
67         audio_cache = 0;
68         video_cache = 0;
69         if(playback_engine && playback_engine->mwindow)
70                 mwindow = playback_engine->mwindow;
71         else
72                 mwindow = 0;
73         show_tc = 0;
74
75
76         input_lock = new Condition(1, "RenderEngine::input_lock");
77         start_lock = new Condition(1, "RenderEngine::start_lock");
78         output_lock = new Condition(1, "RenderEngine::output_lock");
79         interrupt_lock = new Mutex("RenderEngine::interrupt_lock");
80         first_frame_lock = new Condition(1, "RenderEngine::first_frame_lock");
81 }
82
83 RenderEngine::~RenderEngine()
84 {
85         close_output();
86         delete command;
87         delete preferences;
88         if(arender) delete arender;
89         if(vrender) delete vrender;
90         delete input_lock;
91         delete start_lock;
92         delete output_lock;
93         delete interrupt_lock;
94         delete first_frame_lock;
95         delete config;
96         edl->Garbage::remove_user();
97 }
98
99 EDL* RenderEngine::get_edl()
100 {
101 //      return command->get_edl();
102         return edl;
103 }
104
105 int RenderEngine::arm_command(TransportCommand *command)
106 {
107         const int debug = 0;
108 // Prevent this renderengine from accepting another command until finished.
109 // Since the renderengine is often deleted after the input_lock command it must
110 // be locked here as well as in the calling routine.
111         if(debug) printf("RenderEngine::arm_command %d\n", __LINE__);
112
113
114         input_lock->lock("RenderEngine::arm_command");
115
116         if(!edl)
117         {
118                 edl = new EDL;
119                 edl->create_objects();
120                 edl->copy_all(command->get_edl());
121         }
122         this->command->copy_from(command);
123
124 // Fix background rendering asset to use current dimensions and ignore
125 // headers.
126         preferences->brender_asset->frame_rate = command->get_edl()->session->frame_rate;
127         preferences->brender_asset->width = command->get_edl()->session->output_w;
128         preferences->brender_asset->height = command->get_edl()->session->output_h;
129         preferences->brender_asset->use_header = 0;
130         preferences->brender_asset->layers = 1;
131         preferences->brender_asset->video_data = 1;
132
133         done = 0;
134         interrupted = 0;
135
136 // Retool configuration for this node
137         this->config->copy_from(command->get_edl()->session->playback_config);
138         VideoOutConfig *vconfig = this->config->vconfig;
139         AudioOutConfig *aconfig = this->config->aconfig;
140         if(command->realtime)
141         {
142                 if(command->single_frame() && vconfig->driver != PLAYBACK_X11_GL)
143                 {
144                         vconfig->driver = PLAYBACK_X11;
145                 }
146         }
147         else
148         {
149                 vconfig->driver = PLAYBACK_X11;
150         }
151
152
153
154         get_duty();
155
156         if(do_audio)
157         {
158                 fragment_len = aconfig->fragment_size;
159 // Larger of audio_module_fragment and fragment length adjusted for speed
160 // Extra memory must be allocated for rendering slow motion.
161                 float speed = command->get_speed();
162                 adjusted_fragment_len = speed >= 1.0 ?
163                         (int64_t)(aconfig->fragment_size * speed + 0.5) :
164                         (int64_t)(aconfig->fragment_size / speed + 0.5) ;
165                 if(adjusted_fragment_len < aconfig->fragment_size)
166                         adjusted_fragment_len = aconfig->fragment_size;
167         }
168
169 // Set lock so audio doesn't start until video has started.
170         if(do_video)
171         {
172                 while(first_frame_lock->get_value() > 0)
173                         first_frame_lock->lock("RenderEngine::arm_command");
174         }
175         else
176 // Set lock so audio doesn't wait for video which is never to come.
177         {
178                 while(first_frame_lock->get_value() <= 0)
179                         first_frame_lock->unlock();
180         }
181
182         open_output();
183         create_render_threads();
184         arm_render_threads();
185         if(debug) printf("RenderEngine::arm_command %d\n", __LINE__);
186
187         return 0;
188 }
189
190 void RenderEngine::get_duty()
191 {
192         do_audio = 0;
193         do_video = 0;
194
195 //printf("RenderEngine::get_duty %d\n", __LINE__);
196         if(!command->single_frame() &&
197                 get_edl()->tracks->playable_audio_tracks() &&
198                 get_edl()->session->audio_channels)
199         {
200                 do_audio = 1;
201         }
202
203 //printf("RenderEngine::get_duty %d\n", __LINE__);
204         if(get_edl()->tracks->playable_video_tracks())
205         {
206 //printf("RenderEngine::get_duty %d\n", __LINE__);
207                 do_video = 1;
208         }
209 }
210
211 void RenderEngine::create_render_threads()
212 {
213         if(do_video && !vrender)
214         {
215                 vrender = new VRender(this);
216         }
217
218         if(do_audio && !arender)
219         {
220                 arender = new ARender(this);
221         }
222 }
223
224
225 int RenderEngine::get_output_w()
226 {
227         return get_edl()->session->output_w;
228 }
229
230 int RenderEngine::get_output_h()
231 {
232         return get_edl()->session->output_h;
233 }
234
235 int RenderEngine::brender_available(int position, int direction)
236 {
237         if(playback_engine)
238         {
239                 int64_t corrected_position = position;
240                 if(direction == PLAY_REVERSE)
241                         corrected_position--;
242                 return playback_engine->brender_available(corrected_position);
243         }
244         else
245                 return 0;
246 }
247
248
249 CICache* RenderEngine::get_acache()
250 {
251         if(playback_engine)
252                 return playback_engine->audio_cache;
253         else
254                 return audio_cache;
255 }
256
257 CICache* RenderEngine::get_vcache()
258 {
259         if(playback_engine)
260                 return playback_engine->video_cache;
261         else
262                 return video_cache;
263 }
264
265 void RenderEngine::set_acache(CICache *cache)
266 {
267         this->audio_cache = cache;
268 }
269
270 void RenderEngine::set_vcache(CICache *cache)
271 {
272         this->video_cache = cache;
273 }
274
275
276 double RenderEngine::get_tracking_position()
277 {
278         if(playback_engine)
279                 return playback_engine->get_tracking_position();
280         else
281                 return 0;
282 }
283
284 int RenderEngine::open_output()
285 {
286         if(command->realtime && !is_nested)
287         {
288 // Allocate devices
289                 if(do_audio)
290                 {
291                         audio = new AudioDevice(mwindow);
292                 }
293
294                 if(do_video)
295                 {
296                         video = new VideoDevice(mwindow);
297                 }
298
299 // Initialize sharing
300
301
302 // Start playback
303                 if(do_audio && do_video)
304                 {
305                         video->set_adevice(audio);
306                         audio->set_vdevice(video);
307                 }
308
309
310
311 // Retool playback configuration
312                 if(do_audio)
313                 {
314                         if(audio->open_output(config->aconfig,
315                                 get_edl()->session->sample_rate,
316                                 adjusted_fragment_len,
317                                 get_edl()->session->audio_channels,
318                                 get_edl()->session->real_time_playback))
319                                 do_audio = 0;
320                         else
321                         {
322                                 audio->set_software_positioning(
323                                         get_edl()->session->playback_software_position);
324                                 audio->start_playback();
325                         }
326                 }
327
328                 if(do_video)
329                 {
330                         video->open_output(config->vconfig,
331                                 get_edl()->session->frame_rate,
332                                 get_output_w(),
333                                 get_output_h(),
334                                 output,
335                                 command->single_frame());
336                         video->set_quality(80);
337                         video->set_cpus(preferences->processors);
338                 }
339         }
340
341         return 0;
342 }
343
344 void RenderEngine::reset_sync_position()
345 {
346         timer.update();
347 }
348
349 int64_t RenderEngine::sync_position()
350 {
351 // Use audio device
352 // No danger of race conditions because the output devices are closed after all
353 // threads join.
354         if(do_audio)
355         {
356                 return audio->current_position();
357         }
358
359         if(do_video)
360         {
361                 int64_t result = timer.get_scaled_difference(
362                         get_edl()->session->sample_rate);
363                 return result;
364         }
365         return 0;
366 }
367
368
369 int RenderEngine::start_command()
370 {
371         if(command->realtime && !is_nested)
372         {
373                 interrupt_lock->lock("RenderEngine::start_command");
374                 start_lock->lock("RenderEngine::start_command 1");
375                 Thread::start();
376                 start_lock->lock("RenderEngine::start_command 2");
377                 start_lock->unlock();
378         }
379         return 0;
380 }
381
382 void RenderEngine::arm_render_threads()
383 {
384         if(do_audio)
385         {
386                 arender->arm_command();
387         }
388
389         if(do_video)
390         {
391                 vrender->arm_command();
392         }
393 }
394
395
396 void RenderEngine::start_render_threads()
397 {
398 // Synchronization timer.  Gets reset once again after the first video frame.
399         timer.update();
400
401         if(do_audio)
402         {
403                 arender->start_command();
404         }
405
406         if(do_video)
407         {
408                 vrender->start_command();
409         }
410 }
411
412 void RenderEngine::update_framerate(float framerate)
413 {
414         playback_engine->mwindow->session->actual_frame_rate = framerate;
415         playback_engine->mwindow->preferences_thread->update_framerate();
416 }
417
418 void RenderEngine::wait_render_threads()
419 {
420         if(do_audio)
421         {
422                 arender->Thread::join();
423         }
424
425         if(do_video)
426         {
427                 vrender->Thread::join();
428         }
429 }
430
431 void RenderEngine::interrupt_playback()
432 {
433         interrupt_lock->lock("RenderEngine::interrupt_playback");
434         interrupted = 1;
435         if(do_audio && arender)
436         {
437                 arender->interrupt_playback();
438         }
439
440         if(do_video && vrender)
441         {
442                 vrender->interrupt_playback();
443         }
444         interrupt_lock->unlock();
445 }
446
447 int RenderEngine::close_output()
448 {
449 // Nested engines share devices
450         if(!is_nested)
451         {
452                 if(audio)
453                 {
454                         audio->close_all();
455                         delete audio;
456                         audio = 0;
457                 }
458
459
460
461                 if(video)
462                 {
463                         video->close_all();
464                         delete video;
465                         video = 0;
466                 }
467         }
468
469         return 0;
470 }
471
472 void RenderEngine::get_output_levels(double *levels, int64_t position)
473 {
474         if(do_audio)
475         {
476                 int history_entry = arender->get_history_number(arender->level_samples,
477                         position);
478                 for(int i = 0; i < MAXCHANNELS; i++)
479                 {
480                         if(arender->audio_out[i])
481                                 levels[i] = arender->level_history[i][history_entry];
482                 }
483         }
484 }
485
486 void RenderEngine::get_module_levels(ArrayList<double> *module_levels, int64_t position)
487 {
488         if(do_audio)
489         {
490                 for(int i = 0; i < arender->total_modules; i++)
491                 {
492 //printf("RenderEngine::get_module_levels %p %p\n", ((AModule*)arender->modules[i]), ((AModule*)arender->modules[i])->level_samples);
493                         int history_entry = arender->get_history_number(((AModule*)arender->modules[i])->level_samples, position);
494
495                         module_levels->append(((AModule*)arender->modules[i])->level_history[history_entry]);
496                 }
497         }
498 }
499
500
501
502
503
504 void RenderEngine::run()
505 {
506         start_render_threads();
507         start_lock->unlock();
508         interrupt_lock->unlock();
509
510         wait_render_threads();
511
512         interrupt_lock->lock("RenderEngine::run");
513
514
515         if(interrupted)
516         {
517                 playback_engine->tracking_position = playback_engine->get_tracking_position();
518         }
519
520         close_output();
521
522 // Fix the tracking position
523         if(playback_engine)
524         {
525                 if(command->command == CURRENT_FRAME)
526                 {
527 //printf("RenderEngine::run 4.1 %d\n", playback_engine->tracking_position);
528                         playback_engine->tracking_position = command->playbackstart;
529                 }
530                 else
531                 {
532 // Make sure transport doesn't issue a pause command next
533 //printf("RenderEngine::run 4.1 %d\n", playback_engine->tracking_position);
534                         if(!interrupted)
535                         {
536                                 if(do_audio)
537                                         playback_engine->tracking_position =
538                                                 (double)arender->current_position /
539                                                         command->get_edl()->session->sample_rate;
540                                 else
541                                 if(do_video)
542                                 {
543                                         playback_engine->tracking_position =
544                                                 (double)vrender->current_position /
545                                                         command->get_edl()->session->frame_rate;
546                                 }
547                         }
548
549                         if(!interrupted) playback_engine->command->command = STOP;
550                         playback_engine->stop_tracking();
551
552                 }
553                 playback_engine->is_playing_back = 0;
554         }
555
556         input_lock->unlock();
557         interrupt_lock->unlock();
558 }
559
560
561