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"
27 #include "condition.h"
31 #include "edlsession.h"
33 #include "localsession.h"
34 #include "mainsession.h"
36 #include "overlayframe.h"
37 #include "playabletracks.h"
38 #include "playbackengine.h"
39 #include "preferences.h"
40 #include "preferencesthread.h"
41 #include "renderengine.h"
42 #include "strategies.inc"
44 #include "transportque.h"
48 #include "videoconfig.h"
49 #include "videodevice.h"
50 #include "virtualconsole.h"
51 #include "virtualvconsole.h"
60 VRender::VRender(RenderEngine *renderengine)
61 : CommonRender(renderengine)
63 data_type = TRACK_VIDEO;
65 overlayer = new OverlayFrame(renderengine->preferences->project_smp);
67 vmodule_render_fragment = 0;
70 asynchronous = 0; // render 1 frame at a time
71 framerate_counter = 0;
73 track_w = track_h = 0;
74 output_w = output_h = 0;
83 renderengine->wait_done();
86 delete transition_temp;
90 VirtualConsole* VRender::new_vconsole_object()
92 return new VirtualVConsole(renderengine, this);
95 int VRender::get_total_tracks()
97 return renderengine->get_edl()->tracks->total_video_tracks();
100 Module* VRender::new_module(Track *track)
102 return new VModule(renderengine, this, 0, track);
105 int VRender::flash_output()
107 if( !video_out ) return 0;
108 renderengine->update_scope(video_out);
109 return renderengine->video->write_buffer(video_out, renderengine->get_edl());
112 int VRender::process_buffer(VFrame *video_out,
113 int64_t input_position,
116 // process buffer for non realtime
117 int64_t render_len = 1;
121 this->video_out = video_out;
123 current_position = input_position;
125 reconfigure = vconsole->test_reconfigure(input_position,
128 if(reconfigure) restart_playback();
129 return process_buffer(input_position, use_opengl);
133 int VRender::process_buffer(int64_t input_position,
136 VEdit *playable_edit = 0;
138 int use_vconsole = 1;
141 int use_cache = renderengine->command->single_frame() ? 1 :
142 renderengine->command->get_direction() == PLAY_REVERSE ? -1 : 0;
143 // int use_asynchronous =
144 // renderengine->command->realtime &&
145 // renderengine->get_edl()->session->video_every_frame &&
146 // renderengine->get_edl()->session->video_asynchronous;
149 // Determine the rendering strategy for this frame.
150 use_vconsole = get_use_vconsole(&playable_edit, input_position, use_brender);
151 if(debug) printf("VRender::process_buffer %d use_vconsole=%d\n", __LINE__, use_vconsole);
153 // Negotiate color model
154 colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
155 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
158 // Get output buffer from device
159 if(renderengine->command->realtime && !renderengine->is_nested)
161 renderengine->video->new_output_buffer(&video_out,
163 renderengine->get_edl());
166 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
168 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
172 // Read directly from file to video_out
178 Asset *asset = renderengine->preferences->brender_asset;
179 File *file = renderengine->get_vcache()->check_out(asset,
180 renderengine->get_edl());
184 int64_t corrected_position = current_position;
185 if(renderengine->command->get_direction() == PLAY_REVERSE)
186 corrected_position--;
188 // Cache single frames only
189 // if(use_asynchronous)
190 // file->start_video_decode_thread();
192 file->stop_video_thread();
193 if(use_cache) file->set_cache_frames(1);
194 int64_t normalized_position = (int64_t)(corrected_position *
196 renderengine->get_edl()->session->frame_rate);
198 file->set_video_position(normalized_position,
200 file->read_frame(video_out);
203 if(use_cache) file->set_cache_frames(0);
204 renderengine->get_vcache()->check_in(asset);
211 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
212 result = ((VEdit*)playable_edit)->read_frame(video_out,
214 renderengine->command->get_direction(),
215 renderengine->get_vcache(),
219 // use_asynchronous);
220 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
225 video_out->set_opengl_state(VFrame::RAM);
228 // Read into virtual console
231 // process this buffer now in the virtual console
232 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
239 // Determine if virtual console is needed
240 int VRender::get_use_vconsole(VEdit **playable_edit,
241 int64_t position, int &use_brender)
245 // Background rendering completed
246 if((use_brender = renderengine->brender_available(position,
247 renderengine->command->get_direction())) != 0)
250 // Descend into EDL nest
251 return renderengine->get_edl()->get_use_vconsole(playable_edit,
252 position, renderengine->command->get_direction(),
253 vconsole->playable_tracks);
257 int VRender::get_colormodel(VEdit *playable_edit, int use_vconsole, int use_brender)
259 EDL *edl = renderengine->get_edl();
260 int colormodel = renderengine->get_edl()->session->color_model;
261 VideoOutConfig *vconfig = renderengine->config->vconfig;
262 // check for playback: no plugins, not single frame
263 if( !use_vconsole && !renderengine->command->single_frame() ) {
264 // Get best colormodel supported by the file
265 // colormodel yuv/rgb affects mpeg/jpeg color range,
266 // dont mix them or loose color acccuracy
267 int64_t source_position = 0;
268 Asset *asset = use_brender ?
269 renderengine->preferences->brender_asset :
270 playable_edit->get_nested_asset(&source_position, current_position,
271 renderengine->command->get_direction());
273 File *file = renderengine->get_vcache()->check_out(asset, edl);
275 // damn the color range, full speed ahead
276 if( vconfig->driver == PLAYBACK_X11 && vconfig->use_direct_x11 &&
277 file->colormodel_supported(BC_BGR8888) == BC_BGR8888 )
278 colormodel = BC_BGR8888;
280 // file favorite colormodel may mismatch rgb/yuv
281 int vstream = playable_edit ? playable_edit->channel : -1;
282 int best_colormodel = file->get_best_colormodel(vconfig->driver, vstream);
283 if( BC_CModels::is_yuv(best_colormodel) == BC_CModels::is_yuv(colormodel) )
284 colormodel = best_colormodel;
286 renderengine->get_vcache()->check_in(asset);
300 // Want to know how many samples rendering each frame takes.
301 // Then use this number to predict the next frame that should be rendered.
302 // Be suspicious of frames that render late so have a countdown
303 // before we start dropping.
304 int64_t current_sample, start_sample, end_sample; // Absolute counts.
305 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
306 int64_t delay_countdown = 0; // Frames remaining until delay
307 // Number of frames before next reconfigure
308 int64_t current_input_length;
309 // Number of frames to skip.
310 int64_t frame_step = 1;
311 int use_opengl = (renderengine->video &&
312 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
316 // Number of frames since start of rendering
318 framerate_counter = 0;
319 framerate_timer.update();
321 start_lock->unlock();
322 if(debug) printf("VRender::run %d\n", __LINE__);
325 while(!done && !interrupt )
327 // Perform the most time consuming part of frame decompression now.
328 // Want the condition before, since only 1 frame is rendered
329 // and the number of frames skipped after this frame varies.
330 current_input_length = 1;
331 if( !reconfigure ) reconfigure =
332 vconsole->test_reconfigure(current_position, current_input_length);
333 if(debug) printf("VRender::run %d\n", __LINE__);
339 if(debug) printf("VRender::run %d\n", __LINE__);
340 process_buffer(current_position, use_opengl);
343 if(debug) printf("VRender::run %d\n", __LINE__);
345 if(renderengine->command->single_frame())
347 if(debug) printf("VRender::run %d\n", __LINE__);
353 // Perform synchronization
355 // Determine the delay until the frame needs to be shown.
356 current_sample = (int64_t)(renderengine->sync_position() *
357 renderengine->command->get_speed());
358 // latest sample at which the frame can be shown.
359 end_sample = Units::tosamples(session_frame + 1,
360 renderengine->get_edl()->session->sample_rate,
361 renderengine->get_edl()->session->frame_rate);
362 // earliest sample by which the frame needs to be shown.
363 start_sample = Units::tosamples(session_frame,
364 renderengine->get_edl()->session->sample_rate,
365 renderengine->get_edl()->session->frame_rate);
367 if(first_frame || end_sample < current_sample)
369 // Frame rendered late or this is the first frame. Flash it now.
370 //printf("VRender::run %d\n", __LINE__);
373 if(renderengine->get_edl()->session->video_every_frame)
375 // User wants every frame.
379 if(skip_countdown > 0)
381 // Maybe just a freak.
387 // Get the frames to skip.
388 delay_countdown = VRENDER_THRESHOLD;
390 frame_step += (int64_t)Units::toframes(current_sample,
391 renderengine->get_edl()->session->sample_rate,
392 renderengine->get_edl()->session->frame_rate);
393 frame_step -= (int64_t)Units::toframes(end_sample,
394 renderengine->get_edl()->session->sample_rate,
395 renderengine->get_edl()->session->frame_rate);
400 // Frame rendered early or just in time.
403 if(delay_countdown > 0)
405 // Maybe just a freak
410 skip_countdown = VRENDER_THRESHOLD;
411 if(start_sample > current_sample)
413 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
414 1000 / renderengine->get_edl()->session->sample_rate);
415 if( delay_time > 1000 ) delay_time = 1000;
416 timer.delay(delay_time);
420 // Came after the earliest sample so keep going
425 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
429 if(debug) printf("VRender::run %d\n", __LINE__);
431 // Trigger audio to start
434 renderengine->first_frame_lock->unlock();
436 renderengine->reset_sync_position();
438 if(debug) printf("VRender::run %d\n", __LINE__);
440 session_frame += frame_step;
442 // advance position in project
443 current_input_length = frame_step;
446 // Subtract frame_step in a loop to allow looped playback to drain
447 // printf("VRender::run %d %d %d %d\n",
451 // current_input_length);
452 while(frame_step && current_input_length)
454 // trim current_input_length to range
455 get_boundaries(current_input_length);
457 advance_position(current_input_length);
458 frame_step -= current_input_length;
459 current_input_length = frame_step;
461 // printf("VRender::run %d %d %d %d\n",
465 // current_input_length);
468 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
469 __LINE__, current_position, done);
472 if(renderengine->command->realtime && renderengine->playback_engine &&
473 renderengine->command->command != CURRENT_FRAME &&
474 renderengine->command->command != LAST_FRAME)
476 renderengine->playback_engine->update_tracking(fromunits(current_position));
478 if(debug) printf("VRender::run %d\n", __LINE__);
480 // Calculate the framerate counter
482 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
483 renderengine->command->realtime)
485 renderengine->update_framerate((float)framerate_counter /
486 ((float)framerate_timer.get_difference() / 1000));
487 framerate_counter = 0;
488 framerate_timer.update();
490 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
491 if( !interrupt ) interrupt = renderengine->interrupted;
492 if( !interrupt ) interrupt = renderengine->video->interrupt;
493 if( !interrupt ) interrupt = vconsole->interrupt;
497 // In case we were interrupted before the first loop
498 renderengine->first_frame_lock->unlock();
500 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
503 int VRender::start_playback()
505 // start reading input and sending to vrenderthread
506 // use a thread only if there's a video device
507 if(renderengine->command->realtime)
514 int64_t VRender::tounits(double position, int round)
517 return Units::round(position * renderengine->get_edl()->session->frame_rate);
519 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
522 double VRender::fromunits(int64_t position)
524 return (double)position / renderengine->get_edl()->session->frame_rate;