1 #include "chroma_subsampler.h"
5 #include <movit/effect_util.h>
6 #include <movit/resource_pool.h>
7 #include <movit/util.h>
12 ChromaSubsampler::ChromaSubsampler(ResourcePool *resource_pool)
13 : resource_pool(resource_pool)
15 vector<string> frag_shader_outputs;
17 // Set up stuff for NV12 conversion.
19 // Note: Due to the horizontally co-sited chroma/luma samples in H.264
20 // (chrome position is left for horizontal and center for vertical),
21 // we need to be a bit careful in our subsampling. A diagram will make
22 // this clearer, showing some luma and chroma samples:
37 // Clearly, the rightmost chroma sample here needs to be equivalent to
38 // b/4 + c/2 + d/4. (We could also implement more sophisticated filters,
39 // of course, but as long as the upsampling is not going to be equally
40 // sophisticated, it's probably not worth it.) If we sample once with
41 // no mipmapping, we get just c, ie., no actual filtering in the
42 // horizontal direction. (For the vertical direction, we can just
43 // sample in the middle to get the right filtering.) One could imagine
44 // we could use mipmapping (assuming we can create mipmaps cheaply),
45 // but then, what we'd get is this:
60 // which ends up sampling equally from a and b, which clearly isn't right. Instead,
61 // we need to do two (non-mipmapped) chroma samples, both hitting exactly in-between
64 // Sampling in-between b and c gives us the sample (b+c)/2, and similarly for c and d.
65 // Taking the average of these gives of (b+c)/4 + (c+d)/4 = b/4 + c/2 + d/4, which is
66 // exactly what we want.
68 // See also http://www.poynton.com/PDFs/Merging_RGB_and_422.pdf, pages 6–7.
71 string cbcr_vert_shader =
74 "in vec2 position; \n"
75 "in vec2 texcoord; \n"
76 "out vec2 tc0, tc1; \n"
77 "uniform vec2 foo_chroma_offset_0; \n"
78 "uniform vec2 foo_chroma_offset_1; \n"
82 " // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is: \n"
84 " // 2.000 0.000 0.000 -1.000 \n"
85 " // 0.000 2.000 0.000 -1.000 \n"
86 " // 0.000 0.000 -2.000 -1.000 \n"
87 " // 0.000 0.000 0.000 1.000 \n"
88 " gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); \n"
89 " vec2 flipped_tc = texcoord; \n"
90 " tc0 = flipped_tc + foo_chroma_offset_0; \n"
91 " tc1 = flipped_tc + foo_chroma_offset_1; \n"
93 string cbcr_frag_shader =
95 "in vec2 tc0, tc1; \n"
96 "uniform sampler2D cbcr_tex; \n"
97 "out vec4 FragColor, FragColor2; \n"
99 " FragColor = 0.5 * (texture(cbcr_tex, tc0) + texture(cbcr_tex, tc1)); \n"
100 " FragColor2 = FragColor; \n"
102 cbcr_program_num = resource_pool->compile_glsl_program(cbcr_vert_shader, cbcr_frag_shader, frag_shader_outputs);
105 cbcr_texture_sampler_uniform = glGetUniformLocation(cbcr_program_num, "cbcr_tex");
107 cbcr_position_attribute_index = glGetAttribLocation(cbcr_program_num, "position");
109 cbcr_texcoord_attribute_index = glGetAttribLocation(cbcr_program_num, "texcoord");
112 // Same, for UYVY conversion.
113 string uyvy_vert_shader =
116 "in vec2 position; \n"
117 "in vec2 texcoord; \n"
118 "out vec2 y_tc0, y_tc1, cbcr_tc0, cbcr_tc1; \n"
119 "uniform vec2 foo_luma_offset_0; \n"
120 "uniform vec2 foo_luma_offset_1; \n"
121 "uniform vec2 foo_chroma_offset_0; \n"
122 "uniform vec2 foo_chroma_offset_1; \n"
126 " // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is: \n"
128 " // 2.000 0.000 0.000 -1.000 \n"
129 " // 0.000 2.000 0.000 -1.000 \n"
130 " // 0.000 0.000 -2.000 -1.000 \n"
131 " // 0.000 0.000 0.000 1.000 \n"
132 " gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0); \n"
133 " vec2 flipped_tc = texcoord; \n"
134 " y_tc0 = flipped_tc + foo_luma_offset_0; \n"
135 " y_tc1 = flipped_tc + foo_luma_offset_1; \n"
136 " cbcr_tc0 = flipped_tc + foo_chroma_offset_0; \n"
137 " cbcr_tc1 = flipped_tc + foo_chroma_offset_1; \n"
139 string uyvy_frag_shader =
141 "in vec2 y_tc0, y_tc1, cbcr_tc0, cbcr_tc1; \n"
142 "uniform sampler2D y_tex, cbcr_tex; \n"
143 "out vec4 FragColor; \n"
145 " float y0 = texture(y_tex, y_tc0).r; \n"
146 " float y1 = texture(y_tex, y_tc1).r; \n"
147 " vec2 cbcr0 = texture(cbcr_tex, cbcr_tc0).rg; \n"
148 " vec2 cbcr1 = texture(cbcr_tex, cbcr_tc1).rg; \n"
149 " vec2 cbcr = 0.5 * (cbcr0 + cbcr1); \n"
150 " FragColor = vec4(cbcr.g, y0, cbcr.r, y1); \n"
153 uyvy_program_num = resource_pool->compile_glsl_program(uyvy_vert_shader, uyvy_frag_shader, frag_shader_outputs);
156 uyvy_y_texture_sampler_uniform = glGetUniformLocation(uyvy_program_num, "y_tex");
158 uyvy_cbcr_texture_sampler_uniform = glGetUniformLocation(uyvy_program_num, "cbcr_tex");
160 uyvy_position_attribute_index = glGetAttribLocation(uyvy_program_num, "position");
162 uyvy_texcoord_attribute_index = glGetAttribLocation(uyvy_program_num, "texcoord");
165 // Shared between the two.
171 vbo = generate_vbo(2, GL_FLOAT, sizeof(vertices), vertices);
175 ChromaSubsampler::~ChromaSubsampler()
177 resource_pool->release_glsl_program(cbcr_program_num);
179 resource_pool->release_glsl_program(uyvy_program_num);
181 glDeleteBuffers(1, &vbo);
185 void ChromaSubsampler::subsample_chroma(GLuint cbcr_tex, unsigned width, unsigned height, GLuint dst_tex, GLuint dst2_tex)
188 glGenVertexArrays(1, &vao);
191 glBindVertexArray(vao);
197 fbo = resource_pool->create_fbo(dst_tex);
199 fbo = resource_pool->create_fbo(dst_tex, dst2_tex);
201 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
202 glViewport(0, 0, width/2, height/2);
205 glUseProgram(cbcr_program_num);
208 glActiveTexture(GL_TEXTURE0);
210 glBindTexture(GL_TEXTURE_2D, cbcr_tex);
212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
219 float chroma_offset_0[] = { -1.0f / width, 0.0f };
220 float chroma_offset_1[] = { -0.0f / width, 0.0f };
221 set_uniform_vec2(cbcr_program_num, "foo", "chroma_offset_0", chroma_offset_0);
222 set_uniform_vec2(cbcr_program_num, "foo", "chroma_offset_1", chroma_offset_1);
224 glUniform1i(cbcr_texture_sampler_uniform, 0);
226 glBindBuffer(GL_ARRAY_BUFFER, vbo);
229 for (GLint attr_index : { cbcr_position_attribute_index, cbcr_texcoord_attribute_index }) {
230 glEnableVertexAttribArray(attr_index);
232 glVertexAttribPointer(attr_index, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
236 glDrawArrays(GL_TRIANGLES, 0, 3);
239 for (GLint attr_index : { cbcr_position_attribute_index, cbcr_texcoord_attribute_index }) {
240 glDisableVertexAttribArray(attr_index);
246 glBindFramebuffer(GL_FRAMEBUFFER, 0);
249 resource_pool->release_fbo(fbo);
250 glDeleteVertexArrays(1, &vao);
254 void ChromaSubsampler::create_uyvy(GLuint y_tex, GLuint cbcr_tex, unsigned width, unsigned height, GLuint dst_tex)
257 glGenVertexArrays(1, &vao);
260 glBindVertexArray(vao);
263 GLuint fbo = resource_pool->create_fbo(dst_tex);
264 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
265 glViewport(0, 0, width/2, height);
268 glUseProgram(uyvy_program_num);
271 glUniform1i(uyvy_y_texture_sampler_uniform, 0);
273 glUniform1i(uyvy_cbcr_texture_sampler_uniform, 1);
276 glActiveTexture(GL_TEXTURE0);
278 glBindTexture(GL_TEXTURE_2D, y_tex);
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
284 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
287 glActiveTexture(GL_TEXTURE1);
289 glBindTexture(GL_TEXTURE_2D, cbcr_tex);
291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
293 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
295 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
298 float y_offset_0[] = { -0.5f / width, 0.0f };
299 float y_offset_1[] = { 0.5f / width, 0.0f };
300 float cbcr_offset0[] = { -1.0f / width, 0.0f };
301 float cbcr_offset1[] = { -0.0f / width, 0.0f };
302 set_uniform_vec2(uyvy_program_num, "foo", "luma_offset_0", y_offset_0);
303 set_uniform_vec2(uyvy_program_num, "foo", "luma_offset_1", y_offset_1);
304 set_uniform_vec2(uyvy_program_num, "foo", "chroma_offset_0", cbcr_offset0);
305 set_uniform_vec2(uyvy_program_num, "foo", "chroma_offset_1", cbcr_offset1);
307 glBindBuffer(GL_ARRAY_BUFFER, vbo);
310 for (GLint attr_index : { uyvy_position_attribute_index, uyvy_texcoord_attribute_index }) {
311 if (attr_index == -1) continue;
312 glEnableVertexAttribArray(attr_index);
314 glVertexAttribPointer(attr_index, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
318 glDrawArrays(GL_TRIANGLES, 0, 3);
321 for (GLint attr_index : { uyvy_position_attribute_index, uyvy_texcoord_attribute_index }) {
322 if (attr_index == -1) continue;
323 glDisableVertexAttribArray(attr_index);
327 glActiveTexture(GL_TEXTURE0);
331 glBindFramebuffer(GL_FRAMEBUFFER, 0);
334 resource_pool->release_fbo(fbo);
335 glDeleteVertexArrays(1, &vao);