OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/pepper/video_decoder_shim.h" | 5 #include "content/renderer/pepper/video_decoder_shim.h" |
6 | 6 |
7 #include <GLES2/gl2.h> | 7 #include <GLES2/gl2.h> |
8 #include <GLES2/gl2ext.h> | 8 #include <GLES2/gl2ext.h> |
9 #include <GLES2/gl2extchromium.h> | 9 #include <GLES2/gl2extchromium.h> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #ifndef NDEBUG | |
13 #include "base/logging.h" | |
14 #endif | |
12 #include "base/numerics/safe_conversions.h" | 15 #include "base/numerics/safe_conversions.h" |
13 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
14 #include "cc/blink/context_provider_web_context.h" | 17 #include "cc/blink/context_provider_web_context.h" |
15 #include "content/public/renderer/render_thread.h" | 18 #include "content/public/renderer/render_thread.h" |
16 #include "content/renderer/pepper/pepper_video_decoder_host.h" | 19 #include "content/renderer/pepper/pepper_video_decoder_host.h" |
17 #include "content/renderer/render_thread_impl.h" | 20 #include "content/renderer/render_thread_impl.h" |
18 #include "gpu/command_buffer/client/gles2_implementation.h" | 21 #include "gpu/command_buffer/client/gles2_implementation.h" |
19 #include "media/base/decoder_buffer.h" | 22 #include "media/base/decoder_buffer.h" |
20 #include "media/base/limits.h" | 23 #include "media/base/limits.h" |
21 #include "media/base/video_decoder.h" | 24 #include "media/base/video_decoder.h" |
22 #include "media/blink/skcanvas_video_renderer.h" | 25 #include "media/blink/skcanvas_video_renderer.h" |
23 #include "media/filters/ffmpeg_video_decoder.h" | 26 #include "media/filters/ffmpeg_video_decoder.h" |
24 #include "media/filters/vpx_video_decoder.h" | 27 #include "media/filters/vpx_video_decoder.h" |
25 #include "media/video/picture.h" | 28 #include "media/video/picture.h" |
26 #include "media/video/video_decode_accelerator.h" | 29 #include "media/video/video_decode_accelerator.h" |
27 #include "ppapi/c/pp_errors.h" | 30 #include "ppapi/c/pp_errors.h" |
31 #include "third_party/skia/include/gpu/GrTypes.h" | |
28 | 32 |
29 namespace content { | 33 namespace content { |
30 | 34 |
35 // | |
piman
2015/05/12 03:00:25
nit: remove blank comment line
| |
36 // YUV->RGB converter class using a shader and FBO | |
piman
2015/05/12 03:00:24
nit: end with a .
| |
37 // | |
piman
2015/05/12 03:00:24
nit: remove blank comment line
| |
38 class YUVConverter { | |
39 public: | |
40 YUVConverter(const scoped_refptr<cc_blink::ContextProviderWebContext>&); | |
41 ~YUVConverter(); | |
42 bool Initialize(); | |
43 void Convert(const scoped_refptr<media::VideoFrame>& frame, GLuint tex_out); | |
44 | |
45 private: | |
46 GLuint CreateShader(); | |
47 GLuint CompileShader(const char* name, GLuint type, const char* code); | |
48 GLuint CreateProgram(const char* name, GLuint vshader, GLuint fshader); | |
49 void SetUniform(GLuint program, const char* name, int value); | |
50 void SetUniform(GLuint program, const char* name, float value); | |
51 GLuint CreateTexture(); | |
52 | |
53 scoped_refptr<cc_blink::ContextProviderWebContext> context_provider_; | |
54 gpu::gles2::GLES2Interface* gl_; | |
55 GLuint frame_buffer_; | |
56 GLuint vtx_buffer_; | |
57 GLuint program_; | |
58 | |
59 GLuint y_texture_; | |
60 GLuint u_texture_; | |
61 GLuint v_texture_; | |
62 | |
63 GLuint internal_format_; | |
64 GLuint format_; | |
65 | |
66 GLuint y_width_; | |
67 GLuint y_height_; | |
68 | |
69 GLuint uv_width_; | |
70 GLuint uv_height_; | |
71 | |
72 GLfloat clamp_value_; | |
73 GLuint clamp_width_; | |
74 GLint clamp_width_loc_; | |
75 | |
76 const uint32_t gr_invalidate_state_; | |
77 | |
78 DISALLOW_COPY_AND_ASSIGN(YUVConverter); | |
79 }; | |
80 | |
81 YUVConverter::YUVConverter( | |
82 const scoped_refptr<cc_blink::ContextProviderWebContext>& ctx_p) | |
83 : context_provider_(ctx_p), | |
84 gl_(context_provider_->ContextGL()), | |
85 frame_buffer_(0), | |
86 vtx_buffer_(0), | |
87 program_(0), | |
88 y_texture_(0), | |
89 u_texture_(0), | |
90 v_texture_(0), | |
91 internal_format_(0), | |
92 format_(0), | |
93 y_width_(2), | |
94 y_height_(2), | |
95 uv_width_(2), | |
96 uv_height_(2), | |
97 clamp_value_(1.f), | |
98 clamp_width_(0), | |
99 clamp_width_loc_(0), | |
100 gr_invalidate_state_(kRenderTarget_GrGLBackendState | | |
101 kTextureBinding_GrGLBackendState | | |
102 kView_GrGLBackendState | | |
103 kVertex_GrGLBackendState | | |
104 kProgram_GrGLBackendState) { | |
piman
2015/05/12 03:00:24
I don't think it's useful to have it as a field si
| |
105 } | |
106 | |
107 YUVConverter::~YUVConverter() { | |
108 // delete textures | |
piman
2015/05/12 03:00:23
nit: remove comment
| |
109 if (y_texture_) | |
110 gl_->DeleteTextures(1, &y_texture_); | |
111 | |
112 if (u_texture_) | |
113 gl_->DeleteTextures(1, &u_texture_); | |
114 | |
115 if (v_texture_) | |
116 gl_->DeleteTextures(1, &v_texture_); | |
117 | |
118 // delete framebuffer | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
119 if (frame_buffer_) | |
120 gl_->DeleteFramebuffers(1, &frame_buffer_); | |
121 | |
122 // delete vertex buffer | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
123 if (vtx_buffer_) | |
124 gl_->DeleteBuffers(1, &vtx_buffer_); | |
125 | |
126 // delete program | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
127 if (program_) | |
128 gl_->DeleteProgram(program_); | |
129 } | |
130 | |
131 GLuint YUVConverter::CreateTexture() { | |
132 GLuint tex = 0; | |
133 | |
134 // create texture | |
piman
2015/05/12 03:00:25
nit: remove comment
| |
135 gl_->GenTextures(1, &tex); | |
136 gl_->BindTexture(GL_TEXTURE_2D, tex); | |
137 | |
138 // create texture with default size - will be resized upon first frame | |
139 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 2, 2, 0, format_, | |
140 GL_UNSIGNED_BYTE, NULL); | |
141 | |
142 // set params | |
piman
2015/05/12 03:00:25
nit: remove comment
| |
143 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
144 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
145 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
146 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
147 | |
148 // unbind | |
piman
2015/05/12 03:00:25
nit: remove comment
| |
149 gl_->BindTexture(GL_TEXTURE_2D, 0); | |
150 | |
151 return tex; | |
152 } | |
153 | |
154 GLuint YUVConverter::CompileShader(const char* name, | |
155 GLuint type, | |
156 const char* code) { | |
157 GLuint shader = gl_->CreateShader(type); | |
158 | |
159 gl_->ShaderSource(shader, 1, (const GLchar**)&code, NULL); | |
160 gl_->CompileShader(shader); | |
161 | |
162 #ifndef NDEBUG | |
163 GLint status = 0; | |
164 | |
165 gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
166 if (status != GL_TRUE) { | |
167 GLint max_length = 0; | |
168 GLint actual_length = 0; | |
169 gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); | |
170 | |
171 // The maxLength includes the NULL character | |
piman
2015/05/12 03:00:23
nit: max_length
| |
172 std::string error_log(max_length, 0); | |
173 gl_->GetShaderInfoLog(shader, max_length, &actual_length, &error_log[0]); | |
174 | |
175 LOG(ERROR) << name << " shader compilation failed: " << error_log.c_str(); | |
176 gl_->DeleteShader(shader); | |
177 return 0; | |
178 } | |
179 #endif | |
180 | |
181 return shader; | |
182 } | |
183 | |
184 GLuint YUVConverter::CreateProgram(const char* name, | |
185 GLuint vshader, | |
186 GLuint fshader) { | |
187 GLuint program = gl_->CreateProgram(); | |
188 gl_->AttachShader(program, vshader); | |
189 gl_->AttachShader(program, fshader); | |
190 | |
191 // bind known vertex position | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
192 gl_->BindAttribLocation(program, 0, "position"); | |
193 | |
194 gl_->LinkProgram(program); | |
195 | |
196 #ifndef NDEBUG | |
197 GLint status = 0; | |
198 | |
199 gl_->GetProgramiv(program, GL_LINK_STATUS, &status); | |
200 if (status != GL_TRUE) { | |
201 GLint max_length = 0; | |
202 GLint actual_length = 0; | |
203 gl_->GetProgramiv(program, GL_INFO_LOG_LENGTH, &max_length); | |
204 | |
205 // The maxLength includes the NULL character | |
piman
2015/05/12 03:00:25
nit: max_length
| |
206 std::string error_log(max_length, 0); | |
207 gl_->GetProgramInfoLog(program, max_length, &actual_length, &error_log[0]); | |
208 | |
209 LOG(ERROR) << name << " program linking failed: " << error_log.c_str(); | |
210 return 0; | |
211 } | |
212 #endif | |
213 | |
214 return program; | |
215 } | |
216 | |
217 void YUVConverter::SetUniform(GLuint program, const char* name, int value) { | |
218 GLint loc = gl_->GetUniformLocation(program, name); | |
219 DCHECK(loc != -1); | |
220 gl_->UseProgram(program); | |
221 gl_->Uniform1i(loc, value); | |
222 gl_->UseProgram(0); | |
223 } | |
224 | |
225 void YUVConverter::SetUniform(GLuint program, const char* name, float value) { | |
226 GLint loc = gl_->GetUniformLocation(program, name); | |
227 DCHECK(loc != -1); | |
228 gl_->UseProgram(program); | |
229 gl_->Uniform1f(loc, value); | |
230 gl_->UseProgram(0); | |
231 } | |
232 | |
233 GLuint YUVConverter::CreateShader() { | |
234 const char* vert_shader = | |
235 "precision mediump float;\n" | |
236 "attribute vec2 position;\n" | |
237 "varying vec2 texcoord;\n" | |
238 "uniform float clamp_width;\n" | |
239 "void main()\n" | |
240 "{\n" | |
241 " gl_Position = vec4( position.xy, 0, 1 );\n" | |
242 // convert vertex positions to texcoords, clamp x | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
243 " vec2 tmp = position*0.5+0.5;\n" | |
244 " texcoord = vec2(min(tmp.x, clamp_width), tmp.y);\n" | |
245 "}"; | |
246 | |
247 const char* frag_shader = | |
248 "precision mediump float;\n" | |
249 "varying vec2 texcoord;\n" | |
250 "uniform sampler2D y_sampler;\n" | |
251 "uniform sampler2D u_sampler;\n" | |
252 "uniform sampler2D v_sampler;\n" | |
253 "void main()\n" | |
254 "{\n" | |
255 // These values are magic numbers that are used in the transformation | |
256 // from YUV to RGB color values. They are taken from the following | |
257 // webpage: http://www.fourcc.org/fccyvrgb.php. In this case Rec. 601. | |
258 // yuv_2_rgb and yuv_adjust_constrained were taken from gl_renderer.cc | |
259 " const mat3 yuv_2_rgb = mat3( 1.164, 1.164, 1.164,\n" | |
260 " 0.0, -.391, 2.018,\n" | |
261 " 1.596, -.813, 0.0 );\n" | |
262 // These values map to 16, 128, and 128 respectively, and are computed | |
263 // as a fraction over 256 (e.g. 16 / 256 = 0.0625). | |
264 // They are used in the YUV to RGBA conversion formula: | |
265 // Y - 16 : Gives 16 values of head and footroom for overshooting | |
266 // U - 128 : Turns unsigned U into signed U [-128,127] | |
267 // V - 128 : Turns unsigned V into signed V [-128,127] | |
268 " const vec3 yuv_adjust_constrained = vec3( -0.0625, -0.5, -0.5 );\n" | |
269 | |
270 " vec3 yuv = vec3( texture2D(y_sampler, texcoord).x,\n" | |
271 " texture2D(u_sampler, texcoord).x,\n" | |
272 " texture2D(v_sampler, texcoord).x ) +\n" | |
273 " yuv_adjust_constrained;\n" | |
274 " gl_FragColor = vec4( yuv_2_rgb * yuv, 1.0 );\n" | |
275 "}"; | |
276 | |
277 // | |
278 // Compile vertex and fragment shaders. | |
279 // | |
piman
2015/05/12 03:00:23
nit: remove comment
| |
280 GLuint vertex_shader = | |
281 CompileShader("Vertex Shader", GL_VERTEX_SHADER, vert_shader); | |
282 if (!vertex_shader) { | |
283 return 0; | |
284 } | |
285 | |
286 GLuint fragment_shader = | |
287 CompileShader("Fragment Shader", GL_FRAGMENT_SHADER, frag_shader); | |
288 if (!fragment_shader) { | |
289 gl_->DeleteShader(vertex_shader); | |
290 return 0; | |
291 } | |
292 | |
293 // | |
294 // Create the program | |
295 // | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
296 GLuint program = | |
297 CreateProgram("YUVConverter Program", vertex_shader, fragment_shader); | |
298 | |
299 // delete shaders since they are no longer needed | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
300 gl_->DeleteShader(vertex_shader); | |
301 gl_->DeleteShader(fragment_shader); | |
302 | |
303 if (!program) { | |
304 return 0; | |
305 } | |
306 | |
307 GLuint result = program; | |
308 | |
309 // | |
310 // Bind samplers to texture units - which never change. | |
311 // | |
piman
2015/05/12 03:00:25
nit: remove comment
| |
312 SetUniform(program, "y_sampler", 0); | |
313 SetUniform(program, "u_sampler", 1); | |
314 SetUniform(program, "v_sampler", 2); | |
315 | |
316 // set default clamp width | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
317 SetUniform(program, "clamp_width", clamp_value_); | |
318 | |
319 // store clamp width loc for later | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
320 clamp_width_loc_ = gl_->GetUniformLocation(program, "clamp_width"); | |
321 DCHECK(clamp_width_loc_ != -1); | |
322 | |
323 if (!result) { | |
324 gl_->DeleteProgram(program); | |
325 } | |
326 | |
327 return result; | |
328 } | |
329 | |
330 bool YUVConverter::Initialize() { | |
331 // | |
piman
2015/05/12 03:00:24
nit: remove blank comment line
| |
332 // Determine formats to use based on whether GL_RED is available or | |
333 // whether we need to use GL_LUMINANCE. | |
334 // | |
piman
2015/05/12 03:00:23
nit: remove blank comment line
| |
335 DCHECK(gl_); | |
336 if (context_provider_->ContextCapabilities().gpu.texture_rg) { | |
337 internal_format_ = GL_RED_EXT; | |
338 format_ = GL_RED_EXT; | |
339 } else { | |
340 internal_format_ = GL_LUMINANCE; | |
341 format_ = GL_LUMINANCE; | |
342 } | |
343 | |
344 gl_->PushGroupMarkerEXT(0, "YUVConverterContext"); | |
345 | |
346 gl_->GenFramebuffers(1, &frame_buffer_); | |
347 | |
348 // Create some default textures to hold Y,U,V values | |
piman
2015/05/12 03:00:25
nit: remove comment
| |
349 y_texture_ = CreateTexture(); | |
350 u_texture_ = CreateTexture(); | |
351 v_texture_ = CreateTexture(); | |
352 | |
353 // Vertex positions. Also converted to texcoords in vertex shader. | |
354 GLfloat vtx_buf[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; | |
355 | |
356 gl_->GenBuffers(1, &vtx_buffer_); | |
357 gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); | |
358 gl_->BufferData(GL_ARRAY_BUFFER, 2 * sizeof(GLfloat) * 4, vtx_buf, | |
359 GL_STATIC_DRAW); | |
360 gl_->BindBuffer(GL_ARRAY_BUFFER, 0); | |
361 | |
362 program_ = CreateShader(); | |
363 | |
364 gl_->PopGroupMarkerEXT(); | |
365 | |
366 context_provider_->InvalidateGrContext(gr_invalidate_state_); | |
367 | |
368 return (program_ != 0); | |
369 } | |
370 | |
371 void YUVConverter::Convert(const scoped_refptr<media::VideoFrame>& frame, | |
372 GLuint tex_out) { | |
373 int divisor_height = 1; | |
374 bool supported_format = true; | |
375 | |
376 // make sure we support this format | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
377 switch (frame->format()) { | |
378 case media::VideoFrame::YV12: /* 420 */ | |
379 case media::VideoFrame::I420: /* 420 */ | |
380 divisor_height = 2; | |
381 break; | |
382 | |
383 case media::VideoFrame::YV16: /* 422 */ | |
384 case media::VideoFrame::YV24: /* 444 */ | |
385 break; | |
386 | |
387 default: | |
388 // rest are not supported.. | |
piman
2015/05/12 03:00:23
nit: remove comment
| |
389 supported_format = false; | |
390 break; | |
391 } | |
392 | |
393 gl_->PushGroupMarkerEXT(0, "YUVConverterContext"); | |
394 | |
395 bool set_clamp = false; | |
396 | |
397 uint32_t ywidth = frame->coded_size().width(); | |
398 uint32_t yheight = frame->coded_size().height(); | |
399 | |
400 if (supported_format) { | |
401 DCHECK_EQ(frame->stride(media::VideoFrame::kUPlane), | |
402 frame->stride(media::VideoFrame::kVPlane)); | |
403 | |
404 uint32_t ystride = frame->stride(media::VideoFrame::kYPlane); | |
405 uint32_t uvstride = frame->stride(media::VideoFrame::kUPlane); | |
406 | |
407 // resize textures if necessary | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
408 if (ystride != y_width_ || yheight != y_height_) { | |
409 // choose width based on the stride | |
410 // then, clamp later | |
piman
2015/05/12 03:00:25
You can keep this comment, but it should be a full
| |
411 y_width_ = ystride; | |
412 y_height_ = yheight; | |
413 | |
414 uv_width_ = uvstride; | |
415 uv_height_ = y_height_ / divisor_height; | |
416 | |
417 if ((ystride != ywidth) || (clamp_width_ != ywidth)) { | |
piman
2015/05/12 03:00:25
I'm not sure I understand the need for this outer
| |
418 if (clamp_width_ != ywidth) { | |
419 // clamp width to avoid sampling padding pixels | |
piman
2015/05/12 03:00:24
nit: remove comment (assuming you explained above)
| |
420 clamp_width_ = ywidth; | |
421 clamp_value_ = | |
422 static_cast<float>(ywidth) / static_cast<float>(ystride); | |
423 // clamp to 1/2 pixel inside to avoid bilinear sampling errors | |
piman
2015/05/12 03:00:24
nit: remove comment (assuming you explained above)
| |
424 clamp_value_ -= (1.f / (2.f * static_cast<float>(ystride))); | |
425 | |
426 set_clamp = true; | |
427 } | |
428 } | |
429 | |
430 // | |
431 // Resize the textures and upload data | |
432 // | |
piman
2015/05/12 03:00:23
nit: remove comment
| |
433 gl_->ActiveTexture(GL_TEXTURE0); | |
434 gl_->BindTexture(GL_TEXTURE_2D, y_texture_); | |
435 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, y_width_, y_height_, | |
436 0, format_, GL_UNSIGNED_BYTE, | |
437 frame->data(media::VideoFrame::kYPlane)); | |
438 | |
439 gl_->ActiveTexture(GL_TEXTURE1); | |
440 gl_->BindTexture(GL_TEXTURE_2D, u_texture_); | |
441 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, uv_height_, | |
442 0, format_, GL_UNSIGNED_BYTE, | |
443 frame->data(media::VideoFrame::kUPlane)); | |
444 | |
445 gl_->ActiveTexture(GL_TEXTURE2); | |
446 gl_->BindTexture(GL_TEXTURE_2D, v_texture_); | |
447 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, uv_height_, | |
448 0, format_, GL_UNSIGNED_BYTE, | |
449 frame->data(media::VideoFrame::kVPlane)); | |
450 } else { | |
451 // | |
452 // Bind textures and upload texture data | |
453 // | |
piman
2015/05/12 03:00:24
nit: remove comment
| |
454 gl_->ActiveTexture(GL_TEXTURE0); | |
455 gl_->BindTexture(GL_TEXTURE_2D, y_texture_); | |
456 gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, y_width_, y_height_, format_, | |
457 GL_UNSIGNED_BYTE, | |
458 frame->data(media::VideoFrame::kYPlane)); | |
459 | |
460 gl_->ActiveTexture(GL_TEXTURE1); | |
461 gl_->BindTexture(GL_TEXTURE_2D, u_texture_); | |
462 gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, format_, | |
463 GL_UNSIGNED_BYTE, | |
464 frame->data(media::VideoFrame::kUPlane)); | |
465 | |
466 gl_->ActiveTexture(GL_TEXTURE2); | |
467 gl_->BindTexture(GL_TEXTURE_2D, v_texture_); | |
468 gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, format_, | |
469 GL_UNSIGNED_BYTE, | |
470 frame->data(media::VideoFrame::kVPlane)); | |
471 } | |
472 } else { | |
473 // | |
474 // Unsupported format - set textures to be 4x4 and produce a red output | |
475 // 2x2 texture didn't seem to work | |
476 // | |
piman
2015/05/12 03:00:25
If the format is not supported by the fast path, w
| |
477 uint8_t yudata[4 * 4]; | |
478 memset(yudata, 0x5e, 4 * 4); | |
479 gl_->ActiveTexture(GL_TEXTURE0); | |
480 gl_->BindTexture(GL_TEXTURE_2D, y_texture_); | |
481 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, 0, format_, | |
482 GL_UNSIGNED_BYTE, yudata); | |
483 | |
484 gl_->ActiveTexture(GL_TEXTURE1); | |
485 gl_->BindTexture(GL_TEXTURE_2D, u_texture_); | |
486 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, 0, format_, | |
487 GL_UNSIGNED_BYTE, yudata); | |
488 | |
489 uint8_t vdata[4 * 4]; | |
490 memset(vdata, 0xe7, 4 * 4); | |
491 gl_->ActiveTexture(GL_TEXTURE2); | |
492 gl_->BindTexture(GL_TEXTURE_2D, v_texture_); | |
493 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, 0, format_, | |
494 GL_UNSIGNED_BYTE, vdata); | |
495 | |
496 if (clamp_value_ != 1.f) { | |
497 set_clamp = true; | |
498 clamp_value_ = 1.f; | |
499 } | |
500 } | |
501 | |
502 gl_->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer_); | |
503 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | |
504 tex_out, 0); | |
505 | |
506 #ifndef NDEBUG | |
507 // NOTE: we should probably check for framebuffer complete here, but that | |
508 // will slow this method down so check in debug mode | |
piman
2015/05/12 03:00:25
nit: end with a .
| |
509 GLint status = gl_->CheckFramebufferStatus(GL_FRAMEBUFFER); | |
510 if (status != GL_FRAMEBUFFER_COMPLETE) { | |
511 return; | |
512 } | |
513 #endif | |
514 | |
515 gl_->Viewport(0, 0, ywidth, yheight); | |
516 | |
517 gl_->UseProgram(program_); | |
518 | |
519 if (set_clamp) { | |
520 gl_->Uniform1f(clamp_width_loc_, clamp_value_); | |
521 } | |
522 | |
523 gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); | |
524 gl_->EnableVertexAttribArray(0); | |
525 gl_->VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), | |
526 static_cast<const void*>(0)); | |
527 | |
528 gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
529 | |
530 gl_->BindBuffer(GL_ARRAY_BUFFER, 0); | |
531 gl_->DisableVertexAttribArray(0); | |
532 gl_->UseProgram(0); | |
533 gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); | |
534 | |
535 // active texture is still 2 | |
piman
2015/05/12 03:00:23
nit: remove comment
| |
536 gl_->BindTexture(GL_TEXTURE_2D, 0); | |
537 | |
538 gl_->ActiveTexture(GL_TEXTURE1); | |
539 gl_->BindTexture(GL_TEXTURE_2D, 0); | |
540 | |
541 gl_->ActiveTexture(GL_TEXTURE0); | |
542 gl_->BindTexture(GL_TEXTURE_2D, 0); | |
543 | |
544 gl_->PopGroupMarkerEXT(); | |
545 | |
546 context_provider_->InvalidateGrContext(gr_invalidate_state_); | |
547 } | |
548 | |
31 struct VideoDecoderShim::PendingDecode { | 549 struct VideoDecoderShim::PendingDecode { |
32 PendingDecode(uint32_t decode_id, | 550 PendingDecode(uint32_t decode_id, |
33 const scoped_refptr<media::DecoderBuffer>& buffer); | 551 const scoped_refptr<media::DecoderBuffer>& buffer); |
34 ~PendingDecode(); | 552 ~PendingDecode(); |
35 | 553 |
36 const uint32_t decode_id; | 554 const uint32_t decode_id; |
37 const scoped_refptr<media::DecoderBuffer> buffer; | 555 const scoped_refptr<media::DecoderBuffer> buffer; |
38 }; | 556 }; |
39 | 557 |
40 VideoDecoderShim::PendingDecode::PendingDecode( | 558 VideoDecoderShim::PendingDecode::PendingDecode( |
41 uint32_t decode_id, | 559 uint32_t decode_id, |
42 const scoped_refptr<media::DecoderBuffer>& buffer) | 560 const scoped_refptr<media::DecoderBuffer>& buffer) |
43 : decode_id(decode_id), buffer(buffer) { | 561 : decode_id(decode_id), buffer(buffer) { |
44 } | 562 } |
45 | 563 |
46 VideoDecoderShim::PendingDecode::~PendingDecode() { | 564 VideoDecoderShim::PendingDecode::~PendingDecode() { |
47 } | 565 } |
48 | 566 |
49 struct VideoDecoderShim::PendingFrame { | 567 struct VideoDecoderShim::PendingFrame { |
50 explicit PendingFrame(uint32_t decode_id); | 568 explicit PendingFrame(uint32_t decode_id); |
51 PendingFrame(uint32_t decode_id, | 569 PendingFrame(uint32_t decode_id, |
52 const gfx::Size& coded_size, | 570 const scoped_refptr<media::VideoFrame>& frame); |
53 const gfx::Rect& visible_rect); | |
54 ~PendingFrame(); | 571 ~PendingFrame(); |
55 | 572 |
56 const uint32_t decode_id; | 573 const uint32_t decode_id; |
57 const gfx::Size coded_size; | 574 scoped_refptr<media::VideoFrame> video_frame; |
58 const gfx::Rect visible_rect; | |
59 std::vector<uint8_t> argb_pixels; | |
60 | 575 |
61 private: | 576 private: |
62 // This could be expensive to copy, so guard against that. | 577 // This could be expensive to copy, so guard against that. |
63 DISALLOW_COPY_AND_ASSIGN(PendingFrame); | 578 DISALLOW_COPY_AND_ASSIGN(PendingFrame); |
64 }; | 579 }; |
65 | 580 |
66 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) | 581 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) |
67 : decode_id(decode_id) { | 582 : decode_id(decode_id) { |
68 } | 583 } |
69 | 584 |
70 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id, | 585 VideoDecoderShim::PendingFrame::PendingFrame( |
71 const gfx::Size& coded_size, | 586 uint32_t decode_id, |
72 const gfx::Rect& visible_rect) | 587 const scoped_refptr<media::VideoFrame>& frame) |
73 : decode_id(decode_id), | 588 : decode_id(decode_id), video_frame(frame) { |
74 coded_size(coded_size), | |
75 visible_rect(visible_rect), | |
76 argb_pixels(coded_size.width() * coded_size.height() * 4) { | |
77 } | 589 } |
78 | 590 |
79 VideoDecoderShim::PendingFrame::~PendingFrame() { | 591 VideoDecoderShim::PendingFrame::~PendingFrame() { |
80 } | 592 } |
81 | 593 |
82 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving | 594 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving |
83 // calls from the VideoDecodeShim on the main thread and sending results back. | 595 // calls from the VideoDecodeShim on the main thread and sending results back. |
84 // This class is constructed on the main thread, but used and destructed on the | 596 // This class is constructed on the main thread, but used and destructed on the |
85 // media thread. | 597 // media thread. |
86 class VideoDecoderShim::DecoderImpl { | 598 class VideoDecoderShim::DecoderImpl { |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 } | 774 } |
263 | 775 |
264 void VideoDecoderShim::DecoderImpl::OnOutputComplete( | 776 void VideoDecoderShim::DecoderImpl::OnOutputComplete( |
265 const scoped_refptr<media::VideoFrame>& frame) { | 777 const scoped_refptr<media::VideoFrame>& frame) { |
266 // Software decoders are expected to generated frames only when a Decode() | 778 // Software decoders are expected to generated frames only when a Decode() |
267 // call is pending. | 779 // call is pending. |
268 DCHECK(awaiting_decoder_); | 780 DCHECK(awaiting_decoder_); |
269 | 781 |
270 scoped_ptr<PendingFrame> pending_frame; | 782 scoped_ptr<PendingFrame> pending_frame; |
271 if (!frame->end_of_stream()) { | 783 if (!frame->end_of_stream()) { |
272 pending_frame.reset(new PendingFrame( | 784 pending_frame.reset(new PendingFrame(decode_id_, frame)); |
273 decode_id_, frame->coded_size(), frame->visible_rect())); | |
274 // Convert the VideoFrame pixels to ABGR to match VideoDecodeAccelerator. | |
275 media::SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( | |
276 frame, | |
277 &pending_frame->argb_pixels.front(), | |
278 frame->coded_size().width() * 4); | |
279 } else { | 785 } else { |
280 pending_frame.reset(new PendingFrame(decode_id_)); | 786 pending_frame.reset(new PendingFrame(decode_id_)); |
281 } | 787 } |
282 | 788 |
283 main_message_loop_->PostTask(FROM_HERE, | 789 main_message_loop_->PostTask(FROM_HERE, |
284 base::Bind(&VideoDecoderShim::OnOutputComplete, | 790 base::Bind(&VideoDecoderShim::OnOutputComplete, |
285 shim_, | 791 shim_, |
286 base::Passed(&pending_frame))); | 792 base::Passed(&pending_frame))); |
287 } | 793 } |
288 | 794 |
289 void VideoDecoderShim::DecoderImpl::OnResetComplete() { | 795 void VideoDecoderShim::DecoderImpl::OnResetComplete() { |
290 main_message_loop_->PostTask( | 796 main_message_loop_->PostTask( |
291 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_)); | 797 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_)); |
292 } | 798 } |
293 | 799 |
294 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host) | 800 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host) |
295 : state_(UNINITIALIZED), | 801 : state_(UNINITIALIZED), |
296 host_(host), | 802 host_(host), |
297 media_task_runner_( | 803 media_task_runner_( |
298 RenderThreadImpl::current()->GetMediaThreadTaskRunner()), | 804 RenderThreadImpl::current()->GetMediaThreadTaskRunner()), |
299 context_provider_( | 805 context_provider_( |
300 RenderThreadImpl::current()->SharedMainThreadContextProvider()), | 806 RenderThreadImpl::current()->SharedMainThreadContextProvider()), |
301 texture_pool_size_(0), | 807 texture_pool_size_(0), |
302 num_pending_decodes_(0), | 808 num_pending_decodes_(0), |
303 weak_ptr_factory_(this) { | 809 weak_ptr_factory_(this) { |
304 DCHECK(host_); | 810 DCHECK(host_); |
305 DCHECK(media_task_runner_.get()); | 811 DCHECK(media_task_runner_.get()); |
306 DCHECK(context_provider_.get()); | 812 DCHECK(context_provider_.get()); |
307 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr())); | 813 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr())); |
814 yuv_converter_.reset(new YUVConverter(context_provider_)); | |
308 } | 815 } |
309 | 816 |
310 VideoDecoderShim::~VideoDecoderShim() { | 817 VideoDecoderShim::~VideoDecoderShim() { |
311 DCHECK(RenderThreadImpl::current()); | 818 DCHECK(RenderThreadImpl::current()); |
312 // Delete any remaining textures. | 819 // Delete any remaining textures. |
313 TextureIdMap::iterator it = texture_id_map_.begin(); | 820 TextureIdMap::iterator it = texture_id_map_.begin(); |
314 for (; it != texture_id_map_.end(); ++it) | 821 for (; it != texture_id_map_.end(); ++it) |
315 DeleteTexture(it->second); | 822 DeleteTexture(it->second); |
316 texture_id_map_.clear(); | 823 texture_id_map_.clear(); |
317 | 824 |
(...skipping 18 matching lines...) Expand all Loading... | |
336 DCHECK_EQ(state_, UNINITIALIZED); | 843 DCHECK_EQ(state_, UNINITIALIZED); |
337 media::VideoCodec codec = media::kUnknownVideoCodec; | 844 media::VideoCodec codec = media::kUnknownVideoCodec; |
338 if (profile <= media::H264PROFILE_MAX) | 845 if (profile <= media::H264PROFILE_MAX) |
339 codec = media::kCodecH264; | 846 codec = media::kCodecH264; |
340 else if (profile <= media::VP8PROFILE_MAX) | 847 else if (profile <= media::VP8PROFILE_MAX) |
341 codec = media::kCodecVP8; | 848 codec = media::kCodecVP8; |
342 else if (profile <= media::VP9PROFILE_MAX) | 849 else if (profile <= media::VP9PROFILE_MAX) |
343 codec = media::kCodecVP9; | 850 codec = media::kCodecVP9; |
344 DCHECK_NE(codec, media::kUnknownVideoCodec); | 851 DCHECK_NE(codec, media::kUnknownVideoCodec); |
345 | 852 |
853 // initialize yuv converter | |
piman
2015/05/12 03:00:24
nit: remove comment
piman
2015/05/13 04:20:37
ping
| |
854 if (!yuv_converter_->Initialize()) { | |
855 return false; | |
856 } | |
857 | |
346 media::VideoDecoderConfig config( | 858 media::VideoDecoderConfig config( |
347 codec, | 859 codec, |
348 profile, | 860 profile, |
349 media::VideoFrame::YV12, | 861 media::VideoFrame::YV12, |
350 gfx::Size(32, 24), // Small sizes that won't fail. | 862 gfx::Size(32, 24), // Small sizes that won't fail. |
351 gfx::Rect(32, 24), | 863 gfx::Rect(32, 24), |
352 gfx::Size(32, 24), | 864 gfx::Size(32, 24), |
353 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed. | 865 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed. |
354 0 /* extra_data_size */, | 866 0 /* extra_data_size */, |
355 false /* decryption */); | 867 false /* decryption */); |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
464 num_pending_decodes_--; | 976 num_pending_decodes_--; |
465 completed_decodes_.push(decode_id); | 977 completed_decodes_.push(decode_id); |
466 | 978 |
467 // If frames are being queued because we're out of textures, don't notify | 979 // If frames are being queued because we're out of textures, don't notify |
468 // the host that decode has completed. This exerts "back pressure" to keep | 980 // the host that decode has completed. This exerts "back pressure" to keep |
469 // the host from sending buffers that will cause pending_frames_ to grow. | 981 // the host from sending buffers that will cause pending_frames_ to grow. |
470 if (pending_frames_.empty()) | 982 if (pending_frames_.empty()) |
471 NotifyCompletedDecodes(); | 983 NotifyCompletedDecodes(); |
472 } | 984 } |
473 | 985 |
474 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) { | 986 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> pframe) { |
475 DCHECK(RenderThreadImpl::current()); | 987 DCHECK(RenderThreadImpl::current()); |
476 DCHECK(host_); | 988 DCHECK(host_); |
477 | 989 |
478 if (!frame->argb_pixels.empty()) { | 990 if (pframe->video_frame) { |
479 if (texture_size_ != frame->coded_size) { | 991 if (texture_size_ != pframe->video_frame->coded_size()) { |
480 // If the size has changed, all current textures must be dismissed. Add | 992 // If the size has changed, all current textures must be dismissed. Add |
481 // all textures to |textures_to_dismiss_| and dismiss any that aren't in | 993 // all textures to |textures_to_dismiss_| and dismiss any that aren't in |
482 // use by the plugin. We will dismiss the rest as they are recycled. | 994 // use by the plugin. We will dismiss the rest as they are recycled. |
483 for (TextureIdMap::const_iterator it = texture_id_map_.begin(); | 995 for (TextureIdMap::const_iterator it = texture_id_map_.begin(); |
484 it != texture_id_map_.end(); | 996 it != texture_id_map_.end(); |
485 ++it) { | 997 ++it) { |
486 textures_to_dismiss_.insert(it->first); | 998 textures_to_dismiss_.insert(it->first); |
487 } | 999 } |
488 for (TextureIdSet::const_iterator it = available_textures_.begin(); | 1000 for (TextureIdSet::const_iterator it = available_textures_.begin(); |
489 it != available_textures_.end(); | 1001 it != available_textures_.end(); |
490 ++it) { | 1002 ++it) { |
491 DismissTexture(*it); | 1003 DismissTexture(*it); |
492 } | 1004 } |
493 available_textures_.clear(); | 1005 available_textures_.clear(); |
494 FlushCommandBuffer(); | 1006 FlushCommandBuffer(); |
495 | 1007 |
496 DCHECK(pending_texture_mailboxes_.empty()); | 1008 DCHECK(pending_texture_mailboxes_.empty()); |
497 for (uint32_t i = 0; i < texture_pool_size_; i++) | 1009 for (uint32_t i = 0; i < texture_pool_size_; i++) |
498 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); | 1010 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); |
499 | 1011 |
500 host_->RequestTextures(texture_pool_size_, | 1012 host_->RequestTextures(texture_pool_size_, |
501 frame->coded_size, | 1013 pframe->video_frame->coded_size(), GL_TEXTURE_2D, |
502 GL_TEXTURE_2D, | |
503 pending_texture_mailboxes_); | 1014 pending_texture_mailboxes_); |
504 texture_size_ = frame->coded_size; | 1015 texture_size_ = pframe->video_frame->coded_size(); |
505 } | 1016 } |
506 | 1017 |
507 pending_frames_.push(linked_ptr<PendingFrame>(frame.release())); | 1018 pending_frames_.push(linked_ptr<PendingFrame>(pframe.release())); |
508 SendPictures(); | 1019 SendPictures(); |
509 } | 1020 } |
510 } | 1021 } |
511 | 1022 |
512 void VideoDecoderShim::SendPictures() { | 1023 void VideoDecoderShim::SendPictures() { |
513 DCHECK(RenderThreadImpl::current()); | 1024 DCHECK(RenderThreadImpl::current()); |
514 DCHECK(host_); | 1025 DCHECK(host_); |
515 while (!pending_frames_.empty() && !available_textures_.empty()) { | 1026 while (!pending_frames_.empty() && !available_textures_.empty()) { |
516 const linked_ptr<PendingFrame>& frame = pending_frames_.front(); | 1027 const linked_ptr<PendingFrame>& frame = pending_frames_.front(); |
517 | 1028 |
518 TextureIdSet::iterator it = available_textures_.begin(); | 1029 TextureIdSet::iterator it = available_textures_.begin(); |
519 uint32_t texture_id = *it; | 1030 uint32_t texture_id = *it; |
520 available_textures_.erase(it); | 1031 available_textures_.erase(it); |
521 | 1032 |
522 uint32_t local_texture_id = texture_id_map_[texture_id]; | 1033 uint32_t local_texture_id = texture_id_map_[texture_id]; |
523 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | 1034 |
524 gles2->ActiveTexture(GL_TEXTURE0); | 1035 // run the yuv conversion renderer |
piman
2015/05/12 03:00:23
nit: remove comment
piman
2015/05/13 04:20:37
ping
| |
525 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id); | 1036 yuv_converter_->Convert(frame->video_frame, local_texture_id); |
526 #if !defined(OS_ANDROID) | |
527 // BGRA is the native texture format, except on Android, where textures | |
528 // would be uploaded as GL_RGBA. | |
529 gles2->TexImage2D(GL_TEXTURE_2D, | |
530 0, | |
531 GL_BGRA_EXT, | |
532 texture_size_.width(), | |
533 texture_size_.height(), | |
534 0, | |
535 GL_BGRA_EXT, | |
536 GL_UNSIGNED_BYTE, | |
537 &frame->argb_pixels.front()); | |
538 #else | |
539 #error Not implemented. | |
540 #endif | |
541 | 1037 |
542 host_->PictureReady(media::Picture(texture_id, frame->decode_id, | 1038 host_->PictureReady(media::Picture(texture_id, frame->decode_id, |
543 frame->visible_rect, false)); | 1039 frame->video_frame->visible_rect(), |
1040 false)); | |
544 pending_frames_.pop(); | 1041 pending_frames_.pop(); |
545 } | 1042 } |
546 | 1043 |
547 FlushCommandBuffer(); | 1044 FlushCommandBuffer(); |
548 | 1045 |
549 if (pending_frames_.empty()) { | 1046 if (pending_frames_.empty()) { |
550 // If frames aren't backing up, notify the host of any completed decodes so | 1047 // If frames aren't backing up, notify the host of any completed decodes so |
551 // it can send more buffers. | 1048 // it can send more buffers. |
552 NotifyCompletedDecodes(); | 1049 NotifyCompletedDecodes(); |
553 | 1050 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) { | 1090 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) { |
594 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | 1091 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
595 gles2->DeleteTextures(1, &texture_id); | 1092 gles2->DeleteTextures(1, &texture_id); |
596 } | 1093 } |
597 | 1094 |
598 void VideoDecoderShim::FlushCommandBuffer() { | 1095 void VideoDecoderShim::FlushCommandBuffer() { |
599 context_provider_->ContextGL()->Flush(); | 1096 context_provider_->ContextGL()->Flush(); |
600 } | 1097 } |
601 | 1098 |
602 } // namespace content | 1099 } // namespace content |
OLD | NEW |