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"
26 #include "condition.h"
30 #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->project_smp);
66 vmodule_render_fragment = 0;
69 asynchronous = 0; // render 1 frame at a time
70 framerate_counter = 0;
77 renderengine->wait_done();
80 delete transition_temp;
84 VirtualConsole* VRender::new_vconsole_object()
86 return new VirtualVConsole(renderengine, this);
89 int VRender::get_total_tracks()
91 return renderengine->get_edl()->tracks->total_video_tracks();
94 Module* VRender::new_module(Track *track)
96 return new VModule(renderengine, this, 0, track);
99 int VRender::flash_output()
102 return renderengine->video->write_buffer(video_out, renderengine->get_edl());
107 int VRender::process_buffer(VFrame *video_out,
108 int64_t input_position,
111 // process buffer for non realtime
112 int64_t render_len = 1;
116 this->video_out = video_out;
118 current_position = input_position;
120 reconfigure = vconsole->test_reconfigure(input_position,
123 if(reconfigure) restart_playback();
124 return process_buffer(input_position, use_opengl);
128 int VRender::process_buffer(int64_t input_position,
131 VEdit *playable_edit = 0;
133 int use_vconsole = 1;
136 int use_cache = renderengine->command->single_frame();
137 // int use_asynchronous =
138 // renderengine->command->realtime &&
139 // renderengine->get_edl()->session->video_every_frame &&
140 // renderengine->get_edl()->session->video_asynchronous;
143 // Determine the rendering strategy for this frame.
144 use_vconsole = get_use_vconsole(&playable_edit, input_position, use_brender);
145 if(debug) printf("VRender::process_buffer %d use_vconsole=%d\n", __LINE__, use_vconsole);
147 // Negotiate color model
148 colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
149 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
152 // Get output buffer from device
153 if(renderengine->command->realtime && !renderengine->is_nested)
155 renderengine->video->new_output_buffer(&video_out,
157 renderengine->get_edl());
160 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
162 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
166 // Read directly from file to video_out
172 Asset *asset = renderengine->preferences->brender_asset;
173 File *file = renderengine->get_vcache()->check_out(asset,
174 renderengine->get_edl());
178 int64_t corrected_position = current_position;
179 if(renderengine->command->get_direction() == PLAY_REVERSE)
180 corrected_position--;
182 // Cache single frames only
183 // if(use_asynchronous)
184 // file->start_video_decode_thread();
186 file->stop_video_thread();
187 if(use_cache) file->set_cache_frames(1);
188 int64_t normalized_position = (int64_t)(corrected_position *
190 renderengine->get_edl()->session->frame_rate);
192 file->set_video_position(normalized_position,
194 file->read_frame(video_out);
197 if(use_cache) file->set_cache_frames(0);
198 renderengine->get_vcache()->check_in(asset);
205 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
206 result = ((VEdit*)playable_edit)->read_frame(video_out,
208 renderengine->command->get_direction(),
209 renderengine->get_vcache(),
213 // use_asynchronous);
214 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
219 video_out->set_opengl_state(VFrame::RAM);
222 // Read into virtual console
225 // process this buffer now in the virtual console
226 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
233 // Determine if virtual console is needed
234 int VRender::get_use_vconsole(VEdit **playable_edit,
235 int64_t position, int &use_brender)
239 // Background rendering completed
240 if((use_brender = renderengine->brender_available(position,
241 renderengine->command->get_direction())) != 0)
244 // Descend into EDL nest
245 return renderengine->get_edl()->get_use_vconsole(playable_edit,
246 position, renderengine->command->get_direction(),
247 vconsole->playable_tracks);
251 int VRender::get_colormodel(VEdit *playable_edit, int use_vconsole, int use_brender)
253 EDL *edl = renderengine->get_edl();
254 int colormodel = renderengine->get_edl()->session->color_model;
255 VideoOutConfig *vconfig = renderengine->config->vconfig;
256 // check for playback: no plugins, not single frame
257 if( !use_vconsole && !renderengine->command->single_frame() ) {
258 // Get best colormodel supported by the file
259 // colormodel yuv/rgb affects mpeg/jpeg color range,
260 // dont mix them or loose color acccuracy
261 int64_t source_position = 0;
262 Asset *asset = use_brender ?
263 renderengine->preferences->brender_asset :
264 playable_edit->get_nested_asset(&source_position, current_position,
265 renderengine->command->get_direction());
267 File *file = renderengine->get_vcache()->check_out(asset, edl);
269 // damn the color range, full speed ahead
270 if( vconfig->driver == PLAYBACK_X11 && vconfig->use_direct_x11 &&
271 file->colormodel_supported(BC_BGR8888) == BC_BGR8888 )
272 colormodel = BC_BGR8888;
274 // file favorite colormodel may mismatch rgb/yuv
275 int vstream = playable_edit ? playable_edit->channel : -1;
276 int best_colormodel = file->get_best_colormodel(vconfig->driver, vstream);
277 if( BC_CModels::is_yuv(best_colormodel) == BC_CModels::is_yuv(colormodel) )
278 colormodel = best_colormodel;
280 renderengine->get_vcache()->check_in(asset);
294 // Want to know how many samples rendering each frame takes.
295 // Then use this number to predict the next frame that should be rendered.
296 // Be suspicious of frames that render late so have a countdown
297 // before we start dropping.
298 int64_t current_sample, start_sample, end_sample; // Absolute counts.
299 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
300 int64_t delay_countdown = 0; // Frames remaining until delay
301 // Number of frames before next reconfigure
302 int64_t current_input_length;
303 // Number of frames to skip.
304 int64_t frame_step = 1;
305 int use_opengl = (renderengine->video &&
306 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
310 // Number of frames since start of rendering
312 framerate_counter = 0;
313 framerate_timer.update();
315 start_lock->unlock();
316 if(debug) printf("VRender::run %d\n", __LINE__);
319 while(!done && !interrupt )
321 // Perform the most time consuming part of frame decompression now.
322 // Want the condition before, since only 1 frame is rendered
323 // and the number of frames skipped after this frame varies.
324 current_input_length = 1;
326 reconfigure = vconsole->test_reconfigure(current_position,
327 current_input_length);
330 if(debug) printf("VRender::run %d\n", __LINE__);
331 if(reconfigure) restart_playback();
333 if(debug) printf("VRender::run %d\n", __LINE__);
334 process_buffer(current_position, use_opengl);
337 if(debug) printf("VRender::run %d\n", __LINE__);
339 if(renderengine->command->single_frame())
341 if(debug) printf("VRender::run %d\n", __LINE__);
347 // Perform synchronization
349 // Determine the delay until the frame needs to be shown.
350 current_sample = (int64_t)(renderengine->sync_position() *
351 renderengine->command->get_speed());
352 // latest sample at which the frame can be shown.
353 end_sample = Units::tosamples(session_frame + 1,
354 renderengine->get_edl()->session->sample_rate,
355 renderengine->get_edl()->session->frame_rate);
356 // earliest sample by which the frame needs to be shown.
357 start_sample = Units::tosamples(session_frame,
358 renderengine->get_edl()->session->sample_rate,
359 renderengine->get_edl()->session->frame_rate);
361 if(first_frame || end_sample < current_sample)
363 // Frame rendered late or this is the first frame. Flash it now.
364 //printf("VRender::run %d\n", __LINE__);
367 if(renderengine->get_edl()->session->video_every_frame)
369 // User wants every frame.
373 if(skip_countdown > 0)
375 // Maybe just a freak.
381 // Get the frames to skip.
382 delay_countdown = VRENDER_THRESHOLD;
384 frame_step += (int64_t)Units::toframes(current_sample,
385 renderengine->get_edl()->session->sample_rate,
386 renderengine->get_edl()->session->frame_rate);
387 frame_step -= (int64_t)Units::toframes(end_sample,
388 renderengine->get_edl()->session->sample_rate,
389 renderengine->get_edl()->session->frame_rate);
394 // Frame rendered early or just in time.
397 if(delay_countdown > 0)
399 // Maybe just a freak
404 skip_countdown = VRENDER_THRESHOLD;
405 if(start_sample > current_sample)
407 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
408 1000 / renderengine->get_edl()->session->sample_rate);
409 if( delay_time > 1000 ) delay_time = 1000;
410 timer.delay(delay_time);
414 // Came after the earliest sample so keep going
419 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
423 if(debug) printf("VRender::run %d\n", __LINE__);
425 // Trigger audio to start
428 renderengine->first_frame_lock->unlock();
430 renderengine->reset_sync_position();
432 if(debug) printf("VRender::run %d\n", __LINE__);
434 session_frame += frame_step;
436 // advance position in project
437 current_input_length = frame_step;
440 // Subtract frame_step in a loop to allow looped playback to drain
441 // printf("VRender::run %d %d %d %d\n",
445 // current_input_length);
446 while(frame_step && current_input_length)
448 // trim current_input_length to range
449 get_boundaries(current_input_length);
451 advance_position(current_input_length);
452 frame_step -= current_input_length;
453 current_input_length = frame_step;
455 // printf("VRender::run %d %d %d %d\n",
459 // current_input_length);
462 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
463 __LINE__, current_position, done);
466 if(renderengine->command->realtime && renderengine->playback_engine &&
467 renderengine->command->command != CURRENT_FRAME &&
468 renderengine->command->command != LAST_FRAME)
470 renderengine->playback_engine->update_tracking(fromunits(current_position));
472 if(debug) printf("VRender::run %d\n", __LINE__);
474 // Calculate the framerate counter
476 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
477 renderengine->command->realtime)
479 renderengine->update_framerate((float)framerate_counter /
480 ((float)framerate_timer.get_difference() / 1000));
481 framerate_counter = 0;
482 framerate_timer.update();
484 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
485 if( !interrupt ) interrupt = renderengine->interrupted;
486 if( !interrupt ) interrupt = renderengine->video->interrupt;
487 if( !interrupt ) interrupt = vconsole->interrupt;
491 // In case we were interrupted before the first loop
492 renderengine->first_frame_lock->unlock();
494 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
497 int VRender::start_playback()
499 // start reading input and sending to vrenderthread
500 // use a thread only if there's a video device
501 if(renderengine->command->realtime)
508 int64_t VRender::tounits(double position, int round)
511 return Units::round(position * renderengine->get_edl()->session->frame_rate);
513 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
516 double VRender::fromunits(int64_t position)
518 return (double)position / renderengine->get_edl()->session->frame_rate;