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();
104 int VModule::import_frame(VFrame *output, VEdit *current_edit,
105 int64_t input_position, double frame_rate, int direction, int use_opengl)
107 int64_t direction_position;
108 // Translation of edit
109 float in_x, in_y, in_w, in_h;
110 float out_x, out_y, out_w, out_h;
113 double edl_rate = get_edl()->session->frame_rate;
115 int64_t input_position_project = Units::to_int64(input_position *
116 edl_rate / frame_rate + 0.001);
118 if( !output ) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
119 //output->dump_params();
121 if( debug ) printf("VModule::import_frame %d this=%p input_position=%lld direction=%d\n",
122 __LINE__, this, (long long)input_position, direction);
124 // Convert to position corrected for direction
125 direction_position = input_position;
126 if( direction == PLAY_REVERSE ) {
127 if( direction_position > 0 ) direction_position--;
128 if( input_position_project > 0 ) input_position_project--;
130 if( !output ) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
132 VDeviceX11 *x11_device = 0;
134 if( renderengine && renderengine->video ) {
135 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
136 output->set_opengl_state(VFrame::RAM);
137 if( !x11_device ) use_opengl = 0;
141 if( !output ) printf("VModule::import_frame %d output=%p x11_device=%p nested_edl=%p\n",
142 __LINE__, output, x11_device, nested_edl);
144 if( debug ) printf("VModule::import_frame %d current_edit=%p\n",
145 __LINE__, current_edit);
147 // Load frame into output
149 // Create objects for nested EDL
150 if( current_edit && current_edit->nested_edl ) {
152 if( debug ) printf("VModule::import_frame %d nested_edl=%p current_edit->nested_edl=%p\n",
153 __LINE__, nested_edl, current_edit->nested_edl);
155 // Convert requested direction to command
156 if( renderengine->command->command == CURRENT_FRAME ||
157 renderengine->command->command == LAST_FRAME ) {
158 command = renderengine->command->command;
161 if( direction == PLAY_REVERSE ) {
162 if( renderengine->command->single_frame() )
163 command = SINGLE_FRAME_REWIND;
165 command = NORMAL_REWIND;
168 if( renderengine->command->single_frame() )
169 command = SINGLE_FRAME_FWD;
171 command = NORMAL_FWD;
174 if( !nested_edl || nested_edl->id != current_edit->nested_edl->id ) {
175 nested_edl = current_edit->nested_edl;
176 if( nested_renderengine ) {
177 delete nested_renderengine;
178 nested_renderengine = 0;
181 if( !nested_command ) {
182 nested_command = new TransportCommand;
186 if( !nested_renderengine ) {
187 nested_command->command = command;
188 nested_command->get_edl()->copy_all(nested_edl);
189 nested_command->change_type = CHANGE_ALL;
190 nested_command->realtime = renderengine->command->realtime;
191 nested_renderengine = new RenderEngine(0, get_preferences(), 0, 1);
192 nested_renderengine->set_vcache(get_cache());
193 nested_renderengine->arm_command(nested_command);
198 // Update nested command
199 nested_renderengine->command->command = command;
200 nested_command->realtime = renderengine->command->realtime;
203 // Update nested video driver for opengl
204 nested_renderengine->video = renderengine->video;
209 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
213 x11_device->clear_input(output);
215 output->clear_frame();
218 printf("VModule::import_frame %d output=%p\n", __LINE__, output);
221 (current_edit->asset ||
222 (current_edit->nested_edl && nested_renderengine->vrender)) ) {
225 //printf("VModule::import_frame %d cache=%p\n", __LINE__, get_cache());
226 if( current_edit->asset ) {
228 file = get_cache()->check_out(current_edit->asset,
230 // get_cache()->dump();
234 if( file || nested_edl ) {
235 // Make all positions based on requested frame rate.
236 int64_t edit_startproject = Units::to_int64(current_edit->startproject *
239 int64_t edit_startsource = Units::to_int64(current_edit->startsource *
242 // Source position going forward
243 uint64_t position = direction_position -
246 int64_t nested_position = 0;
252 // apply speed curve to source position so the timeline agrees with the playback
253 if( track->has_speed() ) {
254 // integrate position from start of edit.
255 double speed_position = edit_startsource;
256 FloatAutos *speed_autos = (FloatAutos*)track->automation->autos[AUTOMATION_SPEED];
257 speed_position += speed_autos->automation_integral(edit_startproject,
258 direction_position-edit_startproject, PLAY_FORWARD);
259 //printf("VModule::import_frame %d %lld %lld\n", __LINE__, position, (int64_t)speed_position);
260 position = (int64_t)speed_position;
269 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
272 // maybe apply speed curve here, so timeline reflects actual playback
276 // if we hit the end of stream, freeze at last frame
277 uint64_t max_position = 0;
279 max_position = Units::to_int64((double)file->get_video_length() *
281 current_edit->asset->frame_rate - 1);
284 max_position = Units::to_int64(nested_edl->tracks->total_length() *
289 if( position > max_position ) position = max_position;
291 if( position < 0 ) position = 0;
293 int use_cache = renderengine &&
294 renderengine->command->single_frame();
295 // int use_asynchronous = !use_cache &&
297 // Try to make rendering go faster.
298 // But converts some formats to YUV420, which may degrade input format.
299 //// renderengine->command->realtime &&
300 // renderengine->get_edl()->session->video_asynchronous;
303 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
304 // if( use_asynchronous )
305 // file->start_video_decode_thread();
307 file->stop_video_thread();
309 int64_t normalized_position = Units::to_int64(position *
310 current_edit->asset->frame_rate /
312 // printf("VModule::import_frame %d %lld %lld\n",
315 // normalized_position);
316 file->set_layer(current_edit->channel);
317 file->set_video_position(normalized_position,
319 asset_w = current_edit->asset->width;
320 asset_h = current_edit->asset->height;
321 //printf("VModule::import_frame %d normalized_position=%lld\n", __LINE__, normalized_position);
324 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
325 asset_w = nested_edl->session->output_w;
326 asset_h = nested_edl->session->output_h;
327 // Get source position in nested frame rate in direction of playback.
328 nested_position = Units::to_int64(position *
329 nested_edl->session->frame_rate /
331 if( direction == PLAY_REVERSE )
336 // Auto scale if required
337 if( output->get_params()->get("AUTOSCALE", 0) ) {
338 float autoscale_w = output->get_params()->get("AUTOSCALE_W", 1024);
339 float autoscale_h = output->get_params()->get("AUTOSCALE_H", 1024);
340 float x_scale = autoscale_w / asset_w;
341 float y_scale = autoscale_h / asset_h;
349 if( x_scale < y_scale ) {
350 out_w = in_w * x_scale;
351 out_h = in_h * x_scale;
354 out_w = in_w * y_scale;
355 out_h = in_h * y_scale;
358 out_x = track->track_w / 2 - out_w / 2;
359 out_y = track->track_h / 2 - out_h / 2;
363 ((VTrack*)track)->calculate_input_transfer(asset_w,
364 asset_h, input_position_project, direction,
365 in_x, in_y, in_w, in_h,
366 out_x, out_y, out_w, out_h);
369 // printf("VModule::import_frame %d %f %d %f %d\n",
370 // __LINE__, in_w, asset_w, in_h, asset_h);
372 // printf("VModule::import_frame 1 [ilace] Project: mode (%d) Asset: autofixoption (%d), mode (%d), method (%d)\n",
373 // get_edl()->session->interlace_mode,
374 // current_edit->asset->interlace_autofixoption,
375 // current_edit->asset->interlace_mode,
376 // current_edit->asset->interlace_fixmethod);
378 // Determine the interlacing method to use.
379 int interlace_fixmethod = !current_edit->asset ? ILACE_FIXMETHOD_NONE :
380 ilaceautofixmethod2(get_edl()->session->interlace_mode,
381 current_edit->asset->interlace_autofixoption,
382 current_edit->asset->interlace_mode,
383 current_edit->asset->interlace_fixmethod);
385 // char string[BCTEXTLEN];
386 // ilacefixmethod_to_text(string,interlace_fixmethod);
387 // printf("VModule::import_frame 1 [ilace] Compensating by using: '%s'\n",string);
389 // Compensate for the said interlacing...
390 switch( interlace_fixmethod ) {
391 case ILACE_FIXMETHOD_NONE:
394 case ILACE_FIXMETHOD_UPONE:
397 case ILACE_FIXMETHOD_DOWNONE:
401 printf("vmodule::importframe WARNING - unknown fix method for interlacing, no compensation in effect\n");
404 // file -> temp -> output
405 if( !EQUIV(in_x, 0) ||
407 !EQUIV(in_w, track->track_w) ||
408 !EQUIV(in_h, track->track_h) ||
411 !EQUIV(out_w, track->track_w) ||
412 !EQUIV(out_h, track->track_h) ||
413 !EQUIV(in_w, asset_w) ||
414 !EQUIV(in_h, asset_h)) {
415 //printf("VModule::import_frame %d file -> temp -> output\n", __LINE__);
416 // Get temporary input buffer
420 VRender *vrender = (VRender*)commonrender;
421 //printf("VModule::import_frame %d vrender->input_temp=%p\n", __LINE__, vrender->input_temp);
422 input = &vrender->input_temp;
431 ((*input)->get_w() != asset_w ||
432 (*input)->get_h() != asset_h) ) {
439 new VFrame(asset_w, asset_h,
440 get_edl()->session->color_model);
442 (*input)->copy_stacks(output);
445 // Cache for single frame only
447 if( debug ) printf("VModule::import_frame %d this=%p file=%s\n",
450 current_edit->asset->path);
451 if( use_cache ) file->set_cache_frames(1);
452 result = file->read_frame((*input));
453 if( use_cache ) file->set_cache_frames(0);
454 (*input)->set_opengl_state(VFrame::RAM);
458 // If the colormodels differ, change input to nested colormodel
459 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
460 int current_cmodel = output->get_color_model();
461 int output_w = output->get_w();
462 int output_h = output->get_h();
463 VFrame *input2 = (*input);
465 if( nested_cmodel != current_cmodel ) {
466 // If opengl, input -> input -> output
469 // If software, input2 -> input -> output
470 // Use output as a temporary.
474 if( debug ) printf("VModule::import_frame %d this=%p nested_cmodel=%d\n",
479 input2->reallocate(0, -1, 0, 0, 0,
480 (*input)->get_w(), (*input)->get_h(),
486 if( debug ) printf("VModule::import_frame %d this=%p nested_edl=%s input2=%p\n",
487 __LINE__, this, nested_edl->path, input2);
489 result = nested_renderengine->vrender->process_buffer(
490 input2, nested_position, use_opengl);
492 if( debug ) printf("VModule::import_frame %d this=%p nested_edl=%s\n",
497 if( nested_cmodel != current_cmodel ) {
498 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
500 // Change colormodel in hardware.
501 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
502 x11_device->convert_cmodel(input2,
505 // The converted color model is now in hardware, so return the input2 buffer
506 // to the expected color model.
507 input2->reallocate(0, -1, 0, 0, 0,
508 (*input)->get_w(), (*input)->get_h(),
512 // Transfer from input2 to input
513 if( debug ) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d input2=%p input=%p output=%p\n",
514 __LINE__, nested_cmodel, current_cmodel, input2, (*input), output);
515 BC_CModels::transfer((*input)->get_rows(), input2->get_rows(),
517 0, 0, input2->get_w(), input2->get_h(),
518 0, 0, (*input)->get_w(), (*input)->get_h(),
519 nested_cmodel, current_cmodel, 0,
520 input2->get_w(), (*input)->get_w());
521 //printf("VModule::import_frame %d\n", __LINE__);
523 // input2 was the output buffer, so it must be restored
524 input2->reallocate(0, -1, 0, 0, 0,
525 output_w, output_h, current_cmodel, -1);
526 //printf("VModule::import_frame %d\n", __LINE__);
532 // Find an overlayer object to perform the camera transformation
533 OverlayFrame *overlayer = 0;
535 // OpenGL playback uses hardware
537 //printf("VModule::import_frame %d\n", __LINE__);
539 else if( commonrender ) {
541 VRender *vrender = (VRender*)commonrender;
542 overlayer = vrender->overlayer;
547 printf("VModule::import_frame neither plugin_array nor commonrender is defined.\n");
548 if( !overlay_temp ) {
549 overlay_temp = new OverlayFrame(plugin_array->mwindow->preferences->processors);
552 overlayer = overlay_temp;
554 // printf("VModule::import_frame 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
555 // in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h);
558 // for( int j = 0; j < output->get_w() * 3 * 5; j++ )
559 // output->get_rows()[0][j] = 255;
562 x11_device->do_camera(output, (*input),
563 in_x, in_y, in_x + in_w, in_y + in_h,
564 out_x, out_y, out_x + out_w, out_y + out_h);
565 if( debug ) printf("VModule::import_frame %d %d %d\n",
566 __LINE__, output->get_opengl_state(), (*input)->get_opengl_state());
569 output->clear_frame();
570 // get_cache()->check_in(current_edit->asset);
573 // TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage
574 // of producing green borders in floating point translation of YUV
575 int mode = TRANSFER_REPLACE;
576 if( get_edl()->session->interpolation_type != NEAREST_NEIGHBOR &&
577 BC_CModels::is_yuv(output->get_color_model()) )
578 mode = TRANSFER_NORMAL;
580 if( debug ) printf("VModule::import_frame %d temp -> output\n", __LINE__);
581 overlayer->overlay(output, (*input),
582 in_x, in_y, in_x + in_w, in_y + in_h,
583 out_x, out_y, out_x + out_w, out_y + out_h,
584 1, mode, get_edl()->session->interpolation_type);
588 output->copy_stacks((*input));
591 //printf("VModule::import_frame %d\n", __LINE__);
592 //(*input)->dump_params();
593 //output->dump_params();
597 if( debug ) printf("VModule::import_frame %d file -> output nested_edl=%p file=%p\n",
598 __LINE__, nested_edl, file);
600 VFrame **input = &output;
601 // If colormodels differ, reallocate output in nested colormodel.
602 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
603 int current_cmodel = output->get_color_model();
605 if( debug ) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d\n",
610 if( nested_cmodel != current_cmodel ) {
614 input = &((VRender*)commonrender)->input_temp;
620 if( !(*input) ) (*input) = new VFrame;
623 (*input)->reallocate(0, -1, 0, 0, 0,
624 output->get_w(), output->get_h(),
626 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
628 //(*input)->clear_frame();
631 if( debug ) printf("VModule::import_frame %d %p %p\n",
632 __LINE__, (*input)->get_rows(), (*input));
633 result = nested_renderengine->vrender->process_buffer(
634 (*input), nested_position, use_opengl);
635 if( debug ) printf("VModule::import_frame %d\n",
638 // If colormodels differ, change colormodels in opengl if possible.
639 // Swap output for temp if not possible.
640 if( nested_cmodel != current_cmodel ) {
642 x11_device->convert_cmodel(output,
645 // The color model was changed in place, so return output buffer
646 output->reallocate(0, -1, 0, 0, 0,
647 output->get_w(), output->get_h(),
651 // Transfer from temporary to output
652 if( debug ) printf("VModule::import_frame %d %d %d %d %d %d %d\n",
653 __LINE__, (*input)->get_w(), (*input)->get_h(),
654 output->get_w(), output->get_h(), nested_cmodel, current_cmodel);
655 BC_CModels::transfer(output->get_rows(), (*input)->get_rows(),
657 0, 0, (*input)->get_w(), (*input)->get_h(),
658 0, 0, output->get_w(), output->get_h(),
659 nested_cmodel, current_cmodel, 0,
660 (*input)->get_w(), output->get_w());
666 // Cache single frames
667 //memset(output->get_rows()[0], 0xff, 1024);
668 if( use_cache ) file->set_cache_frames(1);
669 result = file->read_frame(output);
670 if( use_cache ) file->set_cache_frames(0);
671 output->set_opengl_state(VFrame::RAM);
676 get_cache()->check_in(current_edit->asset);
685 // printf("VModule::import_frame %d cache=%p\n",
692 if( debug ) printf("VModule::import_frame %d\n", __LINE__);
694 x11_device->clear_input(output);
697 output->clear_frame();
701 if( debug ) printf("VModule::import_frame %d done\n", __LINE__);
707 int VModule::render(VFrame *output,
708 int64_t start_position, int direction, double frame_rate,
709 int use_nudge, int debug_render, int use_opengl)
712 double edl_rate = get_edl()->session->frame_rate;
714 //printf("VModule::render %d %ld\n", __LINE__, start_position);
716 if( use_nudge ) start_position += Units::to_int64(track->nudge *
717 frame_rate / edl_rate);
719 int64_t start_position_project = Units::to_int64(start_position *
720 edl_rate / frame_rate + 0.5);
722 update_transition(start_position_project, direction);
724 VEdit* current_edit = (VEdit*)track->edits->editof(start_position_project,
726 VEdit* previous_edit = 0;
727 //printf("VModule::render %d %p %ld %d\n", __LINE__, current_edit, start_position_project, direction);
730 printf(" VModule::render %d %d %jd %s transition=%p opengl=%d current_edit=%p output=%p\n",
731 __LINE__, use_nudge, start_position_project, track->title,
732 transition, use_opengl, current_edit, output);
734 if( !current_edit ) {
735 output->clear_frame();
736 // We do not apply mask here, since alpha is 0, and neither substracting nor multypling changes it
737 // Another mask mode - "addition" should be added to be able to create mask from empty frames
738 // in this case we would call masking here too...
745 // Process transition
746 if( transition && transition->on ) {
748 // Get temporary buffer
749 VFrame **transition_input = 0;
751 VRender *vrender = (VRender*)commonrender;
752 transition_input = &vrender->transition_temp;
755 transition_input = &transition_temp;
758 if( (*transition_input) &&
759 ((*transition_input)->get_w() != track->track_w ||
760 (*transition_input)->get_h() != track->track_h) ) {
761 delete (*transition_input);
762 (*transition_input) = 0;
765 // Load incoming frame
766 if( !(*transition_input) ) {
767 (*transition_input) =
768 new VFrame(track->track_w, track->track_h,
769 get_edl()->session->color_model);
772 (*transition_input)->copy_stacks(output);
774 //printf("VModule::render %d\n", __LINE__);
775 result = import_frame((*transition_input),
776 current_edit, start_position, frame_rate,
777 direction, use_opengl);
780 // Load transition buffer
781 previous_edit = (VEdit*)current_edit->previous;
783 result |= import_frame(output,
784 previous_edit, start_position, frame_rate,
785 direction, use_opengl);
786 //printf("VModule::render %d %p %p %p %p\n", __LINE__,
787 // (*transition_input), (*transition_input)->get_pbuffer(),
788 // output, output->get_pbuffer());
790 // Execute plugin with transition_input and output here
792 transition_server->set_use_opengl(use_opengl, renderengine->video);
793 transition_server->process_transition((*transition_input), output,
794 (direction == PLAY_FORWARD) ?
795 (start_position_project - current_edit->startproject) :
796 (start_position_project - current_edit->startproject - 1),
800 // Load output buffer
801 result = import_frame(output,
802 current_edit, start_position, frame_rate,
803 direction, use_opengl);
807 MaskAutos *keyframe_set =
808 (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
809 int64_t mask_position = !renderengine ? start_position :
810 renderengine->vrender->current_position;
812 (MaskAuto*)keyframe_set->get_prev_auto(mask_position, direction, current);
814 if( keyframe->apply_before_plugins ) {
815 VDeviceX11 *x11_device = 0;
816 if( use_opengl && renderengine && renderengine->video ) {
817 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
818 if( !x11_device->can_mask(mask_position, keyframe_set) )
821 if( use_opengl && x11_device ) {
822 x11_device->do_mask(output, mask_position, keyframe_set,
827 int cpus = renderengine ?
828 renderengine->preferences->processors :
829 plugin_array->mwindow->preferences->processors;
830 masker = new MaskEngine(cpus);
832 masker->do_mask(output, mask_position, keyframe_set, keyframe, keyframe);
840 void VModule::create_objects()
842 Module::create_objects();