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 "interlacemodes.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->processors);
67 vmodule_render_fragment = 0;
70 asynchronous = 0; // render 1 frame at a time
71 framerate_counter = 0;
78 if(input_temp) delete input_temp;
79 if(transition_temp) delete transition_temp;
80 if(overlayer) delete overlayer;
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 if( playable_edit ) {
148 // Asset and output device must have same resulting de-interlacing method
149 Indexable *source = playable_edit->get_source();
150 if( source->is_asset ) {
151 Asset *asset = (Asset *)source;
152 if( ilaceautofixmethod2(renderengine->edl->session->interlace_mode,
153 asset->interlace_autofixoption, asset->interlace_mode,
154 asset->interlace_fixmethod) != BC_ILACE_FIXMETHOD_NONE )
159 // Negotiate color model
160 colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
161 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
166 // Get output buffer from device
167 if(renderengine->command->realtime &&
168 !renderengine->is_nested)
170 renderengine->video->new_output_buffer(&video_out, colormodel);
173 if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
175 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
179 // Read directly from file to video_out
185 Asset *asset = renderengine->preferences->brender_asset;
186 File *file = renderengine->get_vcache()->check_out(asset,
187 renderengine->get_edl());
191 int64_t corrected_position = current_position;
192 if(renderengine->command->get_direction() == PLAY_REVERSE)
193 corrected_position--;
195 // Cache single frames only
197 file->start_video_decode_thread();
199 file->stop_video_thread();
200 if(use_cache) file->set_cache_frames(1);
201 int64_t normalized_position = (int64_t)(corrected_position *
203 renderengine->get_edl()->session->frame_rate);
205 file->set_video_position(normalized_position,
207 file->read_frame(video_out);
210 if(use_cache) file->set_cache_frames(0);
211 renderengine->get_vcache()->check_in(asset);
218 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
219 result = ((VEdit*)playable_edit)->read_frame(video_out,
221 renderengine->command->get_direction(),
222 renderengine->get_vcache(),
226 /* Insert timecode */
227 if(renderengine->show_tc)
228 insert_timecode(playable_edit,
231 if(debug) printf("VRender::process_buffer %d\n", __LINE__);
236 video_out->set_opengl_state(VFrame::RAM);
239 // Read into virtual console
242 // process this buffer now in the virtual console
243 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
250 // Determine if virtual console is needed
251 int VRender::get_use_vconsole(VEdit **playable_edit,
252 int64_t position, int &use_brender)
256 // Background rendering completed
257 if((use_brender = renderengine->brender_available(position,
258 renderengine->command->get_direction())) != 0)
261 // Descend into EDL nest
262 return renderengine->get_edl()->get_use_vconsole(playable_edit,
264 renderengine->command->get_direction(),
265 vconsole->playable_tracks);
269 int VRender::insert_timecode(Edit* playable_edit,
274 EDLSession *session = renderengine->edl->session;
275 /* Create a vframe with TC and SRC timecode in white
276 * with a black border */
277 VFrame *input = new VFrame(0,
278 output->get_w(), MIN(output->get_h(), 50),
279 output->get_color_model(), output->get_bytes_per_line());
282 int src_position = 0;
284 TRACE("VRender::insert_timecode 10")
288 (renderengine->vrender->current_position +
289 session->get_frame_offset()) / session->frame_rate,
290 session->time_format,
291 session->sample_rate,
293 session->frames_per_foot);
295 TRACE("VRender::insert_timecode 20")
299 TRACE("VRender::insert_timecode 30")
300 src_position = renderengine->vrender->current_position -
301 playable_edit->startproject +
302 playable_edit->startsource +
303 playable_edit->asset->tcstart;
304 TRACE("VRender::insert_timecode 40")
306 src_position / playable_edit->asset->frame_rate,
307 session->time_format,
308 session->sample_rate,
309 playable_edit->asset->frame_rate,
310 session->frames_per_foot);
314 TRACE("VRender::insert_timecode 50")
317 // (renderengine->vrender->current_position - position) / session->frame_rate,
318 session->time_format,
319 session->sample_rate,
321 session->frames_per_foot);
323 TRACE("VRender::insert_timecode 60")
325 //printf("re position %i position %i\n",
326 // renderengine->vrender->current_position, position);
327 //printf("SRC %s TC %s\n", srctc, etc);
329 /* Insert the timecode data onto the input frame */
331 vrender->overlayer->overlay(output, input,
332 input->x, input->y, input->width, input->height,
333 output->x, output->y, output->width, output->height,
335 renderengine->edl->session->interpolation_type);
342 int VRender::get_colormodel(VEdit *playable_edit,
343 int use_vconsole, int use_brender)
345 int colormodel = renderengine->get_edl()->session->color_model;
347 if(!use_vconsole && !renderengine->command->single_frame())
349 // Get best colormodel supported by the file
350 int driver = renderengine->config->vconfig->driver;
356 asset = renderengine->preferences->brender_asset;
360 int64_t source_position = 0;
361 asset = playable_edit->get_nested_asset(&source_position,
363 renderengine->command->get_direction());
368 file = renderengine->get_vcache()->check_out(asset,
369 renderengine->get_edl());
373 colormodel = file->get_best_colormodel(driver);
374 renderengine->get_vcache()->check_in(asset);
393 // Want to know how many samples rendering each frame takes.
394 // Then use this number to predict the next frame that should be rendered.
395 // Be suspicious of frames that render late so have a countdown
396 // before we start dropping.
397 int64_t current_sample, start_sample, end_sample; // Absolute counts.
398 int64_t skip_countdown = VRENDER_THRESHOLD; // frames remaining until drop
399 int64_t delay_countdown = VRENDER_THRESHOLD; // Frames remaining until delay
400 // Number of frames before next reconfigure
401 int64_t current_input_length;
402 // Number of frames to skip.
403 int64_t frame_step = 1;
404 int use_opengl = (renderengine->video &&
405 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
409 // Number of frames since start of rendering
411 framerate_counter = 0;
412 framerate_timer.update();
414 start_lock->unlock();
415 if(debug) printf("VRender::run %d\n", __LINE__);
418 while(!done && !interrupt )
420 // Perform the most time consuming part of frame decompression now.
421 // Want the condition before, since only 1 frame is rendered
422 // and the number of frames skipped after this frame varies.
423 current_input_length = 1;
425 reconfigure = vconsole->test_reconfigure(current_position,
426 current_input_length);
429 if(debug) printf("VRender::run %d\n", __LINE__);
430 if(reconfigure) restart_playback();
432 if(debug) printf("VRender::run %d\n", __LINE__);
433 process_buffer(current_position, use_opengl);
436 if(debug) printf("VRender::run %d\n", __LINE__);
438 if(renderengine->command->single_frame())
440 if(debug) printf("VRender::run %d\n", __LINE__);
446 // Perform synchronization
448 // Determine the delay until the frame needs to be shown.
449 current_sample = (int64_t)(renderengine->sync_position() *
450 renderengine->command->get_speed());
451 // latest sample at which the frame can be shown.
452 end_sample = Units::tosamples(session_frame,
453 renderengine->get_edl()->session->sample_rate,
454 renderengine->get_edl()->session->frame_rate);
455 // earliest sample by which the frame needs to be shown.
456 start_sample = Units::tosamples(session_frame - 1,
457 renderengine->get_edl()->session->sample_rate,
458 renderengine->get_edl()->session->frame_rate);
460 if(first_frame || end_sample < current_sample)
462 // Frame rendered late or this is the first frame. Flash it now.
463 //printf("VRender::run %d\n", __LINE__);
466 if(renderengine->get_edl()->session->video_every_frame)
468 // User wants every frame.
472 if(skip_countdown > 0)
474 // Maybe just a freak.
480 // Get the frames to skip.
481 delay_countdown = VRENDER_THRESHOLD;
483 frame_step += (int64_t)Units::toframes(current_sample,
484 renderengine->get_edl()->session->sample_rate,
485 renderengine->get_edl()->session->frame_rate);
486 frame_step -= (int64_t)Units::toframes(end_sample,
487 renderengine->get_edl()->session->sample_rate,
488 renderengine->get_edl()->session->frame_rate);
493 // Frame rendered early or just in time.
496 if(delay_countdown > 0)
498 // Maybe just a freak
503 skip_countdown = VRENDER_THRESHOLD;
504 if(start_sample > current_sample)
506 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
507 1000 / renderengine->get_edl()->session->sample_rate);
508 if( delay_time > 1000 ) delay_time = 1000;
509 timer.delay(delay_time);
513 // Came after the earliest sample so keep going
518 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
522 if(debug) printf("VRender::run %d\n", __LINE__);
524 // Trigger audio to start
527 renderengine->first_frame_lock->unlock();
529 renderengine->reset_sync_position();
531 if(debug) printf("VRender::run %d\n", __LINE__);
533 session_frame += frame_step;
535 // advance position in project
536 current_input_length = frame_step;
539 // Subtract frame_step in a loop to allow looped playback to drain
540 // printf("VRender::run %d %d %d %d\n",
544 // current_input_length);
545 while(frame_step && current_input_length)
547 // trim current_input_length to range
548 get_boundaries(current_input_length);
550 advance_position(current_input_length);
551 frame_step -= current_input_length;
552 current_input_length = frame_step;
554 // printf("VRender::run %d %d %d %d\n",
558 // current_input_length);
561 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
562 __LINE__, current_position, done);
565 if(renderengine->command->realtime &&
566 renderengine->playback_engine &&
567 renderengine->command->command != CURRENT_FRAME)
569 renderengine->playback_engine->update_tracking(fromunits(current_position));
571 if(debug) printf("VRender::run %d\n", __LINE__);
573 // Calculate the framerate counter
575 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
576 renderengine->command->realtime)
578 renderengine->update_framerate((float)framerate_counter /
579 ((float)framerate_timer.get_difference() / 1000));
580 framerate_counter = 0;
581 framerate_timer.update();
583 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
585 interrupt = renderengine->video->interrupt;
589 // In case we were interrupted before the first loop
590 renderengine->first_frame_lock->unlock();
592 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
595 int VRender::start_playback()
597 // start reading input and sending to vrenderthread
598 // use a thread only if there's a video device
599 if(renderengine->command->realtime)
606 int64_t VRender::tounits(double position, int round)
609 return Units::round(position * renderengine->get_edl()->session->frame_rate);
611 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
614 double VRender::fromunits(int64_t position)
616 return (double)position / renderengine->get_edl()->session->frame_rate;