4 * Copyright (C) 2009-2013 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
24 #include "bcpbuffer.h"
25 #include "bcsignals.h"
28 #include "commonrender.h"
31 #include "edlsession.h"
34 #include "floatautos.h"
36 #include "maskautos.h"
38 #include "overlayframe.h"
40 #include "pluginarray.h"
41 #include "preferences.h"
42 #include "renderengine.h"
43 #include "sharedlocation.h"
45 #include "transition.h"
46 #include "transportque.h"
48 #include "vattachmentpoint.h"
49 #include "vdevicex11.h"
52 #include "videodevice.h"
53 #include "virtualvconsole.h"
59 #include "interlacemodes.h"
60 #include "maskengine.h"
61 #include "automation.h"
63 VModule::VModule(RenderEngine *renderengine,
64 CommonRender *commonrender,
65 PluginArray *plugin_array,
67 : Module(renderengine, commonrender, plugin_array, track)
69 data_type = TRACK_VIDEO;
78 if(overlay_temp) delete overlay_temp;
79 if(input_temp) delete input_temp;
80 if(transition_temp) delete transition_temp;
85 AttachmentPoint* VModule::new_attachment(Plugin *plugin)
87 return new VAttachmentPoint(renderengine, plugin);
90 int VModule::get_buffer_size()
95 CICache* VModule::get_cache()
98 return renderengine->get_vcache();
108 int VModule::import_frame(VFrame *output, VEdit *current_edit,
109 int64_t input_position, double frame_rate, int direction, int use_opengl)
111 int64_t direction_position;
112 // Translation of edit
113 float in_x, in_y, in_w, in_h;
114 float out_x, out_y, out_w, out_h;
117 double edl_rate = get_edl()->session->frame_rate;
119 int64_t input_position_project = Units::to_int64(input_position *
120 edl_rate / frame_rate + 0.001);
122 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
123 //output->dump_params();
125 if(debug) printf("VModule::import_frame %d this=%p input_position=%lld direction=%d\n",
126 __LINE__, this, (long long)input_position, direction);
128 // Convert to position corrected for direction
129 direction_position = input_position;
130 if(direction == PLAY_REVERSE) {
131 if( direction_position > 0 ) direction_position--;
132 if( input_position_project > 0 ) input_position_project--;
134 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
136 VDeviceX11 *x11_device = 0;
139 if(renderengine && renderengine->video)
141 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
142 output->set_opengl_state(VFrame::RAM);
143 if(!x11_device) use_opengl = 0;
147 if(!output) printf("VModule::import_frame %d output=%p x11_device=%p nested_edl=%p\n",
148 __LINE__, output, x11_device, nested_edl);
150 if(debug) printf("VModule::import_frame %d current_edit=%p\n",
151 __LINE__, current_edit);
153 // Load frame into output
155 // Create objects for nested EDL
156 if(current_edit && current_edit->nested_edl) {
158 if(debug) printf("VModule::import_frame %d nested_edl=%p current_edit->nested_edl=%p\n",
159 __LINE__, nested_edl, current_edit->nested_edl);
161 // Convert requested direction to command
162 if( renderengine->command->command == CURRENT_FRAME ||
163 renderengine->command->command == LAST_FRAME )
165 command = renderengine->command->command;
168 if(direction == PLAY_REVERSE)
170 if(renderengine->command->single_frame())
171 command = SINGLE_FRAME_REWIND;
173 command = NORMAL_REWIND;
177 if(renderengine->command->single_frame())
178 command = SINGLE_FRAME_FWD;
180 command = NORMAL_FWD;
183 if(!nested_edl || nested_edl->id != current_edit->nested_edl->id)
185 nested_edl = current_edit->nested_edl;
186 if(nested_renderengine)
188 delete nested_renderengine;
189 nested_renderengine = 0;
194 nested_command = new TransportCommand;
198 if(!nested_renderengine)
200 nested_command->command = command;
201 nested_command->get_edl()->copy_all(nested_edl);
202 nested_command->change_type = CHANGE_ALL;
203 nested_command->realtime = renderengine->command->realtime;
204 nested_renderengine = new RenderEngine(0, get_preferences(), 0, 1);
205 nested_renderengine->set_vcache(get_cache());
206 nested_renderengine->arm_command(nested_command);
212 // Update nested command
213 nested_renderengine->command->command = command;
214 nested_command->realtime = renderengine->command->realtime;
217 // Update nested video driver for opengl
218 nested_renderengine->video = renderengine->video;
224 if(debug) printf("VModule::import_frame %d\n", __LINE__);
226 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
229 (current_edit->asset ||
230 (current_edit->nested_edl && nested_renderengine->vrender)))
234 //printf("VModule::import_frame %d cache=%p\n", __LINE__, get_cache());
235 if( current_edit->asset ) {
237 file = get_cache()->check_out(current_edit->asset,
239 // get_cache()->dump();
243 if(file || nested_edl)
245 // Make all positions based on requested frame rate.
246 int64_t edit_startproject = Units::to_int64(current_edit->startproject *
249 int64_t edit_startsource = Units::to_int64(current_edit->startsource *
252 // Source position going forward
253 uint64_t position = direction_position -
256 int64_t nested_position = 0;
262 // apply speed curve to source position so the timeline agrees with the playback
263 if(track->has_speed())
265 // integrate position from start of edit.
266 double speed_position = edit_startsource;
267 FloatAutos *speed_autos = (FloatAutos*)track->automation->autos[AUTOMATION_SPEED];
268 speed_position += speed_autos->automation_integral(edit_startproject,
269 direction_position-edit_startproject, PLAY_FORWARD);
270 //printf("VModule::import_frame %d %lld %lld\n", __LINE__, position, (int64_t)speed_position);
271 position = (int64_t)speed_position;
280 if(debug) printf("VModule::import_frame %d\n", __LINE__);
283 // maybe apply speed curve here, so timeline reflects actual playback
287 // if we hit the end of stream, freeze at last frame
288 uint64_t max_position = 0;
291 max_position = Units::to_int64((double)file->get_video_length() *
293 current_edit->asset->frame_rate - 1);
297 max_position = Units::to_int64(nested_edl->tracks->total_length() *
302 if(position > max_position) position = max_position;
304 if(position < 0) position = 0;
306 int use_cache = renderengine &&
307 renderengine->command->single_frame();
308 // int use_asynchronous = !use_cache &&
310 // Try to make rendering go faster.
311 // But converts some formats to YUV420, which may degrade input format.
312 //// renderengine->command->realtime &&
313 // renderengine->get_edl()->session->video_asynchronous;
317 if(debug) printf("VModule::import_frame %d\n", __LINE__);
318 // if(use_asynchronous)
319 // file->start_video_decode_thread();
321 file->stop_video_thread();
323 int64_t normalized_position = Units::to_int64(position *
324 current_edit->asset->frame_rate /
326 // printf("VModule::import_frame %d %lld %lld\n",
329 // normalized_position);
330 file->set_layer(current_edit->channel);
331 file->set_video_position(normalized_position,
333 asset_w = current_edit->asset->width;
334 asset_h = current_edit->asset->height;
335 //printf("VModule::import_frame %d normalized_position=%lld\n", __LINE__, normalized_position);
339 if(debug) printf("VModule::import_frame %d\n", __LINE__);
340 asset_w = nested_edl->session->output_w;
341 asset_h = nested_edl->session->output_h;
342 // Get source position in nested frame rate in direction of playback.
343 nested_position = Units::to_int64(position *
344 nested_edl->session->frame_rate /
346 if(direction == PLAY_REVERSE)
351 // Auto scale if required
352 if(output->get_params()->get("AUTOSCALE", 0))
354 float autoscale_w = output->get_params()->get("AUTOSCALE_W", 1024);
355 float autoscale_h = output->get_params()->get("AUTOSCALE_H", 1024);
356 float x_scale = autoscale_w / asset_w;
357 float y_scale = autoscale_h / asset_h;
365 if(x_scale < y_scale)
367 out_w = in_w * x_scale;
368 out_h = in_h * x_scale;
372 out_w = in_w * y_scale;
373 out_h = in_h * y_scale;
376 out_x = track->track_w / 2 - out_w / 2;
377 out_y = track->track_h / 2 - out_h / 2;
382 ((VTrack*)track)->calculate_input_transfer(asset_w,
384 input_position_project,
396 // printf("VModule::import_frame %d %f %d %f %d\n",
403 // printf("VModule::import_frame 1 [ilace] Project: mode (%d) Asset: autofixoption (%d), mode (%d), method (%d)\n",
404 // get_edl()->session->interlace_mode,
405 // current_edit->asset->interlace_autofixoption,
406 // current_edit->asset->interlace_mode,
407 // current_edit->asset->interlace_fixmethod);
409 // Determine the interlacing method to use.
410 int interlace_fixmethod = !current_edit->asset ? ILACE_FIXMETHOD_NONE :
411 ilaceautofixmethod2(get_edl()->session->interlace_mode,
412 current_edit->asset->interlace_autofixoption,
413 current_edit->asset->interlace_mode,
414 current_edit->asset->interlace_fixmethod);
416 // char string[BCTEXTLEN];
417 // ilacefixmethod_to_text(string,interlace_fixmethod);
418 // printf("VModule::import_frame 1 [ilace] Compensating by using: '%s'\n",string);
420 // Compensate for the said interlacing...
421 switch (interlace_fixmethod) {
422 case ILACE_FIXMETHOD_NONE:
425 case ILACE_FIXMETHOD_UPONE:
428 case ILACE_FIXMETHOD_DOWNONE:
432 printf("vmodule::importframe WARNING - unknown fix method for interlacing, no compensation in effect\n");
435 // file -> temp -> output
436 if( !EQUIV(in_x, 0) ||
438 !EQUIV(in_w, track->track_w) ||
439 !EQUIV(in_h, track->track_h) ||
442 !EQUIV(out_w, track->track_w) ||
443 !EQUIV(out_h, track->track_h) ||
444 !EQUIV(in_w, asset_w) ||
445 !EQUIV(in_h, asset_h))
447 //printf("VModule::import_frame %d file -> temp -> output\n", __LINE__);
452 // Get temporary input buffer
457 VRender *vrender = (VRender*)commonrender;
458 //printf("VModule::import_frame %d vrender->input_temp=%p\n", __LINE__, vrender->input_temp);
459 input = &vrender->input_temp;
469 ((*input)->get_w() != asset_w ||
470 (*input)->get_h() != asset_h))
483 new VFrame(asset_w, asset_h,
484 get_edl()->session->color_model);
489 (*input)->copy_stacks(output);
492 // Cache for single frame only
495 if(debug) printf("VModule::import_frame %d this=%p file=%s\n",
498 current_edit->asset->path);
499 if(use_cache) file->set_cache_frames(1);
500 result = file->read_frame((*input));
501 if(use_cache) file->set_cache_frames(0);
502 (*input)->set_opengl_state(VFrame::RAM);
507 // If the colormodels differ, change input to nested colormodel
508 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
509 int current_cmodel = output->get_color_model();
510 int output_w = output->get_w();
511 int output_h = output->get_h();
512 VFrame *input2 = (*input);
514 if(nested_cmodel != current_cmodel)
516 // If opengl, input -> input -> output
522 // If software, input2 -> input -> output
523 // Use output as a temporary.
527 if(debug) printf("VModule::import_frame %d this=%p nested_cmodel=%d\n",
532 input2->reallocate(0,
545 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s input2=%p\n",
551 result = nested_renderengine->vrender->process_buffer(
556 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s\n",
561 if(nested_cmodel != current_cmodel)
563 if(debug) printf("VModule::import_frame %d\n", __LINE__);
566 // Change colormodel in hardware.
567 if(debug) printf("VModule::import_frame %d\n", __LINE__);
568 x11_device->convert_cmodel(input2,
571 // The converted color model is now in hardware, so return the input2 buffer
572 // to the expected color model.
573 input2->reallocate(0,
585 // Transfer from input2 to input
586 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d input2=%p input=%p output=%p\n",
593 BC_CModels::transfer((*input)->get_rows(),
614 //printf("VModule::import_frame %d\n", __LINE__);
616 // input2 was the output buffer, so it must be restored
617 input2->reallocate(0,
626 //printf("VModule::import_frame %d\n", __LINE__);
632 // Find an overlayer object to perform the camera transformation
633 OverlayFrame *overlayer = 0;
635 // OpenGL playback uses hardware
638 //printf("VModule::import_frame %d\n", __LINE__);
644 VRender *vrender = (VRender*)commonrender;
645 overlayer = vrender->overlayer;
651 printf("VModule::import_frame neither plugin_array nor commonrender is defined.\n");
654 overlay_temp = new OverlayFrame(plugin_array->mwindow->preferences->processors);
657 overlayer = overlay_temp;
659 // printf("VModule::import_frame 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
670 // for(int j = 0; j < output->get_w() * 3 * 5; j++)
671 // output->get_rows()[0][j] = 255;
675 x11_device->do_camera(output,
685 if(debug) printf("VModule::import_frame %d %d %d\n",
687 output->get_opengl_state(),
688 (*input)->get_opengl_state());
695 output->clear_frame();
698 // get_cache()->check_in(current_edit->asset);
701 // TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage
702 // of producing green borders in floating point translation of YUV
703 int mode = TRANSFER_REPLACE;
704 if(get_edl()->session->interpolation_type != NEAREST_NEIGHBOR &&
705 BC_CModels::is_yuv(output->get_color_model()))
706 mode = TRANSFER_NORMAL;
708 if(debug) printf("VModule::import_frame %d temp -> output\n", __LINE__);
709 overlayer->overlay(output,
721 get_edl()->session->interpolation_type);
725 output->copy_stacks((*input));
728 //printf("VModule::import_frame %d\n", __LINE__);
729 //(*input)->dump_params();
730 //output->dump_params();
735 if(debug) printf("VModule::import_frame %d file -> output nested_edl=%p file=%p\n",
741 VFrame **input = &output;
742 // If colormodels differ, reallocate output in nested colormodel.
743 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
744 int current_cmodel = output->get_color_model();
746 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d\n",
751 if(nested_cmodel != current_cmodel)
760 input = &((VRender*)commonrender)->input_temp;
767 if(!(*input)) (*input) = new VFrame;
770 (*input)->reallocate(0,
779 if(debug) printf("VModule::import_frame %d\n",
782 //(*input)->clear_frame();
785 if(debug) printf("VModule::import_frame %d %p %p\n",
787 (*input)->get_rows(),
789 result = nested_renderengine->vrender->process_buffer(
793 if(debug) printf("VModule::import_frame %d\n",
796 // If colormodels differ, change colormodels in opengl if possible.
797 // Swap output for temp if not possible.
798 if(nested_cmodel != current_cmodel)
802 x11_device->convert_cmodel(output,
805 // The color model was changed in place, so return output buffer
806 output->reallocate(0,
818 // Transfer from temporary to output
819 if(debug) printf("VModule::import_frame %d %d %d %d %d %d %d\n",
827 BC_CModels::transfer(output->get_rows(),
828 (*input)->get_rows(),
856 // Cache single frames
857 //memset(output->get_rows()[0], 0xff, 1024);
858 if(use_cache) file->set_cache_frames(1);
859 result = file->read_frame(output);
860 if(use_cache) file->set_cache_frames(0);
861 output->set_opengl_state(VFrame::RAM);
867 get_cache()->check_in(current_edit->asset);
874 if(debug) printf("VModule::import_frame %d\n", __LINE__);
877 x11_device->clear_input(output);
881 output->clear_frame();
886 // printf("VModule::import_frame %d cache=%p\n",
894 if(debug) printf("VModule::import_frame %d\n", __LINE__);
897 x11_device->clear_input(output);
901 output->clear_frame();
905 if(debug) printf("VModule::import_frame %d done\n", __LINE__);
912 int VModule::render(VFrame *output,
913 int64_t start_position,
921 double edl_rate = get_edl()->session->frame_rate;
923 //printf("VModule::render %d %ld\n", __LINE__, start_position);
925 if(use_nudge) start_position += Units::to_int64(track->nudge *
926 frame_rate / edl_rate);
928 int64_t start_position_project = Units::to_int64(start_position *
929 edl_rate / frame_rate + 0.5);
931 update_transition(start_position_project,
934 VEdit* current_edit = (VEdit*)track->edits->editof(start_position_project,
937 VEdit* previous_edit = 0;
938 //printf("VModule::render %d %p %ld %d\n", __LINE__, current_edit, start_position_project, direction);
941 printf(" VModule::render %d %d %jd %s transition=%p opengl=%d current_edit=%p output=%p\n",
944 start_position_project,
953 output->clear_frame();
954 // We do not apply mask here, since alpha is 0, and neither substracting nor multypling changes it
955 // Another mask mode - "addition" should be added to be able to create mask from empty frames
956 // in this case we would call masking here too...
963 // Process transition
964 if(transition && transition->on)
967 // Get temporary buffer
968 VFrame **transition_input = 0;
971 VRender *vrender = (VRender*)commonrender;
972 transition_input = &vrender->transition_temp;
976 transition_input = &transition_temp;
979 if((*transition_input) &&
980 ((*transition_input)->get_w() != track->track_w ||
981 (*transition_input)->get_h() != track->track_h))
983 delete (*transition_input);
984 (*transition_input) = 0;
987 // Load incoming frame
988 if(!(*transition_input))
990 (*transition_input) =
991 new VFrame(track->track_w, track->track_h,
992 get_edl()->session->color_model);
995 (*transition_input)->copy_stacks(output);
997 //printf("VModule::render %d\n", __LINE__);
998 result = import_frame((*transition_input),
1006 // Load transition buffer
1007 previous_edit = (VEdit*)current_edit->previous;
1009 result |= import_frame(output,
1015 //printf("VModule::render %d\n", __LINE__);
1017 // printf("VModule::render %d %p %p %p %p\n",
1019 // (*transition_input),
1020 // (*transition_input)->get_pbuffer(),
1022 // output->get_pbuffer());
1025 // Execute plugin with transition_input and output here
1027 transition_server->set_use_opengl(use_opengl, renderengine->video);
1028 transition_server->process_transition((*transition_input),
1030 (direction == PLAY_FORWARD) ?
1031 (start_position_project - current_edit->startproject) :
1032 (start_position_project - current_edit->startproject - 1),
1033 transition->length);
1037 // Load output buffer
1038 result = import_frame(output,
1047 MaskAutos *keyframe_set =
1048 (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
1049 int64_t mask_position = !renderengine ? start_position :
1050 renderengine->vrender->current_position;
1051 MaskAuto *keyframe =
1052 (MaskAuto*)keyframe_set->get_prev_auto(mask_position, direction, current);
1054 if( keyframe->apply_before_plugins ) {
1055 VDeviceX11 *x11_device = 0;
1056 if(use_opengl && renderengine && renderengine->video) {
1057 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
1058 if( !x11_device->can_mask(mask_position, keyframe_set) )
1061 if( use_opengl && x11_device ) {
1062 x11_device->do_mask(output, mask_position, keyframe_set,
1063 keyframe, keyframe);
1067 int cpus = renderengine ?
1068 renderengine->preferences->processors :
1069 plugin_array->mwindow->preferences->processors;
1070 masker = new MaskEngine(cpus);
1072 masker->do_mask(output, mask_position, keyframe_set, keyframe, keyframe);
1084 void VModule::create_objects()
1086 Module::create_objects();