Index: content/renderer/pepper/video_decoder_shim.cc |
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc |
index 35594a8e3a609ac7fbefd241b8aa3a8fe66a2b42..8177bcb2a9ba150f06fa1a2e69a5a3178210b592 100644 |
--- a/content/renderer/pepper/video_decoder_shim.cc |
+++ b/content/renderer/pepper/video_decoder_shim.cc |
@@ -9,6 +9,9 @@ |
#include <GLES2/gl2extchromium.h> |
#include "base/bind.h" |
+#ifndef NDEBUG |
+#include "base/logging.h" |
+#endif |
#include "base/numerics/safe_conversions.h" |
#include "base/single_thread_task_runner.h" |
#include "cc/blink/context_provider_web_context.h" |
@@ -25,9 +28,580 @@ |
#include "media/video/picture.h" |
#include "media/video/video_decode_accelerator.h" |
#include "ppapi/c/pp_errors.h" |
+#include "third_party/skia/include/gpu/GrTypes.h" |
namespace content { |
+static const uint32_t kGrInvalidateState = |
+ kRenderTarget_GrGLBackendState | kTextureBinding_GrGLBackendState | |
+ kView_GrGLBackendState | kVertex_GrGLBackendState | |
+ kProgram_GrGLBackendState; |
+ |
+// YUV->RGB converter class using a shader and FBO. |
+class VideoDecoderShim::YUVConverter { |
+ public: |
+ YUVConverter(const scoped_refptr<cc_blink::ContextProviderWebContext>&); |
+ ~YUVConverter(); |
+ bool Initialize(); |
+ void Convert(const scoped_refptr<media::VideoFrame>& frame, GLuint tex_out); |
+ |
+ private: |
+ GLuint CreateShader(); |
+ GLuint CompileShader(const char* name, GLuint type, const char* code); |
+ GLuint CreateProgram(const char* name, GLuint vshader, GLuint fshader); |
+ void SetUniform(GLuint program, const char* name, int value); |
+ void SetUniform(GLuint program, const char* name, float value); |
+ GLuint CreateTexture(); |
+ void SetTexcoordClamp(uint32_t stride, uint32_t width); |
+ |
+ scoped_refptr<cc_blink::ContextProviderWebContext> context_provider_; |
+ gpu::gles2::GLES2Interface* gl_; |
+ GLuint frame_buffer_; |
+ GLuint vtx_buffer_; |
bbudge
2015/05/14 16:45:48
nit: vertex_buffer_
CodeByThePound
2015/05/14 17:42:08
Done.
|
+ GLuint program_; |
+ |
+ GLuint y_texture_; |
+ GLuint u_texture_; |
+ GLuint v_texture_; |
+ GLuint a_texture_; |
+ |
+ GLuint internal_format_; |
+ GLuint format_; |
+ media::VideoFrame::Format video_format_; |
+ |
+ GLuint y_width_; |
+ GLuint y_height_; |
+ |
+ GLuint uv_width_; |
+ GLuint uv_height_; |
+ uint32_t uv_height_divisor_; |
+ |
+ GLfloat clamp_value_; |
+ GLuint clamp_width_; |
+ GLint clamp_width_loc_; |
+ |
+ GLint yuv_matrix_loc_; |
+ GLint yuv_adjust_loc_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(YUVConverter); |
+}; |
+ |
+VideoDecoderShim::YUVConverter::YUVConverter( |
+ const scoped_refptr<cc_blink::ContextProviderWebContext>& ctx_p) |
bbudge
2015/05/14 16:45:48
nit: could you name this context_provider
CodeByThePound
2015/05/14 17:42:08
Done.
|
+ : context_provider_(ctx_p), |
+ gl_(context_provider_->ContextGL()), |
+ frame_buffer_(0), |
+ vtx_buffer_(0), |
+ program_(0), |
+ y_texture_(0), |
+ u_texture_(0), |
+ v_texture_(0), |
+ a_texture_(0), |
+ internal_format_(0), |
+ format_(0), |
+ video_format_(media::VideoFrame::UNKNOWN), |
+ y_width_(2), |
+ y_height_(2), |
+ uv_width_(2), |
+ uv_height_(2), |
+ uv_height_divisor_(1), |
+ clamp_value_(1.f), |
+ clamp_width_(0), |
+ clamp_width_loc_(0), |
+ yuv_matrix_loc_(0), |
+ yuv_adjust_loc_(0) { |
+} |
+ |
+VideoDecoderShim::YUVConverter::~YUVConverter() { |
+ if (y_texture_) |
+ gl_->DeleteTextures(1, &y_texture_); |
+ |
+ if (u_texture_) |
+ gl_->DeleteTextures(1, &u_texture_); |
+ |
+ if (v_texture_) |
+ gl_->DeleteTextures(1, &v_texture_); |
+ |
+ if (a_texture_) |
+ gl_->DeleteTextures(1, &a_texture_); |
+ |
+ if (frame_buffer_) |
+ gl_->DeleteFramebuffers(1, &frame_buffer_); |
+ |
+ if (vtx_buffer_) |
+ gl_->DeleteBuffers(1, &vtx_buffer_); |
+ |
+ if (program_) |
+ gl_->DeleteProgram(program_); |
+} |
+ |
+GLuint VideoDecoderShim::YUVConverter::CreateTexture() { |
+ GLuint tex = 0; |
+ |
+ gl_->GenTextures(1, &tex); |
+ gl_->BindTexture(GL_TEXTURE_2D, tex); |
+ |
+ // Create texture with default size - will be resized upon first frame. |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 2, 2, 0, format_, |
+ GL_UNSIGNED_BYTE, NULL); |
+ |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
+ |
+ gl_->BindTexture(GL_TEXTURE_2D, 0); |
+ |
+ return tex; |
+} |
+ |
+GLuint VideoDecoderShim::YUVConverter::CompileShader(const char* name, |
+ GLuint type, |
+ const char* code) { |
+ GLuint shader = gl_->CreateShader(type); |
+ |
+ gl_->ShaderSource(shader, 1, (const GLchar**)&code, NULL); |
+ gl_->CompileShader(shader); |
+ |
+#ifndef NDEBUG |
+ GLint status = 0; |
+ |
+ gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &status); |
+ if (status != GL_TRUE) { |
+ GLint max_length = 0; |
+ GLint actual_length = 0; |
+ gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); |
+ |
+ // The max_length includes the NULL character. |
+ std::string error_log(max_length, 0); |
+ gl_->GetShaderInfoLog(shader, max_length, &actual_length, &error_log[0]); |
+ |
+ LOG(ERROR) << name << " shader compilation failed: " << error_log.c_str(); |
+ gl_->DeleteShader(shader); |
+ return 0; |
+ } |
+#endif |
+ |
+ return shader; |
+} |
+ |
+GLuint VideoDecoderShim::YUVConverter::CreateProgram(const char* name, |
+ GLuint vshader, |
+ GLuint fshader) { |
+ GLuint program = gl_->CreateProgram(); |
+ gl_->AttachShader(program, vshader); |
+ gl_->AttachShader(program, fshader); |
+ |
+ gl_->BindAttribLocation(program, 0, "position"); |
+ |
+ gl_->LinkProgram(program); |
+ |
+#ifndef NDEBUG |
+ GLint status = 0; |
+ |
+ gl_->GetProgramiv(program, GL_LINK_STATUS, &status); |
+ if (status != GL_TRUE) { |
+ GLint max_length = 0; |
+ GLint actual_length = 0; |
+ gl_->GetProgramiv(program, GL_INFO_LOG_LENGTH, &max_length); |
+ |
+ // The max_length includes the NULL character. |
+ std::string error_log(max_length, 0); |
+ gl_->GetProgramInfoLog(program, max_length, &actual_length, &error_log[0]); |
+ |
+ LOG(ERROR) << name << " program linking failed: " << error_log.c_str(); |
+ return 0; |
+ } |
+#endif |
+ |
+ return program; |
+} |
+ |
+void VideoDecoderShim::YUVConverter::SetUniform(GLuint program, |
+ const char* name, |
+ int value) { |
+ GLint loc = gl_->GetUniformLocation(program, name); |
+ DCHECK(loc != -1); |
+ gl_->UseProgram(program); |
+ gl_->Uniform1i(loc, value); |
+ gl_->UseProgram(0); |
bbudge
2015/05/14 16:45:48
Is there a reason you set these inside the functio
CodeByThePound
2015/05/14 17:42:08
Removed SetUniform functions and inlined the code
|
+} |
+ |
+void VideoDecoderShim::YUVConverter::SetUniform(GLuint program, |
+ const char* name, |
+ float value) { |
+ GLint loc = gl_->GetUniformLocation(program, name); |
+ DCHECK(loc != -1); |
+ gl_->UseProgram(program); |
+ gl_->Uniform1f(loc, value); |
+ gl_->UseProgram(0); |
+} |
+ |
+GLuint VideoDecoderShim::YUVConverter::CreateShader() { |
+ const char* vert_shader = |
+ "precision mediump float;\n" |
+ "attribute vec2 position;\n" |
+ "varying vec2 texcoord;\n" |
+ "uniform float clamp_width;\n" |
+ "void main()\n" |
+ "{\n" |
+ " gl_Position = vec4( position.xy, 0, 1 );\n" |
+ " vec2 tmp = position*0.5+0.5;\n" |
+ " texcoord = vec2(min(tmp.x, clamp_width), tmp.y);\n" |
+ "}"; |
+ |
+ const char* frag_shader = |
+ "precision mediump float;\n" |
+ "varying vec2 texcoord;\n" |
+ "uniform sampler2D y_sampler;\n" |
+ "uniform sampler2D u_sampler;\n" |
+ "uniform sampler2D v_sampler;\n" |
+ "uniform sampler2D a_sampler;\n" |
+ "uniform mat3 yuv_matrix;\n" |
+ "uniform vec3 yuv_adjust;\n" |
+ "void main()\n" |
+ "{\n" |
+ " vec3 yuv = vec3(texture2D(y_sampler, texcoord).x,\n" |
+ " texture2D(u_sampler, texcoord).x,\n" |
+ " texture2D(v_sampler, texcoord).x) +\n" |
+ " yuv_adjust;\n" |
+ " gl_FragColor = vec4(yuv_matrix * yuv, texture2D(a_sampler, " |
+ "texcoord).x);\n" |
+ "}"; |
+ |
+ GLuint vertex_shader = |
+ CompileShader("Vertex Shader", GL_VERTEX_SHADER, vert_shader); |
+ if (!vertex_shader) { |
+ return 0; |
+ } |
+ |
+ GLuint fragment_shader = |
+ CompileShader("Fragment Shader", GL_FRAGMENT_SHADER, frag_shader); |
+ if (!fragment_shader) { |
+ gl_->DeleteShader(vertex_shader); |
+ return 0; |
+ } |
+ |
+ GLuint program = |
+ CreateProgram("YUVConverter Program", vertex_shader, fragment_shader); |
+ |
+ gl_->DeleteShader(vertex_shader); |
+ gl_->DeleteShader(fragment_shader); |
+ |
+ if (!program) { |
+ return 0; |
+ } |
+ |
+ SetUniform(program, "y_sampler", 0); |
+ SetUniform(program, "u_sampler", 1); |
+ SetUniform(program, "v_sampler", 2); |
+ SetUniform(program, "a_sampler", 3); |
+ |
+ SetUniform(program, "clamp_width", clamp_value_); |
+ |
+ clamp_width_loc_ = gl_->GetUniformLocation(program, "clamp_width"); |
+ DCHECK(clamp_width_loc_ != -1); |
+ |
+ yuv_matrix_loc_ = gl_->GetUniformLocation(program, "yuv_matrix"); |
+ DCHECK(yuv_matrix_loc_ != -1); |
+ |
+ yuv_adjust_loc_ = gl_->GetUniformLocation(program, "yuv_adjust"); |
+ DCHECK(yuv_adjust_loc_ != -1); |
+ |
+ return program; |
+} |
+ |
+bool VideoDecoderShim::YUVConverter::Initialize() { |
+ DCHECK(gl_); |
bbudge
2015/05/14 16:45:48
DCHECK in the constructor.
CodeByThePound
2015/05/14 17:42:08
Done.
|
+ // If texture_rg extension is not available, use slower GL_LUMINANCE. |
+ if (context_provider_->ContextCapabilities().gpu.texture_rg) { |
+ internal_format_ = GL_RED_EXT; |
+ format_ = GL_RED_EXT; |
+ } else { |
+ internal_format_ = GL_LUMINANCE; |
+ format_ = GL_LUMINANCE; |
+ } |
+ |
+ if (context_provider_->ContextCapabilities().gpu.max_texture_image_units < |
+ 4) { |
+ // We support YUVA textures and require 4 texture units in the fragment |
+ // stage. |
+ return false; |
+ } |
+ |
+ gl_->PushGroupMarkerEXT(0, "YUVConverterContext"); |
+ |
+ gl_->GenFramebuffers(1, &frame_buffer_); |
+ |
+ y_texture_ = CreateTexture(); |
+ u_texture_ = CreateTexture(); |
+ v_texture_ = CreateTexture(); |
+ a_texture_ = CreateTexture(); |
+ |
+ // Vertex positions. Also converted to texcoords in vertex shader. |
+ GLfloat vtx_buf[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; |
+ |
+ gl_->GenBuffers(1, &vtx_buffer_); |
+ gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); |
+ gl_->BufferData(GL_ARRAY_BUFFER, 2 * sizeof(GLfloat) * 4, vtx_buf, |
+ GL_STATIC_DRAW); |
+ gl_->BindBuffer(GL_ARRAY_BUFFER, 0); |
+ |
+ program_ = CreateShader(); |
+ |
+ gl_->PopGroupMarkerEXT(); |
+ |
+ context_provider_->InvalidateGrContext(kGrInvalidateState); |
+ |
+ return (program_ != 0); |
+} |
+ |
+void VideoDecoderShim::YUVConverter::SetTexcoordClamp(uint32_t stride, |
+ uint32_t width) { |
+ clamp_width_ = width; |
+ if (width != stride) { |
+ // Clamp texcoord width to avoid sampling padding pixels. |
+ clamp_value_ = static_cast<float>(width) / static_cast<float>(stride); |
+ // Further clamp to 1/2 pixel inside to avoid bilinear sampling errors. |
+ clamp_value_ -= (1.f / (2.f * static_cast<float>(stride))); |
+ } else { |
+ // No clamping necessary if width and stride are equal. |
+ clamp_value_ = 1.f; |
+ } |
+} |
+ |
+void VideoDecoderShim::YUVConverter::Convert( |
+ const scoped_refptr<media::VideoFrame>& frame, |
+ GLuint tex_out) { |
+ const float* yuv_matrix = 0; |
+ const float* yuv_adjust = 0; |
+ |
+ if (video_format_ != frame->format()) { |
+ // The constants below were taken from cc/output/gl_renderer.cc. |
+ // These values are magic numbers that are used in the transformation from |
+ // YUV |
bbudge
2015/05/14 16:45:48
nit strange formatting
CodeByThePound
2015/05/14 17:42:09
Must have been done by git cl format. Fixed.
|
+ // to RGB color values. They are taken from the following webpage: |
+ // http://www.fourcc.org/fccyvrgb.php |
+ const float yuv_to_rgb_rec601[9] = { |
+ 1.164f, 1.164f, 1.164f, 0.0f, -.391f, 2.018f, 1.596f, -.813f, 0.0f, |
+ }; |
+ const float yuv_to_rgb_jpeg[9] = { |
+ 1.f, 1.f, 1.f, 0.0f, -.34414f, 1.772f, 1.402f, -.71414f, 0.0f, |
+ }; |
+ const float yuv_to_rgb_rec709[9] = { |
+ 1.164f, 1.164f, 1.164f, 0.0f, -0.213f, 2.112f, 1.793f, -0.533f, 0.0f, |
+ }; |
+ |
+ // These values map to 16, 128, and 128 respectively, and are computed |
+ // as a fraction over 256 (e.g. 16 / 256 = 0.0625). |
+ // They are used in the YUV to RGBA conversion formula: |
+ // Y - 16 : Gives 16 values of head and footroom for overshooting |
+ // U - 128 : Turns unsigned U into signed U [-128,127] |
+ // V - 128 : Turns unsigned V into signed V [-128,127] |
+ const float yuv_adjust_constrained[3] = { |
+ -0.0625f, -0.5f, -0.5f, |
+ }; |
+ // Same as above, but without the head and footroom. |
+ const float yuv_adjust_full[3] = { |
+ 0.0f, -0.5f, -0.5f, |
+ }; |
+ |
+ switch (frame->format()) { |
+ case media::VideoFrame::YV12: /* 420 */ |
bbudge
2015/05/14 16:45:48
nit: C++ style comments.
CodeByThePound
2015/05/14 17:42:08
Done.
|
+ case media::VideoFrame::YV12A: |
+ case media::VideoFrame::I420: |
+ uv_height_divisor_ = 2; |
+ yuv_matrix = yuv_to_rgb_rec601; |
+ yuv_adjust = yuv_adjust_constrained; |
+ break; |
+ |
+ case media::VideoFrame::YV12HD: /* 420 */ |
+ uv_height_divisor_ = 2; |
+ yuv_matrix = yuv_to_rgb_rec709; |
+ yuv_adjust = yuv_adjust_constrained; |
+ break; |
+ |
+ case media::VideoFrame::YV12J: /* 420 */ |
+ uv_height_divisor_ = 2; |
+ yuv_matrix = yuv_to_rgb_jpeg; |
+ yuv_adjust = yuv_adjust_full; |
+ break; |
+ |
+ case media::VideoFrame::YV16: /* 422 */ |
+ case media::VideoFrame::YV24: /* 444 */ |
+ uv_height_divisor_ = 1; |
+ yuv_matrix = yuv_to_rgb_rec601; |
+ yuv_adjust = yuv_adjust_constrained; |
+ break; |
+ |
+ default: |
+ NOTREACHED(); |
+ } |
+ |
+ video_format_ = frame->format(); |
+ |
+ // Zero these so everything is reset below. |
+ y_width_ = y_height_ = 0; |
+ } |
+ |
+ gl_->PushGroupMarkerEXT(0, "YUVConverterContext"); |
+ |
+ bool set_clamp = false; |
+ |
+ uint32_t ywidth = frame->coded_size().width(); |
+ uint32_t yheight = frame->coded_size().height(); |
+ |
+ DCHECK_EQ(frame->stride(media::VideoFrame::kUPlane), |
+ frame->stride(media::VideoFrame::kVPlane)); |
+ |
+ uint32_t ystride = frame->stride(media::VideoFrame::kYPlane); |
+ uint32_t uvstride = frame->stride(media::VideoFrame::kUPlane); |
+ |
+ // The following code assumes that extended GLES 2.0 state like |
+ // UNPACK_SKIP* and UNPACK_ROW_LENGTH (if available) are set to defaults. |
+ gl_->PixelStorei(GL_UNPACK_ALIGNMENT, 1); |
+ |
+ if (ystride != y_width_ || yheight != y_height_) { |
+ // Choose width based on the stride. Clamp texcoords below. |
+ y_width_ = ystride; |
+ y_height_ = yheight; |
+ |
+ uv_width_ = uvstride; |
+ uv_height_ = y_height_ / uv_height_divisor_; |
+ |
+ SetTexcoordClamp(ystride, ywidth); |
+ set_clamp = true; |
+ |
+ // Re-create to resize the textures and upload data. |
+ gl_->ActiveTexture(GL_TEXTURE0); |
+ gl_->BindTexture(GL_TEXTURE_2D, y_texture_); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, y_width_, y_height_, 0, |
+ format_, GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kYPlane)); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE1); |
+ gl_->BindTexture(GL_TEXTURE_2D, u_texture_); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, uv_height_, |
+ 0, format_, GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kUPlane)); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE2); |
+ gl_->BindTexture(GL_TEXTURE_2D, v_texture_); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, uv_width_, uv_height_, |
+ 0, format_, GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kVPlane)); |
+ |
+ if (video_format_ == media::VideoFrame::YV12A) { |
+ DCHECK_EQ(frame->stride(media::VideoFrame::kYPlane), |
+ frame->stride(media::VideoFrame::kAPlane)); |
+ gl_->ActiveTexture(GL_TEXTURE3); |
+ gl_->BindTexture(GL_TEXTURE_2D, a_texture_); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, y_width_, y_height_, |
+ 0, format_, GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kAPlane)); |
+ } else { |
+ // if there is no alpha channel, then create a 2x2 texture with full |
+ // alpha. |
+ const uint8_t alpha[4] = {0xff, 0xff, 0xff, 0xff}; |
+ gl_->ActiveTexture(GL_TEXTURE3); |
+ gl_->BindTexture(GL_TEXTURE_2D, a_texture_); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format_, 2, 2, 0, format_, |
+ GL_UNSIGNED_BYTE, alpha); |
+ } |
+ } else { |
+ // Width may have changed even though stride remained the same. |
+ if (clamp_width_ != ywidth) { |
+ SetTexcoordClamp(ystride, ywidth); |
+ set_clamp = true; |
+ } |
+ |
+ // Bind textures and upload texture data |
+ gl_->ActiveTexture(GL_TEXTURE0); |
+ gl_->BindTexture(GL_TEXTURE_2D, y_texture_); |
+ gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, y_width_, y_height_, format_, |
+ GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kYPlane)); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE1); |
+ gl_->BindTexture(GL_TEXTURE_2D, u_texture_); |
+ gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, format_, |
+ GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kUPlane)); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE2); |
+ gl_->BindTexture(GL_TEXTURE_2D, v_texture_); |
+ gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, uv_width_, uv_height_, format_, |
+ GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kVPlane)); |
+ |
+ if (video_format_ == media::VideoFrame::YV12A) { |
+ DCHECK_EQ(frame->stride(media::VideoFrame::kYPlane), |
+ frame->stride(media::VideoFrame::kAPlane)); |
+ gl_->ActiveTexture(GL_TEXTURE3); |
+ gl_->BindTexture(GL_TEXTURE_2D, a_texture_); |
+ gl_->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, y_width_, y_height_, format_, |
+ GL_UNSIGNED_BYTE, |
+ frame->data(media::VideoFrame::kAPlane)); |
+ } else { |
+ gl_->ActiveTexture(GL_TEXTURE3); |
+ gl_->BindTexture(GL_TEXTURE_2D, a_texture_); |
+ } |
+ } |
+ |
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer_); |
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
+ tex_out, 0); |
+ |
+#ifndef NDEBUG |
+ // We should probably check for framebuffer complete here, but that |
+ // will slow this method down so check only in debug mode. |
+ GLint status = gl_->CheckFramebufferStatus(GL_FRAMEBUFFER); |
+ if (status != GL_FRAMEBUFFER_COMPLETE) { |
+ return; |
+ } |
+#endif |
+ |
+ gl_->Viewport(0, 0, ywidth, yheight); |
+ |
+ gl_->UseProgram(program_); |
+ |
+ if (set_clamp) { |
+ gl_->Uniform1f(clamp_width_loc_, clamp_value_); |
+ } |
+ |
+ if (yuv_matrix) { |
+ gl_->UniformMatrix3fv(yuv_matrix_loc_, 1, 0, yuv_matrix); |
+ gl_->Uniform3fv(yuv_adjust_loc_, 1, yuv_adjust); |
+ } |
+ |
+ gl_->BindBuffer(GL_ARRAY_BUFFER, vtx_buffer_); |
+ gl_->EnableVertexAttribArray(0); |
+ gl_->VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), |
+ static_cast<const void*>(0)); |
+ |
+ gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
+ |
bbudge
2015/05/14 16:45:48
I think a big comment explaining about restoring s
CodeByThePound
2015/05/14 17:42:08
Added.
|
+ gl_->BindBuffer(GL_ARRAY_BUFFER, 0); |
+ gl_->DisableVertexAttribArray(0); |
+ gl_->UseProgram(0); |
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); |
+ |
+ gl_->BindTexture(GL_TEXTURE_2D, 0); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE2); |
+ gl_->BindTexture(GL_TEXTURE_2D, 0); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE1); |
+ gl_->BindTexture(GL_TEXTURE_2D, 0); |
+ |
+ gl_->ActiveTexture(GL_TEXTURE0); |
+ gl_->BindTexture(GL_TEXTURE_2D, 0); |
+ |
+ gl_->PopGroupMarkerEXT(); |
+ |
+ context_provider_->InvalidateGrContext(kGrInvalidateState); |
+} |
+ |
struct VideoDecoderShim::PendingDecode { |
PendingDecode(uint32_t decode_id, |
const scoped_refptr<media::DecoderBuffer>& buffer); |
@@ -49,14 +623,11 @@ VideoDecoderShim::PendingDecode::~PendingDecode() { |
struct VideoDecoderShim::PendingFrame { |
explicit PendingFrame(uint32_t decode_id); |
PendingFrame(uint32_t decode_id, |
- const gfx::Size& coded_size, |
- const gfx::Rect& visible_rect); |
+ const scoped_refptr<media::VideoFrame>& frame); |
~PendingFrame(); |
const uint32_t decode_id; |
- const gfx::Size coded_size; |
- const gfx::Rect visible_rect; |
- std::vector<uint8_t> argb_pixels; |
+ scoped_refptr<media::VideoFrame> video_frame; |
private: |
// This could be expensive to copy, so guard against that. |
@@ -67,13 +638,10 @@ VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) |
: decode_id(decode_id) { |
} |
-VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id, |
- const gfx::Size& coded_size, |
- const gfx::Rect& visible_rect) |
- : decode_id(decode_id), |
- coded_size(coded_size), |
- visible_rect(visible_rect), |
- argb_pixels(coded_size.width() * coded_size.height() * 4) { |
+VideoDecoderShim::PendingFrame::PendingFrame( |
+ uint32_t decode_id, |
+ const scoped_refptr<media::VideoFrame>& frame) |
+ : decode_id(decode_id), video_frame(frame) { |
} |
VideoDecoderShim::PendingFrame::~PendingFrame() { |
@@ -269,13 +837,7 @@ void VideoDecoderShim::DecoderImpl::OnOutputComplete( |
scoped_ptr<PendingFrame> pending_frame; |
if (!frame->end_of_stream()) { |
- pending_frame.reset(new PendingFrame( |
- decode_id_, frame->coded_size(), frame->visible_rect())); |
- // Convert the VideoFrame pixels to ABGR to match VideoDecodeAccelerator. |
- media::SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( |
- frame, |
- &pending_frame->argb_pixels.front(), |
- frame->coded_size().width() * 4); |
+ pending_frame.reset(new PendingFrame(decode_id_, frame)); |
} else { |
pending_frame.reset(new PendingFrame(decode_id_)); |
} |
@@ -305,6 +867,7 @@ VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host) |
DCHECK(media_task_runner_.get()); |
DCHECK(context_provider_.get()); |
decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr())); |
+ yuv_converter_.reset(new YUVConverter(context_provider_)); |
bbudge
2015/05/14 16:45:48
nit: this could be initialized sooner, in the init
CodeByThePound
2015/05/14 17:42:08
Done.
|
} |
VideoDecoderShim::~VideoDecoderShim() { |
@@ -343,6 +906,10 @@ bool VideoDecoderShim::Initialize( |
codec = media::kCodecVP9; |
DCHECK_NE(codec, media::kUnknownVideoCodec); |
+ if (!yuv_converter_->Initialize()) { |
+ return false; |
bbudge
2015/05/14 16:45:48
Does this mean software fallback won't work in som
CodeByThePound
2015/05/14 17:42:08
The YUVConverter initialization could fail if 1) t
bbudge
2015/05/14 20:47:59
Let's leave it as is for now. We could add fallbac
|
+ } |
+ |
media::VideoDecoderConfig config( |
codec, |
profile, |
@@ -471,12 +1038,12 @@ void VideoDecoderShim::OnDecodeComplete(int32_t result, uint32_t decode_id) { |
NotifyCompletedDecodes(); |
} |
-void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) { |
+void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> pframe) { |
bbudge
2015/05/14 16:45:48
nit: Chrome style is usually 'frame'
CodeByThePound
2015/05/14 17:42:08
Done.
|
DCHECK(RenderThreadImpl::current()); |
DCHECK(host_); |
- if (!frame->argb_pixels.empty()) { |
- if (texture_size_ != frame->coded_size) { |
+ if (pframe->video_frame) { |
+ if (texture_size_ != pframe->video_frame->coded_size()) { |
// If the size has changed, all current textures must be dismissed. Add |
// all textures to |textures_to_dismiss_| and dismiss any that aren't in |
// use by the plugin. We will dismiss the rest as they are recycled. |
@@ -498,13 +1065,12 @@ void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) { |
pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); |
host_->RequestTextures(texture_pool_size_, |
- frame->coded_size, |
- GL_TEXTURE_2D, |
+ pframe->video_frame->coded_size(), GL_TEXTURE_2D, |
pending_texture_mailboxes_); |
- texture_size_ = frame->coded_size; |
+ texture_size_ = pframe->video_frame->coded_size(); |
} |
- pending_frames_.push(linked_ptr<PendingFrame>(frame.release())); |
+ pending_frames_.push(linked_ptr<PendingFrame>(pframe.release())); |
SendPictures(); |
} |
} |
@@ -520,27 +1086,12 @@ void VideoDecoderShim::SendPictures() { |
available_textures_.erase(it); |
uint32_t local_texture_id = texture_id_map_[texture_id]; |
- gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
- gles2->ActiveTexture(GL_TEXTURE0); |
- gles2->BindTexture(GL_TEXTURE_2D, local_texture_id); |
-#if !defined(OS_ANDROID) |
- // BGRA is the native texture format, except on Android, where textures |
- // would be uploaded as GL_RGBA. |
- gles2->TexImage2D(GL_TEXTURE_2D, |
- 0, |
- GL_BGRA_EXT, |
- texture_size_.width(), |
- texture_size_.height(), |
- 0, |
- GL_BGRA_EXT, |
- GL_UNSIGNED_BYTE, |
- &frame->argb_pixels.front()); |
-#else |
-#error Not implemented. |
-#endif |
+ |
+ yuv_converter_->Convert(frame->video_frame, local_texture_id); |
host_->PictureReady(media::Picture(texture_id, frame->decode_id, |
- frame->visible_rect, false)); |
+ frame->video_frame->visible_rect(), |
+ false)); |
pending_frames_.pop(); |
} |