1 #include "bcdisplayinfo.h"
9 #include "pluginvclient.h"
10 #include "transportque.inc"
17 #define TOP_FIELD_FIRST 0
18 #define BOTTOM_FIELD_FIRST 1
21 class FrameFieldWindow;
25 // 601 to RGB expansion is provided as a convenience for OpenGL users since
26 // frame bobbing is normally done during playback together with 601 to RGB expansion.
27 // It's not optimized for software.
30 class FrameFieldConfig
34 int equivalent(FrameFieldConfig &src);
41 class FrameFieldTop : public BC_Radial
44 FrameFieldTop(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
47 FrameFieldWindow *gui;
51 class FrameFieldBottom : public BC_Radial
54 FrameFieldBottom(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
57 FrameFieldWindow *gui;
61 class FrameFieldDouble : public BC_CheckBox
64 FrameFieldDouble(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
67 FrameFieldWindow *gui;
70 class FrameFieldShift : public BC_CheckBox
73 FrameFieldShift(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
76 FrameFieldWindow *gui;
79 class FrameFieldAvg : public BC_CheckBox
82 FrameFieldAvg(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
85 FrameFieldWindow *gui;
88 class FrameFieldWindow : public BC_Window
91 FrameFieldWindow(FrameField *plugin, int x, int y);
92 void create_objects();
96 FrameFieldBottom *bottom;
100 PLUGIN_THREAD_HEADER(FrameField, FrameFieldThread, FrameFieldWindow)
104 class FrameField : public PluginVClient
107 FrameField(PluginServer *server);
110 PLUGIN_CLASS_MEMBERS(FrameFieldConfig, FrameFieldThread);
112 int process_buffer(VFrame *frame,
113 int64_t start_position,
118 void save_data(KeyFrame *keyframe);
119 void read_data(KeyFrame *keyframe);
122 // Constructs odd or even rows from the average of the surrounding rows.
123 void average_rows(int offset, VFrame *frame);
127 // Last frame requested
130 int64_t field_number;
132 int64_t current_frame_number;
134 int64_t src_frame_number;
137 // Temporary storage of input frame for OpenGL
138 BC_Texture *src_texture;
139 // Signal OpenGL handler a new frame was read.
141 // Reading frames at a different rate requires us to store the aggregation
142 // state when the frame isn't read.
143 int aggregate_rgb601;
144 int rgb601_direction;
155 REGISTER_PLUGIN(FrameField)
159 FrameFieldConfig::FrameFieldConfig()
161 field_dominance = TOP_FIELD_FIRST;
164 int FrameFieldConfig::equivalent(FrameFieldConfig &src)
166 return src.field_dominance == field_dominance;
176 FrameFieldWindow::FrameFieldWindow(FrameField *plugin, int x, int y)
177 : BC_Window(plugin->gui_string,
188 this->plugin = plugin;
191 void FrameFieldWindow::create_objects()
194 add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
195 y += top->get_h() + 5;
196 add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
197 y += bottom->get_h() + 5;
202 WINDOW_CLOSE_EVENT(FrameFieldWindow)
215 FrameFieldTop::FrameFieldTop(FrameField *plugin,
216 FrameFieldWindow *gui,
221 plugin->config.field_dominance == TOP_FIELD_FIRST,
222 _("Top field first"))
224 this->plugin = plugin;
228 int FrameFieldTop::handle_event()
230 plugin->config.field_dominance = TOP_FIELD_FIRST;
231 gui->bottom->update(0);
232 plugin->send_configure_change();
240 FrameFieldBottom::FrameFieldBottom(FrameField *plugin,
241 FrameFieldWindow *gui,
246 plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
247 _("Bottom field first"))
249 this->plugin = plugin;
253 int FrameFieldBottom::handle_event()
255 plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
257 plugin->send_configure_change();
267 PLUGIN_THREAD_OBJECT(FrameField, FrameFieldThread, FrameFieldWindow)
284 FrameField::FrameField(PluginServer *server)
285 : PluginVClient(server)
287 PLUGIN_CONSTRUCTOR_MACRO
290 src_frame_number = -1;
293 aggregate_rgb601 = 0;
294 rgb601_direction = 0;
298 FrameField::~FrameField()
300 PLUGIN_DESTRUCTOR_MACRO
302 if(src_frame) delete src_frame;
303 if(src_texture) delete src_texture;
307 // 0 - current frame field 0, prev frame field 1
308 // 1 - current frame field 0, current frame field 1, copy current to prev
309 // 2 - current frame field 0, prev frame field 1
311 int FrameField::process_buffer(VFrame *frame,
312 int64_t start_position,
315 load_configuration();
319 // Calculate current field based on absolute position so the algorithm isn't
320 // relative to where playback started.
321 field_number = get_source_position() % 2;
323 if (get_direction() == PLAY_REVERSE)
326 field_number = (field_number + 1) % 2;
330 current_frame_number = start_position / 2;
335 // Read new frames directly into output frame for hardware
339 // Read into temporary for software
341 src_frame->get_color_model() != frame->get_color_model())
349 src_frame = new VFrame(0,
352 frame->get_color_model());
358 // Import source frame at half frame rate
359 if(current_frame_number != src_frame_number ||
360 // If same frame was requested, assume it was a configuration change and reprocess.
361 start_position == last_frame)
365 current_frame_number,
368 src_frame_number = current_frame_number;
379 int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
383 unsigned char **src_rows = src_frame->get_rows();
384 unsigned char **output_rows = frame->get_rows();
388 if(field_number == 0)
390 if(config.field_dominance == TOP_FIELD_FIRST)
392 for(int i = 0; i < frame->get_h() - 1; i += 2)
394 // Copy even lines of src to both lines of output
395 memcpy(output_rows[i],
400 // Average empty rows
401 /* if(config.avg) */ average_rows(0, frame);
405 for(int i = 0; i < frame->get_h() - 1; i += 2)
407 // Copy odd lines of current to both lines of output with shift up.
408 memcpy(output_rows[i + 1],
413 // Average empty rows
414 /* if(config.avg) */ average_rows(1, frame);
420 if(config.field_dominance == TOP_FIELD_FIRST)
422 for(int i = 0; i < frame->get_h() - 1; i += 2)
424 // Copy odd lines of src to both lines of output
425 memcpy(output_rows[i + 1],
430 // Average empty rows
431 /* if(config.avg) */ average_rows(1, frame);
435 for(int i = 0; i < frame->get_h() - 1; i += 2)
437 // Copy even lines of src to both lines of output.
438 memcpy(output_rows[i],
443 // Average empty rows
444 /* if(config.avg) */ average_rows(0, frame);
448 last_frame = start_position;
453 // Averaging 2 pixels
454 #define AVERAGE(type, temp_type, components, offset) \
456 type **rows = (type**)frame->get_rows(); \
457 int w = frame->get_w(); \
458 int h = frame->get_h(); \
459 int row_size = components * w; \
460 for(int i = offset; i < h - 3; i += 2) \
462 type *row1 = rows[i]; \
463 type *row2 = rows[i + 1]; \
464 type *row3 = rows[i + 2]; \
465 for(int j = 0; j < row_size; j++) \
467 temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
468 *row2++ = (sum / 2); \
473 // Averaging 6 pixels
474 #define AVERAGE_BAK(type, components, offset) \
476 type **rows = (type**)frame->get_rows(); \
477 int w = frame->get_w(); \
478 int h = frame->get_h(); \
480 for(int i = offset; i < h - 3; i += 2) \
482 type *row1 = rows[i]; \
483 type *row2 = rows[i + 1]; \
484 type *row3 = rows[i + 2]; \
486 int64_t pixel1[4], pixel2[4], pixel3[4]; \
487 int64_t pixel4[4], pixel5[4], pixel6[4]; \
490 for(int j = 0; j < components; j++) \
492 pixel1[j] = *row1++; \
493 pixel4[j] = *row3++; \
494 *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
497 for(int j = 2; j < row_size; j++) \
499 for(int k = 0; k < components; k++) \
501 pixel2[k] = *row1++; \
502 pixel5[k] = *row3++; \
505 for(int k = 0; k < components; k++) \
509 *row2++ = (pixel1[k] + \
515 pixel1[k] = pixel2[k]; \
516 pixel4[k] = pixel5[k]; \
521 for(int j = 0; j < components; j++) \
523 *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
528 void FrameField::average_rows(int offset, VFrame *frame)
530 //printf("FrameField::average_rows 1 %d\n", offset);
531 switch(frame->get_color_model())
535 AVERAGE(unsigned char, int64_t, 3, offset);
538 AVERAGE(float, float, 3, offset);
542 AVERAGE(unsigned char, int64_t, 4, offset);
545 AVERAGE(float, float, 4, offset);
549 AVERAGE(uint16_t, int64_t, 3, offset);
551 case BC_RGBA16161616:
552 case BC_YUVA16161616:
553 AVERAGE(uint16_t, int64_t, 4, offset);
560 char* FrameField::plugin_title() { return N_("Frames to fields"); }
561 int FrameField::is_realtime() { return 1; }
563 NEW_PICON_MACRO(FrameField)
565 SHOW_GUI_MACRO(FrameField, FrameFieldThread)
567 RAISE_WINDOW_MACRO(FrameField)
569 SET_STRING_MACRO(FrameField);
571 int FrameField::load_configuration()
573 KeyFrame *prev_keyframe;
574 FrameFieldConfig old_config = config;
576 prev_keyframe = get_prev_keyframe(get_source_position());
577 read_data(prev_keyframe);
579 return !old_config.equivalent(config);
582 int FrameField::load_defaults()
584 char directory[BCTEXTLEN];
585 // set the default directory
586 sprintf(directory, "%sframefield.rc", BCASTDIR);
589 defaults = new BC_Hash(directory);
592 config.field_dominance = defaults->get("DOMINANCE", config.field_dominance);
596 int FrameField::save_defaults()
598 defaults->update("DOMINANCE", config.field_dominance);
603 void FrameField::save_data(KeyFrame *keyframe)
607 // cause data to be stored directly in text
608 output.set_shared_string(keyframe->data, MESSAGESIZE);
609 output.tag.set_title("FRAME_FIELD");
610 output.tag.set_property("DOMINANCE", config.field_dominance);
612 output.terminate_string();
615 void FrameField::read_data(KeyFrame *keyframe)
619 input.set_shared_string(keyframe->data, strlen(keyframe->data));
623 while(!input.read_tag())
625 if(input.tag.title_is("FRAME_FIELD"))
627 config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
632 void FrameField::update_gui()
636 if(load_configuration())
638 thread->window->lock_window();
639 thread->window->top->update(config.field_dominance == TOP_FIELD_FIRST);
640 thread->window->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
641 thread->window->unlock_window();
646 int FrameField::handle_opengl()
649 static char *field_frag =
650 "uniform sampler2D tex;\n"
651 "uniform float double_line_h;\n"
652 "uniform float y_offset;\n"
655 " vec2 coord = gl_TexCoord[0].st;\n"
656 /* Number of double lines + fraction of current double line */
657 " float half_y = (coord.y - y_offset) / double_line_h;\n"
658 /* Lines comprising current double line */
659 " float line1 = floor(half_y) * double_line_h + y_offset;\n"
660 " float line2 = line1 + double_line_h;\n"
661 /* Distance from line1 to line2 */
662 " float frac = fract(half_y);\n"
663 " gl_FragColor = mix(\n"
664 " texture2D(tex, vec2(coord.x, line1)), \n"
665 " texture2D(tex, vec2(coord.x, line2)), \n"
669 static char *_601_to_rgb_frag =
672 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
675 static char *_601_to_yuv_frag =
678 " gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
681 static char *rgb_to_601_frag =
684 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
687 static char *yuv_to_601_frag =
690 " gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
696 if(get_output()->get_opengl_state() != VFrame::SCREEN)
698 // Copy new frame to temporary texture
699 get_output()->to_texture();
701 // Draw it only to copy it to the temporary.
702 get_output()->enable_opengl();
703 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
704 get_output()->bind_texture(0);
705 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
706 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
707 get_output()->draw_texture();
710 get_output()->enable_opengl();
711 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
712 glActiveTexture(GL_TEXTURE0);
713 BC_Texture::new_texture(&src_texture,
714 get_output()->get_w(),
715 get_output()->get_h(),
716 get_output()->get_color_model());
717 src_texture->bind(0);
718 glCopyTexSubImage2D(GL_TEXTURE_2D,
724 get_output()->get_w(),
725 get_output()->get_h());
727 // Store aggregation state only when reading a frame
728 //printf("FrameField::handle_opengl %p\n", get_output());
729 //get_output()->dump_stacks();
730 if(prev_effect_is("RGB - 601") ||
731 next_effect_is("RGB - 601"))
733 aggregate_rgb601 = 1;
734 rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
737 aggregate_rgb601 = 0;
741 get_output()->enable_opengl();
744 unsigned int frag = 0;
745 float y_offset = 0.0;
746 if(field_number == 0)
748 if(config.field_dominance == BOTTOM_FIELD_FIRST)
753 if(config.field_dominance == TOP_FIELD_FIRST)
757 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
758 glActiveTexture(GL_TEXTURE0);
759 BC_Texture::new_texture(&src_texture,
760 get_output()->get_w(),
761 get_output()->get_h(),
762 get_output()->get_color_model());
765 char *shaders[3] = { 0, 0, 0 };
766 shaders[0] = field_frag;
769 // Aggregate with other effect
770 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
773 if(rgb601_direction == 1)
775 if(cmodel_is_yuv(get_output()->get_color_model()))
776 shaders[1] = yuv_to_601_frag;
778 shaders[1] = rgb_to_601_frag;
781 if(rgb601_direction == 2)
783 if(cmodel_is_yuv(get_output()->get_color_model()))
784 shaders[1] = _601_to_yuv_frag;
786 shaders[1] = _601_to_rgb_frag;
792 frag = VFrame::make_shader(0, shaders[0], shaders[1], shaders[2], 0);
798 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
799 glUniform1f(glGetUniformLocation(frag, "double_line_h"),
800 2.0 / src_texture->get_texture_h());
801 glUniform1f(glGetUniformLocation(frag, "y_offset"),
802 y_offset / src_texture->get_texture_h());
807 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
808 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
809 get_output()->draw_texture();
812 get_output()->set_opengl_state(VFrame::SCREEN);
814 // Reset for other operations
815 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
816 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);