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 #include "base/logging.h" | |
12 #include "base/numerics/safe_conversions.h" | 13 #include "base/numerics/safe_conversions.h" |
13 #include "base/single_thread_task_runner.h" | 14 #include "base/single_thread_task_runner.h" |
14 #include "cc/blink/context_provider_web_context.h" | 15 #include "cc/blink/context_provider_web_context.h" |
15 #include "content/public/renderer/render_thread.h" | 16 #include "content/public/renderer/render_thread.h" |
16 #include "content/renderer/pepper/pepper_video_decoder_host.h" | 17 #include "content/renderer/pepper/pepper_video_decoder_host.h" |
17 #include "content/renderer/render_thread_impl.h" | 18 #include "content/renderer/render_thread_impl.h" |
18 #include "gpu/command_buffer/client/gles2_implementation.h" | 19 #include "gpu/command_buffer/client/gles2_implementation.h" |
19 #include "media/base/decoder_buffer.h" | 20 #include "media/base/decoder_buffer.h" |
20 #include "media/base/limits.h" | 21 #include "media/base/limits.h" |
21 #include "media/base/video_decoder.h" | 22 #include "media/base/video_decoder.h" |
22 #include "media/blink/skcanvas_video_renderer.h" | 23 #include "media/blink/skcanvas_video_renderer.h" |
23 #include "media/filters/ffmpeg_video_decoder.h" | 24 #include "media/filters/ffmpeg_video_decoder.h" |
24 #include "media/filters/vpx_video_decoder.h" | 25 #include "media/filters/vpx_video_decoder.h" |
25 #include "media/video/picture.h" | 26 #include "media/video/picture.h" |
26 #include "media/video/video_decode_accelerator.h" | 27 #include "media/video/video_decode_accelerator.h" |
27 #include "ppapi/c/pp_errors.h" | 28 #include "ppapi/c/pp_errors.h" |
28 | 29 |
29 namespace content { | 30 namespace content { |
30 | 31 |
32 // | |
33 // YUV->RGB converter class using a shader and FBO | |
34 // | |
35 class YUVConv { | |
36 public: | |
bbudge
2015/05/01 20:08:59
chromium indent of public/private is 1 space, then
| |
37 YUVConv( const scoped_refptr<cc_blink::ContextProviderWebContext> & ); | |
bbudge
2015/05/01 20:08:59
Chromium coding style: no space after '(', '>', an
| |
38 ~YUVConv(); | |
39 bool Initialize(); | |
40 void Convert( const scoped_refptr<media::VideoFrame> & frame, | |
41 GLuint texOut ); | |
bbudge
2015/05/01 20:08:59
Style: Chrome uses texture_out style rather than c
| |
42 | |
43 private: | |
44 GLuint CreateShader(); | |
45 GLuint CompileShader( const char * name, GLuint type, const char * code ); | |
bbudge
2015/05/01 20:08:58
s/char * name/char* name
| |
46 GLuint CreateProgram( const char * name, GLuint vshader, GLuint fshader ); | |
47 bool SetUniform( GLuint program, const char * name, int value ); | |
48 bool SetUniform( GLuint program, const char * name, float value ); | |
49 GLuint CreateTexture(); | |
50 | |
51 private: | |
bbudge
2015/05/01 20:08:59
duplicate private
| |
52 scoped_refptr<cc_blink::ContextProviderWebContext> context_provider_; | |
53 gpu::gles2::GLES2Interface* gl_; | |
54 GLuint frame_buffer_; | |
55 GLuint vtx_buffer_; | |
56 GLuint program_; | |
57 | |
58 GLuint y_texture_; | |
59 GLuint u_texture_; | |
60 GLuint v_texture_; | |
61 | |
62 GLuint internal_format_; | |
63 GLuint format_; | |
64 | |
65 GLuint y_width_; | |
66 GLuint y_height_; | |
67 | |
68 GLuint uv_width_; | |
69 GLuint uv_height_; | |
70 | |
71 GLfloat clamp_value_; | |
72 GLuint clamp_width_; | |
73 GLint clamp_width_loc_; | |
74 | |
75 DISALLOW_COPY_AND_ASSIGN(YUVConv); | |
76 }; | |
77 | |
78 YUVConv::YUVConv( | |
79 const scoped_refptr<cc_blink::ContextProviderWebContext> & ctx_p ) | |
bbudge
2015/05/01 20:08:59
s/ctx_p/context_provider
You can use 'git cl form
| |
80 : context_provider_( ctx_p ) | |
81 , gl_( context_provider_->ContextGL() ) | |
bbudge
2015/05/01 20:08:59
Chromium style puts the commas on the previous lin
| |
82 , frame_buffer_(0) | |
83 , vtx_buffer_(0) | |
84 , program_(0) | |
85 , y_texture_(0) | |
86 , u_texture_(0) | |
87 , v_texture_(0) | |
88 , internal_format_(0) | |
89 , format_(0) | |
90 , y_width_(2) | |
91 , y_height_(2) | |
92 , uv_width_(2) | |
93 , uv_height_(2) | |
94 , clamp_value_(1.f) | |
95 , clamp_width_(0) | |
96 , clamp_width_loc_(0) { | |
97 } | |
98 | |
99 YUVConv::~YUVConv() { | |
100 // delete textures | |
101 if( y_texture_ ) | |
102 gl_->DeleteTextures( 1, &y_texture_ ); | |
103 | |
104 if( u_texture_ ) | |
105 gl_->DeleteTextures( 1, &u_texture_ ); | |
106 | |
107 if( v_texture_ ) | |
108 gl_->DeleteTextures( 1, &v_texture_ ); | |
109 | |
110 // delete framebuffer | |
111 if( frame_buffer_ ) | |
112 gl_->DeleteFramebuffers( 1, &frame_buffer_ ); | |
113 | |
114 // delete vertex buffer | |
115 if( vtx_buffer_ ) | |
116 gl_->DeleteBuffers( 1, &vtx_buffer_ ); | |
117 | |
118 // delete program | |
119 if( program_ ) | |
120 gl_->DeleteProgram( program_ ); | |
121 } | |
122 | |
123 GLuint YUVConv::CreateTexture() { | |
124 GLuint tex = 0; | |
125 | |
126 // create texture | |
127 gl_->GenTextures(1, &tex); | |
128 gl_->BindTexture(GL_TEXTURE_2D, tex); | |
129 | |
130 // create texture with default size - will be resized upon first frame | |
131 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 2, 2, 0, format_, | |
132 GL_UNSIGNED_BYTE, NULL); | |
133 | |
134 // set params | |
135 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
136 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
137 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
138 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
139 | |
140 // unbind | |
141 gl_->BindTexture(GL_TEXTURE_2D, 0); | |
142 | |
143 return tex; | |
144 } | |
145 | |
146 GLuint YUVConv::CompileShader( const char * name, GLuint type, | |
147 const char * code ) { | |
148 GLuint shader = gl_->CreateShader( type ); | |
149 | |
150 gl_->ShaderSource(shader, 1, (const GLchar**) &code, NULL); | |
151 gl_->CompileShader(shader); | |
152 | |
153 GLint status = 0; | |
154 | |
155 gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
156 if(status != GL_TRUE) | |
157 { | |
158 GLint maxLength = 0; | |
159 gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); | |
160 | |
161 // The maxLength includes the NULL character | |
162 std::string errorLog(0,maxLength); | |
163 gl_->GetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); | |
164 | |
165 LOG(ERROR) << name << " shader compilation failed: " | |
166 << errorLog.c_str(); | |
167 gl_->DeleteShader( shader ); | |
168 return 0; | |
169 } | |
170 | |
171 return shader; | |
172 } | |
173 | |
174 GLuint YUVConv::CreateProgram( const char * name, GLuint vshader, | |
175 GLuint fshader ) { | |
176 GLuint program = gl_->CreateProgram(); | |
177 gl_->AttachShader(program, vshader); | |
178 gl_->AttachShader(program, fshader); | |
179 | |
180 // bind known vertex position | |
181 gl_->BindAttribLocation(program, 0, "aPosition"); | |
182 | |
183 gl_->LinkProgram(program); | |
184 | |
185 GLint status = 0; | |
186 | |
187 gl_->GetProgramiv(program, GL_LINK_STATUS, &status); | |
188 if(status != GL_TRUE) | |
189 { | |
190 GLint maxLength = 0; | |
191 gl_->GetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); | |
192 | |
193 // The maxLength includes the NULL character | |
194 std::string errorLog(0,maxLength); | |
195 gl_->GetProgramInfoLog(program, maxLength, &maxLength, &errorLog[0]); | |
196 | |
197 LOG(ERROR) << name << " program linking failed: " | |
198 << errorLog.c_str(); | |
199 return 0; | |
200 } | |
201 | |
202 return program; | |
203 } | |
204 | |
205 bool YUVConv::SetUniform( GLuint program, const char * name, | |
206 int value ) { | |
207 bool result = false; | |
208 GLint loc = gl_->GetUniformLocation( program, name ); | |
209 if( loc != -1 ) | |
210 { | |
211 gl_->UseProgram( program ); | |
212 gl_->Uniform1i( loc, value ); | |
213 gl_->UseProgram( 0 ); | |
214 result = true; | |
215 } | |
216 | |
217 return result; | |
218 } | |
219 | |
220 bool YUVConv::SetUniform( GLuint program, const char * name, | |
221 float value ) { | |
222 bool result = false; | |
223 GLint loc = gl_->GetUniformLocation( program, name ); | |
224 if( loc != -1 ) | |
225 { | |
226 gl_->UseProgram( program ); | |
227 gl_->Uniform1f( loc, value ); | |
228 gl_->UseProgram( 0 ); | |
229 result = true; | |
230 } | |
231 | |
232 return result; | |
233 } | |
234 | |
235 GLuint YUVConv::CreateShader() { | |
236 const char* vertShader = | |
237 "precision mediump float;\n" | |
238 "attribute vec2 aPosition;\n" | |
239 "varying vec2 vTexCoord;\n" | |
240 "uniform float clamp_width;\n" | |
241 "void main()\n" | |
242 "{\n" | |
243 " gl_Position = vec4( aPosition.xy, 0, 1 );\n" | |
244 // convert vertex positions to texcoords, clamp x | |
245 " vec2 tmp = aPosition*0.5+0.5;\n" | |
246 " vTexCoord = vec2(min(tmp.x, clamp_width), tmp.y);\n" | |
247 "}"; | |
248 | |
249 const char* fragShader = | |
250 "precision mediump float;\n" | |
251 "varying vec2 vTexCoord;\n" | |
252 "#define texSample texture2D\n" | |
253 "#define fragColor gl_FragColor\n" | |
254 "uniform sampler2D texY;\n" | |
255 "uniform sampler2D texU;\n" | |
256 "uniform sampler2D texV;\n" | |
257 "void main()\n" | |
258 "{\n" | |
259 // These values are magic numbers that are used in the transformation | |
260 // from YUV to RGB color values. They are taken from the following | |
261 // webpage: http://www.fourcc.org/fccyvrgb.php. In this case Rec. 601. | |
262 // yuv_2_rgb and yuv_adjust_constrained were taken from gl_renderer.cc | |
263 " const mat3 yuv_2_rgb = mat3( 1.164, 1.164, 1.164,\n" | |
264 " 0.0, -.391, 2.018,\n" | |
265 " 1.596, -.813, 0.0 );\n" | |
266 // These values map to 16, 128, and 128 respectively, and are computed | |
267 // as a fraction over 256 (e.g. 16 / 256 = 0.0625). | |
268 // They are used in the YUV to RGBA conversion formula: | |
269 // Y - 16 : Gives 16 values of head and footroom for overshooting | |
270 // U - 128 : Turns unsigned U into signed U [-128,127] | |
271 // V - 128 : Turns unsigned V into signed V [-128,127] | |
272 " const vec3 yuv_adjust_constrained = vec3( -0.0625, -0.5, -0.5 );\n" | |
273 | |
274 " vec3 yuv = vec3( texSample(texY, vTexCoord).x,\n" | |
275 " texSample(texU, vTexCoord).x,\n" | |
276 " texSample(texV, vTexCoord).x ) +\n" | |
277 " yuv_adjust_constrained;\n" | |
278 " fragColor = vec4( yuv_2_rgb * yuv, 1.0 );\n" | |
279 "}"; | |
280 | |
281 // | |
282 // Compile vertex and fragment shaders. | |
283 // | |
284 GLuint vertexShader = CompileShader( "Vertex Shader", GL_VERTEX_SHADER, | |
285 vertShader ); | |
286 if( !vertexShader ) | |
287 { | |
288 return 0; | |
289 } | |
290 | |
291 GLuint fragmentShader = CompileShader("Fragment Shader", GL_FRAGMENT_SHADER, | |
292 fragShader); | |
293 if( !fragmentShader ) | |
294 { | |
295 gl_->DeleteShader( vertexShader ); | |
296 return 0; | |
297 } | |
298 | |
299 // | |
300 // Create the program | |
301 // | |
302 GLuint program = CreateProgram( "YUVConv Program", vertexShader, | |
303 fragmentShader ); | |
304 | |
305 // delete shaders since they are no longer needed | |
306 gl_->DeleteShader( vertexShader ); | |
307 gl_->DeleteShader( fragmentShader ); | |
308 | |
309 if( !program ) | |
310 { | |
311 return 0; | |
312 } | |
313 | |
314 GLuint result = program; | |
315 | |
316 // | |
317 // Bind samplers to texture units - which never change. | |
318 // | |
319 if( !SetUniform( program, "texY", 0 ) ) | |
320 { | |
321 result = 0; | |
322 } | |
323 if( !SetUniform( program, "texU", 1 ) ) | |
324 { | |
325 result = 0; | |
326 } | |
327 if( !SetUniform( program, "texV", 2 ) ) | |
328 { | |
329 result = 0; | |
330 } | |
331 | |
332 // set default clamp width | |
333 if( !SetUniform( program, "clamp_width", clamp_value_ ) ) | |
334 { | |
335 result = 0; | |
336 } | |
337 | |
338 // store clamp width loc for later | |
339 clamp_width_loc_ = gl_->GetUniformLocation( program, "clamp_width" ); | |
340 if( clamp_width_loc_ < 0 ) | |
341 { | |
342 result = 0; | |
343 } | |
344 | |
345 if( !result ) | |
346 { | |
347 gl_->DeleteProgram( program ); | |
348 } | |
349 | |
350 return result; | |
351 } | |
352 | |
353 bool YUVConv::Initialize() { | |
354 | |
355 // | |
356 // Determine formats to use based on whether GL_RED is available or | |
357 // whether we need to use GL_LUMINANCE. | |
358 // | |
359 DCHECK(gl_); | |
360 if( context_provider_->ContextCapabilities().gpu.texture_rg ) | |
361 { | |
362 internal_format_ = GL_RED_EXT; | |
363 format_ = GL_RED_EXT; | |
364 } | |
365 else | |
366 { | |
367 internal_format_ = GL_LUMINANCE; | |
368 format_ = GL_LUMINANCE; | |
369 } | |
370 | |
371 // set group marker | |
372 gl_->PushGroupMarkerEXT(0, "YUVConvContext"); | |
373 | |
374 // | |
375 // Create Framebuffer | |
376 // | |
377 gl_->GenFramebuffers(1, &frame_buffer_); | |
378 | |
379 // | |
380 // Create some default textures to hold Y,U,V values | |
381 // | |
382 y_texture_ = CreateTexture(); | |
383 u_texture_ = CreateTexture(); | |
384 v_texture_ = CreateTexture(); | |
385 | |
386 // | |
387 // Create Vertex Buffer | |
388 // | |
389 // Vertex positions. Also converted to texcoords in vertex shader. | |
390 GLfloat vtxBuf[] = { -1.f, -1.f, | |
391 1.f, -1.f, | |
392 -1.f, 1.f, | |
393 1.f, 1.f }; | |
394 | |
395 // Create buffer | |
396 gl_->GenBuffers(1, &vtx_buffer_); | |
397 gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); | |
398 // Populate it | |
399 gl_->BufferData(GL_ARRAY_BUFFER, 2*sizeof( GLfloat ) * 4, vtxBuf, | |
400 GL_STATIC_DRAW ); | |
401 // unbind | |
402 gl_->BindBuffer(GL_ARRAY_BUFFER, 0); | |
403 | |
404 // | |
405 // Create Shader | |
406 // | |
407 program_ = CreateShader(); | |
408 | |
409 gl_->PopGroupMarkerEXT(); | |
410 | |
411 return true; | |
412 } | |
413 | |
414 void YUVConv::Convert( const scoped_refptr<media::VideoFrame> & frame, | |
415 GLuint texOut ) { | |
416 int divisor_height; | |
417 bool supported_format = true; | |
418 | |
419 // make sure we support this format | |
420 switch( frame->format() ) | |
421 { | |
422 case media::VideoFrame::YV12: /* 420 */ | |
423 case media::VideoFrame::I420: /* 420 */ | |
424 divisor_height = 2; | |
425 break; | |
426 | |
427 case media::VideoFrame::YV16: /* 422 */ | |
428 divisor_height = 1; | |
429 break; | |
430 | |
431 case media::VideoFrame::YV24: /* 444 */ | |
432 divisor_height = 1; | |
433 break; | |
434 | |
435 default: | |
436 // rest are not supported.. | |
437 supported_format = false; | |
438 break; | |
439 } | |
440 | |
441 // set group marker | |
442 gl_->PushGroupMarkerEXT(0, "YUVConvContext"); | |
443 | |
444 bool set_clamp = false; | |
445 | |
446 uint32_t ywidth = frame->coded_size().width(); | |
447 uint32_t yheight = frame->coded_size().height(); | |
448 | |
449 if( supported_format ) | |
450 { | |
451 DCHECK_EQ(frame->stride(media::VideoFrame::kUPlane), | |
452 frame->stride(media::VideoFrame::kVPlane)); | |
453 | |
454 uint32_t ystride = frame->stride(media::VideoFrame::kYPlane); | |
455 uint32_t uvstride = frame->stride(media::VideoFrame::kUPlane); | |
456 | |
457 // resize textures if necessary | |
458 if( ystride != y_width_ || yheight != y_height_ ) | |
459 { | |
460 // choose width based on the stride | |
461 // then, clamp later | |
462 y_width_ = ystride; | |
463 y_height_ = yheight; | |
464 | |
465 uv_width_ = uvstride; | |
466 uv_height_ = y_height_ / divisor_height; | |
467 | |
468 if( (ystride != ywidth) || (clamp_width_ != ywidth) ) | |
469 { | |
470 if( clamp_width_ != ywidth ) | |
471 { | |
472 // clamp width to avoid sampling padding pixels | |
473 clamp_width_ = ywidth; | |
474 clamp_value_ = static_cast<float>( ywidth ) / | |
475 static_cast<float>( ystride ); | |
476 // clamp to 1/2 pixel inside to avoid bilinear sampling errors | |
477 clamp_value_ -= (1.f / | |
478 (2.f * static_cast<float>( ystride ) ) ); | |
479 | |
480 set_clamp = true; | |
481 } | |
482 } | |
483 | |
484 // | |
485 // Resize the textures and upload data | |
486 // | |
487 gl_->ActiveTexture( GL_TEXTURE0 ); | |
488 gl_->BindTexture( GL_TEXTURE_2D, y_texture_ ); | |
489 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, y_width_, y_height_, | |
490 0, format_, GL_UNSIGNED_BYTE, | |
491 frame->data(media::VideoFrame::kYPlane)); | |
492 | |
493 gl_->ActiveTexture( GL_TEXTURE1 ); | |
494 gl_->BindTexture( GL_TEXTURE_2D, u_texture_ ); | |
495 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, | |
496 uv_height_, 0, format_, GL_UNSIGNED_BYTE, | |
497 frame->data(media::VideoFrame::kUPlane)); | |
498 | |
499 gl_->ActiveTexture( GL_TEXTURE2 ); | |
500 gl_->BindTexture( GL_TEXTURE_2D, v_texture_ ); | |
501 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, | |
502 uv_height_, 0, format_, GL_UNSIGNED_BYTE, | |
503 frame->data(media::VideoFrame::kVPlane)); | |
504 } | |
505 else | |
506 { | |
507 // | |
508 // Bind textures and upload texture data | |
509 // | |
510 gl_->ActiveTexture( GL_TEXTURE0 ); | |
511 gl_->BindTexture( GL_TEXTURE_2D, y_texture_ ); | |
512 gl_->TexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, y_width_, y_height_, | |
513 format_, GL_UNSIGNED_BYTE, | |
514 frame->data(media::VideoFrame::kYPlane)); | |
515 | |
516 gl_->ActiveTexture( GL_TEXTURE1 ); | |
517 gl_->BindTexture( GL_TEXTURE_2D, u_texture_ ); | |
518 gl_->TexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, | |
519 format_, GL_UNSIGNED_BYTE, | |
520 frame->data(media::VideoFrame::kUPlane)); | |
521 | |
522 gl_->ActiveTexture( GL_TEXTURE2 ); | |
523 gl_->BindTexture( GL_TEXTURE_2D, v_texture_ ); | |
524 gl_->TexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, | |
525 format_, GL_UNSIGNED_BYTE, | |
526 frame->data(media::VideoFrame::kVPlane)); | |
527 } | |
528 } | |
529 else | |
530 { | |
531 // | |
532 // Unsupported format - set textures to be 4x4 and produce a red output | |
533 // 2x2 texture didn't seem to work | |
534 // | |
535 uint8_t yudata[4*4]; | |
536 memset( yudata, 0x5e, 4*4 ); | |
537 gl_->ActiveTexture( GL_TEXTURE0 ); | |
538 gl_->BindTexture( GL_TEXTURE_2D, y_texture_ ); | |
539 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, | |
540 0, format_, GL_UNSIGNED_BYTE, yudata ); | |
541 | |
542 gl_->ActiveTexture( GL_TEXTURE1 ); | |
543 gl_->BindTexture( GL_TEXTURE_2D, u_texture_ ); | |
544 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, | |
545 0, format_, GL_UNSIGNED_BYTE, yudata ); | |
546 | |
547 uint8_t vdata[4*4]; | |
548 memset( vdata, 0xe7, 4*4 ); | |
549 gl_->ActiveTexture( GL_TEXTURE2 ); | |
550 gl_->BindTexture( GL_TEXTURE_2D, v_texture_ ); | |
551 gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 4, 4, | |
552 0, format_, GL_UNSIGNED_BYTE, vdata ); | |
553 | |
554 if( clamp_value_ != 1.f ) | |
555 { | |
556 set_clamp = true; | |
557 clamp_value_ = 1.f; | |
558 } | |
559 } | |
560 | |
561 // since we modify the viewport, save it here | |
562 // MMM - is this necessary? | |
563 GLint viewport[4] = {0}; | |
564 gl_->GetIntegerv( GL_VIEWPORT, viewport ); | |
565 | |
566 // | |
567 // bind framebuffer and output texture | |
568 // | |
569 gl_->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer_); | |
570 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
571 GL_TEXTURE_2D, texOut, 0); | |
572 | |
573 #ifndef NDEBUG | |
574 // NOTE: we should probably check for framebuffer complete here, but that | |
575 // will slow this method down so check in debug mode | |
576 GLint status = gl_->CheckFramebufferStatus( GL_FRAMEBUFFER ); | |
577 if( status != GL_FRAMEBUFFER_COMPLETE ) | |
578 { | |
579 return; | |
580 } | |
581 #endif | |
582 | |
583 // | |
584 // Set viewport as dimensions of the video frame | |
585 // | |
586 gl_->Viewport( 0, 0, ywidth, yheight ); | |
587 | |
588 // | |
589 // Enable shader | |
590 // | |
591 gl_->UseProgram( program_ ); | |
592 | |
593 if( set_clamp ) | |
594 { | |
595 gl_->Uniform1f( clamp_width_loc_, clamp_value_ ); | |
596 } | |
597 | |
598 // | |
599 // Render | |
600 // | |
601 gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); | |
602 gl_->EnableVertexAttribArray(0); | |
603 gl_->VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), | |
604 static_cast<const void *>(0)); | |
605 | |
606 // draw | |
607 gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
608 | |
609 // disable everything we enabled | |
610 gl_->BindBuffer(GL_ARRAY_BUFFER, 0); | |
611 gl_->DisableVertexAttribArray(0); | |
612 gl_->UseProgram(0); | |
613 gl_->BindFramebuffer( GL_FRAMEBUFFER, 0 ); | |
614 | |
615 // unbind textures | |
616 // active texture is still 2 | |
617 gl_->BindTexture( GL_TEXTURE_2D, 0 ); | |
618 | |
619 gl_->ActiveTexture( GL_TEXTURE1 ); | |
620 gl_->BindTexture( GL_TEXTURE_2D, 0 ); | |
621 | |
622 gl_->ActiveTexture( GL_TEXTURE0 ); | |
623 gl_->BindTexture( GL_TEXTURE_2D, 0 ); | |
624 | |
625 // reset former viewport | |
626 gl_->Viewport( viewport[0], viewport[1], viewport[2], viewport[3] ); | |
627 | |
628 gl_->PopGroupMarkerEXT(); | |
629 } | |
630 | |
31 struct VideoDecoderShim::PendingDecode { | 631 struct VideoDecoderShim::PendingDecode { |
32 PendingDecode(uint32_t decode_id, | 632 PendingDecode(uint32_t decode_id, |
33 const scoped_refptr<media::DecoderBuffer>& buffer); | 633 const scoped_refptr<media::DecoderBuffer>& buffer); |
34 ~PendingDecode(); | 634 ~PendingDecode(); |
35 | 635 |
36 const uint32_t decode_id; | 636 const uint32_t decode_id; |
37 const scoped_refptr<media::DecoderBuffer> buffer; | 637 const scoped_refptr<media::DecoderBuffer> buffer; |
38 }; | 638 }; |
39 | 639 |
40 VideoDecoderShim::PendingDecode::PendingDecode( | 640 VideoDecoderShim::PendingDecode::PendingDecode( |
41 uint32_t decode_id, | 641 uint32_t decode_id, |
42 const scoped_refptr<media::DecoderBuffer>& buffer) | 642 const scoped_refptr<media::DecoderBuffer>& buffer) |
43 : decode_id(decode_id), buffer(buffer) { | 643 : decode_id(decode_id), buffer(buffer) { |
44 } | 644 } |
45 | 645 |
46 VideoDecoderShim::PendingDecode::~PendingDecode() { | 646 VideoDecoderShim::PendingDecode::~PendingDecode() { |
47 } | 647 } |
48 | 648 |
49 struct VideoDecoderShim::PendingFrame { | 649 struct VideoDecoderShim::PendingFrame { |
50 explicit PendingFrame(uint32_t decode_id); | 650 explicit PendingFrame(uint32_t decode_id); |
51 PendingFrame(uint32_t decode_id, | 651 PendingFrame(uint32_t decode_id, |
52 const gfx::Size& coded_size, | 652 const scoped_refptr<media::VideoFrame>& frame); |
53 const gfx::Rect& visible_rect); | |
54 ~PendingFrame(); | 653 ~PendingFrame(); |
55 | 654 |
56 const uint32_t decode_id; | 655 const uint32_t decode_id; |
57 const gfx::Size coded_size; | 656 scoped_refptr<media::VideoFrame> video_frame; |
58 const gfx::Rect visible_rect; | |
59 std::vector<uint8_t> argb_pixels; | |
60 | 657 |
61 private: | 658 private: |
62 // This could be expensive to copy, so guard against that. | 659 // This could be expensive to copy, so guard against that. |
63 DISALLOW_COPY_AND_ASSIGN(PendingFrame); | 660 DISALLOW_COPY_AND_ASSIGN(PendingFrame); |
64 }; | 661 }; |
65 | 662 |
66 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) | 663 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) |
67 : decode_id(decode_id) { | 664 : decode_id(decode_id) { |
68 } | 665 } |
69 | 666 |
70 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id, | 667 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id, |
71 const gfx::Size& coded_size, | 668 const scoped_refptr<media::VideoFrame>& frame) |
72 const gfx::Rect& visible_rect) | |
73 : decode_id(decode_id), | 669 : decode_id(decode_id), |
74 coded_size(coded_size), | 670 video_frame(frame) { |
75 visible_rect(visible_rect), | |
76 argb_pixels(coded_size.width() * coded_size.height() * 4) { | |
77 } | 671 } |
78 | 672 |
79 VideoDecoderShim::PendingFrame::~PendingFrame() { | 673 VideoDecoderShim::PendingFrame::~PendingFrame() { |
80 } | 674 } |
81 | 675 |
82 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving | 676 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving |
83 // calls from the VideoDecodeShim on the main thread and sending results back. | 677 // 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 | 678 // This class is constructed on the main thread, but used and destructed on the |
85 // media thread. | 679 // media thread. |
86 class VideoDecoderShim::DecoderImpl { | 680 class VideoDecoderShim::DecoderImpl { |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 } | 856 } |
263 | 857 |
264 void VideoDecoderShim::DecoderImpl::OnOutputComplete( | 858 void VideoDecoderShim::DecoderImpl::OnOutputComplete( |
265 const scoped_refptr<media::VideoFrame>& frame) { | 859 const scoped_refptr<media::VideoFrame>& frame) { |
266 // Software decoders are expected to generated frames only when a Decode() | 860 // Software decoders are expected to generated frames only when a Decode() |
267 // call is pending. | 861 // call is pending. |
268 DCHECK(awaiting_decoder_); | 862 DCHECK(awaiting_decoder_); |
269 | 863 |
270 scoped_ptr<PendingFrame> pending_frame; | 864 scoped_ptr<PendingFrame> pending_frame; |
271 if (!frame->end_of_stream()) { | 865 if (!frame->end_of_stream()) { |
272 pending_frame.reset(new PendingFrame( | 866 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 { | 867 } else { |
280 pending_frame.reset(new PendingFrame(decode_id_)); | 868 pending_frame.reset(new PendingFrame(decode_id_)); |
281 } | 869 } |
282 | 870 |
283 main_message_loop_->PostTask(FROM_HERE, | 871 main_message_loop_->PostTask(FROM_HERE, |
284 base::Bind(&VideoDecoderShim::OnOutputComplete, | 872 base::Bind(&VideoDecoderShim::OnOutputComplete, |
285 shim_, | 873 shim_, |
286 base::Passed(&pending_frame))); | 874 base::Passed(&pending_frame))); |
287 } | 875 } |
288 | 876 |
289 void VideoDecoderShim::DecoderImpl::OnResetComplete() { | 877 void VideoDecoderShim::DecoderImpl::OnResetComplete() { |
290 main_message_loop_->PostTask( | 878 main_message_loop_->PostTask( |
291 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_)); | 879 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_)); |
292 } | 880 } |
293 | 881 |
294 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host) | 882 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host) |
295 : state_(UNINITIALIZED), | 883 : state_(UNINITIALIZED), |
296 host_(host), | 884 host_(host), |
297 media_task_runner_( | 885 media_task_runner_( |
298 RenderThreadImpl::current()->GetMediaThreadTaskRunner()), | 886 RenderThreadImpl::current()->GetMediaThreadTaskRunner()), |
299 context_provider_( | 887 context_provider_( |
300 RenderThreadImpl::current()->SharedMainThreadContextProvider()), | 888 RenderThreadImpl::current()->SharedMainThreadContextProvider()), |
301 texture_pool_size_(0), | 889 texture_pool_size_(0), |
302 num_pending_decodes_(0), | 890 num_pending_decodes_(0), |
303 weak_ptr_factory_(this) { | 891 weak_ptr_factory_(this) { |
304 DCHECK(host_); | 892 DCHECK(host_); |
305 DCHECK(media_task_runner_.get()); | 893 DCHECK(media_task_runner_.get()); |
306 DCHECK(context_provider_.get()); | 894 DCHECK(context_provider_.get()); |
307 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr())); | 895 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr())); |
896 yuv_converter_.reset(new YUVConv( context_provider_ )); | |
308 } | 897 } |
309 | 898 |
310 VideoDecoderShim::~VideoDecoderShim() { | 899 VideoDecoderShim::~VideoDecoderShim() { |
311 DCHECK(RenderThreadImpl::current()); | 900 DCHECK(RenderThreadImpl::current()); |
312 // Delete any remaining textures. | 901 // Delete any remaining textures. |
313 TextureIdMap::iterator it = texture_id_map_.begin(); | 902 TextureIdMap::iterator it = texture_id_map_.begin(); |
314 for (; it != texture_id_map_.end(); ++it) | 903 for (; it != texture_id_map_.end(); ++it) |
315 DeleteTexture(it->second); | 904 DeleteTexture(it->second); |
316 texture_id_map_.clear(); | 905 texture_id_map_.clear(); |
317 | 906 |
(...skipping 18 matching lines...) Expand all Loading... | |
336 DCHECK_EQ(state_, UNINITIALIZED); | 925 DCHECK_EQ(state_, UNINITIALIZED); |
337 media::VideoCodec codec = media::kUnknownVideoCodec; | 926 media::VideoCodec codec = media::kUnknownVideoCodec; |
338 if (profile <= media::H264PROFILE_MAX) | 927 if (profile <= media::H264PROFILE_MAX) |
339 codec = media::kCodecH264; | 928 codec = media::kCodecH264; |
340 else if (profile <= media::VP8PROFILE_MAX) | 929 else if (profile <= media::VP8PROFILE_MAX) |
341 codec = media::kCodecVP8; | 930 codec = media::kCodecVP8; |
342 else if (profile <= media::VP9PROFILE_MAX) | 931 else if (profile <= media::VP9PROFILE_MAX) |
343 codec = media::kCodecVP9; | 932 codec = media::kCodecVP9; |
344 DCHECK_NE(codec, media::kUnknownVideoCodec); | 933 DCHECK_NE(codec, media::kUnknownVideoCodec); |
345 | 934 |
935 // initialize yuv converter | |
936 if( !yuv_converter_->Initialize() ) | |
937 { | |
938 return false; | |
939 } | |
940 | |
346 media::VideoDecoderConfig config( | 941 media::VideoDecoderConfig config( |
347 codec, | 942 codec, |
348 profile, | 943 profile, |
349 media::VideoFrame::YV12, | 944 media::VideoFrame::YV12, |
350 gfx::Size(32, 24), // Small sizes that won't fail. | 945 gfx::Size(32, 24), // Small sizes that won't fail. |
351 gfx::Rect(32, 24), | 946 gfx::Rect(32, 24), |
352 gfx::Size(32, 24), | 947 gfx::Size(32, 24), |
353 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed. | 948 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed. |
354 0 /* extra_data_size */, | 949 0 /* extra_data_size */, |
355 false /* decryption */); | 950 false /* decryption */); |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
464 num_pending_decodes_--; | 1059 num_pending_decodes_--; |
465 completed_decodes_.push(decode_id); | 1060 completed_decodes_.push(decode_id); |
466 | 1061 |
467 // If frames are being queued because we're out of textures, don't notify | 1062 // 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 | 1063 // 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. | 1064 // the host from sending buffers that will cause pending_frames_ to grow. |
470 if (pending_frames_.empty()) | 1065 if (pending_frames_.empty()) |
471 NotifyCompletedDecodes(); | 1066 NotifyCompletedDecodes(); |
472 } | 1067 } |
473 | 1068 |
474 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) { | 1069 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> pframe) { |
475 DCHECK(RenderThreadImpl::current()); | 1070 DCHECK(RenderThreadImpl::current()); |
476 DCHECK(host_); | 1071 DCHECK(host_); |
477 | 1072 |
478 if (!frame->argb_pixels.empty()) { | 1073 if ( pframe->video_frame ) { |
479 if (texture_size_ != frame->coded_size) { | 1074 if (texture_size_ != pframe->video_frame->coded_size()) { |
480 // If the size has changed, all current textures must be dismissed. Add | 1075 // 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 | 1076 // 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. | 1077 // use by the plugin. We will dismiss the rest as they are recycled. |
483 for (TextureIdMap::const_iterator it = texture_id_map_.begin(); | 1078 for (TextureIdMap::const_iterator it = texture_id_map_.begin(); |
484 it != texture_id_map_.end(); | 1079 it != texture_id_map_.end(); |
485 ++it) { | 1080 ++it) { |
486 textures_to_dismiss_.insert(it->first); | 1081 textures_to_dismiss_.insert(it->first); |
487 } | 1082 } |
488 for (TextureIdSet::const_iterator it = available_textures_.begin(); | 1083 for (TextureIdSet::const_iterator it = available_textures_.begin(); |
489 it != available_textures_.end(); | 1084 it != available_textures_.end(); |
490 ++it) { | 1085 ++it) { |
491 DismissTexture(*it); | 1086 DismissTexture(*it); |
492 } | 1087 } |
493 available_textures_.clear(); | 1088 available_textures_.clear(); |
494 FlushCommandBuffer(); | 1089 FlushCommandBuffer(); |
495 | 1090 |
496 DCHECK(pending_texture_mailboxes_.empty()); | 1091 DCHECK(pending_texture_mailboxes_.empty()); |
497 for (uint32_t i = 0; i < texture_pool_size_; i++) | 1092 for (uint32_t i = 0; i < texture_pool_size_; i++) |
498 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); | 1093 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); |
499 | 1094 |
500 host_->RequestTextures(texture_pool_size_, | 1095 host_->RequestTextures(texture_pool_size_, |
501 frame->coded_size, | 1096 pframe->video_frame->coded_size(), |
502 GL_TEXTURE_2D, | 1097 GL_TEXTURE_2D, |
503 pending_texture_mailboxes_); | 1098 pending_texture_mailboxes_); |
504 texture_size_ = frame->coded_size; | 1099 texture_size_ = pframe->video_frame->coded_size(); |
505 } | 1100 } |
506 | 1101 |
507 pending_frames_.push(linked_ptr<PendingFrame>(frame.release())); | 1102 pending_frames_.push(linked_ptr<PendingFrame>(pframe.release())); |
508 SendPictures(); | 1103 SendPictures(); |
509 } | 1104 } |
510 } | 1105 } |
511 | 1106 |
512 void VideoDecoderShim::SendPictures() { | 1107 void VideoDecoderShim::SendPictures() { |
513 DCHECK(RenderThreadImpl::current()); | 1108 DCHECK(RenderThreadImpl::current()); |
514 DCHECK(host_); | 1109 DCHECK(host_); |
515 while (!pending_frames_.empty() && !available_textures_.empty()) { | 1110 while (!pending_frames_.empty() && !available_textures_.empty()) { |
516 const linked_ptr<PendingFrame>& frame = pending_frames_.front(); | 1111 const linked_ptr<PendingFrame>& frame = pending_frames_.front(); |
517 | 1112 |
518 TextureIdSet::iterator it = available_textures_.begin(); | 1113 TextureIdSet::iterator it = available_textures_.begin(); |
519 uint32_t texture_id = *it; | 1114 uint32_t texture_id = *it; |
520 available_textures_.erase(it); | 1115 available_textures_.erase(it); |
521 | 1116 |
522 uint32_t local_texture_id = texture_id_map_[texture_id]; | 1117 uint32_t local_texture_id = texture_id_map_[texture_id]; |
523 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | 1118 |
524 gles2->ActiveTexture(GL_TEXTURE0); | 1119 // run the yuv conversion renderer |
525 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id); | 1120 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 | 1121 |
542 host_->PictureReady(media::Picture(texture_id, frame->decode_id, | 1122 host_->PictureReady(media::Picture(texture_id, frame->decode_id, |
543 frame->visible_rect, false)); | 1123 frame->video_frame->visible_rect(), |
1124 false)); | |
544 pending_frames_.pop(); | 1125 pending_frames_.pop(); |
545 } | 1126 } |
546 | 1127 |
547 FlushCommandBuffer(); | 1128 FlushCommandBuffer(); |
548 | 1129 |
549 if (pending_frames_.empty()) { | 1130 if (pending_frames_.empty()) { |
550 // If frames aren't backing up, notify the host of any completed decodes so | 1131 // If frames aren't backing up, notify the host of any completed decodes so |
551 // it can send more buffers. | 1132 // it can send more buffers. |
552 NotifyCompletedDecodes(); | 1133 NotifyCompletedDecodes(); |
553 | 1134 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) { | 1174 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) { |
594 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | 1175 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
595 gles2->DeleteTextures(1, &texture_id); | 1176 gles2->DeleteTextures(1, &texture_id); |
596 } | 1177 } |
597 | 1178 |
598 void VideoDecoderShim::FlushCommandBuffer() { | 1179 void VideoDecoderShim::FlushCommandBuffer() { |
599 context_provider_->ContextGL()->Flush(); | 1180 context_provider_->ContextGL()->Flush(); |
600 } | 1181 } |
601 | 1182 |
602 } // namespace content | 1183 } // namespace content |
OLD | NEW |