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"
58 #include "interlacemodes.h"
59 #include "maskengine.h"
60 #include "automation.h"
62 VModule::VModule(RenderEngine *renderengine,
63 CommonRender *commonrender,
64 PluginArray *plugin_array,
66 : Module(renderengine, commonrender, plugin_array, track)
68 data_type = TRACK_VIDEO;
77 if(overlay_temp) delete overlay_temp;
78 if(input_temp) delete input_temp;
79 if(transition_temp) delete transition_temp;
84 AttachmentPoint* VModule::new_attachment(Plugin *plugin)
86 return new VAttachmentPoint(renderengine, plugin);
89 int VModule::get_buffer_size()
94 CICache* VModule::get_cache()
97 return renderengine->get_vcache();
107 int VModule::import_frame(VFrame *output,
109 int64_t input_position,
114 int64_t direction_position;
115 // Translation of edit
126 double edl_rate = get_edl()->session->frame_rate;
128 int64_t input_position_project = Units::to_int64(input_position *
139 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
140 //output->dump_params();
143 if(debug) printf("VModule::import_frame %d this=%p input_position=%lld direction=%d\n",
146 (long long)input_position,
149 // Convert to position corrected for direction
150 direction_position = input_position;
151 if(direction == PLAY_REVERSE)
153 direction_position--;
154 input_position_project--;
156 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
158 VDeviceX11 *x11_device = 0;
161 if(renderengine && renderengine->video)
163 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
164 output->set_opengl_state(VFrame::RAM);
165 if(!x11_device) use_opengl = 0;
169 if(!output) printf("VModule::import_frame %d output=%p x11_device=%p nested_edl=%p\n",
176 if(debug) printf("VModule::import_frame %d current_edit=%p\n",
181 // Load frame into output
183 // Create objects for nested EDL
185 current_edit->nested_edl)
188 if(debug) printf("VModule::import_frame %d nested_edl=%p current_edit->nested_edl=%p\n",
191 current_edit->nested_edl);
193 // Convert requested direction to command
194 if(renderengine->command->command == CURRENT_FRAME)
196 command = CURRENT_FRAME;
199 if(direction == PLAY_REVERSE)
201 if(renderengine->command->single_frame())
202 command = SINGLE_FRAME_REWIND;
204 command = NORMAL_REWIND;
208 if(renderengine->command->single_frame())
209 command = SINGLE_FRAME_FWD;
211 command = NORMAL_FWD;
214 if(!nested_edl || nested_edl->id != current_edit->nested_edl->id)
216 nested_edl = current_edit->nested_edl;
217 if(nested_renderengine)
219 delete nested_renderengine;
220 nested_renderengine = 0;
225 nested_command = new TransportCommand;
229 if(!nested_renderengine)
231 nested_command->command = command;
232 nested_command->get_edl()->copy_all(nested_edl);
233 nested_command->change_type = CHANGE_ALL;
234 nested_command->realtime = renderengine->command->realtime;
235 nested_renderengine = new RenderEngine(0,
238 renderengine ? renderengine->channeldb : 0,
240 nested_renderengine->set_vcache(get_cache());
241 nested_renderengine->arm_command(nested_command);
247 // Update nested command
248 nested_renderengine->command->command = command;
249 nested_command->realtime = renderengine->command->realtime;
252 // Update nested video driver for opengl
253 nested_renderengine->video = renderengine->video;
259 if(debug) printf("VModule::import_frame %d\n", __LINE__);
261 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
264 (current_edit->asset ||
265 (current_edit->nested_edl && nested_renderengine->vrender)))
269 if(debug) printf("VModule::import_frame %d cache=%p\n",
272 if(current_edit->asset)
275 file = get_cache()->check_out(current_edit->asset,
277 // get_cache()->dump();
281 if(file || nested_edl)
283 // Make all positions based on requested frame rate.
284 int64_t edit_startproject = Units::to_int64(current_edit->startproject *
287 int64_t edit_startsource = Units::to_int64(current_edit->startsource *
290 // Source position going forward
291 uint64_t position = direction_position -
294 int64_t nested_position = 0;
300 // apply speed curve to source position so the timeline agrees with the playback
301 if(track->has_speed())
303 // integrate position from start of edit.
304 double speed_position = edit_startsource;
305 FloatAuto *previous = 0;
307 FloatAutos *speed_autos = (FloatAutos*)track->automation->autos[AUTOMATION_SPEED];
308 for(int64_t i = edit_startproject; i < direction_position; i++)
310 double speed = speed_autos->get_value(i,
314 speed_position += speed;
316 //printf("VModule::import_frame %d %lld %lld\n", __LINE__, position, (int64_t)speed_position);
317 position = (int64_t)speed_position;
326 if(debug) printf("VModule::import_frame %d\n", __LINE__);
329 // maybe apply speed curve here, so timeline reflects actual playback
333 // if we hit the end of stream, freeze at last frame
334 uint64_t max_position = 0;
337 max_position = Units::to_int64((double)file->get_video_length() *
339 current_edit->asset->frame_rate - 1);
343 max_position = Units::to_int64(nested_edl->tracks->total_playable_length() *
348 if(position > max_position) position = max_position;
350 if(position < 0) position = 0;
352 int use_cache = renderengine &&
353 renderengine->command->single_frame();
354 int use_asynchronous = !use_cache && renderengine &&
355 // Try to make rendering go faster.
356 // But converts some formats to YUV420, which may degrade input format.
357 // renderengine->command->realtime &&
358 renderengine->get_edl()->session->video_asynchronous;
362 if(debug) printf("VModule::import_frame %d\n", __LINE__);
364 file->start_video_decode_thread();
366 file->stop_video_thread();
368 int64_t normalized_position = Units::to_int64(position *
369 current_edit->asset->frame_rate /
371 // printf("VModule::import_frame %d %lld %lld\n",
374 // normalized_position);
375 file->set_layer(current_edit->channel);
376 file->set_video_position(normalized_position,
378 asset_w = current_edit->asset->width;
379 asset_h = current_edit->asset->height;
380 //printf("VModule::import_frame %d normalized_position=%lld\n", __LINE__, normalized_position);
384 if(debug) printf("VModule::import_frame %d\n", __LINE__);
385 asset_w = nested_edl->session->output_w;
386 asset_h = nested_edl->session->output_h;
387 // Get source position in nested frame rate in direction of playback.
388 nested_position = Units::to_int64(position *
389 nested_edl->session->frame_rate /
391 if(direction == PLAY_REVERSE)
396 // Auto scale if required
397 if(output->get_params()->get("AUTOSCALE", 0))
399 float autoscale_w = output->get_params()->get("AUTOSCALE_W", 1024);
400 float autoscale_h = output->get_params()->get("AUTOSCALE_H", 1024);
401 float x_scale = autoscale_w / asset_w;
402 float y_scale = autoscale_h / asset_h;
410 if(x_scale < y_scale)
412 out_w = in_w * x_scale;
413 out_h = in_h * x_scale;
417 out_w = in_w * y_scale;
418 out_h = in_h * y_scale;
421 out_x = track->track_w / 2 - out_w / 2;
422 out_y = track->track_h / 2 - out_h / 2;
427 ((VTrack*)track)->calculate_input_transfer(asset_w,
429 input_position_project,
441 // printf("VModule::import_frame %d %f %d %f %d\n",
448 // printf("VModule::import_frame 1 [ilace] Project: mode (%d) Asset: autofixoption (%d), mode (%d), method (%d)\n",
449 // get_edl()->session->interlace_mode,
450 // current_edit->asset->interlace_autofixoption,
451 // current_edit->asset->interlace_mode,
452 // current_edit->asset->interlace_fixmethod);
454 // Determine the interlacing method to use.
455 int interlace_fixmethod = ilaceautofixmethod2(get_edl()->session->interlace_mode,
456 current_edit->asset->interlace_autofixoption,
457 current_edit->asset->interlace_mode,
458 current_edit->asset->interlace_fixmethod);
460 // char string[BCTEXTLEN];
461 // ilacefixmethod_to_text(string,interlace_fixmethod);
462 // printf("VModule::import_frame 1 [ilace] Compensating by using: '%s'\n",string);
464 // Compensate for the said interlacing...
465 switch (interlace_fixmethod) {
466 case BC_ILACE_FIXMETHOD_NONE:
469 case BC_ILACE_FIXMETHOD_UPONE:
472 case BC_ILACE_FIXMETHOD_DOWNONE:
476 printf("vmodule::importframe WARNING - unknown fix method for interlacing, no compensation in effect\n");
479 // file -> temp -> output
480 if( !EQUIV(in_x, 0) ||
482 !EQUIV(in_w, track->track_w) ||
483 !EQUIV(in_h, track->track_h) ||
486 !EQUIV(out_w, track->track_w) ||
487 !EQUIV(out_h, track->track_h) ||
488 !EQUIV(in_w, asset_w) ||
489 !EQUIV(in_h, asset_h))
491 if(debug) printf("VModule::import_frame %d file -> temp -> output\n", __LINE__);
496 // Get temporary input buffer
501 VRender *vrender = (VRender*)commonrender;
502 //printf("VModule::import_frame %d vrender->input_temp=%p\n", __LINE__, vrender->input_temp);
503 input = &vrender->input_temp;
513 ((*input)->get_w() != asset_w ||
514 (*input)->get_h() != asset_h))
526 (*input) = new VFrame(0,
530 get_edl()->session->color_model,
536 (*input)->copy_stacks(output);
539 // Cache for single frame only
542 if(debug) printf("VModule::import_frame %d this=%p file=%s\n",
545 current_edit->asset->path);
546 if(use_cache) file->set_cache_frames(1);
547 result = file->read_frame((*input));
548 if(use_cache) file->set_cache_frames(0);
549 (*input)->set_opengl_state(VFrame::RAM);
554 // If the colormodels differ, change input to nested colormodel
555 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
556 int current_cmodel = output->get_color_model();
557 int output_w = output->get_w();
558 int output_h = output->get_h();
559 VFrame *input2 = (*input);
561 if(nested_cmodel != current_cmodel)
563 // If opengl, input -> input -> output
569 // If software, input2 -> input -> output
570 // Use output as a temporary.
574 if(debug) printf("VModule::import_frame %d this=%p nested_cmodel=%d\n",
579 input2->reallocate(0,
592 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s input2=%p\n",
598 result = nested_renderengine->vrender->process_buffer(
603 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s\n",
608 if(nested_cmodel != current_cmodel)
610 if(debug) printf("VModule::import_frame %d\n", __LINE__);
613 // Change colormodel in hardware.
614 if(debug) printf("VModule::import_frame %d\n", __LINE__);
615 x11_device->convert_cmodel(input2,
618 // The converted color model is now in hardware, so return the input2 buffer
619 // to the expected color model.
620 input2->reallocate(0,
632 // Transfer from input2 to input
633 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d input2=%p input=%p output=%p\n",
640 BC_CModels::transfer((*input)->get_rows(),
661 //printf("VModule::import_frame %d\n", __LINE__);
663 // input2 was the output buffer, so it must be restored
664 input2->reallocate(0,
673 //printf("VModule::import_frame %d\n", __LINE__);
679 // Find an overlayer object to perform the camera transformation
680 OverlayFrame *overlayer = 0;
682 // OpenGL playback uses hardware
685 //printf("VModule::import_frame %d\n", __LINE__);
691 VRender *vrender = (VRender*)commonrender;
692 overlayer = vrender->overlayer;
698 printf("VModule::import_frame neither plugin_array nor commonrender is defined.\n");
701 overlay_temp = new OverlayFrame(plugin_array->mwindow->preferences->processors);
704 overlayer = overlay_temp;
706 // printf("VModule::import_frame 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
717 // for(int j = 0; j < output->get_w() * 3 * 5; j++)
718 // output->get_rows()[0][j] = 255;
722 x11_device->do_camera(output,
732 if(debug) printf("VModule::import_frame %d %d %d\n",
734 output->get_opengl_state(),
735 (*input)->get_opengl_state());
742 output->clear_frame();
745 // get_cache()->check_in(current_edit->asset);
748 // TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage
749 // of producing green borders in floating point translation of YUV
750 int mode = TRANSFER_REPLACE;
751 if(get_edl()->session->interpolation_type != NEAREST_NEIGHBOR &&
752 BC_CModels::is_yuv(output->get_color_model()))
753 mode = TRANSFER_NORMAL;
755 if(debug) printf("VModule::import_frame %d temp -> output\n", __LINE__);
756 overlayer->overlay(output,
768 get_edl()->session->interpolation_type);
771 output->copy_stacks((*input));
776 if(debug) printf("VModule::import_frame %d file -> output nested_edl=%p file=%p\n",
782 VFrame **input = &output;
783 // If colormodels differ, reallocate output in nested colormodel.
784 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
785 int current_cmodel = output->get_color_model();
787 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d\n",
792 if(nested_cmodel != current_cmodel)
801 input = &((VRender*)commonrender)->input_temp;
808 if(!(*input)) (*input) = new VFrame;
811 (*input)->reallocate(0,
820 if(debug) printf("VModule::import_frame %d\n",
823 //(*input)->clear_frame();
826 if(debug) printf("VModule::import_frame %d %p %p\n",
828 (*input)->get_rows(),
830 result = nested_renderengine->vrender->process_buffer(
834 if(debug) printf("VModule::import_frame %d\n",
837 // If colormodels differ, change colormodels in opengl if possible.
838 // Swap output for temp if not possible.
839 if(nested_cmodel != current_cmodel)
843 x11_device->convert_cmodel(output,
846 // The color model was changed in place, so return output buffer
847 output->reallocate(0,
859 // Transfer from temporary to output
860 if(debug) printf("VModule::import_frame %d %d %d %d %d %d %d\n",
868 BC_CModels::transfer(output->get_rows(),
869 (*input)->get_rows(),
897 // Cache single frames
898 //memset(output->get_rows()[0], 0xff, 1024);
899 if(use_cache) file->set_cache_frames(1);
900 result = file->read_frame(output);
901 if(use_cache) file->set_cache_frames(0);
902 output->set_opengl_state(VFrame::RAM);
908 get_cache()->check_in(current_edit->asset);
915 if(debug) printf("VModule::import_frame %d\n", __LINE__);
918 x11_device->clear_input(output);
922 output->clear_frame();
930 if(debug) printf("VModule::import_frame %d\n", __LINE__);
933 x11_device->clear_input(output);
937 output->clear_frame();
941 if(debug) printf("VModule::import_frame %d done\n", __LINE__);
948 int VModule::render(VFrame *output,
949 int64_t start_position,
957 double edl_rate = get_edl()->session->frame_rate;
959 //printf("VModule::render %d %ld\n", __LINE__, start_position);
961 if(use_nudge) start_position += Units::to_int64(track->nudge *
962 frame_rate / edl_rate);
964 int64_t start_position_project = Units::to_int64(start_position *
965 edl_rate / frame_rate + 0.5);
967 update_transition(start_position_project,
970 VEdit* current_edit = (VEdit*)track->edits->editof(start_position_project,
973 VEdit* previous_edit = 0;
974 //printf("VModule::render %d %p %ld %d\n", __LINE__, current_edit, start_position_project, direction);
977 printf(" VModule::render %d %d %ld %s transition=%p opengl=%d current_edit=%p output=%p\n",
980 start_position_project,
989 output->clear_frame();
990 // We do not apply mask here, since alpha is 0, and neither substracting nor multypling changes it
991 // Another mask mode - "addition" should be added to be able to create mask from empty frames
992 // in this case we would call masking here too...
999 // Process transition
1000 if(transition && transition->on)
1003 // Get temporary buffer
1004 VFrame **transition_input = 0;
1007 VRender *vrender = (VRender*)commonrender;
1008 transition_input = &vrender->transition_temp;
1012 transition_input = &transition_temp;
1015 if((*transition_input) &&
1016 ((*transition_input)->get_w() != track->track_w ||
1017 (*transition_input)->get_h() != track->track_h))
1019 delete (*transition_input);
1020 (*transition_input) = 0;
1023 // Load incoming frame
1024 if(!(*transition_input))
1026 (*transition_input) = new VFrame(0,
1030 get_edl()->session->color_model,
1034 (*transition_input)->copy_stacks(output);
1036 //printf("VModule::render %d\n", __LINE__);
1037 result = import_frame((*transition_input),
1045 // Load transition buffer
1046 previous_edit = (VEdit*)current_edit->previous;
1048 result |= import_frame(output,
1054 //printf("VModule::render %d\n", __LINE__);
1056 // printf("VModule::render %d %p %p %p %p\n",
1058 // (*transition_input),
1059 // (*transition_input)->get_pbuffer(),
1061 // output->get_pbuffer());
1064 // Execute plugin with transition_input and output here
1066 transition_server->set_use_opengl(use_opengl, renderengine->video);
1067 transition_server->process_transition((*transition_input),
1069 (direction == PLAY_FORWARD) ?
1070 (start_position_project - current_edit->startproject) :
1071 (start_position_project - current_edit->startproject - 1),
1072 transition->length);
1076 // Load output buffer
1077 result = import_frame(output,
1085 int64_t mask_position = !renderengine ? start_position :
1086 renderengine->vrender->current_position;
1088 MaskAutos *keyframe_set =
1089 (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
1090 MaskAuto *keyframe =
1091 (MaskAuto*)keyframe_set->get_prev_auto(mask_position, direction, current);
1092 if( keyframe->apply_before_plugins ) {
1094 int cpus = renderengine ?
1095 renderengine->preferences->processors :
1096 plugin_array->mwindow->preferences->processors;
1097 masker = new MaskEngine(cpus);
1099 masker->do_mask(output, mask_position, keyframe_set, keyframe, keyframe);
1110 void VModule::create_objects()
1112 Module::create_objects();