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