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->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, input_position, use_brender);
144 if(debug) printf("VRender::process_buffer %d use_vconsole=%d\n", __LINE__, use_vconsole);
146 // Negotiate color model
147 colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
148 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
151 // Get output buffer from device
152 if(renderengine->command->realtime && !renderengine->is_nested)
154 renderengine->video->new_output_buffer(&video_out,
156 renderengine->get_edl());
159 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
161 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
165 // Read directly from file to video_out
171 Asset *asset = renderengine->preferences->brender_asset;
172 File *file = renderengine->get_vcache()->check_out(asset,
173 renderengine->get_edl());
177 int64_t corrected_position = current_position;
178 if(renderengine->command->get_direction() == PLAY_REVERSE)
179 corrected_position--;
181 // Cache single frames only
182 // if(use_asynchronous)
183 // file->start_video_decode_thread();
185 file->stop_video_thread();
186 if(use_cache) file->set_cache_frames(1);
187 int64_t normalized_position = (int64_t)(corrected_position *
189 renderengine->get_edl()->session->frame_rate);
191 file->set_video_position(normalized_position,
193 file->read_frame(video_out);
196 if(use_cache) file->set_cache_frames(0);
197 renderengine->get_vcache()->check_in(asset);
204 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
205 result = ((VEdit*)playable_edit)->read_frame(video_out,
207 renderengine->command->get_direction(),
208 renderengine->get_vcache(),
212 // use_asynchronous);
213 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
218 video_out->set_opengl_state(VFrame::RAM);
221 // Read into virtual console
224 // process this buffer now in the virtual console
225 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
232 // Determine if virtual console is needed
233 int VRender::get_use_vconsole(VEdit **playable_edit,
234 int64_t position, int &use_brender)
238 // Background rendering completed
239 if((use_brender = renderengine->brender_available(position,
240 renderengine->command->get_direction())) != 0)
243 // Descend into EDL nest
244 return renderengine->get_edl()->get_use_vconsole(playable_edit,
245 position, renderengine->command->get_direction(),
246 vconsole->playable_tracks);
250 int VRender::get_colormodel(VEdit *playable_edit, int use_vconsole, int use_brender)
252 EDL *edl = renderengine->get_edl();
253 int colormodel = renderengine->get_edl()->session->color_model;
254 VideoOutConfig *vconfig = renderengine->config->vconfig;
255 // check for playback: no plugins, not single frame
256 if( !use_vconsole && !renderengine->command->single_frame() ) {
257 // Get best colormodel supported by the file
258 // colormodel yuv/rgb affects mpeg/jpeg color range,
259 // dont mix them or loose color acccuracy
260 int64_t source_position = 0;
261 Asset *asset = use_brender ?
262 renderengine->preferences->brender_asset :
263 playable_edit->get_nested_asset(&source_position, current_position,
264 renderengine->command->get_direction());
266 File *file = renderengine->get_vcache()->check_out(asset, edl);
268 // damn the color range, full speed ahead
269 if( vconfig->driver == PLAYBACK_X11 && vconfig->use_direct_x11 &&
270 file->colormodel_supported(BC_BGR8888) == BC_BGR8888 )
271 colormodel = BC_BGR8888;
273 // file favorite colormodel may mismatch rgb/yuv
274 int vstream = playable_edit ? playable_edit->channel : -1;
275 int best_colormodel = file->get_best_colormodel(vconfig->driver, vstream);
276 if( BC_CModels::is_yuv(best_colormodel) == BC_CModels::is_yuv(colormodel) )
277 colormodel = best_colormodel;
279 renderengine->get_vcache()->check_in(asset);
293 // Want to know how many samples rendering each frame takes.
294 // Then use this number to predict the next frame that should be rendered.
295 // Be suspicious of frames that render late so have a countdown
296 // before we start dropping.
297 int64_t current_sample, start_sample, end_sample; // Absolute counts.
298 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
299 int64_t delay_countdown = 0; // Frames remaining until delay
300 // Number of frames before next reconfigure
301 int64_t current_input_length;
302 // Number of frames to skip.
303 int64_t frame_step = 1;
304 int use_opengl = (renderengine->video &&
305 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
309 // Number of frames since start of rendering
311 framerate_counter = 0;
312 framerate_timer.update();
314 start_lock->unlock();
315 if(debug) printf("VRender::run %d\n", __LINE__);
318 while(!done && !interrupt )
320 // Perform the most time consuming part of frame decompression now.
321 // Want the condition before, since only 1 frame is rendered
322 // and the number of frames skipped after this frame varies.
323 current_input_length = 1;
325 reconfigure = vconsole->test_reconfigure(current_position,
326 current_input_length);
329 if(debug) printf("VRender::run %d\n", __LINE__);
330 if(reconfigure) restart_playback();
332 if(debug) printf("VRender::run %d\n", __LINE__);
333 process_buffer(current_position, use_opengl);
336 if(debug) printf("VRender::run %d\n", __LINE__);
338 if(renderengine->command->single_frame())
340 if(debug) printf("VRender::run %d\n", __LINE__);
346 // Perform synchronization
348 // Determine the delay until the frame needs to be shown.
349 current_sample = (int64_t)(renderengine->sync_position() *
350 renderengine->command->get_speed());
351 // latest sample at which the frame can be shown.
352 end_sample = Units::tosamples(session_frame + 1,
353 renderengine->get_edl()->session->sample_rate,
354 renderengine->get_edl()->session->frame_rate);
355 // earliest sample by which the frame needs to be shown.
356 start_sample = Units::tosamples(session_frame,
357 renderengine->get_edl()->session->sample_rate,
358 renderengine->get_edl()->session->frame_rate);
360 if(first_frame || end_sample < current_sample)
362 // Frame rendered late or this is the first frame. Flash it now.
363 //printf("VRender::run %d\n", __LINE__);
366 if(renderengine->get_edl()->session->video_every_frame)
368 // User wants every frame.
372 if(skip_countdown > 0)
374 // Maybe just a freak.
380 // Get the frames to skip.
381 delay_countdown = VRENDER_THRESHOLD;
383 frame_step += (int64_t)Units::toframes(current_sample,
384 renderengine->get_edl()->session->sample_rate,
385 renderengine->get_edl()->session->frame_rate);
386 frame_step -= (int64_t)Units::toframes(end_sample,
387 renderengine->get_edl()->session->sample_rate,
388 renderengine->get_edl()->session->frame_rate);
393 // Frame rendered early or just in time.
396 if(delay_countdown > 0)
398 // Maybe just a freak
403 skip_countdown = VRENDER_THRESHOLD;
404 if(start_sample > current_sample)
406 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
407 1000 / renderengine->get_edl()->session->sample_rate);
408 if( delay_time > 1000 ) delay_time = 1000;
409 timer.delay(delay_time);
413 // Came after the earliest sample so keep going
418 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
422 if(debug) printf("VRender::run %d\n", __LINE__);
424 // Trigger audio to start
427 renderengine->first_frame_lock->unlock();
429 renderengine->reset_sync_position();
431 if(debug) printf("VRender::run %d\n", __LINE__);
433 session_frame += frame_step;
435 // advance position in project
436 current_input_length = frame_step;
439 // Subtract frame_step in a loop to allow looped playback to drain
440 // printf("VRender::run %d %d %d %d\n",
444 // current_input_length);
445 while(frame_step && current_input_length)
447 // trim current_input_length to range
448 get_boundaries(current_input_length);
450 advance_position(current_input_length);
451 frame_step -= current_input_length;
452 current_input_length = frame_step;
454 // printf("VRender::run %d %d %d %d\n",
458 // current_input_length);
461 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
462 __LINE__, current_position, done);
465 if(renderengine->command->realtime && renderengine->playback_engine &&
466 renderengine->command->command != CURRENT_FRAME &&
467 renderengine->command->command != LAST_FRAME)
469 renderengine->playback_engine->update_tracking(fromunits(current_position));
471 if(debug) printf("VRender::run %d\n", __LINE__);
473 // Calculate the framerate counter
475 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
476 renderengine->command->realtime)
478 renderengine->update_framerate((float)framerate_counter /
479 ((float)framerate_timer.get_difference() / 1000));
480 framerate_counter = 0;
481 framerate_timer.update();
483 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
485 interrupt = renderengine->video->interrupt;
489 // In case we were interrupted before the first loop
490 renderengine->first_frame_lock->unlock();
492 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
495 int VRender::start_playback()
497 // start reading input and sending to vrenderthread
498 // use a thread only if there's a video device
499 if(renderengine->command->realtime)
506 int64_t VRender::tounits(double position, int round)
509 return Units::round(position * renderengine->get_edl()->session->frame_rate);
511 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
514 double VRender::fromunits(int64_t position)
516 return (double)position / renderengine->get_edl()->session->frame_rate;