4 * Copyright (C) 1997-2011 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
29 #include "timeavgwindow.h"
30 #include "transportque.h"
42 REGISTER_PLUGIN(TimeAvgMain)
48 TimeAvgConfig::TimeAvgConfig()
51 mode = TimeAvgConfig::AVERAGE;
58 void TimeAvgConfig::copy_from(TimeAvgConfig *src)
60 this->frames = src->frames;
61 this->mode = src->mode;
62 this->paranoid = src->paranoid;
63 this->nosubtract = src->nosubtract;
64 this->threshold = src->threshold;
65 this->border = src->border;
68 int TimeAvgConfig::equivalent(TimeAvgConfig *src)
70 return frames == src->frames &&
72 paranoid == src->paranoid &&
73 nosubtract == src->nosubtract &&
74 threshold == src->threshold &&
75 border == src->border;
90 TimeAvgMain::TimeAvgMain(PluginServer *server)
91 : PluginVClient(server)
97 history_start = -0x7fffffff;
103 TimeAvgMain::~TimeAvgMain()
107 if(accumulation) delete [] accumulation;
110 for(int i = 0; i < config.frames; i++)
114 if(history_frame) delete [] history_frame;
115 if(history_valid) delete [] history_valid;
118 const char* TimeAvgMain::plugin_title() { return _("Time Average"); }
119 int TimeAvgMain::is_realtime() { return 1; }
123 NEW_WINDOW_MACRO(TimeAvgMain, TimeAvgWindow);
127 int TimeAvgMain::process_buffer(VFrame *frame,
128 int64_t start_position,
131 int h = frame->get_h();
132 int w = frame->get_w();
133 int color_model = frame->get_color_model();
135 int reset = load_configuration();
137 // reset buffer on the keyframes
138 int64_t actual_previous_number = start_position;
139 if(get_direction() == PLAY_FORWARD)
141 actual_previous_number--;
142 if(actual_previous_number < get_source_start())
146 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
147 if(keyframe->position > 0 &&
148 actual_previous_number < keyframe->position)
154 actual_previous_number++;
155 if(actual_previous_number >= get_source_start() + get_total_len())
159 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
160 if(keyframe->position > 0 &&
161 actual_previous_number >= keyframe->position)
166 // Allocate accumulation
167 if(!accumulation || reset)
169 if(!accumulation) accumulation = new unsigned char[w *
171 BC_CModels::components(color_model) *
172 MAX(sizeof(float), sizeof(int))];
173 reset_accum(w, h, color_model);
176 if(!config.nosubtract &&
177 (config.mode == TimeAvgConfig::AVERAGE ||
178 config.mode == TimeAvgConfig::ACCUMULATE ||
179 config.mode == TimeAvgConfig::GREATER ||
180 config.mode == TimeAvgConfig::LESS))
182 // Reallocate history
185 if(config.frames != history_size)
188 int64_t *history_frame2;
190 history2 = new VFrame*[config.frames];
191 history_frame2 = new int64_t[config.frames];
192 history_valid2 = new int[config.frames];
194 // Copy existing frames over
196 for(i = 0, j = 0; i < config.frames && j < history_size; i++, j++)
198 history2[i] = history[j];
199 history_frame2[i] = history_frame[i];
200 history_valid2[i] = history_valid[i];
203 // Delete extra previous frames and subtract from accumulation
204 for( ; j < history_size; j++)
206 subtract_accum(history[j]);
210 delete [] history_frame;
211 delete [] history_valid;
215 for( ; i < config.frames; i++)
217 history2[i] = new VFrame(w, h, color_model);
218 history_frame2[i] = -0x7fffffff;
219 history_valid2[i] = 0;
223 history_frame = history_frame2;
224 history_valid = history_valid2;
226 history_size = config.frames;
232 history = new VFrame*[config.frames];
233 for(int i = 0; i < config.frames; i++)
234 history[i] = new VFrame(w, h, color_model);
235 history_size = config.frames;
236 history_frame = new int64_t[config.frames];
237 bzero(history_frame, sizeof(int64_t) * config.frames);
238 history_valid = new int[config.frames];
239 bzero(history_valid, sizeof(int) * config.frames);
242 //printf("TimeAvgMain::process_buffer %d\n", __LINE__);
247 // Create new history frames based on current frame
248 int64_t *new_history_frames = new int64_t[history_size];
249 for(int i = 0; i < history_size; i++)
251 new_history_frames[history_size - i - 1] = start_position - i;
254 // Subtract old history frames from accumulation buffer
255 // which are not in the new vector
257 for(int i = 0; i < history_size; i++)
259 // Old frame is valid
263 for(int j = 0; j < history_size; j++)
265 // Old frame is equal to a new frame
266 if(history_frame[i] == new_history_frames[j])
273 // Didn't find old frame in new frames
276 if(config.mode == TimeAvgConfig::AVERAGE ||
277 config.mode == TimeAvgConfig::ACCUMULATE)
279 subtract_accum(history[i]);
282 history_valid[i] = 0;
287 // If all frames are still valid, assume tweek occurred upstream and reload.
288 if(config.paranoid && no_change)
290 for(int i = 0; i < history_size; i++)
292 history_valid[i] = 0;
295 if(config.mode == TimeAvgConfig::AVERAGE ||
296 config.mode == TimeAvgConfig::ACCUMULATE)
298 reset_accum(w, h, color_model);
302 // Add new history frames which are not in the old vector
303 for(int i = 0; i < history_size; i++)
305 // Find new frame in old vector
307 for(int j = 0; j < history_size; j++)
309 if(history_valid[j] && history_frame[j] == new_history_frames[i])
316 // Didn't find new frame in old vector
319 // Get first unused entry
320 for(int j = 0; j < history_size; j++)
322 if(!history_valid[j])
324 // Load new frame into it
325 history_frame[j] = new_history_frames[i];
326 history_valid[j] = 1;
327 read_frame(history[j],
332 if(config.mode == TimeAvgConfig::AVERAGE ||
333 config.mode == TimeAvgConfig::ACCUMULATE)
335 add_accum(history[j]);
342 delete [] new_history_frames;
345 // No history subtraction
349 for(int i = 0; i < config.frames; i++)
355 if(history_frame) delete [] history_frame;
356 if(history_valid) delete [] history_valid;
361 // Clamp prev_frame to history size
362 prev_frame = MAX(start_position - config.frames + 1, prev_frame);
364 // Force reload if not repositioned or just started
365 if( (config.paranoid && prev_frame == start_position) ||
368 //printf("TimeAvgMain::process_buffer %d\n", __LINE__);
369 prev_frame = start_position - config.frames + 1;
370 prev_frame = MAX(0, prev_frame);
371 reset_accum(w, h, color_model);
374 // printf("TimeAvgMain::process_buffer %d prev_frame=%jd start_position=%jd\n",
375 // __LINE__, prev_frame, start_position);
376 for(int64_t i = prev_frame; i <= start_position; i++)
384 printf("TimeAvgMain::process_buffer %d prev_frame=%jd start_position=%jd i=%jd\n",
385 __LINE__, prev_frame, start_position, i);
388 // If we don't add 1, it rereads the frame again
389 prev_frame = start_position + 1;
398 // Transfer accumulation to output with division if average is desired.
399 transfer_accum(frame);
401 //printf("TimeAvgMain::process_buffer %d\n", __LINE__);
416 // Reset accumulation
417 #define SET_ACCUM(type, components, luma, chroma) \
419 type *row = (type*)accumulation; \
422 for(int i = 0; i < w * h; i++) \
427 if(components == 4) *row++ = luma; \
432 bzero(row, w * h * sizeof(type) * components); \
437 void TimeAvgMain::reset_accum(int w, int h, int color_model)
439 if(config.mode == TimeAvgConfig::LESS)
444 SET_ACCUM(int, 3, 0xff, 0xff)
447 SET_ACCUM(float, 3, 1.0, 1.0)
450 SET_ACCUM(int, 4, 0xff, 0xff)
453 SET_ACCUM(float, 4, 1.0, 1.0)
456 SET_ACCUM(int, 3, 0xff, 0x80)
459 SET_ACCUM(int, 4, 0xff, 0x80)
462 SET_ACCUM(int, 3, 0xffff, 0x8000)
464 case BC_YUVA16161616:
465 SET_ACCUM(int, 4, 0xffff, 0x8000)
474 SET_ACCUM(int, 3, 0x0, 0x0)
477 SET_ACCUM(float, 3, 0x0, 0x0)
480 SET_ACCUM(int, 4, 0x0, 0x0)
483 SET_ACCUM(float, 4, 0x0, 0x0)
486 SET_ACCUM(int, 3, 0x0, 0x80)
489 SET_ACCUM(int, 4, 0x0, 0x80)
492 SET_ACCUM(int, 3, 0x0, 0x8000)
494 case BC_YUVA16161616:
495 SET_ACCUM(int, 4, 0x0, 0x8000)
501 #define RGB_TO_VALUE(r, g, b) \
502 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
504 // Only AVERAGE and ACCUMULATE use this
505 #define SUBTRACT_ACCUM(type, \
510 for(int i = 0; i < h; i++) \
512 accum_type *accum_row = (accum_type*)accumulation + \
513 i * w * components; \
514 type *frame_row = (type*)frame->get_rows()[i]; \
515 for(int j = 0; j < w; j++) \
517 *accum_row++ -= *frame_row++; \
518 *accum_row++ -= (accum_type)*frame_row++ - chroma; \
519 *accum_row++ -= (accum_type)*frame_row++ - chroma; \
520 if(components == 4) *accum_row++ -= *frame_row++; \
526 void TimeAvgMain::subtract_accum(VFrame *frame)
529 if(config.nosubtract) return;
530 int w = frame->get_w();
531 int h = frame->get_h();
533 switch(frame->get_color_model())
536 SUBTRACT_ACCUM(unsigned char, int, 3, 0x0)
539 SUBTRACT_ACCUM(float, float, 3, 0x0)
542 SUBTRACT_ACCUM(unsigned char, int, 4, 0x0)
545 SUBTRACT_ACCUM(float, float, 4, 0x0)
548 SUBTRACT_ACCUM(unsigned char, int, 3, 0x80)
551 SUBTRACT_ACCUM(unsigned char, int, 4, 0x80)
554 SUBTRACT_ACCUM(uint16_t, int, 3, 0x8000)
556 case BC_YUVA16161616:
557 SUBTRACT_ACCUM(uint16_t, int, 4, 0x8000)
563 // The behavior has to be very specific to the color model because we rely on
564 // the value of full black to determine what pixel to show.
565 #define ADD_ACCUM(type, accum_type, components, chroma, max) \
567 if(config.mode == TimeAvgConfig::REPLACE) \
569 type threshold = config.threshold; \
570 if(sizeof(type) == 4) \
572 /* Compare all pixels if border */ \
573 if(config.border > 0) \
575 int border = config.border; \
576 int h_border = h - border - 1; \
577 int w_border = w - border - 1; \
578 int kernel_size = (border * 2 + 1) * (border * 2 + 1); \
579 for(int i = border; i < h_border; i++) \
581 for(int j = border; j < w_border; j++) \
584 for(int k = -border; k <= border; k++) \
586 type *frame_row = (type*)frame->get_rows()[i + k]; \
587 for(int l = -border; l <= border; l++) \
589 type *frame_pixel = frame_row + (j + l) * components; \
590 /* Compare alpha if 4 channel */ \
591 if(components == 4) \
593 if(frame_pixel[3] > threshold) \
597 if(sizeof(type) == 4) \
599 /* Compare luma if 3 channel */ \
600 if(RGB_TO_VALUE(frame_pixel[0], frame_pixel[1], frame_pixel[2]) >= \
609 if(frame_pixel[0] >= threshold) \
615 if(RGB_TO_VALUE(frame_pixel[0], frame_pixel[1], frame_pixel[2]) >= threshold) \
622 if(copy_it == kernel_size) \
624 accum_type *accum_row = (accum_type*)accumulation + \
625 i * w * components + j * components; \
626 type *frame_row = (type*)frame->get_rows()[i] + j * components; \
627 *accum_row++ = *frame_row++; \
628 *accum_row++ = *frame_row++; \
629 *accum_row++ = *frame_row++; \
630 if(components == 4) *accum_row++ = *frame_row++; \
637 /* Compare only relevant pixel if no border */ \
639 for(int i = 0; i < h; i++) \
641 accum_type *accum_row = (accum_type*)accumulation + \
642 i * w * components; \
643 type *frame_row = (type*)frame->get_rows()[i]; \
644 for(int j = 0; j < w; j++) \
647 /* Compare alpha if 4 channel */ \
648 if(components == 4) \
650 if(frame_row[3] > threshold) \
654 if(sizeof(type) == 4) \
656 /* Compare luma if 3 channel */ \
657 if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) >= \
666 if(frame_row[0] >= threshold) \
672 if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) >= threshold) \
679 *accum_row++ = *frame_row++; \
680 *accum_row++ = *frame_row++; \
681 *accum_row++ = *frame_row++; \
682 if(components == 4) *accum_row++ = *frame_row++; \
686 frame_row += components; \
687 accum_row += components; \
694 if(config.mode == TimeAvgConfig::GREATER) \
696 for(int i = 0; i < h; i++) \
698 accum_type *accum_row = (accum_type*)accumulation + \
699 i * w * components; \
700 type *frame_row = (type*)frame->get_rows()[i]; \
701 for(int j = 0; j < w; j++) \
704 /* Compare alpha if 4 channel */ \
705 if(components == 4) \
707 if(frame_row[3] > accum_row[3]) copy_it = 1; \
712 /* Compare YUV luma if 3 channel */ \
713 if(frame_row[0] > accum_row[0]) copy_it = 1; \
717 /* Compare RGB luma if 3 channel */ \
718 if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) > \
719 RGB_TO_VALUE(accum_row[0], accum_row[1], accum_row[2])) \
725 *accum_row++ = *frame_row++; \
726 *accum_row++ = *frame_row++; \
727 *accum_row++ = *frame_row++; \
728 if(components == 4) *accum_row++ = *frame_row++; \
732 accum_row += components; \
733 frame_row += components; \
739 if(config.mode == TimeAvgConfig::LESS) \
741 for(int i = 0; i < h; i++) \
743 accum_type *accum_row = (accum_type*)accumulation + \
744 i * w * components; \
745 type *frame_row = (type*)frame->get_rows()[i]; \
746 for(int j = 0; j < w; j++) \
749 /* Compare alpha if 4 channel */ \
750 if(components == 4) \
752 if(frame_row[3] < accum_row[3]) copy_it = 1; \
757 /* Compare YUV luma if 3 channel */ \
758 if(frame_row[0] < accum_row[0]) copy_it = 1; \
762 /* Compare RGB luma if 3 channel */ \
763 if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) < \
764 RGB_TO_VALUE(accum_row[0], accum_row[1], accum_row[2])) \
770 *accum_row++ = *frame_row++; \
771 *accum_row++ = *frame_row++; \
772 *accum_row++ = *frame_row++; \
773 if(components == 4) *accum_row++ = *frame_row++; \
777 accum_row += components; \
778 frame_row += components; \
785 for(int i = 0; i < h; i++) \
787 accum_type *accum_row = (accum_type*)accumulation + \
788 i * w * components; \
789 type *frame_row = (type*)frame->get_rows()[i]; \
790 for(int j = 0; j < w; j++) \
792 *accum_row++ += *frame_row++; \
793 *accum_row++ += (accum_type)*frame_row++ - chroma; \
794 *accum_row++ += (accum_type)*frame_row++ - chroma; \
795 if(components == 4) *accum_row++ += *frame_row++; \
802 void TimeAvgMain::add_accum(VFrame *frame)
804 int w = frame->get_w();
805 int h = frame->get_h();
807 switch(frame->get_color_model())
810 ADD_ACCUM(unsigned char, int, 3, 0x0, 0xff)
813 ADD_ACCUM(float, float, 3, 0x0, 1.0)
816 ADD_ACCUM(unsigned char, int, 4, 0x0, 0xff)
819 ADD_ACCUM(float, float, 4, 0x0, 1.0)
822 ADD_ACCUM(unsigned char, int, 3, 0x80, 0xff)
825 ADD_ACCUM(unsigned char, int, 4, 0x80, 0xff)
828 ADD_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
830 case BC_YUVA16161616:
831 ADD_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
836 #define TRANSFER_ACCUM(type, accum_type, components, chroma, max) \
838 if(config.mode == TimeAvgConfig::AVERAGE) \
840 accum_type denominator = config.frames; \
841 for(int i = 0; i < h; i++) \
843 accum_type *accum_row = (accum_type*)accumulation + \
844 i * w * components; \
845 type *frame_row = (type*)frame->get_rows()[i]; \
846 for(int j = 0; j < w; j++) \
848 *frame_row++ = *accum_row++ / denominator; \
849 *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
850 *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
851 if(components == 4) *frame_row++ = *accum_row++ / denominator; \
856 /* Rescan history every time for these modes */ \
857 if(!config.nosubtract && config.mode == TimeAvgConfig::GREATER) \
859 frame->copy_from(history[0]); \
860 for(int k = 1; k < config.frames; k++) \
862 VFrame *history_frame = history[k]; \
864 for(int i = 0; i < h; i++) \
866 type *history_row = (type*)history_frame->get_rows()[i]; \
867 type *frame_row = (type*)frame->get_rows()[i]; \
869 for(int j = 0; j < w; j++) \
872 /* Compare alpha if 4 channel */ \
873 if(components == 4) \
875 if(history_row[3] > frame_row[3]) copy_it = 1; \
880 /* Compare YUV luma if 3 channel */ \
881 if(history_row[0] > frame_row[0]) copy_it = 1; \
885 /* Compare RGB luma if 3 channel */ \
886 if(RGB_TO_VALUE(history_row[0], history_row[1], history_row[2]) > \
887 RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2])) \
893 *frame_row++ = *history_row++; \
894 *frame_row++ = *history_row++; \
895 *frame_row++ = *history_row++; \
896 if(components == 4) *frame_row++ = *history_row++; \
900 frame_row += components; \
901 history_row += components; \
908 if(!config.nosubtract && config.mode == TimeAvgConfig::LESS) \
910 frame->copy_from(history[0]); \
911 for(int k = 1; k < config.frames; k++) \
913 VFrame *history_frame = history[k]; \
915 for(int i = 0; i < h; i++) \
917 type *history_row = (type*)history_frame->get_rows()[i]; \
918 type *frame_row = (type*)frame->get_rows()[i]; \
920 for(int j = 0; j < w; j++) \
923 /* Compare alpha if 4 channel */ \
924 if(components == 4) \
926 if(history_row[3] < frame_row[3]) copy_it = 1; \
931 /* Compare YUV luma if 3 channel */ \
932 if(history_row[0] < frame_row[0]) copy_it = 1; \
936 /* Compare RGB luma if 3 channel */ \
937 if(RGB_TO_VALUE(history_row[0], history_row[1], history_row[2]) < \
938 RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2])) \
944 *frame_row++ = *history_row++; \
945 *frame_row++ = *history_row++; \
946 *frame_row++ = *history_row++; \
947 if(components == 4) *frame_row++ = *history_row++; \
951 frame_row += components; \
952 history_row += components; \
960 for(int i = 0; i < h; i++) \
962 accum_type *accum_row = (accum_type*)accumulation + \
963 i * w * components; \
964 type *frame_row = (type*)frame->get_rows()[i]; \
965 for(int j = 0; j < w; j++) \
967 *frame_row++ = *accum_row++; \
968 *frame_row++ = *accum_row++; \
969 *frame_row++ = *accum_row++; \
970 if(components == 4) *frame_row++ = *accum_row++; \
977 void TimeAvgMain::transfer_accum(VFrame *frame)
979 int w = frame->get_w();
980 int h = frame->get_h();
982 switch(frame->get_color_model())
985 TRANSFER_ACCUM(unsigned char, int, 3, 0x0, 0xff)
988 TRANSFER_ACCUM(float, float, 3, 0x0, 1)
991 TRANSFER_ACCUM(unsigned char, int, 4, 0x0, 0xff)
994 TRANSFER_ACCUM(float, float, 4, 0x0, 1)
997 TRANSFER_ACCUM(unsigned char, int, 3, 0x80, 0xff)
1000 TRANSFER_ACCUM(unsigned char, int, 4, 0x80, 0xff)
1003 TRANSFER_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
1005 case BC_YUVA16161616:
1006 TRANSFER_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
1013 int TimeAvgMain::load_configuration()
1015 KeyFrame *prev_keyframe;
1016 TimeAvgConfig old_config;
1017 old_config.copy_from(&config);
1019 prev_keyframe = get_prev_keyframe(get_source_position());
1020 read_data(prev_keyframe);
1021 return !old_config.equivalent(&config);
1024 void TimeAvgMain::save_data(KeyFrame *keyframe)
1028 // cause data to be stored directly in text
1029 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
1030 output.tag.set_title("TIME_AVERAGE");
1031 output.tag.set_property("FRAMES", config.frames);
1032 output.tag.set_property("MODE", config.mode);
1033 output.tag.set_property("PARANOID", config.paranoid);
1034 output.tag.set_property("NOSUBTRACT", config.nosubtract);
1035 output.tag.set_property("THRESHOLD", config.threshold);
1036 output.tag.set_property("BORDER", config.border);
1037 output.append_tag();
1038 output.tag.set_title("/TIME_AVERAGE");
1039 output.append_tag();
1040 output.append_newline();
1041 output.terminate_string();
1044 void TimeAvgMain::read_data(KeyFrame *keyframe)
1048 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
1050 while(!input.read_tag())
1052 if(input.tag.title_is("TIME_AVERAGE"))
1054 config.frames = input.tag.get_property("FRAMES", config.frames);
1055 config.mode = input.tag.get_property("MODE", config.mode);
1056 config.paranoid = input.tag.get_property("PARANOID", config.paranoid);
1057 config.nosubtract = input.tag.get_property("NOSUBTRACT", config.nosubtract);
1058 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
1059 config.border = input.tag.get_property("BORDER", config.border);
1065 void TimeAvgMain::update_gui()
1069 if(load_configuration())
1071 thread->window->lock_window("TimeAvgMain::update_gui");
1072 ((TimeAvgWindow*)thread->window)->total_frames->update(config.frames);
1073 ((TimeAvgWindow*)thread->window)->threshold->update(config.threshold);
1074 ((TimeAvgWindow*)thread->window)->update_toggles();
1075 ((TimeAvgWindow*)thread->window)->paranoid->update(config.paranoid);
1076 ((TimeAvgWindow*)thread->window)->no_subtract->update(config.nosubtract);
1077 ((TimeAvgWindow*)thread->window)->threshold->update(config.threshold);
1078 ((TimeAvgWindow*)thread->window)->border->update(config.border);
1079 thread->window->unlock_window();