4 * Copyright (C) 2008 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
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
29 #include "pluginvclient.h"
30 #include "transportque.inc"
37 #define TOP_FIELD_FIRST 0
38 #define BOTTOM_FIELD_FIRST 1
41 class FrameFieldWindow;
46 class FrameFieldConfig
50 int equivalent(FrameFieldConfig &src);
57 class FrameFieldTop : public BC_Radial
60 FrameFieldTop(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
63 FrameFieldWindow *gui;
67 class FrameFieldBottom : public BC_Radial
70 FrameFieldBottom(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
73 FrameFieldWindow *gui;
77 class FrameFieldDouble : public BC_CheckBox
80 FrameFieldDouble(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
83 FrameFieldWindow *gui;
86 class FrameFieldShift : public BC_CheckBox
89 FrameFieldShift(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
92 FrameFieldWindow *gui;
95 class FrameFieldAvg : public BC_CheckBox
98 FrameFieldAvg(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
101 FrameFieldWindow *gui;
104 class FrameFieldWindow : public PluginClientWindow
107 FrameFieldWindow(FrameField *plugin);
108 void create_objects();
111 FrameFieldBottom *bottom;
118 class FrameField : public PluginVClient
121 FrameField(PluginServer *server);
124 PLUGIN_CLASS_MEMBERS(FrameFieldConfig);
126 int process_buffer(VFrame *frame,
127 int64_t start_position,
130 void save_data(KeyFrame *keyframe);
131 void read_data(KeyFrame *keyframe);
134 // Constructs odd or even rows from the average of the surrounding rows.
135 void average_rows(int offset, VFrame *frame);
139 // Last frame requested
142 int64_t field_number;
144 int64_t current_frame_number;
146 int64_t src_frame_number;
149 // Temporary storage of input frame for OpenGL
150 BC_Texture *src_texture;
151 // Signal OpenGL handler a new frame was read.
153 // Reading frames at a different rate requires us to store the aggregation
154 // state when the frame isn't read.
155 int aggregate_rgb601;
156 int rgb601_direction;
167 REGISTER_PLUGIN(FrameField)
171 FrameFieldConfig::FrameFieldConfig()
173 field_dominance = TOP_FIELD_FIRST;
176 int FrameFieldConfig::equivalent(FrameFieldConfig &src)
178 return src.field_dominance == field_dominance;
188 FrameFieldWindow::FrameFieldWindow(FrameField *plugin)
189 : PluginClientWindow(plugin,
196 this->plugin = plugin;
199 void FrameFieldWindow::create_objects()
202 int ys5 = yS(5), ys10 = yS(10);
203 int x = xs10, y = ys10;
204 add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
205 y += top->get_h() + ys5;
206 add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
207 y += bottom->get_h() + ys5;
223 FrameFieldTop::FrameFieldTop(FrameField *plugin,
224 FrameFieldWindow *gui,
229 plugin->config.field_dominance == TOP_FIELD_FIRST,
230 _("Top field first"))
232 this->plugin = plugin;
236 int FrameFieldTop::handle_event()
238 plugin->config.field_dominance = TOP_FIELD_FIRST;
239 gui->bottom->update(0);
240 plugin->send_configure_change();
248 FrameFieldBottom::FrameFieldBottom(FrameField *plugin,
249 FrameFieldWindow *gui,
254 plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
255 _("Bottom field first"))
257 this->plugin = plugin;
261 int FrameFieldBottom::handle_event()
263 plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
265 plugin->send_configure_change();
291 FrameField::FrameField(PluginServer *server)
292 : PluginVClient(server)
297 src_frame_number = -1;
300 aggregate_rgb601 = 0;
301 rgb601_direction = 0;
305 FrameField::~FrameField()
309 if(src_frame) delete src_frame;
310 if(src_texture) delete src_texture;
314 // 0 - current frame field 0, prev frame field 1
315 // 1 - current frame field 0, current frame field 1, copy current to prev
316 // 2 - current frame field 0, prev frame field 1
318 int FrameField::process_buffer(VFrame *frame,
319 int64_t start_position,
322 load_configuration();
326 // Calculate current field based on absolute position so the algorithm isn't
327 // relative to where playback started.
328 field_number = get_source_position() % 2;
330 if (get_direction() == PLAY_REVERSE)
333 field_number = (field_number + 1) % 2;
337 current_frame_number = start_position / 2;
342 // Read new frames directly into output frame for hardware
346 // Read into temporary for software
348 src_frame->get_color_model() != frame->get_color_model())
356 src_frame = new VFrame(frame->get_w(), frame->get_h(),
357 frame->get_color_model(), 0);
363 // Import source frame at half frame rate
364 if(current_frame_number != src_frame_number ||
365 // If same frame was requested, assume it was a configuration change and reprocess.
366 start_position == last_frame)
370 current_frame_number,
373 src_frame_number = current_frame_number;
384 int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
387 unsigned char **src_rows = src_frame->get_rows();
388 unsigned char **output_rows = frame->get_rows();
392 if(field_number == 0)
394 if(config.field_dominance == TOP_FIELD_FIRST)
396 for(int i = 0; i < frame->get_h() - 1; i += 2)
398 // Copy even lines of src to both lines of output
399 memcpy(output_rows[i],
404 // Average empty rows
405 /* if(config.avg) */ average_rows(0, frame);
409 for(int i = 0; i < frame->get_h() - 1; i += 2)
411 // Copy odd lines of current to both lines of output with shift up.
412 memcpy(output_rows[i + 1],
417 // Average empty rows
418 /* if(config.avg) */ average_rows(1, frame);
424 if(config.field_dominance == TOP_FIELD_FIRST)
426 for(int i = 0; i < frame->get_h() - 1; i += 2)
428 // Copy odd lines of src to both lines of output
429 memcpy(output_rows[i + 1],
434 // Average empty rows
435 /* if(config.avg) */ average_rows(1, frame);
439 for(int i = 0; i < frame->get_h() - 1; i += 2)
441 // Copy even lines of src to both lines of output.
442 memcpy(output_rows[i],
447 // Average empty rows
448 /* if(config.avg) */ average_rows(0, frame);
452 last_frame = start_position;
457 // Averaging 2 pixels
458 #define AVERAGE(type, temp_type, components, offset) \
460 type **rows = (type**)frame->get_rows(); \
461 int w = frame->get_w(); \
462 int h = frame->get_h(); \
463 int row_size = components * w; \
464 for(int i = offset; i < h - 3; i += 2) \
466 type *row1 = rows[i]; \
467 type *row2 = rows[i + 1]; \
468 type *row3 = rows[i + 2]; \
469 for(int j = 0; j < row_size; j++) \
471 temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
472 *row2++ = (sum / 2); \
477 // Averaging 6 pixels
478 #define AVERAGE_BAK(type, components, offset) \
480 type **rows = (type**)frame->get_rows(); \
481 int w = frame->get_w(); \
482 int h = frame->get_h(); \
484 for(int i = offset; i < h - 3; i += 2) \
486 type *row1 = rows[i]; \
487 type *row2 = rows[i + 1]; \
488 type *row3 = rows[i + 2]; \
490 int64_t pixel1[4], pixel2[4], pixel3[4]; \
491 int64_t pixel4[4], pixel5[4], pixel6[4]; \
494 for(int j = 0; j < components; j++) \
496 pixel1[j] = *row1++; \
497 pixel4[j] = *row3++; \
498 *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
501 for(int j = 2; j < row_size; j++) \
503 for(int k = 0; k < components; k++) \
505 pixel2[k] = *row1++; \
506 pixel5[k] = *row3++; \
509 for(int k = 0; k < components; k++) \
513 *row2++ = (pixel1[k] + \
519 pixel1[k] = pixel2[k]; \
520 pixel4[k] = pixel5[k]; \
525 for(int j = 0; j < components; j++) \
527 *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
532 void FrameField::average_rows(int offset, VFrame *frame)
534 //printf("FrameField::average_rows 1 %d\n", offset);
535 switch(frame->get_color_model())
539 AVERAGE(unsigned char, int64_t, 3, offset);
542 AVERAGE(float, float, 3, offset);
546 AVERAGE(unsigned char, int64_t, 4, offset);
549 AVERAGE(float, float, 4, offset);
553 AVERAGE(uint16_t, int64_t, 3, offset);
555 case BC_RGBA16161616:
556 case BC_YUVA16161616:
557 AVERAGE(uint16_t, int64_t, 4, offset);
564 const char* FrameField::plugin_title() { return N_("Frames to fields"); }
565 int FrameField::is_realtime() { return 1; }
567 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
569 int FrameField::load_configuration()
571 KeyFrame *prev_keyframe;
572 FrameFieldConfig old_config = config;
574 prev_keyframe = get_prev_keyframe(get_source_position());
575 read_data(prev_keyframe);
577 return !old_config.equivalent(config);
581 void FrameField::save_data(KeyFrame *keyframe)
585 // cause data to be stored directly in text
586 output.set_shared_output(keyframe->xbuf);
587 output.tag.set_title("FRAME_FIELD");
588 output.tag.set_property("DOMINANCE", config.field_dominance);
590 output.tag.set_title("/FRAME_FIELD");
592 output.append_newline();
593 output.terminate_string();
596 void FrameField::read_data(KeyFrame *keyframe)
600 input.set_shared_input(keyframe->xbuf);
602 while(!input.read_tag())
604 if(input.tag.title_is("FRAME_FIELD"))
606 config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
611 void FrameField::update_gui()
615 if(load_configuration())
617 thread->window->lock_window();
618 ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
619 ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
620 thread->window->unlock_window();
625 int FrameField::handle_opengl()
628 static const char *field_frag =
629 "uniform sampler2D tex;\n"
630 "uniform float double_line_h;\n"
631 "uniform float y_offset;\n"
634 " vec2 coord = gl_TexCoord[0].st;\n"
635 /* Number of double lines + fraction of current double line */
636 " float half_y = (coord.y - y_offset) / double_line_h;\n"
637 /* Lines comprising current double line */
638 " float line1 = floor(half_y) * double_line_h + y_offset;\n"
639 " float line2 = line1 + double_line_h;\n"
640 /* Distance from line1 to line2 */
641 " float frac = fract(half_y);\n"
642 " gl_FragColor = mix(\n"
643 " texture2D(tex, vec2(coord.x, line1)), \n"
644 " texture2D(tex, vec2(coord.x, line2)), \n"
648 static const char *_601_to_rgb_frag =
651 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
654 static const char *_601_to_yuv_frag =
657 " gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
660 static const char *rgb_to_601_frag =
663 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
666 static const char *yuv_to_601_frag =
669 " gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
675 if(get_output()->get_opengl_state() != VFrame::SCREEN)
677 // Copy new frame to temporary texture
678 get_output()->to_texture();
680 // Draw it only to copy it to the temporary.
681 get_output()->enable_opengl();
682 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
683 get_output()->bind_texture(0);
684 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
685 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
686 get_output()->draw_texture();
689 get_output()->enable_opengl();
690 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
691 glActiveTexture(GL_TEXTURE0);
692 BC_Texture::new_texture(&src_texture,
693 get_output()->get_w(),
694 get_output()->get_h(),
695 get_output()->get_color_model());
696 src_texture->bind(0);
697 glCopyTexSubImage2D(GL_TEXTURE_2D,
703 get_output()->get_w(),
704 get_output()->get_h());
706 // Store aggregation state only when reading a frame
707 //printf("FrameField::handle_opengl %p\n", get_output());
708 //get_output()->dump_stacks();
709 if(prev_effect_is(_("RGB - 601")) ||
710 next_effect_is(_("RGB - 601")))
712 aggregate_rgb601 = 1;
713 rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
716 aggregate_rgb601 = 0;
720 get_output()->enable_opengl();
723 float y_offset = 0.0;
724 if(field_number == 0)
726 if(config.field_dominance == BOTTOM_FIELD_FIRST)
731 if(config.field_dominance == TOP_FIELD_FIRST)
735 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
736 glActiveTexture(GL_TEXTURE0);
737 BC_Texture::new_texture(&src_texture,
738 get_output()->get_w(),
739 get_output()->get_h(),
740 get_output()->get_color_model());
742 const char *shader_stack[16];
743 memset(shader_stack,0, sizeof(shader_stack));
744 int current_shader = 0;
746 shader_stack[current_shader++] = field_frag;
748 // Aggregate with other effect
749 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
750 if( aggregate_rgb601 ) {
751 switch( rgb601_direction ) {
753 shader_stack[current_shader++] =
754 BC_CModels::is_yuv(get_output()->get_color_model()) ?
755 yuv_to_601_frag : rgb_to_601_frag;
758 shader_stack[current_shader++] =
759 BC_CModels::is_yuv(get_output()->get_color_model()) ?
760 _601_to_yuv_frag : _601_to_rgb_frag;
765 shader_stack[current_shader] = 0;
766 unsigned int shader = VFrame::make_shader(shader_stack);
768 glUseProgram(shader);
769 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
770 glUniform1f(glGetUniformLocation(shader, "double_line_h"),
771 2.0 / src_texture->get_texture_h());
772 glUniform1f(glGetUniformLocation(shader, "y_offset"),
773 y_offset / src_texture->get_texture_h());
776 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
777 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
778 get_output()->draw_texture();
781 get_output()->set_opengl_state(VFrame::SCREEN);
783 // Reset for other operations
784 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);