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
23 #include "bcsignals.h"
25 #include "condition.h"
29 #include "edlsession.h"
32 #include "localsession.h"
33 #include "mainsession.h"
35 #include "overlayframe.h"
36 #include "playabletracks.h"
37 #include "playbackengine.h"
38 #include "preferences.h"
39 #include "preferencesthread.h"
40 #include "renderengine.h"
41 #include "strategies.inc"
43 #include "transportque.h"
47 #include "videoconfig.h"
48 #include "videodevice.h"
49 #include "virtualconsole.h"
50 #include "virtualvconsole.h"
59 VRender::VRender(RenderEngine *renderengine)
60 : CommonRender(renderengine)
62 data_type = TRACK_VIDEO;
64 overlayer = new OverlayFrame(renderengine->preferences->processors);
66 vmodule_render_fragment = 0;
69 asynchronous = 0; // render 1 frame at a time
70 framerate_counter = 0;
77 if(input_temp) delete input_temp;
78 if(transition_temp) delete transition_temp;
79 if(overlayer) delete overlayer;
83 VirtualConsole* VRender::new_vconsole_object()
85 return new VirtualVConsole(renderengine, this);
88 int VRender::get_total_tracks()
90 return renderengine->get_edl()->tracks->total_video_tracks();
93 Module* VRender::new_module(Track *track)
95 return new VModule(renderengine, this, 0, track);
98 int VRender::flash_output()
101 return renderengine->video->write_buffer(video_out, renderengine->get_edl());
106 int VRender::process_buffer(VFrame *video_out,
107 int64_t input_position,
110 // process buffer for non realtime
111 int64_t render_len = 1;
115 this->video_out = video_out;
117 current_position = input_position;
119 reconfigure = vconsole->test_reconfigure(input_position,
122 if(reconfigure) restart_playback();
123 return process_buffer(input_position, use_opengl);
127 int VRender::process_buffer(int64_t input_position,
130 VEdit *playable_edit = 0;
132 int use_vconsole = 1;
135 int use_cache = renderengine->command->single_frame();
136 int use_asynchronous =
137 renderengine->command->realtime &&
138 renderengine->get_edl()->session->video_every_frame &&
139 renderengine->get_edl()->session->video_asynchronous;
142 // Determine the rendering strategy for this frame.
143 use_vconsole = get_use_vconsole(&playable_edit,
146 if(debug) printf("VRender::process_buffer %d use_vconsole=%d\n", __LINE__, use_vconsole);
148 // Negotiate color model
149 colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
150 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
155 // Get output buffer from device
156 if(renderengine->command->realtime &&
157 !renderengine->is_nested)
159 renderengine->video->new_output_buffer(&video_out, colormodel);
162 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
164 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
168 // Read directly from file to video_out
174 Asset *asset = renderengine->preferences->brender_asset;
175 File *file = renderengine->get_vcache()->check_out(asset,
176 renderengine->get_edl());
180 int64_t corrected_position = current_position;
181 if(renderengine->command->get_direction() == PLAY_REVERSE)
182 corrected_position--;
184 // Cache single frames only
186 file->start_video_decode_thread();
188 file->stop_video_thread();
189 if(use_cache) file->set_cache_frames(1);
190 int64_t normalized_position = (int64_t)(corrected_position *
192 renderengine->get_edl()->session->frame_rate);
194 file->set_video_position(normalized_position,
196 file->read_frame(video_out);
199 if(use_cache) file->set_cache_frames(0);
200 renderengine->get_vcache()->check_in(asset);
207 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
208 result = ((VEdit*)playable_edit)->read_frame(video_out,
210 renderengine->command->get_direction(),
211 renderengine->get_vcache(),
215 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
220 video_out->set_opengl_state(VFrame::RAM);
223 // Read into virtual console
226 // process this buffer now in the virtual console
227 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
234 // Determine if virtual console is needed
235 int VRender::get_use_vconsole(VEdit* *playable_edit,
236 int64_t position, int &use_brender)
240 // Background rendering completed
241 if((use_brender = renderengine->brender_available(position,
242 renderengine->command->get_direction())) != 0)
245 // Descend into EDL nest
246 return renderengine->get_edl()->get_use_vconsole(playable_edit,
248 renderengine->command->get_direction(),
249 vconsole->playable_tracks);
252 int VRender::get_colormodel(VEdit *playable_edit,
253 int use_vconsole, int use_brender)
255 int colormodel = renderengine->get_edl()->session->color_model;
257 if(!use_vconsole && !renderengine->command->single_frame())
259 // Get best colormodel supported by the file
260 int driver = renderengine->config->vconfig->driver;
266 asset = renderengine->preferences->brender_asset;
270 int64_t source_position = 0;
271 asset = playable_edit->get_nested_asset(&source_position,
273 renderengine->command->get_direction());
278 file = renderengine->get_vcache()->check_out(asset,
279 renderengine->get_edl());
283 colormodel = file->get_best_colormodel(driver);
284 renderengine->get_vcache()->check_in(asset);
303 // Want to know how many samples rendering each frame takes.
304 // Then use this number to predict the next frame that should be rendered.
305 // Be suspicious of frames that render late so have a countdown
306 // before we start dropping.
307 int64_t current_sample, start_sample, end_sample; // Absolute counts.
308 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
309 int64_t delay_countdown = VRENDER_THRESHOLD; // Frames remaining until delay
310 // Number of frames before next reconfigure
311 int64_t current_input_length;
312 // Number of frames to skip.
313 int64_t frame_step = 1;
314 int use_opengl = (renderengine->video &&
315 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
319 // Number of frames since start of rendering
321 framerate_counter = 0;
322 framerate_timer.update();
324 start_lock->unlock();
325 if(debug) printf("VRender::run %d\n", __LINE__);
328 while(!done && !interrupt )
330 // Perform the most time consuming part of frame decompression now.
331 // Want the condition before, since only 1 frame is rendered
332 // and the number of frames skipped after this frame varies.
333 current_input_length = 1;
335 reconfigure = vconsole->test_reconfigure(current_position,
336 current_input_length);
339 if(debug) printf("VRender::run %d\n", __LINE__);
340 if(reconfigure) restart_playback();
342 if(debug) printf("VRender::run %d\n", __LINE__);
343 process_buffer(current_position, use_opengl);
346 if(debug) printf("VRender::run %d\n", __LINE__);
348 if(renderengine->command->single_frame())
350 if(debug) printf("VRender::run %d\n", __LINE__);
356 // Perform synchronization
358 // Determine the delay until the frame needs to be shown.
359 current_sample = (int64_t)(renderengine->sync_position() *
360 renderengine->command->get_speed());
361 // latest sample at which the frame can be shown.
362 end_sample = Units::tosamples(session_frame,
363 renderengine->get_edl()->session->sample_rate,
364 renderengine->get_edl()->session->frame_rate);
365 // earliest sample by which the frame needs to be shown.
366 start_sample = Units::tosamples(session_frame - 1,
367 renderengine->get_edl()->session->sample_rate,
368 renderengine->get_edl()->session->frame_rate);
370 if(first_frame || end_sample < current_sample)
372 // Frame rendered late or this is the first frame. Flash it now.
373 //printf("VRender::run %d\n", __LINE__);
376 if(renderengine->get_edl()->session->video_every_frame)
378 // User wants every frame.
382 if(skip_countdown > 0)
384 // Maybe just a freak.
390 // Get the frames to skip.
391 delay_countdown = VRENDER_THRESHOLD;
393 frame_step += (int64_t)Units::toframes(current_sample,
394 renderengine->get_edl()->session->sample_rate,
395 renderengine->get_edl()->session->frame_rate);
396 frame_step -= (int64_t)Units::toframes(end_sample,
397 renderengine->get_edl()->session->sample_rate,
398 renderengine->get_edl()->session->frame_rate);
403 // Frame rendered early or just in time.
406 if(delay_countdown > 0)
408 // Maybe just a freak
413 skip_countdown = VRENDER_THRESHOLD;
414 if(start_sample > current_sample)
416 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
417 1000 / renderengine->get_edl()->session->sample_rate);
418 if( delay_time > 1000 ) delay_time = 1000;
419 timer.delay(delay_time);
423 // Came after the earliest sample so keep going
428 //printf("VRender::run %d " _LD "\n", __LINE__, current_input_length);
432 if(debug) printf("VRender::run %d\n", __LINE__);
434 // Trigger audio to start
437 renderengine->first_frame_lock->unlock();
439 renderengine->reset_sync_position();
441 if(debug) printf("VRender::run %d\n", __LINE__);
443 session_frame += frame_step;
445 // advance position in project
446 current_input_length = frame_step;
449 // Subtract frame_step in a loop to allow looped playback to drain
450 // printf("VRender::run %d %d %d %d\n",
454 // current_input_length);
455 while(frame_step && current_input_length)
457 // trim current_input_length to range
458 get_boundaries(current_input_length);
460 advance_position(current_input_length);
461 frame_step -= current_input_length;
462 current_input_length = frame_step;
464 // printf("VRender::run %d %d %d %d\n",
468 // current_input_length);
471 if(debug) printf("VRender::run %d current_position=" _LD " done=%d\n",
472 __LINE__, current_position, done);
475 if(renderengine->command->realtime &&
476 renderengine->playback_engine &&
477 renderengine->command->command != CURRENT_FRAME)
479 renderengine->playback_engine->update_tracking(fromunits(current_position));
481 if(debug) printf("VRender::run %d\n", __LINE__);
483 // Calculate the framerate counter
485 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
486 renderengine->command->realtime)
488 renderengine->update_framerate((float)framerate_counter /
489 ((float)framerate_timer.get_difference() / 1000));
490 framerate_counter = 0;
491 framerate_timer.update();
493 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
495 interrupt = renderengine->video->interrupt;
499 // In case we were interrupted before the first loop
500 renderengine->first_frame_lock->unlock();
502 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
505 int VRender::start_playback()
507 // start reading input and sending to vrenderthread
508 // use a thread only if there's a video device
509 if(renderengine->command->realtime)
516 int64_t VRender::tounits(double position, int round)
519 return Units::round(position * renderengine->get_edl()->session->frame_rate);
521 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
524 double VRender::fromunits(int64_t position)
526 return (double)position / renderengine->get_edl()->session->frame_rate;