1 #include "colormodels.h"
4 #include "ivtcwindow.h"
10 #define _(String) gettext(String)
11 #define gettext_noop(String) String
12 #define N_(String) gettext_noop (String)
15 REGISTER_PLUGIN(IVTCMain)
17 IVTCConfig::IVTCConfig()
26 IVTCMain::IVTCMain(PluginServer *server)
27 : PluginVClient(server)
29 PLUGIN_CONSTRUCTOR_MACRO
35 PLUGIN_DESTRUCTOR_MACRO
39 if(temp_frame[0]) delete temp_frame[0];
40 if(temp_frame[1]) delete temp_frame[1];
43 for(int i = 0; i < (smp + 1); i++)
49 char* IVTCMain::plugin_title() { return _("Inverse Telecine"); }
51 int IVTCMain::is_realtime() { return 1; }
54 int IVTCMain::load_defaults()
56 char directory[BCTEXTLEN], string[BCTEXTLEN];
57 // set the default directory
58 sprintf(directory, "%sivtc.rc", BCASTDIR);
61 defaults = new Defaults(directory);
64 config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset);
65 config.first_field = defaults->get("FIRST_FIELD", config.first_field);
66 config.automatic = defaults->get("AUTOMATIC", config.automatic);
67 config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold);
68 config.pattern = defaults->get("PATTERN", config.pattern);
72 int IVTCMain::save_defaults()
74 defaults->update("FRAME_OFFSET", config.frame_offset);
75 defaults->update("FIRST_FIELD", config.first_field);
76 defaults->update("AUTOMATIC", config.automatic);
77 defaults->update("AUTO_THRESHOLD", config.auto_threshold);
78 defaults->update("PATTERN", config.pattern);
83 #include "picon_png.h"
84 NEW_PICON_MACRO(IVTCMain)
85 SHOW_GUI_MACRO(IVTCMain, IVTCThread)
86 SET_STRING_MACRO(IVTCMain)
87 RAISE_WINDOW_MACRO(IVTCMain)
91 int IVTCMain::load_configuration()
93 KeyFrame *prev_keyframe;
95 prev_keyframe = get_prev_keyframe(get_source_position());
96 // Must also switch between interpolation between keyframes and using first keyframe
97 read_data(prev_keyframe);
102 void IVTCMain::save_data(KeyFrame *keyframe)
106 // cause data to be stored directly in text
107 output.set_shared_string(keyframe->data, MESSAGESIZE);
108 output.tag.set_title("IVTC");
109 output.tag.set_property("FRAME_OFFSET", config.frame_offset);
110 output.tag.set_property("FIRST_FIELD", config.first_field);
111 output.tag.set_property("AUTOMATIC", config.automatic);
112 output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold);
113 output.tag.set_property("PATTERN", config.pattern);
115 output.terminate_string();
118 void IVTCMain::read_data(KeyFrame *keyframe)
122 input.set_shared_string(keyframe->data, strlen(keyframe->data));
129 result = input.read_tag();
133 if(input.tag.title_is("IVTC"))
135 config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset);
136 config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field);
137 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
138 new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold);
139 config.pattern = input.tag.get_property("PATTERN", config.pattern);
146 void IVTCMain::compare_fields(VFrame *frame1,
152 for(int i = 0; i < (smp + 1); i++)
154 engine[i]->start_process_frame(frame1, frame2);
157 for(int i = 0; i < (smp + 1); i++)
159 engine[i]->wait_process_frame();
160 field1 += engine[i]->field1;
161 field2 += engine[i]->field2;
165 #define ABS(x) ((x) > 0 ? (x) : -(x))
167 // Pattern A B BC CD D
168 int IVTCMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
170 load_configuration();
172 //printf("IVTCMain::process_realtime 1\n");
180 total_average = (int64_t)(project_frame_rate + 0.5);
182 int y1, y2, y_increment;
183 y_increment = input_ptr->get_h() / (smp + 1);
186 engine = new IVTCEngine*[(smp + 1)];
187 for(int i = 0; i < (smp + 1); i++)
189 y2 = y1 + y_increment;
190 if(i == (PluginClient::smp + 1) - 1 &&
191 y2 < input_ptr->get_h() - 1)
192 y2 = input_ptr->get_h() - 1;
193 engine[i] = new IVTCEngine(this, y1, y2);
199 // Determine position in pattern
200 int pattern_position = (PluginClient::source_position + config.frame_offset) % 5;
202 //printf("IVTCMain::process_realtime %d %d\n", pattern_position, config.first_field);
203 if(!temp_frame[0]) temp_frame[0] = new VFrame(0,
206 input_ptr->get_color_model(),
208 if(!temp_frame[1]) temp_frame[1] = new VFrame(0,
211 input_ptr->get_color_model(),
214 int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w();
217 if(config.pattern == 1)
219 temp_frame[1]->copy_from(input_ptr);
221 // Recycle previous bottom or top
222 for(int i = 0; i < input_ptr->get_h(); i++)
224 if((i + config.first_field) & 1)
225 memcpy(output_ptr->get_rows()[i],
226 input_ptr->get_rows()[i],
229 memcpy(output_ptr->get_rows()[i],
230 temp_frame[0]->get_rows()[i],
235 VFrame *temp = temp_frame[0];
236 temp_frame[0] = temp_frame[1];
237 temp_frame[1] = temp;
240 // Determine where in the pattern we are
246 compare_fields(temp_frame[0],
251 int64_t field_difference = field2 - field1;
255 temp_frame[1]->copy_from(input_ptr);
257 // Automatically generate field difference threshold using weighted average of previous frames
261 threshold = ABS(field_difference);
263 //printf("IVTCMain::process_realtime 1 %d %lld %lld %lld\n", state, average, threshold, field_difference);
269 // Compute new threshold for next time
270 average = (int64_t)(average * total_average +
271 ABS(field_difference)) / (total_average + 1);
273 for(int i = 0; i < input_ptr->get_h(); i++)
275 if((i + new_field) & 1)
276 memcpy(output_ptr->get_rows()[i],
277 input_ptr->get_rows()[i],
280 memcpy(output_ptr->get_rows()[i],
281 temp_frame[0]->get_rows()[i],
286 // Neither field changed enough
288 if(ABS(field_difference) <= threshold ||
293 // Compute new threshold for next time
294 average = (int64_t)(average * total_average +
295 ABS(field_difference)) / (total_average + 1);
297 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
298 output_ptr->copy_from(input_ptr);
301 // Field 2 changed more than field 1
302 if(field_difference > threshold)
304 // BC bottom field new
308 // Compute new threshold for next time
309 average = (int64_t)(average * total_average +
310 ABS(field_difference)) / (total_average + 1);
312 for(int i = 0; i < input_ptr->get_h(); i++)
315 memcpy(output_ptr->get_rows()[i],
316 temp_frame[0]->get_rows()[i],
319 memcpy(output_ptr->get_rows()[i],
320 input_ptr->get_rows()[i],
325 // Field 1 changed more than field 2
326 if(field_difference < -threshold)
332 // Compute new threshold for next time
333 average = (int64_t)(average * total_average +
334 ABS(field_difference)) / (total_average + 1);
336 for(int i = 0; i < input_ptr->get_h(); i++)
339 memcpy(output_ptr->get_rows()[i],
340 input_ptr->get_rows()[i],
343 memcpy(output_ptr->get_rows()[i],
344 temp_frame[0]->get_rows()[i],
350 VFrame *temp = temp_frame[0];
351 temp_frame[0] = temp_frame[1];
352 temp_frame[1] = temp;
355 switch(pattern_position)
360 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
361 output_ptr->copy_from(input_ptr);
365 temp_frame[0]->copy_from(input_ptr);
366 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
367 output_ptr->copy_from(input_ptr);
371 // Save one field for next frame. Reuse previous frame.
372 temp_frame[1]->copy_from(input_ptr);
373 output_ptr->copy_from(temp_frame[0]);
377 // Combine previous field with current field.
378 for(int i = 0; i < input_ptr->get_h(); i++)
380 if((i + config.first_field) & 1)
381 memcpy(output_ptr->get_rows()[i],
382 input_ptr->get_rows()[i],
385 memcpy(output_ptr->get_rows()[i],
386 temp_frame[1]->get_rows()[i],
391 //printf("IVTCMain::process_realtime 2\n");
396 void IVTCMain::update_gui()
400 load_configuration();
401 thread->window->lock_window();
402 thread->window->frame_offset->update((int64_t)config.frame_offset);
403 thread->window->first_field->update(config.first_field);
404 thread->window->automatic->update(config.automatic);
405 thread->window->pattern[0]->update(config.pattern == 0);
406 thread->window->pattern[1]->update(config.pattern == 1);
407 thread->window->unlock_window();
414 IVTCEngine::IVTCEngine(IVTCMain *plugin, int start_y, int end_y)
417 this->plugin = plugin;
419 this->start_y = start_y;
426 IVTCEngine::~IVTCEngine()
431 // Use all channels to get more info
432 #define COMPARE_ROWS(result, row1, row2, type, width, components) \
434 for(int i = 0; i < width * components; i++) \
436 result += labs(((type*)row1)[i] - ((type*)row2)[i]); \
440 #define COMPARE_FIELDS(rows1, rows2, type, width, height, components) \
442 int w = width * components; \
445 for(int i = 0; i < h; i++) \
447 type *row1 = (type*)(rows1)[i]; \
448 type *row2 = (type*)(rows2)[i]; \
451 for(int j = 0; j < w; j++) \
453 field2 += labs(row1[j] - row2[j]); \
456 for(int j = 0; j < w; j++) \
458 field1 += labs(row1[j] - row2[j]); \
463 #define COMPARE_FIELDS_YUV(rows1, rows2, type, width, height, components) \
465 int w = width * components; \
468 for(int i = 0; i < h; i++) \
470 type *row1 = (type*)(rows1)[i]; \
471 type *row2 = (type*)(rows2)[i]; \
474 for(int j = 0; j < w; j += components) \
476 field2 += labs(row1[j] - row2[j]); \
479 for(int j = 0; j < w; j += components) \
481 field1 += labs(row1[j] - row2[j]); \
487 void IVTCEngine::run()
494 output_lock.unlock();
500 switch(input->get_color_model())
503 COMPARE_FIELDS(input->get_rows() + start_y,
504 output->get_rows() + start_y,
511 COMPARE_FIELDS_YUV(input->get_rows() + start_y,
512 output->get_rows() + start_y,
519 COMPARE_FIELDS(input->get_rows() + start_y,
520 output->get_rows() + start_y,
527 COMPARE_FIELDS_YUV(input->get_rows() + start_y,
528 output->get_rows() + start_y,
535 COMPARE_FIELDS(input->get_rows() + start_y,
536 output->get_rows() + start_y,
543 COMPARE_FIELDS_YUV(input->get_rows() + start_y,
544 output->get_rows() + start_y,
550 case BC_RGBA16161616:
551 COMPARE_FIELDS(input->get_rows() + start_y,
552 output->get_rows() + start_y,
558 case BC_YUVA16161616:
559 COMPARE_FIELDS_YUV(input->get_rows() + start_y,
560 output->get_rows() + start_y,
567 output_lock.unlock();
572 int IVTCEngine::start_process_frame(VFrame *output, VFrame *input)
574 this->output = output;
580 int IVTCEngine::wait_process_frame()