4 * Copyright (C) 2009-2013 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "bcpbuffer.h"
26 #include "bcsignals.h"
29 #include "commonrender.h"
32 #include "edlsession.h"
35 #include "floatautos.h"
36 #include "mainerror.h"
38 #include "maskautos.h"
40 #include "overlayframe.h"
42 #include "pluginarray.h"
43 #include "preferences.h"
44 #include "renderengine.h"
45 #include "sharedlocation.h"
47 #include "transition.h"
48 #include "transportque.h"
50 #include "vattachmentpoint.h"
51 #include "vdevicex11.h"
54 #include "videodevice.h"
55 #include "virtualvconsole.h"
61 #include "maskengine.h"
62 #include "automation.h"
64 VModule::VModule(RenderEngine *renderengine,
65 CommonRender *commonrender,
66 PluginArray *plugin_array,
68 : Module(renderengine, commonrender, plugin_array, track)
70 data_type = TRACK_VIDEO;
79 if( overlay_temp ) delete overlay_temp;
80 if( input_temp ) delete input_temp;
81 if( transition_temp ) delete transition_temp;
86 AttachmentPoint* VModule::new_attachment(Plugin *plugin)
88 return new VAttachmentPoint(renderengine, plugin);
91 int VModule::get_buffer_size()
96 CICache* VModule::get_cache()
99 return renderengine->get_vcache();
105 int VModule::import_frame(VFrame *output, VEdit *current_edit,
106 int64_t input_position, double frame_rate, int direction, int use_opengl)
108 int64_t direction_position=0;
109 // Translation of edit
110 float in_x=0.0, in_y=0.0, in_w=0.0, in_h=0.0;
111 float out_x=0.0, out_y=0.0, out_w=0.0, out_h=0.0;
114 double edl_rate = get_edl()->session->frame_rate;
116 int64_t input_position_project = Units::to_int64(input_position *
117 edl_rate / frame_rate + 0.001);
119 if( !output ) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
120 //output->dump_params();
122 if( debug ) printf("VModule::import_frame %d this=%p input_position=%lld direction=%d\n",
123 __LINE__, this, (long long)input_position, direction);
125 // Convert to position corrected for direction
126 direction_position = input_position;
127 if( direction == PLAY_REVERSE ) {
128 if( direction_position > 0 ) direction_position--;
129 if( input_position_project > 0 ) input_position_project--;
131 if( !output ) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
133 VDeviceX11 *x11_device = 0;
135 if( renderengine && renderengine->video ) {
136 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
137 output->set_opengl_state(VFrame::RAM);
138 if( !x11_device ) use_opengl = 0;
142 if( !output ) printf("VModule::import_frame %d output=%p x11_device=%p nested_edl=%p\n",
143 __LINE__, output, x11_device, nested_edl);
145 if( debug ) printf("VModule::import_frame %d current_edit=%p\n",
146 __LINE__, current_edit);
148 // Load frame into output
150 // Create objects for nested EDL
151 if( current_edit && current_edit->nested_edl ) {
153 if( debug ) printf("VModule::import_frame %d nested_edl=%p current_edit->nested_edl=%p\n",
154 __LINE__, nested_edl, current_edit->nested_edl);
156 // Convert requested direction to command
157 if( renderengine->command->command == CURRENT_FRAME ||
158 renderengine->command->command == LAST_FRAME ) {
159 command = renderengine->command->command;
162 if( direction == PLAY_REVERSE ) {
163 if( renderengine->command->single_frame() )
164 command = SINGLE_FRAME_REWIND;
166 command = NORMAL_REWIND;
169 if( renderengine->command->single_frame() )
170 command = SINGLE_FRAME_FWD;
172 command = NORMAL_FWD;
175 if( !nested_edl || nested_edl->id != current_edit->nested_edl->id ) {
176 nested_edl = current_edit->nested_edl;
177 if( nested_renderengine ) {
178 delete nested_renderengine;
179 nested_renderengine = 0;
181 if( !nested_command )
182 nested_command = new TransportCommand(get_preferences());
183 nested_command->command = command;
184 nested_command->get_edl()->copy_all(nested_edl);
185 nested_command->change_type = CHANGE_ALL;
186 nested_command->realtime = renderengine->command->realtime;
187 nested_renderengine = new RenderEngine(0, get_preferences(), 0, 1);
188 nested_renderengine->set_vcache(get_cache());
189 nested_renderengine->arm_command(nested_command);
193 // Update nested command
194 nested_renderengine->command->command = command;
195 nested_command->realtime = renderengine->command->realtime;
198 // Update nested video driver for opengl
199 nested_renderengine->video = renderengine->video;
204 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
208 x11_device->clear_input(output);
210 output->clear_frame();
213 printf("VModule::import_frame %d output=%p\n", __LINE__, output);
216 (current_edit->asset ||
217 (current_edit->nested_edl && nested_renderengine->vrender)) ) {
220 //printf("VModule::import_frame %d cache=%p\n", __LINE__, get_cache());
221 if( current_edit->asset ) {
223 file = get_cache()->check_out(current_edit->asset, get_edl());
224 // get_cache()->dump();
228 if( file || nested_edl ) {
229 int64_t nested_position = 0;
230 // Source position going forward, in edl framerate
231 int64_t pos = Units::to_int64((double)direction_position / frame_rate * edl_rate);
232 int64_t len = pos - current_edit->startproject;
233 FloatAutos *speed_autos = !track->has_speed() ? 0 :
234 (FloatAutos*)track->automation->autos[AUTOMATION_SPEED];
235 if( speed_autos && len > 0 )
236 len = speed_autos->automation_integral(current_edit->startproject, len, PLAY_FORWARD);
237 pos = current_edit->startsource + len;
238 // Make positions based on requested frame rate.
239 int64_t position = Units::to_int64((double)pos * frame_rate / edl_rate);
241 int64_t video_length;
242 int asset_w, asset_h;
244 asset_w = current_edit->asset->width;
245 asset_h = current_edit->asset->height;
246 video_length = file->get_video_length();
249 asset_w = nested_edl->session->output_w;
250 asset_h = nested_edl->session->output_h;
251 video_length = nested_edl->tracks->total_length();
254 VFrame *&input = commonrender ?
255 ((VRender*)commonrender)->input_temp : // Realtime playback
256 input_temp ; // Menu effect
257 VFrame::get_temp(input, asset_w, asset_h, get_edl()->session->color_model);
259 int use_cache = renderengine->command->single_frame() ? 1 :
260 renderengine->command->get_direction() == PLAY_REVERSE ? -1 : 0;
262 // int use_asynchronous = !use_cache &&
264 // Try to make rendering go faster.
265 // But converts some formats to YUV420, which may degrade input format.
266 //// renderengine->command->realtime &&
267 // renderengine->get_edl()->session->video_asynchronous;
270 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
271 // if( use_asynchronous )
272 // file->start_video_decode_thread();
274 file->stop_video_thread();
276 VEdit *vnext = (VEdit *)current_edit->next;
277 pos = Units::to_int64((double)input_position / frame_rate * edl_rate);
278 if( renderengine->preferences->cache_transitions && !use_cache &&
279 // cache transitions, not caching and inside transition
280 vnext && vnext->transition && vnext->transition->on &&
281 file->get_video_length() >= 0 && pos >= vnext->startproject &&
282 pos < vnext->startproject + vnext->transition->length ) {
283 file->set_cache_frames(0);
284 file->set_layer(current_edit->channel);
285 VEdit *vnext = (VEdit *)current_edit->next;
286 Track *track = current_edit->track;
287 FloatAutos *speed_autos = (FloatAutos*)(track->has_speed() ?
288 track->automation->autos[AUTOMATION_SPEED] : 0);
289 int64_t end = vnext->startproject + vnext->transition->length;
291 int count = renderengine->preferences->cache_size /
292 input->get_data_size() / 2; // try to burn only 1/2 of cache
293 while( !result && pos < end && count > 0 ) {
294 int64_t curr_pos = pos - current_edit->startproject;
295 if( curr_pos > 0 && speed_autos )
296 curr_pos = speed_autos->automation_integral(
297 current_edit->startproject, curr_pos, PLAY_FORWARD);
298 curr_pos += current_edit->startsource;
299 int64_t norm_pos = Units::to_int64((double)curr_pos *
300 current_edit->asset->frame_rate / edl_rate);
301 if( norm_pos < 0 || video_length < 0 )
303 else if( norm_pos >= video_length )
304 norm_pos = video_length-1;
306 if( file->get_cache_frame(input, norm_pos) )
307 break; // if inside a cache run
309 file->purge_cache(); // start new run
311 file->set_cache_frames(1);
312 file->set_video_position(norm_pos, 0);
313 result = file->read_frame(input);
319 int64_t normalized_position = Units::to_int64((double)position *
320 current_edit->asset->frame_rate / frame_rate);
321 if( normalized_position < 0 || video_length < 0 )
322 normalized_position = 0;
323 else if( normalized_position >= video_length )
324 normalized_position = video_length-1;
325 //printf("VModule::import_frame %d %lld %lld\n", __LINE__, position, normalized_position);
326 file->set_layer(current_edit->channel);
327 file->set_video_position(normalized_position, 0);
330 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
331 // Get source position in nested frame rate in direction of playback.
332 nested_position = Units::to_int64(position *
333 nested_edl->session->frame_rate /
335 if( direction == PLAY_REVERSE )
340 // Auto scale if required
341 if( output->get_params()->get("AUTOSCALE", 0) ) {
342 float autoscale_w = output->get_params()->get("AUTOSCALE_W", 1024);
343 float autoscale_h = output->get_params()->get("AUTOSCALE_H", 1024);
344 float x_scale = autoscale_w / asset_w;
345 float y_scale = autoscale_h / asset_h;
353 if( x_scale < y_scale ) {
354 out_w = in_w * x_scale;
355 out_h = in_h * x_scale;
358 out_w = in_w * y_scale;
359 out_h = in_h * y_scale;
362 out_x = track->track_w / 2 - out_w / 2;
363 out_y = track->track_h / 2 - out_h / 2;
367 ((VTrack*)track)->calculate_input_transfer(asset_w,
368 asset_h, input_position_project, direction,
369 in_x, in_y, in_w, in_h,
370 out_x, out_y, out_w, out_h);
373 // printf("VModule::import_frame %d %f %d %f %d\n",
374 // __LINE__, in_w, asset_w, in_h, asset_h);
376 // file -> temp -> output
377 if( !EQUIV(in_x, 0) ||
379 !EQUIV(in_w, track->track_w) ||
380 !EQUIV(in_h, track->track_h) ||
383 !EQUIV(out_w, track->track_w) ||
384 !EQUIV(out_h, track->track_h) ||
385 !EQUIV(in_w, asset_w) ||
386 !EQUIV(in_h, asset_h)) {
387 //printf("VModule::import_frame %d file -> temp -> output\n", __LINE__);
388 // Get temporary input buffer
389 VFrame **input = commonrender ? // Realtime playback
390 &((VRender*)commonrender)->input_temp :
391 &input_temp ; // Menu effect
392 VFrame::get_temp(*input, asset_w, asset_h,
393 get_edl()->session->color_model);
394 (*input)->copy_stacks(output);
396 // Cache for single frame, reverse playback
398 if( debug ) printf("VModule::import_frame %d this=%p file=%s\n",
401 current_edit->asset->path);
403 file->set_cache_frames(use_cache);
404 result = file->read_frame((*input));
406 file->set_cache_frames(0);
407 (*input)->set_opengl_state(VFrame::RAM);
411 // If the colormodels differ, change input to nested colormodel
412 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
413 int current_cmodel = output->get_color_model();
414 int output_w = output->get_w();
415 int output_h = output->get_h();
416 VFrame *input2 = (*input);
418 if( nested_cmodel != current_cmodel ) {
419 // If opengl, input -> input -> output
422 // If software, input2 -> input -> output
423 // Use output as a temporary.
427 if( debug ) printf("VModule::import_frame %d this=%p nested_cmodel=%d\n",
432 input2->reallocate(0, -1, 0, 0, 0,
433 (*input)->get_w(), (*input)->get_h(),
439 if( debug ) printf("VModule::import_frame %d this=%p nested_edl=%s input2=%p\n",
440 __LINE__, this, nested_edl->path, input2);
442 result = nested_renderengine->vrender->process_buffer(
443 input2, nested_position, use_opengl);
445 if( debug ) printf("VModule::import_frame %d this=%p nested_edl=%s\n",
450 if( nested_cmodel != current_cmodel ) {
451 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
453 // Change colormodel in hardware.
454 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
455 x11_device->convert_cmodel(input2,
458 // The converted color model is now in hardware, so return the input2 buffer
459 // to the expected color model.
460 input2->reallocate(0, -1, 0, 0, 0,
461 (*input)->get_w(), (*input)->get_h(),
465 // Transfer from input2 to input
466 if( debug ) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d input2=%p input=%p output=%p\n",
467 __LINE__, nested_cmodel, current_cmodel, input2, (*input), output);
468 BC_CModels::transfer((*input)->get_rows(), input2->get_rows(),
470 0, 0, input2->get_w(), input2->get_h(),
471 0, 0, (*input)->get_w(), (*input)->get_h(),
472 nested_cmodel, current_cmodel, 0,
473 input2->get_w(), (*input)->get_w());
474 //printf("VModule::import_frame %d\n", __LINE__);
476 // input2 was the output buffer, so it must be restored
477 input2->reallocate(0, -1, 0, 0, 0,
478 output_w, output_h, current_cmodel, -1);
479 //printf("VModule::import_frame %d\n", __LINE__);
485 // Find an overlayer object to perform the camera transformation
486 OverlayFrame *overlayer = 0;
488 // OpenGL playback uses hardware
490 //printf("VModule::import_frame %d\n", __LINE__);
492 else if( commonrender ) {
494 VRender *vrender = (VRender*)commonrender;
495 overlayer = vrender->overlayer;
500 printf("VModule::import_frame neither plugin_array nor commonrender is defined.\n");
501 if( !overlay_temp ) {
502 overlay_temp = new OverlayFrame(plugin_array->mwindow->preferences->processors);
505 overlayer = overlay_temp;
507 // printf("VModule::import_frame 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
508 // in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h);
511 // for( int j = 0; j < output->get_w() * 3 * 5; j++ )
512 // output->get_rows()[0][j] = 255;
515 x11_device->do_camera(output, (*input),
516 in_x, in_y, in_x + in_w, in_y + in_h,
517 out_x, out_y, out_x + out_w, out_y + out_h);
518 if( debug ) printf("VModule::import_frame %d %d %d\n",
519 __LINE__, output->get_opengl_state(), (*input)->get_opengl_state());
522 output->clear_frame();
523 // get_cache()->check_in(current_edit->asset);
526 // TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage
527 // of producing green borders in floating point translation of YUV
528 int mode = TRANSFER_REPLACE;
529 if( get_edl()->session->interpolation_type != NEAREST_NEIGHBOR &&
530 BC_CModels::is_yuv(output->get_color_model()) )
531 mode = TRANSFER_NORMAL;
533 if( debug ) printf("VModule::import_frame %d temp -> output\n", __LINE__);
534 overlayer->overlay(output, (*input),
535 in_x, in_y, in_x + in_w, in_y + in_h,
536 out_x, out_y, out_x + out_w, out_y + out_h,
537 1, mode, get_edl()->session->interpolation_type);
541 output->copy_stacks((*input));
544 //printf("VModule::import_frame %d\n", __LINE__);
545 //(*input)->dump_params();
546 //output->dump_params();
550 if( debug ) printf("VModule::import_frame %d file -> output nested_edl=%p file=%p\n",
551 __LINE__, nested_edl, file);
553 VFrame **input = &output;
554 // If colormodels differ, reallocate output in nested colormodel.
555 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
556 int current_cmodel = output->get_color_model();
558 if( debug ) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d\n",
563 if( nested_cmodel != current_cmodel ) {
567 input = &((VRender*)commonrender)->input_temp;
573 if( !(*input) ) (*input) = new VFrame;
576 (*input)->reallocate(0, -1, 0, 0, 0,
577 output->get_w(), output->get_h(),
579 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
581 //(*input)->clear_frame();
584 if( debug ) printf("VModule::import_frame %d %p %p\n",
585 __LINE__, (*input)->get_rows(), (*input));
586 result = nested_renderengine->vrender->process_buffer(
587 (*input), nested_position, use_opengl);
588 if( debug ) printf("VModule::import_frame %d\n",
591 // If colormodels differ, change colormodels in opengl if possible.
592 // Swap output for temp if not possible.
593 if( nested_cmodel != current_cmodel ) {
595 x11_device->convert_cmodel(output,
598 // The color model was changed in place, so return output buffer
599 output->reallocate(0, -1, 0, 0, 0,
600 output->get_w(), output->get_h(),
604 // Transfer from temporary to output
605 if( debug ) printf("VModule::import_frame %d %d %d %d %d %d %d\n",
606 __LINE__, (*input)->get_w(), (*input)->get_h(),
607 output->get_w(), output->get_h(), nested_cmodel, current_cmodel);
608 BC_CModels::transfer(output->get_rows(), (*input)->get_rows(),
610 0, 0, (*input)->get_w(), (*input)->get_h(),
611 0, 0, output->get_w(), output->get_h(),
612 nested_cmodel, current_cmodel, 0,
613 (*input)->get_w(), output->get_w());
619 // Cache single frames
620 //memset(output->get_rows()[0], 0xff, 1024);
622 file->set_cache_frames(use_cache);
623 result = file->read_frame(output);
625 file->set_cache_frames(0);
626 output->set_opengl_state(VFrame::RAM);
631 get_cache()->check_in(current_edit->asset);
640 // printf("VModule::import_frame %d cache=%p\n",
647 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
649 x11_device->clear_input(output);
652 output->clear_frame();
656 if( debug ) printf("VModule::import_frame %d done\n", __LINE__);
662 int VModule::render(VFrame *output,
663 int64_t start_position, int direction, double frame_rate,
664 int use_nudge, int debug_render, int use_opengl)
667 double edl_rate = get_edl()->session->frame_rate;
669 //printf("VModule::render %d %ld\n", __LINE__, start_position);
671 if( use_nudge ) start_position += Units::to_int64(track->nudge *
672 frame_rate / edl_rate);
674 int64_t start_position_project = Units::to_int64(start_position *
675 edl_rate / frame_rate + 0.5);
677 update_transition(start_position_project, direction);
679 VEdit* current_edit = (VEdit*)track->edits->editof(start_position_project,
681 VEdit* previous_edit = 0;
682 //printf("VModule::render %d %p %ld %d\n", __LINE__, current_edit, start_position_project, direction);
684 Plugin *transition = get_edl()->tracks->plugin_exists(transition_id);
686 printf(" VModule::render %d %d %jd %s transition=%p opengl=%d current_edit=%p output=%p\n",
687 __LINE__, use_nudge, start_position_project, track->title,
688 transition, use_opengl, current_edit, output);
690 if( !current_edit ) {
691 output->clear_frame();
692 // We do not apply mask here, since alpha is 0, and neither substracting nor multypling changes it
693 // Another mask mode - "addition" should be added to be able to create mask from empty frames
694 // in this case we would call masking here too...
701 // Process transition
702 if( transition && transition->on ) {
704 // Get temporary buffer
705 VFrame **transition_input = 0;
707 VRender *vrender = (VRender*)commonrender;
708 transition_input = &vrender->transition_temp;
711 transition_input = &transition_temp;
714 if( (*transition_input) &&
715 ((*transition_input)->get_w() != track->track_w ||
716 (*transition_input)->get_h() != track->track_h) ) {
717 delete (*transition_input);
718 (*transition_input) = 0;
721 // Load incoming frame
722 if( !(*transition_input) ) {
723 (*transition_input) =
724 new VFrame(track->track_w, track->track_h,
725 get_edl()->session->color_model);
728 (*transition_input)->copy_stacks(output);
730 //printf("VModule::render %d\n", __LINE__);
731 result = import_frame((*transition_input),
732 current_edit, start_position, frame_rate,
733 direction, use_opengl);
736 // Load transition buffer
737 previous_edit = (VEdit*)current_edit->previous;
739 result |= import_frame(output,
740 previous_edit, start_position, frame_rate,
741 direction, use_opengl);
742 //printf("VModule::render %d %p %p %p %p\n", __LINE__,
743 // (*transition_input), (*transition_input)->get_pbuffer(),
744 // output, output->get_pbuffer());
746 if( transition_server ) {
747 // Execute plugin with transition_input and output here
749 transition_server->set_use_opengl(use_opengl, renderengine->video);
750 transition_server->process_transition((*transition_input), output,
751 (direction == PLAY_FORWARD) ?
752 (start_position_project - current_edit->startproject) :
753 (start_position_project - current_edit->startproject - 1),
757 eprintf("missing transition plugin: %s\n", transition->title);
760 // Load output buffer
761 result = import_frame(output,
762 current_edit, start_position, frame_rate,
763 direction, use_opengl);
767 MaskAutos *keyframe_set =
768 (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
769 int64_t mask_position = !renderengine ? start_position :
770 renderengine->vrender->current_position;
772 (MaskAuto*)keyframe_set->get_prev_auto(mask_position, direction, current);
774 if( keyframe->apply_before_plugins && keyframe->has_active_mask() ) {
775 VDeviceX11 *x11_device = 0;
776 if( use_opengl && renderengine && renderengine->video ) {
777 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
778 if( !x11_device->can_mask(mask_position, keyframe_set) ) {
779 if( output->get_opengl_state() != VFrame::RAM ) {
780 int w = output->get_w(), h = output->get_h();
781 x11_device->do_camera(output, output,
782 0,0,w,h, 0,0,w,h); // copy to ram
787 if( use_opengl && x11_device ) {
788 x11_device->do_mask(output, mask_position, keyframe_set,
793 int cpus = renderengine ?
794 renderengine->preferences->processors :
795 plugin_array->mwindow->preferences->processors;
796 masker = new MaskEngine(cpus);
798 masker->do_mask(output, mask_position, keyframe_set, keyframe, keyframe);
806 void VModule::create_objects()
808 Module::create_objects();