4 * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
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.
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.
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
25 #include "audiodevice.h"
26 #include "bcsignals.h"
28 #include "condition.h"
30 #include "edlsession.h"
31 #include "meterhistory.h"
34 #include "playbackengine.h"
35 #include "preferences.h"
36 #include "preferencesthread.h"
37 #include "renderengine.h"
38 #include "mainsession.h"
40 #include "transportque.h"
41 #include "videodevice.h"
43 #include "workarounds.h"
47 RenderEngine::RenderEngine(PlaybackEngine *playback_engine,
48 Preferences *preferences,
53 this->playback_engine = playback_engine;
54 this->output = output;
55 this->is_nested = is_nested;
58 config = new PlaybackConfig;
64 this->preferences = new Preferences;
65 this->command = new TransportCommand(preferences);
66 this->preferences->copy_from(preferences);
71 mwindow = !playback_engine ? 0 : playback_engine->mwindow;
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");
81 RenderEngine::~RenderEngine()
86 if(arender) delete arender;
87 if(vrender) delete vrender;
92 delete interrupt_lock;
93 delete first_frame_lock;
95 if( edl ) edl->Garbage::remove_user();
98 EDL* RenderEngine::get_edl()
100 // return command->get_edl();
104 int RenderEngine::arm_command(TransportCommand *command)
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__);
113 input_lock->lock("RenderEngine::arm_command");
118 edl->create_objects();
119 edl->copy_all(command->get_edl());
121 this->command->copy_from(command);
123 // Fix background rendering asset to use current dimensions and ignore
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;
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)
141 if(command->single_frame() && vconfig->driver != PLAYBACK_X11_GL)
143 vconfig->driver = PLAYBACK_X11;
148 vconfig->driver = PLAYBACK_X11;
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;
168 // Set lock so audio doesn't start until video has started.
171 while(first_frame_lock->get_value() > 0)
172 first_frame_lock->lock("RenderEngine::arm_command");
175 // Set lock so audio doesn't wait for video which is never to come.
177 while(first_frame_lock->get_value() <= 0)
178 first_frame_lock->unlock();
182 create_render_threads();
183 arm_render_threads();
184 if(debug) printf("RenderEngine::arm_command %d\n", __LINE__);
189 void RenderEngine::get_duty()
194 //printf("RenderEngine::get_duty %d\n", __LINE__);
195 if( get_edl()->tracks->playable_audio_tracks() &&
196 get_edl()->session->audio_channels )
198 do_audio = !command->single_frame() ? 1 : 0;
199 if( command->toggle_audio ) do_audio = !do_audio;
202 //printf("RenderEngine::get_duty %d\n", __LINE__);
203 if(get_edl()->tracks->playable_video_tracks())
205 //printf("RenderEngine::get_duty %d\n", __LINE__);
210 void RenderEngine::create_render_threads()
212 if(do_video && !vrender)
214 vrender = new VRender(this);
217 if(do_audio && !arender)
219 arender = new ARender(this);
224 int RenderEngine::get_output_w()
226 return get_edl()->session->output_w;
229 int RenderEngine::get_output_h()
231 return get_edl()->session->output_h;
234 int RenderEngine::brender_available(int position, int direction)
238 int64_t corrected_position = position;
239 if(direction == PLAY_REVERSE)
240 corrected_position--;
241 return playback_engine->brender_available(corrected_position);
248 CICache* RenderEngine::get_acache()
251 return playback_engine->audio_cache;
256 CICache* RenderEngine::get_vcache()
259 return playback_engine->video_cache;
264 void RenderEngine::set_acache(CICache *cache)
266 this->audio_cache = cache;
269 void RenderEngine::set_vcache(CICache *cache)
271 this->video_cache = cache;
275 double RenderEngine::get_tracking_position()
278 return playback_engine->get_tracking_position();
283 int RenderEngine::open_output()
285 if(command->realtime && !is_nested)
290 audio = new AudioDevice(mwindow);
295 video = new VideoDevice(mwindow);
298 // Initialize sharing
302 if(do_audio && do_video)
304 video->set_adevice(audio);
305 audio->set_vdevice(video);
310 // Retool playback configuration
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))
321 audio->set_software_positioning(
322 get_edl()->session->playback_software_position);
323 audio->start_playback();
329 video->open_output(config->vconfig,
330 get_edl()->session->frame_rate,
334 command->single_frame());
335 video->set_quality(80);
336 video->set_cpus(preferences->processors);
343 void RenderEngine::reset_sync_position()
348 int64_t RenderEngine::sync_position()
351 // No danger of race conditions because the output devices are closed after all
354 return audio->current_position();
356 int64_t result = timer.get_scaled_difference(
357 get_edl()->session->sample_rate);
364 int RenderEngine::start_command()
366 if( command->realtime && !is_nested ) {
367 interrupt_lock->lock("RenderEngine::start_command");
368 start_lock->lock("RenderEngine::start_command 1");
370 start_lock->lock("RenderEngine::start_command 2");
371 start_lock->unlock();
376 void RenderEngine::arm_render_threads()
378 if( do_audio ) arender->arm_command();
379 if( do_video ) vrender->arm_command();
383 void RenderEngine::start_render_threads()
385 // Synchronization timer. Gets reset once again after the first video frame.
388 if( do_audio ) arender->start_command();
389 if( do_video ) vrender->start_command();
391 start_lock->unlock();
394 void RenderEngine::update_framerate(float framerate)
396 playback_engine->mwindow->session->actual_frame_rate = framerate;
397 playback_engine->mwindow->preferences_thread->update_framerate();
400 void RenderEngine::wait_render_threads()
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");
408 void RenderEngine::interrupt_playback()
410 if( interrupted ) return;
411 interrupt_lock->lock("RenderEngine::interrupt_playback");
414 if( do_audio && arender ) arender->interrupt_playback();
415 if( do_video && vrender ) vrender->interrupt_playback();
417 interrupt_lock->unlock();
420 int RenderEngine::close_output()
422 // Nested engines share devices
423 if( is_nested ) return 0;
426 delete audio; audio = 0;
430 delete video; video = 0;
435 void RenderEngine::get_output_levels(double *levels, int64_t position)
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);
448 void RenderEngine::get_module_levels(ArrayList<double> *module_levels, int64_t position)
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));
462 void RenderEngine::run()
464 render_active->lock("RenderEngine::run");
466 start_render_threads();
467 wait_render_threads();
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);
484 render_active->unlock();
485 interrupt_lock->unlock();
486 input_lock->unlock();
489 void RenderEngine::wait_done()
491 render_active->lock("RenderEngine::wait_done");
492 render_active->unlock();
495 void RenderEngine::update_scope(VFrame *frame)
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();