#include "bccmodels.h" #include "file.h" #include "filexml.h" #include "rgb601.h" #include "rgb601window.h" #include #include PluginClient* new_plugin(PluginServer *server) { return new RGB601Main(server); } RGB601Config::RGB601Config() { frame_offset = 0; first_field = 0; automatic = 1; auto_threshold = 2; } RGB601Main::RGB601Main(PluginServer *server) : PluginVClient(server) { thread = 0; load_defaults(); } RGB601Main::~RGB601Main() { if(thread) { // Set result to 0 to indicate a server side close thread->window->set_done(0); thread->completion.lock(); delete thread; } save_defaults(); delete defaults; } char* RGB601Main::plugin_title() { return "Inverse Telecine"; } int RGB601Main::is_realtime() { return 1; } int RGB601Main::load_defaults() { char directory[1024], string[1024]; // set the default directory sprintf(directory, "%s/rgb601.rc", File::get_config_path()); // load the defaults defaults = new Defaults(directory); defaults->load(); config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset); config.first_field = defaults->get("FIRST_FIELD", config.first_field); config.automatic = defaults->get("AUTOMATIC", config.automatic); config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold); return 0; } int RGB601Main::save_defaults() { defaults->update("FRAME_OFFSET", config.frame_offset); defaults->update("FIRST_FIELD", config.first_field); defaults->update("AUTOMATIC", config.automatic); defaults->update("AUTO_THRESHOLD", config.auto_threshold); defaults->save(); return 0; } void RGB601Main::load_configuration() { KeyFrame *prev_keyframe, *next_keyframe; prev_keyframe = get_prev_keyframe(-1); next_keyframe = get_next_keyframe(-1); // Must also switch between interpolation between keyframes and using first keyframe read_data(prev_keyframe); } void RGB601Main::save_data(KeyFrame *keyframe) { FileXML output; // cause data to be stored directly in text output.set_shared_string(keyframe->get_data(), -MESSAGESIZE); output.tag.set_title("RGB601"); output.tag.set_property("FRAME_OFFSET", config.frame_offset); output.tag.set_property("FIRST_FIELD", config.first_field); output.tag.set_property("AUTOMATIC", config.automatic); output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold); output.append_tag(); output.tag.set_title("/RGB601"); output.append_tag(); output.terminate_string(); } void RGB601Main::read_data(KeyFrame *keyframe) { FileXML input; input.set_shared_string(keyframe->get_data(), strlen(keyframe->get_data())); int result = 0; float new_threshold; while(!result) { result = input.read_tag(); if(!result) { if(input.tag.title_is("RGB601")) { config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset); config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field); config.automatic = input.tag.get_property("AUTOMATIC", config.automatic); new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold); } } } // if(new_threshold != config.auto_threshold) // { // config.auto_threshold = new_threshold; // average = -1; // } if(thread) { thread->window->frame_offset->update((long)config.frame_offset); thread->window->first_field->update(config.first_field); thread->window->automatic->update(config.automatic); // thread->window->threshold->update(config.auto_threshold); } } int RGB601Main::start_realtime() { temp_frame[0] = 0; temp_frame[1] = 0; state = 0; new_field = 0; average = 0; total_average = project_frame_rate; // total_average = 5; return 0; } int RGB601Main::stop_realtime() { if(temp_frame[0]) delete temp_frame[0]; if(temp_frame[1]) delete temp_frame[1]; temp_frame[0] = 0; temp_frame[1] = 0; return 0; } // Use all channels to get more info #define COMPARE_ROWS(result, row1, row2, type, width, components) \ { \ for(int i = 0; i < width * components; i++) \ { \ result += labs(((type*)row1)[i] - ((type*)row2)[i]); \ } \ } int64_t RGB601Main::compare_fields(VFrame *frame1, VFrame *frame2, int field) { int64_t result = 0; for(int row = field; row < frame1->get_h(); row += 2) { switch(frame1->get_color_model()) { case BC_RGB888: case BC_YUV888: COMPARE_ROWS(result, frame1->get_rows()[row], frame2->get_rows()[row], unsigned char, frame1->get_w(), 3); break; case BC_RGBA8888: case BC_YUVA8888: COMPARE_ROWS(result, frame1->get_rows()[row], frame2->get_rows()[row], unsigned char, frame1->get_w(), 4); break; case BC_RGB161616: case BC_YUV161616: COMPARE_ROWS(result, frame1->get_rows()[row], frame2->get_rows()[row], u_int16_t, frame1->get_w(), 3); break; case BC_RGBA16161616: case BC_YUVA16161616: COMPARE_ROWS(result, frame1->get_rows()[row], frame2->get_rows()[row], u_int16_t, frame1->get_w(), 4); break; } } return result; } // Pattern A B BC CD D int RGB601Main::process_realtime(VFrame *input_ptr, VFrame *output_ptr) { load_configuration(); // Determine position in pattern int pattern_position = (PluginClient::source_position + config.frame_offset) % 5; //printf("RGB601Main::process_realtime %d %d\n", pattern_position, config.first_field); if(!temp_frame[0]) temp_frame[0] = new VFrame(0, input_ptr->get_w(), input_ptr->get_h(), input_ptr->get_color_model(), -1); if(!temp_frame[1]) temp_frame[1] = new VFrame(0, input_ptr->get_w(), input_ptr->get_h(), input_ptr->get_color_model(), -1); int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w(); // Determine where in the pattern we are if(config.automatic) { int64_t field1 = compare_fields(temp_frame[0], input_ptr, 0); int64_t field2 = compare_fields(temp_frame[0], input_ptr, 1); int64_t threshold = (int64_t)(config.auto_threshold * input_ptr->get_w() * input_ptr->get_h()); // if(input_ptr->get_color_model() == BC_RGBA8888 || // input_ptr->get_color_model() == BC_RGBA16161616 || // input_ptr->get_color_model() == BC_YUVA8888 || // input_ptr->get_color_model() == BC_YUVA16161616) // threshold *= 4; // else threshold *= 3; if(input_ptr->get_color_model() == BC_RGB161616 || input_ptr->get_color_model() == BC_RGBA16161616 || input_ptr->get_color_model() == BC_YUV161616 || input_ptr->get_color_model() == BC_YUVA16161616) threshold *= 0x100; temp_frame[1]->copy_from(input_ptr); // Adjust threshold over time // if(average >= 0) threshold = average; // else // average = threshold; //printf("RGB601Main::process_realtime %d %lld %lld %lld %lld\n", state, average, threshold, field1, field2); // CD if(state == 3) { state = 4; for(int i = 0; i < input_ptr->get_h(); i++) { if((i + new_field) & 1) memcpy(output_ptr->get_rows()[i], input_ptr->get_rows()[i], row_size); else memcpy(output_ptr->get_rows()[i], temp_frame[0]->get_rows()[i], row_size); } } else // A or B or D if((field1 > threshold && field2 > threshold) || (field1 <= threshold && field2 <= threshold) || state == 4) { state = 0; // Compute new threshold for next time average = (int64_t)(average * total_average + field1 + field2) / (total_average + 2); if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0]) output_ptr->copy_from(input_ptr); } else if(field1 <= threshold && field2 >= threshold) { // BC bottom field new state = 3; new_field = 1; // Compute new threshold for next time average = (int64_t)(average * total_average + field1) / (total_average + 1); for(int i = 0; i < input_ptr->get_h(); i++) { if(i & 1) memcpy(output_ptr->get_rows()[i], temp_frame[0]->get_rows()[i], row_size); else memcpy(output_ptr->get_rows()[i], input_ptr->get_rows()[i], row_size); } } else if(field1 >= threshold && field2 <= threshold) { // BC top field new state = 3; new_field = 0; // Compute new threshold for next time average = (int64_t)(average * total_average + field2) / (total_average + 1); for(int i = 0; i < input_ptr->get_h(); i++) { if(i & 1) memcpy(output_ptr->get_rows()[i], input_ptr->get_rows()[i], row_size); else memcpy(output_ptr->get_rows()[i], temp_frame[0]->get_rows()[i], row_size); } } // Swap temp frames VFrame *temp = temp_frame[0]; temp_frame[0] = temp_frame[1]; temp_frame[1] = temp; } else switch(pattern_position) { // Direct copy case 0: case 4: if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0]) output_ptr->copy_from(input_ptr); break; case 1: temp_frame[0]->copy_from(input_ptr); if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0]) output_ptr->copy_from(input_ptr); break; case 2: // Save one field for next frame. Reuse previous frame. temp_frame[1]->copy_from(input_ptr); output_ptr->copy_from(temp_frame[0]); break; case 3: // Combine previous field with current field. for(int i = 0; i < input_ptr->get_h(); i++) { if((i + config.first_field) & 1) memcpy(output_ptr->get_rows()[i], input_ptr->get_rows()[i], row_size); else memcpy(output_ptr->get_rows()[i], temp_frame[1]->get_rows()[i], row_size); } break; } return 0; } int RGB601Main::show_gui() { load_configuration(); thread = new RGB601Thread(this); thread->start(); return 0; } int RGB601Main::set_string() { if(thread) thread->window->set_title(gui_string); return 0; } void RGB601Main::raise_window() { if(thread) { thread->window->raise_window(); thread->window->flush(); } }