blob: 1df753d167ec05e53d0d1ae55e41ddfb058e48f7 [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 <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