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