blob: 988cd71476073e816cf8e7464433bf4d96b89403 [file] [log] [blame]
// 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 "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) {
// If the SurfaceTexture is using our texture, then detach from it.
if (detach_surface_texture_on_destruction_) {
// We don't really care if we have no context, since it doesn't
// matter if the texture is destroyed here or not. As long as the
// surface texture doesn't try to delete this handle later (after
// it might have been reused), it's fine. Somebody else will delete
// our texture when the picture buffer is destroyed.
surface_texture_->DetachFromGLContext();
shared_state_->set_surface_texture_service_id(0);
}
}
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;
// Have we bound the SurfaceTexture's texture handle to the active
// texture unit yet?
bool bound_texture = false;
// Attach the surface texture to our GL context if needed.
if (!shared_state_->surface_texture_service_id()) {
AttachSurfaceTextureToContext();
bound_texture = true;
}
// Make sure that we have the right image in the front buffer.
bound_texture |= UpdateSurfaceTexture();
InstallTextureMatrix();
// Sneakily bind the ST texture handle in the real GL context.
// If we called UpdateTexImage() to update the ST front buffer, then we can
// skip this. Since one draw/frame is the common case, we optimize for it.
if (!bound_texture)
glBindTexture(GL_TEXTURE_EXTERNAL_OES,
shared_state_->surface_texture_service_id());
// 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) {}
bool AVDACodecImage::UpdateSurfaceTexture() {
// Render via the media codec if needed.
if (codec_buffer_index_ > -1 && media_codec_) {
// We have been given a codec buffer to render, so render it.
// We might want to ask the avda to release any buffers that come
// before us without rendering, just for good measure. However,
// to prevent doing lots of work on the drawing path, we skip it.
// The decoder buffer was still pending.
// This must be synchronous.
media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true);
// Don't bother to check if we're rendered again.
codec_buffer_index_ = -1;
// Swap the rendered image to the front.
surface_texture_->UpdateTexImage();
// Helpfully, this is already column major.
surface_texture_->GetTransformMatrix(gl_matrix_);
// UpdateTexImage() binds the ST's texture.
return true;
}
return false;
}
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() {
GLint surface_texture_service_id;
// Use the PictureBuffer's texture. We could also generate a new texture
// here, but cleaning it up is problematic.
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &surface_texture_service_id);
DCHECK(surface_texture_service_id);
// Attach to our service id.
glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_service_id);
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.
surface_texture_->AttachToGLContext();
shared_state_->set_surface_texture_service_id(surface_texture_service_id);
detach_surface_texture_on_destruction_ = true;
// We do not restore the GL state here.
}
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