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(frame->get_w(), frame->get_h(),
355 frame->get_color_model(), 0);
361 // Import source frame at half frame rate
362 if(current_frame_number != src_frame_number ||
363 // If same frame was requested, assume it was a configuration change and reprocess.
364 start_position == last_frame)
368 current_frame_number,
371 src_frame_number = current_frame_number;
382 int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
385 unsigned char **src_rows = src_frame->get_rows();
386 unsigned char **output_rows = frame->get_rows();
390 if(field_number == 0)
392 if(config.field_dominance == TOP_FIELD_FIRST)
394 for(int i = 0; i < frame->get_h() - 1; i += 2)
396 // Copy even lines of src to both lines of output
397 memcpy(output_rows[i],
402 // Average empty rows
403 /* if(config.avg) */ average_rows(0, frame);
407 for(int i = 0; i < frame->get_h() - 1; i += 2)
409 // Copy odd lines of current to both lines of output with shift up.
410 memcpy(output_rows[i + 1],
415 // Average empty rows
416 /* if(config.avg) */ average_rows(1, frame);
422 if(config.field_dominance == TOP_FIELD_FIRST)
424 for(int i = 0; i < frame->get_h() - 1; i += 2)
426 // Copy odd lines of src to both lines of output
427 memcpy(output_rows[i + 1],
432 // Average empty rows
433 /* if(config.avg) */ average_rows(1, frame);
437 for(int i = 0; i < frame->get_h() - 1; i += 2)
439 // Copy even lines of src to both lines of output.
440 memcpy(output_rows[i],
445 // Average empty rows
446 /* if(config.avg) */ average_rows(0, frame);
450 last_frame = start_position;
455 // Averaging 2 pixels
456 #define AVERAGE(type, temp_type, components, offset) \
458 type **rows = (type**)frame->get_rows(); \
459 int w = frame->get_w(); \
460 int h = frame->get_h(); \
461 int row_size = components * w; \
462 for(int i = offset; i < h - 3; i += 2) \
464 type *row1 = rows[i]; \
465 type *row2 = rows[i + 1]; \
466 type *row3 = rows[i + 2]; \
467 for(int j = 0; j < row_size; j++) \
469 temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
470 *row2++ = (sum / 2); \
475 // Averaging 6 pixels
476 #define AVERAGE_BAK(type, components, offset) \
478 type **rows = (type**)frame->get_rows(); \
479 int w = frame->get_w(); \
480 int h = frame->get_h(); \
482 for(int i = offset; i < h - 3; i += 2) \
484 type *row1 = rows[i]; \
485 type *row2 = rows[i + 1]; \
486 type *row3 = rows[i + 2]; \
488 int64_t pixel1[4], pixel2[4], pixel3[4]; \
489 int64_t pixel4[4], pixel5[4], pixel6[4]; \
492 for(int j = 0; j < components; j++) \
494 pixel1[j] = *row1++; \
495 pixel4[j] = *row3++; \
496 *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
499 for(int j = 2; j < row_size; j++) \
501 for(int k = 0; k < components; k++) \
503 pixel2[k] = *row1++; \
504 pixel5[k] = *row3++; \
507 for(int k = 0; k < components; k++) \
511 *row2++ = (pixel1[k] + \
517 pixel1[k] = pixel2[k]; \
518 pixel4[k] = pixel5[k]; \
523 for(int j = 0; j < components; j++) \
525 *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
530 void FrameField::average_rows(int offset, VFrame *frame)
532 //printf("FrameField::average_rows 1 %d\n", offset);
533 switch(frame->get_color_model())
537 AVERAGE(unsigned char, int64_t, 3, offset);
540 AVERAGE(float, float, 3, offset);
544 AVERAGE(unsigned char, int64_t, 4, offset);
547 AVERAGE(float, float, 4, offset);
551 AVERAGE(uint16_t, int64_t, 3, offset);
553 case BC_RGBA16161616:
554 case BC_YUVA16161616:
555 AVERAGE(uint16_t, int64_t, 4, offset);
562 const char* FrameField::plugin_title() { return N_("Frames to fields"); }
563 int FrameField::is_realtime() { return 1; }
565 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
567 int FrameField::load_configuration()
569 KeyFrame *prev_keyframe;
570 FrameFieldConfig old_config = config;
572 prev_keyframe = get_prev_keyframe(get_source_position());
573 read_data(prev_keyframe);
575 return !old_config.equivalent(config);
579 void FrameField::save_data(KeyFrame *keyframe)
583 // cause data to be stored directly in text
584 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
585 output.tag.set_title("FRAME_FIELD");
586 output.tag.set_property("DOMINANCE", config.field_dominance);
588 output.tag.set_title("/FRAME_FIELD");
590 output.append_newline();
591 output.terminate_string();
594 void FrameField::read_data(KeyFrame *keyframe)
598 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
600 while(!input.read_tag())
602 if(input.tag.title_is("FRAME_FIELD"))
604 config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
609 void FrameField::update_gui()
613 if(load_configuration())
615 thread->window->lock_window();
616 ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
617 ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
618 thread->window->unlock_window();
623 int FrameField::handle_opengl()
626 static const char *field_frag =
627 "uniform sampler2D tex;\n"
628 "uniform float double_line_h;\n"
629 "uniform float y_offset;\n"
632 " vec2 coord = gl_TexCoord[0].st;\n"
633 /* Number of double lines + fraction of current double line */
634 " float half_y = (coord.y - y_offset) / double_line_h;\n"
635 /* Lines comprising current double line */
636 " float line1 = floor(half_y) * double_line_h + y_offset;\n"
637 " float line2 = line1 + double_line_h;\n"
638 /* Distance from line1 to line2 */
639 " float frac = fract(half_y);\n"
640 " gl_FragColor = mix(\n"
641 " texture2D(tex, vec2(coord.x, line1)), \n"
642 " texture2D(tex, vec2(coord.x, line2)), \n"
646 static const char *_601_to_rgb_frag =
649 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
652 static const char *_601_to_yuv_frag =
655 " gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
658 static const char *rgb_to_601_frag =
661 " gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
664 static const char *yuv_to_601_frag =
667 " gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
673 if(get_output()->get_opengl_state() != VFrame::SCREEN)
675 // Copy new frame to temporary texture
676 get_output()->to_texture();
678 // Draw it only to copy it to the temporary.
679 get_output()->enable_opengl();
680 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
681 get_output()->bind_texture(0);
682 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
683 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
684 get_output()->draw_texture();
687 get_output()->enable_opengl();
688 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
689 glActiveTexture(GL_TEXTURE0);
690 BC_Texture::new_texture(&src_texture,
691 get_output()->get_w(),
692 get_output()->get_h(),
693 get_output()->get_color_model());
694 src_texture->bind(0);
695 glCopyTexSubImage2D(GL_TEXTURE_2D,
701 get_output()->get_w(),
702 get_output()->get_h());
704 // Store aggregation state only when reading a frame
705 //printf("FrameField::handle_opengl %p\n", get_output());
706 //get_output()->dump_stacks();
707 if(prev_effect_is(_("RGB - 601")) ||
708 next_effect_is(_("RGB - 601")))
710 aggregate_rgb601 = 1;
711 rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
714 aggregate_rgb601 = 0;
718 get_output()->enable_opengl();
721 float y_offset = 0.0;
722 if(field_number == 0)
724 if(config.field_dominance == BOTTOM_FIELD_FIRST)
729 if(config.field_dominance == TOP_FIELD_FIRST)
733 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
734 glActiveTexture(GL_TEXTURE0);
735 BC_Texture::new_texture(&src_texture,
736 get_output()->get_w(),
737 get_output()->get_h(),
738 get_output()->get_color_model());
740 const char *shader_stack[16];
741 memset(shader_stack,0, sizeof(shader_stack));
742 int current_shader = 0;
744 shader_stack[current_shader++] = field_frag;
746 // Aggregate with other effect
747 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
748 if( aggregate_rgb601 ) {
749 switch( rgb601_direction ) {
751 shader_stack[current_shader++] =
752 BC_CModels::is_yuv(get_output()->get_color_model()) ?
753 yuv_to_601_frag : rgb_to_601_frag;
756 shader_stack[current_shader++] =
757 BC_CModels::is_yuv(get_output()->get_color_model()) ?
758 _601_to_yuv_frag : _601_to_rgb_frag;
763 shader_stack[current_shader] = 0;
764 unsigned int shader = VFrame::make_shader(shader_stack);
766 glUseProgram(shader);
767 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
768 glUniform1f(glGetUniformLocation(shader, "double_line_h"),
769 2.0 / src_texture->get_texture_h());
770 glUniform1f(glGetUniformLocation(shader, "y_offset"),
771 y_offset / src_texture->get_texture_h());
774 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
775 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
776 get_output()->draw_texture();
779 get_output()->set_opengl_state(VFrame::SCREEN);
781 // Reset for other operations
782 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);