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 "overlayframe.h"
38 #include "pluginarray.h"
39 #include "preferences.h"
40 #include "renderengine.h"
41 #include "sharedlocation.h"
43 #include "transition.h"
44 #include "transportque.h"
46 #include "vattachmentpoint.h"
47 #include "vdevicex11.h"
50 #include "videodevice.h"
57 VModule::VModule(RenderEngine *renderengine,
58 CommonRender *commonrender,
59 PluginArray *plugin_array,
61 : Module(renderengine, commonrender, plugin_array, track)
63 data_type = TRACK_VIDEO;
71 if(overlay_temp) delete overlay_temp;
72 if(input_temp) delete input_temp;
73 if(transition_temp) delete transition_temp;
77 AttachmentPoint* VModule::new_attachment(Plugin *plugin)
79 return new VAttachmentPoint(renderengine, plugin);
82 int VModule::get_buffer_size()
87 CICache* VModule::get_cache()
90 return renderengine->get_vcache();
100 int VModule::import_frame(VFrame *output,
102 int64_t input_position,
107 int64_t direction_position;
108 // Translation of edit
119 double edl_rate = get_edl()->session->frame_rate;
121 int64_t input_position_project = Units::to_int64(input_position *
132 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
133 //output->dump_params();
136 if(debug) printf("VModule::import_frame %d this=%p input_position=%lld direction=%d\n",
139 (long long)input_position,
142 // Convert to position corrected for direction
143 direction_position = input_position;
144 if(direction == PLAY_REVERSE)
146 direction_position--;
147 input_position_project--;
149 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
151 VDeviceX11 *x11_device = 0;
154 if(renderengine && renderengine->video)
156 x11_device = (VDeviceX11*)renderengine->video->get_output_base();
157 output->set_opengl_state(VFrame::RAM);
158 if(!x11_device) use_opengl = 0;
162 if(!output) printf("VModule::import_frame %d output=%p x11_device=%p nested_edl=%p\n",
169 if(debug) printf("VModule::import_frame %d current_edit=%p\n",
174 // Load frame into output
176 // Create objects for nested EDL
178 current_edit->nested_edl)
181 if(debug) printf("VModule::import_frame %d nested_edl=%p current_edit->nested_edl=%p\n",
184 current_edit->nested_edl);
186 // Convert requested direction to command
187 if(renderengine->command->command == CURRENT_FRAME)
189 command = CURRENT_FRAME;
192 if(direction == PLAY_REVERSE)
194 if(renderengine->command->single_frame())
195 command = SINGLE_FRAME_REWIND;
197 command = NORMAL_REWIND;
201 if(renderengine->command->single_frame())
202 command = SINGLE_FRAME_FWD;
204 command = NORMAL_FWD;
207 if(!nested_edl || nested_edl->id != current_edit->nested_edl->id)
209 nested_edl = current_edit->nested_edl;
210 if(nested_renderengine)
212 delete nested_renderengine;
213 nested_renderengine = 0;
218 nested_command = new TransportCommand;
222 if(!nested_renderengine)
224 nested_command->command = command;
225 nested_command->get_edl()->copy_all(nested_edl);
226 nested_command->change_type = CHANGE_ALL;
227 nested_command->realtime = renderengine->command->realtime;
228 nested_renderengine = new RenderEngine(0,
231 renderengine ? renderengine->channeldb : 0,
233 nested_renderengine->set_vcache(get_cache());
234 nested_renderengine->arm_command(nested_command);
240 // Update nested command
241 nested_renderengine->command->command = command;
242 nested_command->realtime = renderengine->command->realtime;
245 // Update nested video driver for opengl
246 nested_renderengine->video = renderengine->video;
252 if(debug) printf("VModule::import_frame %d\n", __LINE__);
254 if(!output) printf("VModule::import_frame %d output=%p\n", __LINE__, output);
257 (current_edit->asset ||
258 (current_edit->nested_edl && nested_renderengine->vrender)))
262 if(debug) printf("VModule::import_frame %d cache=%p\n",
265 if(current_edit->asset)
268 file = get_cache()->check_out(current_edit->asset,
270 // get_cache()->dump();
274 if(file || nested_edl)
276 // Make all positions based on requested frame rate.
277 int64_t edit_startproject = Units::to_int64(current_edit->startproject *
280 int64_t edit_startsource = Units::to_int64(current_edit->startsource *
283 // Source position going forward
284 uint64_t position = direction_position -
287 int64_t nested_position = 0;
293 // apply speed curve to source position so the timeline agrees with the playback
294 if(track->has_speed())
296 // integrate position from start of edit.
297 double speed_position = edit_startsource;
298 FloatAuto *previous = 0;
300 FloatAutos *speed_autos = (FloatAutos*)track->automation->autos[AUTOMATION_SPEED];
301 for(int64_t i = edit_startproject; i < direction_position; i++)
303 double speed = speed_autos->get_value(i,
307 speed_position += speed;
309 //printf("VModule::import_frame %d %lld %lld\n", __LINE__, position, (int64_t)speed_position);
310 position = (int64_t)speed_position;
319 if(debug) printf("VModule::import_frame %d\n", __LINE__);
322 // maybe apply speed curve here, so timeline reflects actual playback
326 // if we hit the end of stream, freeze at last frame
327 uint64_t max_position = 0;
330 max_position = Units::to_int64((double)file->get_video_length() *
332 current_edit->asset->frame_rate - 1);
336 max_position = Units::to_int64(nested_edl->tracks->total_playable_length() *
341 if(position > max_position) position = max_position;
343 if(position < 0) position = 0;
345 int use_cache = renderengine &&
346 renderengine->command->single_frame();
347 int use_asynchronous = !use_cache &&
349 // Try to make rendering go faster.
350 // But converts some formats to YUV420, which may degrade input format.
351 // renderengine->command->realtime &&
352 renderengine->get_edl()->session->video_asynchronous;
356 if(debug) printf("VModule::import_frame %d\n", __LINE__);
358 file->start_video_decode_thread();
360 file->stop_video_thread();
362 int64_t normalized_position = Units::to_int64(position *
363 current_edit->asset->frame_rate /
365 // printf("VModule::import_frame %d %lld %lld\n",
368 // normalized_position);
369 file->set_layer(current_edit->channel);
370 file->set_video_position(normalized_position,
372 asset_w = current_edit->asset->width;
373 asset_h = current_edit->asset->height;
374 //printf("VModule::import_frame %d normalized_position=%lld\n", __LINE__, normalized_position);
378 if(debug) printf("VModule::import_frame %d\n", __LINE__);
379 asset_w = nested_edl->session->output_w;
380 asset_h = nested_edl->session->output_h;
381 // Get source position in nested frame rate in direction of playback.
382 nested_position = Units::to_int64(position *
383 nested_edl->session->frame_rate /
385 if(direction == PLAY_REVERSE)
390 // Auto scale if required
391 if(output->get_params()->get("AUTOSCALE", 0))
393 float autoscale_w = output->get_params()->get("AUTOSCALE_W", 1024);
394 float autoscale_h = output->get_params()->get("AUTOSCALE_H", 1024);
395 float x_scale = autoscale_w / asset_w;
396 float y_scale = autoscale_h / asset_h;
404 if(x_scale < y_scale)
406 out_w = in_w * x_scale;
407 out_h = in_h * x_scale;
411 out_w = in_w * y_scale;
412 out_h = in_h * y_scale;
415 out_x = track->track_w / 2 - out_w / 2;
416 out_y = track->track_h / 2 - out_h / 2;
421 ((VTrack*)track)->calculate_input_transfer(asset_w,
423 input_position_project,
435 // printf("VModule::import_frame %d %f %d %f %d\n",
442 // file -> temp -> output
443 if( !EQUIV(in_x, 0) ||
445 !EQUIV(in_w, track->track_w) ||
446 !EQUIV(in_h, track->track_h) ||
449 !EQUIV(out_w, track->track_w) ||
450 !EQUIV(out_h, track->track_h) ||
451 !EQUIV(in_w, asset_w) ||
452 !EQUIV(in_h, asset_h))
454 if(debug) printf("VModule::import_frame %d file -> temp -> output\n", __LINE__);
459 // Get temporary input buffer
464 VRender *vrender = (VRender*)commonrender;
465 //printf("VModule::import_frame %d vrender->input_temp=%p\n", __LINE__, vrender->input_temp);
466 input = &vrender->input_temp;
476 ((*input)->get_w() != asset_w ||
477 (*input)->get_h() != asset_h))
489 (*input) = new VFrame(0,
493 get_edl()->session->color_model,
499 (*input)->copy_stacks(output);
502 // Cache for single frame only
505 if(debug) printf("VModule::import_frame %d this=%p file=%s\n",
508 current_edit->asset->path);
509 if(use_cache) file->set_cache_frames(1);
510 result = file->read_frame((*input));
511 if(use_cache) file->set_cache_frames(0);
512 (*input)->set_opengl_state(VFrame::RAM);
517 // If the colormodels differ, change input to nested colormodel
518 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
519 int current_cmodel = output->get_color_model();
520 int output_w = output->get_w();
521 int output_h = output->get_h();
522 VFrame *input2 = (*input);
524 if(nested_cmodel != current_cmodel)
526 // If opengl, input -> input -> output
532 // If software, input2 -> input -> output
533 // Use output as a temporary.
537 if(debug) printf("VModule::import_frame %d this=%p nested_cmodel=%d\n",
542 input2->reallocate(0,
555 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s input2=%p\n",
561 result = nested_renderengine->vrender->process_buffer(
566 if(debug) printf("VModule::import_frame %d this=%p nested_edl=%s\n",
571 if(nested_cmodel != current_cmodel)
573 if(debug) printf("VModule::import_frame %d\n", __LINE__);
576 // Change colormodel in hardware.
577 if(debug) printf("VModule::import_frame %d\n", __LINE__);
578 x11_device->convert_cmodel(input2,
581 // The converted color model is now in hardware, so return the input2 buffer
582 // to the expected color model.
583 input2->reallocate(0,
595 // Transfer from input2 to input
596 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d input2=%p input=%p output=%p\n",
603 BC_CModels::transfer((*input)->get_rows(),
624 //printf("VModule::import_frame %d\n", __LINE__);
626 // input2 was the output buffer, so it must be restored
627 input2->reallocate(0,
636 //printf("VModule::import_frame %d\n", __LINE__);
642 // Find an overlayer object to perform the camera transformation
643 OverlayFrame *overlayer = 0;
645 // OpenGL playback uses hardware
648 //printf("VModule::import_frame %d\n", __LINE__);
654 VRender *vrender = (VRender*)commonrender;
655 overlayer = vrender->overlayer;
661 printf("VModule::import_frame neither plugin_array nor commonrender is defined.\n");
664 overlay_temp = new OverlayFrame(plugin_array->mwindow->preferences->processors);
667 overlayer = overlay_temp;
669 // printf("VModule::import_frame 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
680 // for(int j = 0; j < output->get_w() * 3 * 5; j++)
681 // output->get_rows()[0][j] = 255;
685 x11_device->do_camera(output,
695 if(debug) printf("VModule::import_frame %d %d %d\n",
697 output->get_opengl_state(),
698 (*input)->get_opengl_state());
705 output->clear_frame();
708 // get_cache()->check_in(current_edit->asset);
711 // TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage
712 // of producing green borders in floating point translation of YUV
713 int mode = TRANSFER_REPLACE;
714 if(get_edl()->session->interpolation_type != NEAREST_NEIGHBOR &&
715 BC_CModels::is_yuv(output->get_color_model()))
716 mode = TRANSFER_NORMAL;
718 if(debug) printf("VModule::import_frame %d temp -> output\n", __LINE__);
719 overlayer->overlay(output,
731 get_edl()->session->interpolation_type);
734 output->copy_stacks((*input));
739 if(debug) printf("VModule::import_frame %d file -> output nested_edl=%p file=%p\n",
745 VFrame **input = &output;
746 // If colormodels differ, reallocate output in nested colormodel.
747 int nested_cmodel = nested_renderengine->get_edl()->session->color_model;
748 int current_cmodel = output->get_color_model();
750 if(debug) printf("VModule::import_frame %d nested_cmodel=%d current_cmodel=%d\n",
755 if(nested_cmodel != current_cmodel)
764 input = &((VRender*)commonrender)->input_temp;
771 if(!(*input)) (*input) = new VFrame;
774 (*input)->reallocate(0,
783 if(debug) printf("VModule::import_frame %d\n",
786 //(*input)->clear_frame();
789 if(debug) printf("VModule::import_frame %d %p %p\n",
791 (*input)->get_rows(),
793 result = nested_renderengine->vrender->process_buffer(
797 if(debug) printf("VModule::import_frame %d\n",
800 // If colormodels differ, change colormodels in opengl if possible.
801 // Swap output for temp if not possible.
802 if(nested_cmodel != current_cmodel)
806 x11_device->convert_cmodel(output,
809 // The color model was changed in place, so return output buffer
810 output->reallocate(0,
822 // Transfer from temporary to output
823 if(debug) printf("VModule::import_frame %d %d %d %d %d %d %d\n",
831 BC_CModels::transfer(output->get_rows(),
832 (*input)->get_rows(),
860 // Cache single frames
861 //memset(output->get_rows()[0], 0xff, 1024);
862 if(use_cache) file->set_cache_frames(1);
863 result = file->read_frame(output);
864 if(use_cache) file->set_cache_frames(0);
865 output->set_opengl_state(VFrame::RAM);
871 get_cache()->check_in(current_edit->asset);
878 if(debug) printf("VModule::import_frame %d\n", __LINE__);
881 x11_device->clear_input(output);
885 output->clear_frame();
893 if(debug) printf("VModule::import_frame %d\n", __LINE__);
896 x11_device->clear_input(output);
900 output->clear_frame();
904 if(debug) printf("VModule::import_frame %d done\n", __LINE__);
911 int VModule::render(VFrame *output,
912 int64_t start_position,
920 double edl_rate = get_edl()->session->frame_rate;
922 //printf("VModule::render %lld\n", start_position);
924 if(use_nudge) start_position += Units::to_int64(track->nudge *
928 int64_t start_position_project = Units::to_int64(start_position *
933 update_transition(start_position_project,
936 VEdit* current_edit = (VEdit*)track->edits->editof(start_position_project,
939 VEdit* previous_edit = 0;
940 //printf("VModule::render %d %p %ld %d\n", __LINE__, current_edit, start_position_project, direction);
943 printf(" VModule::render %d %lld %s transition=%p opengl=%d current_edit=%p output=%p\n",
945 (long long)start_position_project,
954 output->clear_frame();
961 // Process transition
962 if(transition && transition->on)
965 // Get temporary buffer
966 VFrame **transition_input = 0;
969 VRender *vrender = (VRender*)commonrender;
970 transition_input = &vrender->transition_temp;
974 transition_input = &transition_temp;
977 if((*transition_input) &&
978 ((*transition_input)->get_w() != track->track_w ||
979 (*transition_input)->get_h() != track->track_h))
981 delete (*transition_input);
982 (*transition_input) = 0;
985 // Load incoming frame
986 if(!(*transition_input))
988 (*transition_input) = new VFrame(0,
992 get_edl()->session->color_model,
996 (*transition_input)->copy_stacks(output);
998 //printf("VModule::render %d\n", __LINE__);
999 result = import_frame((*transition_input),
1007 // Load transition buffer
1008 previous_edit = (VEdit*)current_edit->previous;
1010 result |= import_frame(output,
1016 //printf("VModule::render %d\n", __LINE__);
1018 // printf("VModule::render %d %p %p %p %p\n",
1020 // (*transition_input),
1021 // (*transition_input)->get_pbuffer(),
1023 // output->get_pbuffer());
1026 // Execute plugin with transition_input and output here
1028 transition_server->set_use_opengl(use_opengl, renderengine->video);
1029 transition_server->process_transition((*transition_input),
1031 (direction == PLAY_FORWARD) ?
1032 (start_position_project - current_edit->startproject) :
1033 (start_position_project - current_edit->startproject - 1),
1034 transition->length);
1038 // Load output buffer
1039 result = import_frame(output,
1056 void VModule::create_objects()
1058 Module::create_objects();