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