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 add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
203 y += top->get_h() + 5;
204 add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
205 y += bottom->get_h() + 5;
221 FrameFieldTop::FrameFieldTop(FrameField *plugin,
222 FrameFieldWindow *gui,
227 plugin->config.field_dominance == TOP_FIELD_FIRST,
228 _("Top field first"))
230 this->plugin = plugin;
234 int FrameFieldTop::handle_event()
236 plugin->config.field_dominance = TOP_FIELD_FIRST;
237 gui->bottom->update(0);
238 plugin->send_configure_change();
246 FrameFieldBottom::FrameFieldBottom(FrameField *plugin,
247 FrameFieldWindow *gui,
252 plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
253 _("Bottom field first"))
255 this->plugin = plugin;
259 int FrameFieldBottom::handle_event()
261 plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
263 plugin->send_configure_change();
289 FrameField::FrameField(PluginServer *server)
290 : PluginVClient(server)
295 src_frame_number = -1;
298 aggregate_rgb601 = 0;
299 rgb601_direction = 0;
303 FrameField::~FrameField()
307 if(src_frame) delete src_frame;
308 if(src_texture) delete src_texture;
312 // 0 - current frame field 0, prev frame field 1
313 // 1 - current frame field 0, current frame field 1, copy current to prev
314 // 2 - current frame field 0, prev frame field 1
316 int FrameField::process_buffer(VFrame *frame,
317 int64_t start_position,
320 load_configuration();
324 // Calculate current field based on absolute position so the algorithm isn't
325 // relative to where playback started.
326 field_number = get_source_position() % 2;
328 if (get_direction() == PLAY_REVERSE)
331 field_number = (field_number + 1) % 2;
335 current_frame_number = start_position / 2;
340 // Read new frames directly into output frame for hardware
344 // Read into temporary for software
346 src_frame->get_color_model() != frame->get_color_model())
354 src_frame = new VFrame(0,
358 frame->get_color_model(),
365 // Import source frame at half frame rate
366 if(current_frame_number != src_frame_number ||
367 // If same frame was requested, assume it was a configuration change and reprocess.
368 start_position == last_frame)
372 current_frame_number,
375 src_frame_number = current_frame_number;
386 int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
389 unsigned char **src_rows = src_frame->get_rows();
390 unsigned char **output_rows = frame->get_rows();
394 if(field_number == 0)
396 if(config.field_dominance == TOP_FIELD_FIRST)
398 for(int i = 0; i < frame->get_h() - 1; i += 2)
400 // Copy even lines of src to both lines of output
401 memcpy(output_rows[i],
406 // Average empty rows
407 /* if(config.avg) */ average_rows(0, frame);
411 for(int i = 0; i < frame->get_h() - 1; i += 2)
413 // Copy odd lines of current to both lines of output with shift up.
414 memcpy(output_rows[i + 1],
419 // Average empty rows
420 /* if(config.avg) */ average_rows(1, frame);
426 if(config.field_dominance == TOP_FIELD_FIRST)
428 for(int i = 0; i < frame->get_h() - 1; i += 2)
430 // Copy odd lines of src to both lines of output
431 memcpy(output_rows[i + 1],
436 // Average empty rows
437 /* if(config.avg) */ average_rows(1, frame);
441 for(int i = 0; i < frame->get_h() - 1; i += 2)
443 // Copy even lines of src to both lines of output.
444 memcpy(output_rows[i],
449 // Average empty rows
450 /* if(config.avg) */ average_rows(0, frame);
454 last_frame = start_position;
459 // Averaging 2 pixels
460 #define AVERAGE(type, temp_type, components, offset) \
462 type **rows = (type**)frame->get_rows(); \
463 int w = frame->get_w(); \
464 int h = frame->get_h(); \
465 int row_size = components * w; \
466 for(int i = offset; i < h - 3; i += 2) \
468 type *row1 = rows[i]; \
469 type *row2 = rows[i + 1]; \
470 type *row3 = rows[i + 2]; \
471 for(int j = 0; j < row_size; j++) \
473 temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
474 *row2++ = (sum / 2); \
479 // Averaging 6 pixels
480 #define AVERAGE_BAK(type, components, offset) \
482 type **rows = (type**)frame->get_rows(); \
483 int w = frame->get_w(); \
484 int h = frame->get_h(); \
486 for(int i = offset; i < h - 3; i += 2) \
488 type *row1 = rows[i]; \
489 type *row2 = rows[i + 1]; \
490 type *row3 = rows[i + 2]; \
492 int64_t pixel1[4], pixel2[4], pixel3[4]; \
493 int64_t pixel4[4], pixel5[4], pixel6[4]; \
496 for(int j = 0; j < components; j++) \
498 pixel1[j] = *row1++; \
499 pixel4[j] = *row3++; \
500 *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
503 for(int j = 2; j < row_size; j++) \
505 for(int k = 0; k < components; k++) \
507 pixel2[k] = *row1++; \
508 pixel5[k] = *row3++; \
511 for(int k = 0; k < components; k++) \
515 *row2++ = (pixel1[k] + \
521 pixel1[k] = pixel2[k]; \
522 pixel4[k] = pixel5[k]; \
527 for(int j = 0; j < components; j++) \
529 *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
534 void FrameField::average_rows(int offset, VFrame *frame)
536 //printf("FrameField::average_rows 1 %d\n", offset);
537 switch(frame->get_color_model())
541 AVERAGE(unsigned char, int64_t, 3, offset);
544 AVERAGE(float, float, 3, offset);
548 AVERAGE(unsigned char, int64_t, 4, offset);
551 AVERAGE(float, float, 4, offset);
555 AVERAGE(uint16_t, int64_t, 3, offset);
557 case BC_RGBA16161616:
558 case BC_YUVA16161616:
559 AVERAGE(uint16_t, int64_t, 4, offset);
566 const char* FrameField::plugin_title() { return _("Frames to fields"); }
567 int FrameField::is_realtime() { return 1; }
569 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
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);
583 void FrameField::save_data(KeyFrame *keyframe)
587 // cause data to be stored directly in text
588 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
589 output.tag.set_title("FRAME_FIELD");
590 output.tag.set_property("DOMINANCE", config.field_dominance);
592 output.tag.set_title("/FRAME_FIELD");
594 output.append_newline();
595 output.terminate_string();
598 void FrameField::read_data(KeyFrame *keyframe)
602 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
604 while(!input.read_tag())
606 if(input.tag.title_is("FRAME_FIELD"))
608 config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
613 void FrameField::update_gui()
617 if(load_configuration())
619 thread->window->lock_window();
620 ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
621 ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
622 thread->window->unlock_window();
627 int FrameField::handle_opengl()
630 static const char *field_frag =
631 "uniform sampler2D tex;\n"
632 "uniform float double_line_h;\n"
633 "uniform float y_offset;\n"
636 " vec2 coord = gl_TexCoord[0].st;\n"
637 /* Number of double lines + fraction of current double line */
638 " float half_y = (coord.y - y_offset) / double_line_h;\n"
639 /* Lines comprising current double line */
640 " float line1 = floor(half_y) * double_line_h + y_offset;\n"
641 " float line2 = line1 + double_line_h;\n"
642 /* Distance from line1 to line2 */
643 " float frac = fract(half_y);\n"
644 " gl_FragColor = mix(\n"
645 " texture2D(tex, vec2(coord.x, line1)), \n"
646 " texture2D(tex, vec2(coord.x, line2)), \n"
650 static const char *_601_to_rgb_frag =
653 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
656 static const char *_601_to_yuv_frag =
659 " gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
662 static const char *rgb_to_601_frag =
665 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
668 static const char *yuv_to_601_frag =
671 " gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
677 if(get_output()->get_opengl_state() != VFrame::SCREEN)
679 // Copy new frame to temporary texture
680 get_output()->to_texture();
682 // Draw it only to copy it to the temporary.
683 get_output()->enable_opengl();
684 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
685 get_output()->bind_texture(0);
686 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
687 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
688 get_output()->draw_texture();
691 get_output()->enable_opengl();
692 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
693 glActiveTexture(GL_TEXTURE0);
694 BC_Texture::new_texture(&src_texture,
695 get_output()->get_w(),
696 get_output()->get_h(),
697 get_output()->get_color_model());
698 src_texture->bind(0);
699 glCopyTexSubImage2D(GL_TEXTURE_2D,
705 get_output()->get_w(),
706 get_output()->get_h());
708 // Store aggregation state only when reading a frame
709 //printf("FrameField::handle_opengl %p\n", get_output());
710 //get_output()->dump_stacks();
711 if(prev_effect_is(_("RGB - 601")) ||
712 next_effect_is(_("RGB - 601")))
714 aggregate_rgb601 = 1;
715 rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
718 aggregate_rgb601 = 0;
722 get_output()->enable_opengl();
725 unsigned int frag = 0;
726 float y_offset = 0.0;
727 if(field_number == 0)
729 if(config.field_dominance == BOTTOM_FIELD_FIRST)
734 if(config.field_dominance == TOP_FIELD_FIRST)
738 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
739 glActiveTexture(GL_TEXTURE0);
740 BC_Texture::new_texture(&src_texture,
741 get_output()->get_w(),
742 get_output()->get_h(),
743 get_output()->get_color_model());
746 const char *shaders[3] = { 0, 0, 0 };
747 shaders[0] = field_frag;
750 // Aggregate with other effect
751 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
754 if(rgb601_direction == 1)
756 if(BC_CModels::is_yuv(get_output()->get_color_model()))
757 shaders[1] = yuv_to_601_frag;
759 shaders[1] = rgb_to_601_frag;
762 if(rgb601_direction == 2)
764 if(BC_CModels::is_yuv(get_output()->get_color_model()))
765 shaders[1] = _601_to_yuv_frag;
767 shaders[1] = _601_to_rgb_frag;
773 frag = VFrame::make_shader(0, shaders[0], shaders[1], shaders[2], 0);
779 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
780 glUniform1f(glGetUniformLocation(frag, "double_line_h"),
781 2.0 / src_texture->get_texture_h());
782 glUniform1f(glGetUniformLocation(frag, "y_offset"),
783 y_offset / src_texture->get_texture_h());
788 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
789 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
790 get_output()->draw_texture();
793 get_output()->set_opengl_state(VFrame::SCREEN);
795 // Reset for other operations
796 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
797 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);