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