blob: 9c0d61ab8619652260d23651f8fa44150ee1d663 [file] [log] [blame]
// Copyright 2014 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/renderer/media/android/stream_texture_factory_synchronous_impl.h"
#include <algorithm>
#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
#include "cc/output/context_provider.h"
#include "content/common/android/surface_texture_peer.h"
#include "content/renderer/render_thread_impl.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "ui/gl/android/surface_texture.h"
using gpu::gles2::GLES2Interface;
namespace content {
namespace {
class StreamTextureProxyImpl
: public StreamTextureProxy,
public base::SupportsWeakPtr<StreamTextureProxyImpl> {
public:
explicit StreamTextureProxyImpl(
StreamTextureFactorySynchronousImpl::ContextProvider* provider);
~StreamTextureProxyImpl() override;
// StreamTextureProxy implementation:
void BindToLoop(int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) override;
void Release() override;
private:
void BindOnThread(int32 stream_id);
void OnFrameAvailable();
// Protects access to |client_| and |loop_|.
base::Lock lock_;
cc::VideoFrameProvider::Client* client_;
scoped_refptr<base::MessageLoopProxy> loop_;
// Accessed on the |loop_| thread only.
base::Closure callback_;
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
context_provider_;
scoped_refptr<gfx::SurfaceTexture> surface_texture_;
float current_matrix_[16];
bool has_updated_;
DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureProxyImpl);
};
StreamTextureProxyImpl::StreamTextureProxyImpl(
StreamTextureFactorySynchronousImpl::ContextProvider* provider)
: client_(NULL), context_provider_(provider), has_updated_(false) {
std::fill(current_matrix_, current_matrix_ + 16, 0);
}
StreamTextureProxyImpl::~StreamTextureProxyImpl() {}
void StreamTextureProxyImpl::Release() {
{
// Cannot call into |client_| anymore (from any thread) after returning
// from here.
base::AutoLock lock(lock_);
client_ = NULL;
}
// Release is analogous to the destructor, so there should be no more external
// calls to this object in Release. Therefore there is no need to acquire the
// lock to access |loop_|.
if (!loop_.get() || loop_->BelongsToCurrentThread() ||
!loop_->DeleteSoon(FROM_HERE, this)) {
delete this;
}
}
void StreamTextureProxyImpl::BindToLoop(
int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) {
DCHECK(loop.get());
{
base::AutoLock lock(lock_);
DCHECK(!loop_.get() || (loop.get() == loop_.get()));
loop_ = loop;
client_ = client;
}
if (loop->BelongsToCurrentThread()) {
BindOnThread(stream_id);
return;
}
// Unretained is safe here only because the object is deleted on |loop_|
// thread.
loop->PostTask(FROM_HERE,
base::Bind(&StreamTextureProxyImpl::BindOnThread,
base::Unretained(this),
stream_id));
}
void StreamTextureProxyImpl::BindOnThread(int32 stream_id) {
surface_texture_ = context_provider_->GetSurfaceTexture(stream_id);
if (!surface_texture_.get()) {
LOG(ERROR) << "Failed to get SurfaceTexture for stream.";
return;
}
callback_ =
base::Bind(&StreamTextureProxyImpl::OnFrameAvailable, AsWeakPtr());
surface_texture_->SetFrameAvailableCallback(callback_);
}
void StreamTextureProxyImpl::OnFrameAvailable() {
// GetTransformMatrix only returns something valid after both is true:
// - OnFrameAvailable was called
// - we called UpdateTexImage
if (has_updated_) {
float matrix[16];
surface_texture_->GetTransformMatrix(matrix);
if (memcmp(current_matrix_, matrix, sizeof(matrix)) != 0) {
memcpy(current_matrix_, matrix, sizeof(matrix));
base::AutoLock lock(lock_);
if (client_)
client_->DidUpdateMatrix(current_matrix_);
}
}
// OnFrameAvailable being called a second time implies that we called
// updateTexImage since after we received the first frame.
has_updated_ = true;
base::AutoLock lock(lock_);
if (client_)
client_->DidReceiveFrame();
}
} // namespace
// static
scoped_refptr<StreamTextureFactorySynchronousImpl>
StreamTextureFactorySynchronousImpl::Create(
const CreateContextProviderCallback& try_create_callback,
int frame_id) {
return new StreamTextureFactorySynchronousImpl(try_create_callback, frame_id);
}
StreamTextureFactorySynchronousImpl::StreamTextureFactorySynchronousImpl(
const CreateContextProviderCallback& try_create_callback,
int frame_id)
: create_context_provider_callback_(try_create_callback),
context_provider_(create_context_provider_callback_.Run()),
frame_id_(frame_id),
observer_(NULL) {}
StreamTextureFactorySynchronousImpl::~StreamTextureFactorySynchronousImpl() {}
StreamTextureProxy* StreamTextureFactorySynchronousImpl::CreateProxy() {
bool had_proxy = !!context_provider_.get();
if (!had_proxy)
context_provider_ = create_context_provider_callback_.Run();
if (!context_provider_.get())
return NULL;
if (observer_ && !had_proxy)
context_provider_->AddObserver(observer_);
return new StreamTextureProxyImpl(context_provider_.get());
}
void StreamTextureFactorySynchronousImpl::EstablishPeer(int32 stream_id,
int player_id) {
DCHECK(context_provider_.get());
scoped_refptr<gfx::SurfaceTexture> surface_texture =
context_provider_->GetSurfaceTexture(stream_id);
if (surface_texture.get()) {
SurfaceTexturePeer::GetInstance()->EstablishSurfaceTexturePeer(
base::GetCurrentProcessHandle(),
surface_texture,
frame_id_,
player_id);
}
}
unsigned StreamTextureFactorySynchronousImpl::CreateStreamTexture(
unsigned texture_target,
unsigned* texture_id,
gpu::Mailbox* texture_mailbox) {
DCHECK(context_provider_.get());
unsigned stream_id = 0;
GLES2Interface* gl = context_provider_->ContextGL();
gl->GenTextures(1, texture_id);
stream_id = gl->CreateStreamTextureCHROMIUM(*texture_id);
gl->GenMailboxCHROMIUM(texture_mailbox->name);
gl->ProduceTextureDirectCHROMIUM(
*texture_id, texture_target, texture_mailbox->name);
return stream_id;
}
void StreamTextureFactorySynchronousImpl::SetStreamTextureSize(
int32 stream_id,
const gfx::Size& size) {}
gpu::gles2::GLES2Interface* StreamTextureFactorySynchronousImpl::ContextGL() {
DCHECK(context_provider_.get());
return context_provider_->ContextGL();
}
void StreamTextureFactorySynchronousImpl::AddObserver(
StreamTextureFactoryContextObserver* obs) {
DCHECK(!observer_);
observer_ = obs;
if (context_provider_.get())
context_provider_->AddObserver(obs);
}
void StreamTextureFactorySynchronousImpl::RemoveObserver(
StreamTextureFactoryContextObserver* obs) {
DCHECK_EQ(observer_, obs);
observer_ = NULL;
if (context_provider_.get())
context_provider_->RemoveObserver(obs);
}
} // namespace content