| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/common/gpu/media/avda_codec_image.h" |
| |
| #include <string.h> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "content/common/gpu/media/avda_shared_state.h" |
| #include "gpu/command_buffer/service/context_group.h" |
| #include "gpu/command_buffer/service/context_state.h" |
| #include "gpu/command_buffer/service/gles2_cmd_decoder.h" |
| #include "gpu/command_buffer/service/texture_manager.h" |
| #include "ui/gl/android/surface_texture.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/scoped_make_current.h" |
| |
| namespace content { |
| |
| AVDACodecImage::AVDACodecImage( |
| const scoped_refptr<AVDASharedState>& shared_state, |
| media::VideoCodecBridge* codec, |
| const base::WeakPtr<gpu::gles2::GLES2Decoder>& decoder, |
| const scoped_refptr<gfx::SurfaceTexture>& surface_texture) |
| : shared_state_(shared_state), |
| codec_buffer_index_(-1), |
| media_codec_(codec), |
| decoder_(decoder), |
| surface_texture_(surface_texture), |
| detach_surface_texture_on_destruction_(false), |
| texture_(0), |
| need_shader_info_(true), |
| texmatrix_uniform_location_(-1) { |
| memset(gl_matrix_, 0, sizeof(gl_matrix_)); |
| gl_matrix_[0] = gl_matrix_[5] = gl_matrix_[10] = gl_matrix_[15] = 1.0f; |
| } |
| |
| AVDACodecImage::~AVDACodecImage() {} |
| |
| void AVDACodecImage::Destroy(bool have_context) {} |
| |
| gfx::Size AVDACodecImage::GetSize() { |
| return size_; |
| } |
| |
| unsigned AVDACodecImage::GetInternalFormat() { |
| return GL_RGBA; |
| } |
| |
| bool AVDACodecImage::BindTexImage(unsigned target) { |
| return false; |
| } |
| |
| void AVDACodecImage::ReleaseTexImage(unsigned target) {} |
| |
| bool AVDACodecImage::CopyTexImage(unsigned target) { |
| if (target != GL_TEXTURE_EXTERNAL_OES) |
| return false; |
| |
| // Verify that the currently bound texture is the right one. If we're not |
| // copying to a Texture that shares our service_id, then we can't do much. |
| // This will force a copy. |
| // TODO(liberato): Fall back to a copy that uses the texture matrix. |
| GLint bound_service_id = 0; |
| glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id); |
| if (bound_service_id != shared_state_->surface_texture_service_id()) |
| return false; |
| |
| // Attach the surface texture to our GL context if needed. |
| if (!shared_state_->surface_texture_is_attached()) |
| AttachSurfaceTextureToContext(); |
| |
| // Make sure that we have the right image in the front buffer. |
| UpdateSurfaceTexture(); |
| |
| InstallTextureMatrix(); |
| |
| // TODO(liberato): Handle the texture matrix properly. |
| // Either we can update the shader with it or we can move all of the logic |
| // to updateTexImage() to the right place in the cc to send it to the shader. |
| // For now, we just skip it. crbug.com/530681 |
| |
| // By setting image state to UNBOUND instead of COPIED we ensure that |
| // CopyTexImage() is called each time the surface texture is used for drawing. |
| // It would be nice if we could do this via asking for the currently bound |
| // Texture, but the active unit never seems to change. |
| texture_->SetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0, this, |
| gpu::gles2::Texture::UNBOUND); |
| |
| return true; |
| } |
| |
| bool AVDACodecImage::CopyTexSubImage(unsigned target, |
| const gfx::Point& offset, |
| const gfx::Rect& rect) { |
| return false; |
| } |
| |
| bool AVDACodecImage::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, |
| int z_order, |
| gfx::OverlayTransform transform, |
| const gfx::Rect& bounds_rect, |
| const gfx::RectF& crop_rect) { |
| return false; |
| } |
| |
| void AVDACodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, |
| uint64_t process_tracing_id, |
| const std::string& dump_name) {} |
| |
| void AVDACodecImage::UpdateSurfaceTexture() { |
| // Render via the media codec if needed. |
| if (codec_buffer_index_ <= -1 || !media_codec_) |
| return; |
| |
| // The decoder buffer is still pending. |
| // This must be synchronous, so wait for OnFrameAvailable. |
| media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true); |
| { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.AvdaCodecImage.WaitTimeForFrame"); |
| shared_state_->WaitForFrameAvailable(); |
| } |
| |
| // Don't bother to check if we're rendered again. |
| codec_buffer_index_ = -1; |
| |
| // Swap the rendered image to the front. |
| scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current; |
| if (!shared_state_->context()->IsCurrent(NULL)) { |
| scoped_make_current.reset(new ui::ScopedMakeCurrent( |
| shared_state_->context(), shared_state_->surface())); |
| } |
| surface_texture_->UpdateTexImage(); |
| |
| // Helpfully, this is already column major. |
| surface_texture_->GetTransformMatrix(gl_matrix_); |
| } |
| |
| void AVDACodecImage::SetMediaCodecBufferIndex(int buffer_index) { |
| codec_buffer_index_ = buffer_index; |
| } |
| |
| int AVDACodecImage::GetMediaCodecBufferIndex() const { |
| return codec_buffer_index_; |
| } |
| |
| void AVDACodecImage::SetSize(const gfx::Size& size) { |
| size_ = size; |
| } |
| |
| void AVDACodecImage::SetMediaCodec(media::MediaCodecBridge* codec) { |
| media_codec_ = codec; |
| } |
| |
| void AVDACodecImage::setTexture(gpu::gles2::Texture* texture) { |
| texture_ = texture; |
| } |
| |
| void AVDACodecImage::AttachSurfaceTextureToContext() { |
| // Attach the surface texture to the first context we're bound on, so that |
| // no context switch is needed later. |
| |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| // The surface texture is already detached, so just attach it. |
| // We could do this earlier, but SurfaceTexture has context affinity, and we |
| // don't want to require a context switch. |
| surface_texture_->AttachToGLContext(); |
| shared_state_->did_attach_surface_texture(); |
| } |
| |
| void AVDACodecImage::InstallTextureMatrix() { |
| // glUseProgram() has been run already -- just modify the uniform. |
| // Updating this via VideoFrameProvider::Client::DidUpdateMatrix() would |
| // be a better solution, except that we'd definitely miss a frame at this |
| // point in drawing. |
| // Our current method assumes that we'll end up being a stream resource, |
| // and that the program has a texMatrix uniform that does what we want. |
| if (need_shader_info_) { |
| GLint program_id = -1; |
| glGetIntegerv(GL_CURRENT_PROGRAM, &program_id); |
| |
| if (program_id >= 0) { |
| // This is memorized from cc/output/shader.cc . |
| const char* uniformName = "texMatrix"; |
| texmatrix_uniform_location_ = |
| glGetUniformLocation(program_id, uniformName); |
| DCHECK(texmatrix_uniform_location_ != -1); |
| } |
| |
| // Only try once. |
| need_shader_info_ = false; |
| } |
| |
| if (texmatrix_uniform_location_ >= 0) { |
| glUniformMatrix4fv(texmatrix_uniform_location_, 1, false, gl_matrix_); |
| } |
| } |
| |
| } // namespace content |