oops - now add termux/android DV patch
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / rgb601 / ivtc.C.2
1 #include "bccmodels.h"
2 #include "file.h"
3 #include "filexml.h"
4 #include "rgb601.h"
5 #include "rgb601window.h"
6
7 #include <stdio.h>
8 #include <string.h>
9
10 PluginClient* new_plugin(PluginServer *server)
11 {
12         return new RGB601Main(server);
13 }
14
15
16 RGB601Config::RGB601Config()
17 {
18         frame_offset = 0;
19         first_field = 0;
20         automatic = 1;
21         auto_threshold = 2;
22 }
23
24 RGB601Main::RGB601Main(PluginServer *server)
25  : PluginVClient(server)
26 {
27         thread = 0;
28         load_defaults();
29 }
30
31 RGB601Main::~RGB601Main()
32 {
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         }
40
41         save_defaults();
42         delete defaults;
43 }
44
45 char* RGB601Main::plugin_title() { return "Inverse Telecine"; }
46 int RGB601Main::is_realtime() { return 1; }
47
48 int RGB601Main::load_defaults()
49 {
50         char directory[1024], string[1024];
51 // set the default directory
52         sprintf(directory, "%s/rgb601.rc", File::get_config_path());
53
54 // load the defaults
55         defaults = new Defaults(directory);
56         defaults->load();
57
58         config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset);
59         config.first_field = defaults->get("FIRST_FIELD", config.first_field);
60         config.automatic = defaults->get("AUTOMATIC", config.automatic);
61         config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold);
62         return 0;
63 }
64
65 int RGB601Main::save_defaults()
66 {
67         defaults->update("FRAME_OFFSET", config.frame_offset);
68         defaults->update("FIRST_FIELD", config.first_field);
69         defaults->update("AUTOMATIC", config.automatic);
70         defaults->update("AUTO_THRESHOLD", config.auto_threshold);
71         defaults->save();
72         return 0;
73 }
74
75 void RGB601Main::load_configuration()
76 {
77         KeyFrame *prev_keyframe, *next_keyframe;
78
79         prev_keyframe = get_prev_keyframe(-1);
80         next_keyframe = get_next_keyframe(-1);
81 // Must also switch between interpolation between keyframes and using first keyframe
82         read_data(prev_keyframe);
83 }
84
85
86 void RGB601Main::save_data(KeyFrame *keyframe)
87 {
88         FileXML output;
89
90 // cause data to be stored directly in text
91         output.set_shared_string(keyframe->get_data(), -MESSAGESIZE);
92         output.tag.set_title("RGB601");
93         output.tag.set_property("FRAME_OFFSET", config.frame_offset);
94         output.tag.set_property("FIRST_FIELD", config.first_field);
95         output.tag.set_property("AUTOMATIC", config.automatic);
96         output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold);
97         output.append_tag();
98         output.tag.set_title("/RGB601");
99         output.append_tag();
100         output.terminate_string();
101 }
102
103 void RGB601Main::read_data(KeyFrame *keyframe)
104 {
105         FileXML input;
106
107         input.set_shared_string(keyframe->get_data(), strlen(keyframe->get_data()));
108
109         int result = 0;
110         float new_threshold;
111
112         while(!result)
113         {
114                 result = input.read_tag();
115
116                 if(!result)
117                 {
118                         if(input.tag.title_is("RGB601"))
119                         {
120                                 config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset);
121                                 config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field);
122                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
123                                 new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold);
124                         }
125                 }
126         }
127
128 //      if(new_threshold != config.auto_threshold)
129 //      {
130 //              config.auto_threshold = new_threshold;
131 //              average = -1;
132 //      }
133
134         if(thread)
135         {
136                 thread->window->frame_offset->update((long)config.frame_offset);
137                 thread->window->first_field->update(config.first_field);
138                 thread->window->automatic->update(config.automatic);
139 //              thread->window->threshold->update(config.auto_threshold);
140         }
141 }
142
143
144 int RGB601Main::start_realtime()
145 {
146         temp_frame[0] = 0;
147         temp_frame[1] = 0;
148         state = 0;
149         new_field = 0;
150         average = 0;
151         total_average = project_frame_rate;
152 //      total_average = 5;
153         return 0;
154 }
155
156 int RGB601Main::stop_realtime()
157 {
158         if(temp_frame[0]) delete temp_frame[0];
159         if(temp_frame[1]) delete temp_frame[1];
160         temp_frame[0] = 0;
161         temp_frame[1] = 0;
162         return 0;
163 }
164
165 // Use all channels to get more info
166 #define COMPARE_ROWS(result, row1, row2, type, width, components) \
167 { \
168         for(int i = 0; i < width * components; i++) \
169         { \
170                 result += labs(((type*)row1)[i] - ((type*)row2)[i]); \
171         } \
172 }
173
174 int64_t RGB601Main::compare_fields(VFrame *frame1, VFrame *frame2, int field)
175 {
176         int64_t result = 0;
177         for(int row = field; row < frame1->get_h(); row += 2)
178         {
179                 switch(frame1->get_color_model())
180                 {
181                         case BC_RGB888:
182                         case BC_YUV888:
183                                 COMPARE_ROWS(result,
184                                         frame1->get_rows()[row],
185                                         frame2->get_rows()[row],
186                                         unsigned char,
187                                         frame1->get_w(),
188                                         3);
189                                 break;
190
191                         case BC_RGBA8888:
192                         case BC_YUVA8888:
193                                 COMPARE_ROWS(result,
194                                         frame1->get_rows()[row],
195                                         frame2->get_rows()[row],
196                                         unsigned char,
197                                         frame1->get_w(),
198                                         4);
199                                 break;
200
201                         case BC_RGB161616:
202                         case BC_YUV161616:
203                                 COMPARE_ROWS(result,
204                                         frame1->get_rows()[row],
205                                         frame2->get_rows()[row],
206                                         u_int16_t,
207                                         frame1->get_w(),
208                                         3);
209                                 break;
210
211                         case BC_RGBA16161616:
212                         case BC_YUVA16161616:
213                                 COMPARE_ROWS(result,
214                                         frame1->get_rows()[row],
215                                         frame2->get_rows()[row],
216                                         u_int16_t,
217                                         frame1->get_w(),
218                                         4);
219                                 break;
220                 }
221         }
222         return result;
223 }
224
225 // Pattern A B BC CD D
226 int RGB601Main::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
227 {
228         load_configuration();
229
230 // Determine position in pattern
231         int pattern_position = (PluginClient::source_position + config.frame_offset) % 5;
232
233 //printf("RGB601Main::process_realtime %d %d\n", pattern_position, config.first_field);
234         if(!temp_frame[0]) temp_frame[0] = new VFrame(0,
235                 input_ptr->get_w(),
236                 input_ptr->get_h(),
237                 input_ptr->get_color_model(),
238                 -1);
239         if(!temp_frame[1]) temp_frame[1] = new VFrame(0,
240                 input_ptr->get_w(),
241                 input_ptr->get_h(),
242                 input_ptr->get_color_model(),
243                 -1);
244
245         int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w();
246
247 // Determine where in the pattern we are
248         if(config.automatic)
249         {
250                 int64_t field1 = compare_fields(temp_frame[0], input_ptr, 0);
251                 int64_t field2 = compare_fields(temp_frame[0], input_ptr, 1);
252                 int64_t threshold = (int64_t)(config.auto_threshold *
253                         input_ptr->get_w() *
254                         input_ptr->get_h());
255
256 //              if(input_ptr->get_color_model() == BC_RGBA8888 ||
257 //                      input_ptr->get_color_model() == BC_RGBA16161616 ||
258 //                      input_ptr->get_color_model() == BC_YUVA8888 ||
259 //                      input_ptr->get_color_model() == BC_YUVA16161616)
260 //                      threshold *= 4;
261 //              else
262                         threshold *= 3;
263
264                 if(input_ptr->get_color_model() == BC_RGB161616 ||
265                         input_ptr->get_color_model() == BC_RGBA16161616 ||
266                         input_ptr->get_color_model() == BC_YUV161616 ||
267                         input_ptr->get_color_model() == BC_YUVA16161616)
268                         threshold *= 0x100;
269
270                 temp_frame[1]->copy_from(input_ptr);
271
272 // Adjust threshold over time
273 //              if(average >= 0)
274                         threshold = average;
275 //              else
276 //                      average = threshold;
277
278 //printf("RGB601Main::process_realtime %d %lld %lld %lld %lld\n", state, average, threshold, field1, field2);
279 // CD
280                 if(state == 3)
281                 {
282                         state = 4;
283                         for(int i = 0; i < input_ptr->get_h(); i++)
284                         {
285                                 if((i + new_field) & 1)
286                                         memcpy(output_ptr->get_rows()[i],
287                                                 input_ptr->get_rows()[i],
288                                                 row_size);
289                                 else
290                                         memcpy(output_ptr->get_rows()[i],
291                                                 temp_frame[0]->get_rows()[i],
292                                                 row_size);
293                         }
294                 }
295                 else
296 // A or B or D
297                 if((field1 > threshold && field2 > threshold) ||
298                         (field1 <= threshold && field2 <= threshold) ||
299                         state == 4)
300                 {
301                         state = 0;
302
303 // Compute new threshold for next time
304                         average = (int64_t)(average * total_average +
305                                 field1 +
306                                 field2) / (total_average + 2);
307
308                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
309                                 output_ptr->copy_from(input_ptr);
310                 }
311                 else
312                 if(field1 <= threshold && field2 >= threshold)
313                 {
314 // BC bottom field new
315                         state = 3;
316                         new_field = 1;
317
318 // Compute new threshold for next time
319                         average = (int64_t)(average * total_average +
320                                 field1) / (total_average + 1);
321
322                         for(int i = 0; i < input_ptr->get_h(); i++)
323                         {
324                                 if(i & 1)
325                                         memcpy(output_ptr->get_rows()[i],
326                                                 temp_frame[0]->get_rows()[i],
327                                                 row_size);
328                                 else
329                                         memcpy(output_ptr->get_rows()[i],
330                                                 input_ptr->get_rows()[i],
331                                                 row_size);
332                         }
333                 }
334                 else
335                 if(field1 >= threshold && field2 <= threshold)
336                 {
337 // BC top field new
338                         state = 3;
339                         new_field = 0;
340
341 // Compute new threshold for next time
342                         average = (int64_t)(average * total_average +
343                                 field2) / (total_average + 1);
344
345                         for(int i = 0; i < input_ptr->get_h(); i++)
346                         {
347                                 if(i & 1)
348                                         memcpy(output_ptr->get_rows()[i],
349                                                 input_ptr->get_rows()[i],
350                                                 row_size);
351                                 else
352                                         memcpy(output_ptr->get_rows()[i],
353                                                 temp_frame[0]->get_rows()[i],
354                                                 row_size);
355                         }
356                 }
357
358 // Swap temp frames
359                 VFrame *temp = temp_frame[0];
360                 temp_frame[0] = temp_frame[1];
361                 temp_frame[1] = temp;
362         }
363         else
364         switch(pattern_position)
365         {
366 // Direct copy
367                 case 0:
368                 case 4:
369                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
370                                 output_ptr->copy_from(input_ptr);
371                         break;
372
373                 case 1:
374                         temp_frame[0]->copy_from(input_ptr);
375                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
376                                 output_ptr->copy_from(input_ptr);
377                         break;
378
379                 case 2:
380 // Save one field for next frame.  Reuse previous frame.
381                         temp_frame[1]->copy_from(input_ptr);
382                         output_ptr->copy_from(temp_frame[0]);
383                         break;
384
385                 case 3:
386 // Combine previous field with current field.
387                         for(int i = 0; i < input_ptr->get_h(); i++)
388                         {
389                                 if((i + config.first_field) & 1)
390                                         memcpy(output_ptr->get_rows()[i],
391                                                 input_ptr->get_rows()[i],
392                                                 row_size);
393                                 else
394                                         memcpy(output_ptr->get_rows()[i],
395                                                 temp_frame[1]->get_rows()[i],
396                                                 row_size);
397                         }
398                         break;
399         }
400
401         return 0;
402 }
403
404 int RGB601Main::show_gui()
405 {
406         load_configuration();
407         thread = new RGB601Thread(this);
408         thread->start();
409         return 0;
410 }
411
412 int RGB601Main::set_string()
413 {
414         if(thread) thread->window->set_title(gui_string);
415         return 0;
416 }
417
418 void RGB601Main::raise_window()
419 {
420         if(thread)
421         {
422                 thread->window->raise_window();
423                 thread->window->flush();
424         }
425 }
426
427
428