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, colormodel);
157 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
159 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
163 // Read directly from file to video_out
169 Asset *asset = renderengine->preferences->brender_asset;
170 File *file = renderengine->get_vcache()->check_out(asset,
171 renderengine->get_edl());
175 int64_t corrected_position = current_position;
176 if(renderengine->command->get_direction() == PLAY_REVERSE)
177 corrected_position--;
179 // Cache single frames only
181 file->start_video_decode_thread();
183 file->stop_video_thread();
184 if(use_cache) file->set_cache_frames(1);
185 int64_t normalized_position = (int64_t)(corrected_position *
187 renderengine->get_edl()->session->frame_rate);
189 file->set_video_position(normalized_position,
191 file->read_frame(video_out);
194 if(use_cache) file->set_cache_frames(0);
195 renderengine->get_vcache()->check_in(asset);
202 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
203 result = ((VEdit*)playable_edit)->read_frame(video_out,
205 renderengine->command->get_direction(),
206 renderengine->get_vcache(),
210 /* Insert timecode */
211 if(renderengine->show_tc)
212 insert_timecode(playable_edit,
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,
247 position, renderengine->command->get_direction(),
248 vconsole->playable_tracks);
252 int VRender::insert_timecode(Edit* playable_edit,
257 EDLSession *session = renderengine->edl->session;
258 /* Create a vframe with TC and SRC timecode in white
259 * with a black border */
260 VFrame *input = new VFrame(0,
261 output->get_w(), MIN(output->get_h(), 50),
262 output->get_color_model(), output->get_bytes_per_line());
265 int src_position = 0;
267 TRACE("VRender::insert_timecode 10")
271 (renderengine->vrender->current_position +
272 session->get_frame_offset()) / session->frame_rate,
273 session->time_format,
274 session->sample_rate,
276 session->frames_per_foot);
278 TRACE("VRender::insert_timecode 20")
282 TRACE("VRender::insert_timecode 30")
283 src_position = renderengine->vrender->current_position -
284 playable_edit->startproject +
285 playable_edit->startsource +
286 playable_edit->asset->tcstart;
287 TRACE("VRender::insert_timecode 40")
289 src_position / playable_edit->asset->frame_rate,
290 session->time_format,
291 session->sample_rate,
292 playable_edit->asset->frame_rate,
293 session->frames_per_foot);
297 TRACE("VRender::insert_timecode 50")
300 // (renderengine->vrender->current_position - position) / session->frame_rate,
301 session->time_format,
302 session->sample_rate,
304 session->frames_per_foot);
306 TRACE("VRender::insert_timecode 60")
308 //printf("re position %i position %i\n",
309 // renderengine->vrender->current_position, position);
310 //printf("SRC %s TC %s\n", srctc, etc);
312 /* Insert the timecode data onto the input frame */
314 vrender->overlayer->overlay(output, input,
315 input->x, input->y, input->width, input->height,
316 output->x, output->y, output->width, output->height,
318 renderengine->edl->session->interpolation_type);
325 int VRender::get_colormodel(VEdit *playable_edit,
326 int use_vconsole, int use_brender)
328 int colormodel = renderengine->get_edl()->session->color_model;
330 if(!use_vconsole && !renderengine->command->single_frame())
332 // Get best colormodel supported by the file
333 int driver = renderengine->config->vconfig->driver;
339 asset = renderengine->preferences->brender_asset;
343 int64_t source_position = 0;
344 asset = playable_edit->get_nested_asset(&source_position,
346 renderengine->command->get_direction());
348 // ffmpeg files are side effected by color_model, affects colorspace,color_range
349 if( asset && asset->format != FILE_FFMPEG )
351 file = renderengine->get_vcache()->check_out(asset,
352 renderengine->get_edl());
356 colormodel = file->get_best_colormodel(driver);
357 renderengine->get_vcache()->check_in(asset);
376 // Want to know how many samples rendering each frame takes.
377 // Then use this number to predict the next frame that should be rendered.
378 // Be suspicious of frames that render late so have a countdown
379 // before we start dropping.
380 int64_t current_sample, start_sample, end_sample; // Absolute counts.
381 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
382 int64_t delay_countdown = VRENDER_THRESHOLD; // Frames remaining until delay
383 // Number of frames before next reconfigure
384 int64_t current_input_length;
385 // Number of frames to skip.
386 int64_t frame_step = 1;
387 int use_opengl = (renderengine->video &&
388 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
392 // Number of frames since start of rendering
394 framerate_counter = 0;
395 framerate_timer.update();
397 start_lock->unlock();
398 if(debug) printf("VRender::run %d\n", __LINE__);
401 while(!done && !interrupt )
403 // Perform the most time consuming part of frame decompression now.
404 // Want the condition before, since only 1 frame is rendered
405 // and the number of frames skipped after this frame varies.
406 current_input_length = 1;
408 reconfigure = vconsole->test_reconfigure(current_position,
409 current_input_length);
412 if(debug) printf("VRender::run %d\n", __LINE__);
413 if(reconfigure) restart_playback();
415 if(debug) printf("VRender::run %d\n", __LINE__);
416 process_buffer(current_position, use_opengl);
419 if(debug) printf("VRender::run %d\n", __LINE__);
421 if(renderengine->command->single_frame())
423 if(debug) printf("VRender::run %d\n", __LINE__);
429 // Perform synchronization
431 // Determine the delay until the frame needs to be shown.
432 current_sample = (int64_t)(renderengine->sync_position() *
433 renderengine->command->get_speed());
434 // latest sample at which the frame can be shown.
435 end_sample = Units::tosamples(session_frame,
436 renderengine->get_edl()->session->sample_rate,
437 renderengine->get_edl()->session->frame_rate);
438 // earliest sample by which the frame needs to be shown.
439 start_sample = Units::tosamples(session_frame - 1,
440 renderengine->get_edl()->session->sample_rate,
441 renderengine->get_edl()->session->frame_rate);
443 if(first_frame || end_sample < current_sample)
445 // Frame rendered late or this is the first frame. Flash it now.
446 //printf("VRender::run %d\n", __LINE__);
449 if(renderengine->get_edl()->session->video_every_frame)
451 // User wants every frame.
455 if(skip_countdown > 0)
457 // Maybe just a freak.
463 // Get the frames to skip.
464 delay_countdown = VRENDER_THRESHOLD;
466 frame_step += (int64_t)Units::toframes(current_sample,
467 renderengine->get_edl()->session->sample_rate,
468 renderengine->get_edl()->session->frame_rate);
469 frame_step -= (int64_t)Units::toframes(end_sample,
470 renderengine->get_edl()->session->sample_rate,
471 renderengine->get_edl()->session->frame_rate);
476 // Frame rendered early or just in time.
479 if(delay_countdown > 0)
481 // Maybe just a freak
486 skip_countdown = VRENDER_THRESHOLD;
487 if(start_sample > current_sample)
489 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
490 1000 / renderengine->get_edl()->session->sample_rate);
491 if( delay_time > 1000 ) delay_time = 1000;
492 timer.delay(delay_time);
496 // Came after the earliest sample so keep going
501 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
505 if(debug) printf("VRender::run %d\n", __LINE__);
507 // Trigger audio to start
510 renderengine->first_frame_lock->unlock();
512 renderengine->reset_sync_position();
514 if(debug) printf("VRender::run %d\n", __LINE__);
516 session_frame += frame_step;
518 // advance position in project
519 current_input_length = frame_step;
522 // Subtract frame_step in a loop to allow looped playback to drain
523 // printf("VRender::run %d %d %d %d\n",
527 // current_input_length);
528 while(frame_step && current_input_length)
530 // trim current_input_length to range
531 get_boundaries(current_input_length);
533 advance_position(current_input_length);
534 frame_step -= current_input_length;
535 current_input_length = frame_step;
537 // printf("VRender::run %d %d %d %d\n",
541 // current_input_length);
544 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
545 __LINE__, current_position, done);
548 if(renderengine->command->realtime &&
549 renderengine->playback_engine &&
550 renderengine->command->command != CURRENT_FRAME)
552 renderengine->playback_engine->update_tracking(fromunits(current_position));
554 if(debug) printf("VRender::run %d\n", __LINE__);
556 // Calculate the framerate counter
558 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
559 renderengine->command->realtime)
561 renderengine->update_framerate((float)framerate_counter /
562 ((float)framerate_timer.get_difference() / 1000));
563 framerate_counter = 0;
564 framerate_timer.update();
566 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
568 interrupt = renderengine->video->interrupt;
572 // In case we were interrupted before the first loop
573 renderengine->first_frame_lock->unlock();
575 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
578 int VRender::start_playback()
580 // start reading input and sending to vrenderthread
581 // use a thread only if there's a video device
582 if(renderengine->command->realtime)
589 int64_t VRender::tounits(double position, int round)
592 return Units::round(position * renderengine->get_edl()->session->frame_rate);
594 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
597 double VRender::fromunits(int64_t position)
599 return (double)position / renderengine->get_edl()->session->frame_rate;