r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / ivtc / ivtc.C.2
blob4e28327267383c8068e7a0a9be6dc4cb3de54bf4
1 #include "colormodels.h"
2 #include "filexml.h"
3 #include "picon_png.h"
4 #include "ivtc.h"
5 #include "ivtcwindow.h"
7 #include <stdio.h>
8 #include <string.h>
10 PluginClient* new_plugin(PluginServer *server)
12         return new IVTCMain(server);
16 IVTCConfig::IVTCConfig()
18         frame_offset = 0;
19         first_field = 0;
20         automatic = 1;
21         auto_threshold = 2;
24 IVTCMain::IVTCMain(PluginServer *server)
25  : PluginVClient(server)
27         thread = 0;
28         load_defaults();
31 IVTCMain::~IVTCMain()
33         if(thread)
34         {
35 // Set result to 0 to indicate a server side close
36                 thread->window->set_done(0);
37                 thread->completion.lock();
38                 delete thread;
39         }
41         save_defaults();
42         delete defaults;
45 char* IVTCMain::plugin_title() { return "Inverse Telecine"; }
46 int IVTCMain::is_realtime() { return 1; }
48 VFrame* IVTCMain::new_picon()
50         return new VFrame(picon_png);
53 int IVTCMain::load_defaults()
55         char directory[1024], string[1024];
56 // set the default directory
57         sprintf(directory, "%sivtc.rc", BCASTDIR);
59 // load the defaults
60         defaults = new Defaults(directory);
61         defaults->load();
63         config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset);
64         config.first_field = defaults->get("FIRST_FIELD", config.first_field);
65         config.automatic = defaults->get("AUTOMATIC", config.automatic);
66         config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold);
67         return 0;
70 int IVTCMain::save_defaults()
72         defaults->update("FRAME_OFFSET", config.frame_offset);
73         defaults->update("FIRST_FIELD", config.first_field);
74         defaults->update("AUTOMATIC", config.automatic);
75         defaults->update("AUTO_THRESHOLD", config.auto_threshold);
76         defaults->save();
77         return 0;
80 void IVTCMain::load_configuration()
82         KeyFrame *prev_keyframe, *next_keyframe;
84         prev_keyframe = get_prev_keyframe(-1);
85         next_keyframe = get_next_keyframe(-1);
86 // Must also switch between interpolation between keyframes and using first keyframe
87         read_data(prev_keyframe);
91 void IVTCMain::save_data(KeyFrame *keyframe)
93         FileXML output;
95 // cause data to be stored directly in text
96         output.set_shared_string(keyframe->data, MESSAGESIZE);
97         output.tag.set_title("IVTC");
98         output.tag.set_property("FRAME_OFFSET", config.frame_offset);
99         output.tag.set_property("FIRST_FIELD", config.first_field);
100         output.tag.set_property("AUTOMATIC", config.automatic);
101         output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold);
102         output.append_tag();
103         output.terminate_string();
106 void IVTCMain::read_data(KeyFrame *keyframe)
108         FileXML input;
110         input.set_shared_string(keyframe->data, strlen(keyframe->data));
112         int result = 0;
113         float new_threshold;
115         while(!result)
116         {
117                 result = input.read_tag();
119                 if(!result)
120                 {
121                         if(input.tag.title_is("IVTC"))
122                         {
123                                 config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset);
124                                 config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field);
125                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
126                                 new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold);
127                         }
128                 }
129         }
131 //      if(new_threshold != config.auto_threshold)
132 //      {
133 //              config.auto_threshold = new_threshold;
134 //              average = -1;
135 //      }
137         if(thread) 
138         {
139                 thread->window->frame_offset->update((long)config.frame_offset);
140                 thread->window->first_field->update(config.first_field);
141                 thread->window->automatic->update(config.automatic);
142 //              thread->window->threshold->update(config.auto_threshold);
143         }
147 int IVTCMain::start_realtime()
149         temp_frame[0] = 0;
150         temp_frame[1] = 0;
151         state = 0;
152         new_field = 0;
153         average = 0;
154         total_average = project_frame_rate;
155 //      total_average = 5;
156         return 0;
159 int IVTCMain::stop_realtime()
161         if(temp_frame[0]) delete temp_frame[0];
162         if(temp_frame[1]) delete temp_frame[1];
163         temp_frame[0] = 0;
164         temp_frame[1] = 0;
165         return 0;
168 // Use all channels to get more info
169 #define COMPARE_ROWS(result, row1, row2, type, width, components) \
170 { \
171         for(int i = 0; i < width * components; i++) \
172         { \
173                 result += labs(((type*)row1)[i] - ((type*)row2)[i]); \
174         } \
177 int64_t IVTCMain::compare_fields(VFrame *frame1, VFrame *frame2, int field)
179         int64_t result = 0;
180         for(int row = field; row < frame1->get_h(); row += 2)
181         {
182                 switch(frame1->get_color_model())
183                 {
184                         case BC_RGB888:
185                         case BC_YUV888:
186                                 COMPARE_ROWS(result, 
187                                         frame1->get_rows()[row], 
188                                         frame2->get_rows()[row], 
189                                         unsigned char, 
190                                         frame1->get_w(), 
191                                         3);
192                                 break;
194                         case BC_RGBA8888:
195                         case BC_YUVA8888:
196                                 COMPARE_ROWS(result, 
197                                         frame1->get_rows()[row], 
198                                         frame2->get_rows()[row], 
199                                         unsigned char, 
200                                         frame1->get_w(),
201                                         4);
202                                 break;
204                         case BC_RGB161616:
205                         case BC_YUV161616:
206                                 COMPARE_ROWS(result, 
207                                         frame1->get_rows()[row], 
208                                         frame2->get_rows()[row], 
209                                         u_int16_t, 
210                                         frame1->get_w(),
211                                         3);
212                                 break;
213                         
214                         case BC_RGBA16161616:
215                         case BC_YUVA16161616:
216                                 COMPARE_ROWS(result, 
217                                         frame1->get_rows()[row], 
218                                         frame2->get_rows()[row], 
219                                         u_int16_t, 
220                                         frame1->get_w(),
221                                         4);
222                                 break;
223                 }
224         }
225         return result;
228 // Pattern A B BC CD D
229 int IVTCMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
231         load_configuration();
233 // Determine position in pattern
234         int pattern_position = (PluginClient::source_position + config.frame_offset) % 5;
236 //printf("IVTCMain::process_realtime %d %d\n", pattern_position, config.first_field);
237         if(!temp_frame[0]) temp_frame[0] = new VFrame(0,
238                 input_ptr->get_w(),
239                 input_ptr->get_h(),
240                 input_ptr->get_color_model(),
241                 -1);
242         if(!temp_frame[1]) temp_frame[1] = new VFrame(0,
243                 input_ptr->get_w(),
244                 input_ptr->get_h(),
245                 input_ptr->get_color_model(),
246                 -1);
248         int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w();
250 // Determine where in the pattern we are
251         if(config.automatic)
252         {
253                 int64_t field1 = compare_fields(temp_frame[0], input_ptr, 0);
254                 int64_t field2 = compare_fields(temp_frame[0], input_ptr, 1);
255                 int64_t threshold = (int64_t)(config.auto_threshold * 
256                         input_ptr->get_w() * 
257                         input_ptr->get_h());
259 //              if(input_ptr->get_color_model() == BC_RGBA8888 ||
260 //                      input_ptr->get_color_model() == BC_RGBA16161616 ||
261 //                      input_ptr->get_color_model() == BC_YUVA8888 ||
262 //                      input_ptr->get_color_model() == BC_YUVA16161616)
263 //                      threshold *= 4;
264 //              else
265                         threshold *= 3;
267                 if(input_ptr->get_color_model() == BC_RGB161616 ||
268                         input_ptr->get_color_model() == BC_RGBA16161616 ||
269                         input_ptr->get_color_model() == BC_YUV161616 ||
270                         input_ptr->get_color_model() == BC_YUVA16161616)
271                         threshold *= 0x100;
273                 temp_frame[1]->copy_from(input_ptr);
275 // Adjust threshold over time
276 //              if(average >= 0)
277                         threshold = average;
278 //              else
279 //                      average = threshold;
281 //printf("IVTCMain::process_realtime %d %lld %lld %lld %lld\n", state, average, threshold, field1, field2);
282 // CD
283                 if(state == 3)
284                 {
285                         state = 4;
286                         for(int i = 0; i < input_ptr->get_h(); i++)
287                         {
288                                 if((i + new_field) & 1)
289                                         memcpy(output_ptr->get_rows()[i], 
290                                                 input_ptr->get_rows()[i],
291                                                 row_size);
292                                 else
293                                         memcpy(output_ptr->get_rows()[i],
294                                                 temp_frame[0]->get_rows()[i],
295                                                 row_size);
296                         }
297                 }
298                 else
299 // A or B or D
300                 if((field1 > threshold && field2 > threshold) ||
301                         (field1 <= threshold && field2 <= threshold) ||
302                         state == 4)
303                 {
304                         state = 0;
306 // Compute new threshold for next time
307                         average = (int64_t)(average * total_average + 
308                                 field1 + 
309                                 field2) / (total_average + 2);
311                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
312                                 output_ptr->copy_from(input_ptr);
313                 }
314                 else
315                 if(field1 <= threshold && field2 >= threshold)
316                 {
317 // BC bottom field new
318                         state = 3;
319                         new_field = 1;
321 // Compute new threshold for next time
322                         average = (int64_t)(average * total_average + 
323                                 field1) / (total_average + 1);
325                         for(int i = 0; i < input_ptr->get_h(); i++)
326                         {
327                                 if(i & 1)
328                                         memcpy(output_ptr->get_rows()[i], 
329                                                 temp_frame[0]->get_rows()[i],
330                                                 row_size);
331                                 else
332                                         memcpy(output_ptr->get_rows()[i],
333                                                 input_ptr->get_rows()[i],
334                                                 row_size);
335                         }
336                 }
337                 else
338                 if(field1 >= threshold && field2 <= threshold)
339                 {
340 // BC top field new
341                         state = 3;
342                         new_field = 0;
344 // Compute new threshold for next time
345                         average = (int64_t)(average * total_average + 
346                                 field2) / (total_average + 1);
348                         for(int i = 0; i < input_ptr->get_h(); i++)
349                         {
350                                 if(i & 1)
351                                         memcpy(output_ptr->get_rows()[i],
352                                                 input_ptr->get_rows()[i],
353                                                 row_size);
354                                 else
355                                         memcpy(output_ptr->get_rows()[i], 
356                                                 temp_frame[0]->get_rows()[i],
357                                                 row_size);
358                         }
359                 }
361 // Swap temp frames
362                 VFrame *temp = temp_frame[0];
363                 temp_frame[0] = temp_frame[1];
364                 temp_frame[1] = temp;
365         }
366         else
367         switch(pattern_position)
368         {
369 // Direct copy
370                 case 0:
371                 case 4:
372                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
373                                 output_ptr->copy_from(input_ptr);
374                         break;
376                 case 1:
377                         temp_frame[0]->copy_from(input_ptr);
378                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
379                                 output_ptr->copy_from(input_ptr);
380                         break;
382                 case 2:
383 // Save one field for next frame.  Reuse previous frame.
384                         temp_frame[1]->copy_from(input_ptr);
385                         output_ptr->copy_from(temp_frame[0]);
386                         break;
388                 case 3:
389 // Combine previous field with current field.
390                         for(int i = 0; i < input_ptr->get_h(); i++)
391                         {
392                                 if((i + config.first_field) & 1)
393                                         memcpy(output_ptr->get_rows()[i], 
394                                                 input_ptr->get_rows()[i],
395                                                 row_size);
396                                 else
397                                         memcpy(output_ptr->get_rows()[i], 
398                                                 temp_frame[1]->get_rows()[i],
399                                                 row_size);
400                         }
401                         break;
402         }
404         return 0;
407 int IVTCMain::show_gui()
409         load_configuration();
410         thread = new IVTCThread(this);
411         thread->start();
412         return 0;
415 int IVTCMain::set_string()
417         if(thread) thread->window->set_title(gui_string);
418         return 0;
421 void IVTCMain::raise_window()
423         if(thread)
424         {
425                 thread->window->raise_window();
426                 thread->window->flush();
427         }