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