// Copyright 2014 The Chromium Authors
// 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.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "gpu/ipc/client/client_shared_image_interface.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "gpu/ipc/common/gpu_channel.mojom.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "ui/gfx/geometry/size.h"
namespace content {
StreamTextureProxy::StreamTextureProxy(std::unique_ptr<StreamTextureHost> host)
: host_(std::move(host)) {}
StreamTextureProxy::~StreamTextureProxy() {}
void StreamTextureProxy::Release() {
// Cannot call |received_frame_cb_| after returning from here.
ClearReceivedFrameCB();
// |this| can be deleted by the |task_runner_| on the compositor thread by
// posting task to that thread. So we need to clear the
// |create_video_frame_cb_| here first so that its not called on the
// compositor thread before |this| is deleted. The problem is that
// |create_video_frame_cb_| is provided by the owner of StreamTextureProxy,
// which is being destroyed and is releasing StreamTextureProxy.
ClearCreateVideoFrameCB();
// 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 |task_runner_|.
if (!task_runner_.get() || task_runner_->BelongsToCurrentThread() ||
!task_runner_->DeleteSoon(FROM_HERE, this)) {
delete this;
}
}
void StreamTextureProxy::ClearReceivedFrameCB() {
base::AutoLock lock(lock_);
received_frame_cb_.Reset();
}
void StreamTextureProxy::ClearCreateVideoFrameCB() {
base::AutoLock lock(lock_);
create_video_frame_cb_.Reset();
}
void StreamTextureProxy::BindToTaskRunner(
const base::RepeatingClosure& received_frame_cb,
const CreateVideoFrameCB& create_video_frame_cb,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(task_runner.get());
{
base::AutoLock lock(lock_);
DCHECK(!task_runner_.get() || (task_runner.get() == task_runner_.get()));
task_runner_ = task_runner;
received_frame_cb_ = received_frame_cb;
create_video_frame_cb_ = create_video_frame_cb;
}
if (task_runner->BelongsToCurrentThread()) {
BindOnThread();
return;
}
// Unretained is safe here only because the object is deleted on |loop_|
// thread.
task_runner->PostTask(FROM_HERE,
base::BindOnce(&StreamTextureProxy::BindOnThread,
base::Unretained(this)));
}
void StreamTextureProxy::BindOnThread() {
host_->BindToCurrentThread(this);
}
void StreamTextureProxy::OnFrameAvailable() {
base::AutoLock lock(lock_);
if (!received_frame_cb_.is_null())
received_frame_cb_.Run();
}
void StreamTextureProxy::OnFrameWithInfoAvailable(
const gpu::Mailbox& mailbox,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const std::optional<gpu::VulkanYCbCrInfo>& ycbcr_info) {
base::AutoLock lock(lock_);
// Set the ycbcr info before running the received frame callback so that the
// first frame has it.
if (!create_video_frame_cb_.is_null())
create_video_frame_cb_.Run(mailbox, coded_size, visible_rect, ycbcr_info);
if (!received_frame_cb_.is_null())
received_frame_cb_.Run();
}
void StreamTextureProxy::ForwardStreamTextureForSurfaceRequest(
const base::UnguessableToken& request_token) {
base::AutoLock lock(lock_);
if (!task_runner_)
return;
if (!task_runner_->BelongsToCurrentThread()) {
// Note that Unretained is safe here because this object is deleted
// exclusively by posting a task to the same task runner, after its owner
// has dropped the only reference to it.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&StreamTextureProxy::ForwardStreamTextureForSurfaceRequest,
base::Unretained(this), request_token));
return;
}
host_->ForwardStreamTextureForSurfaceRequest(request_token);
}
void StreamTextureProxy::UpdateRotatedVisibleSize(const gfx::Size& size) {
base::AutoLock lock(lock_);
if (!task_runner_)
return;
if (!task_runner_->BelongsToCurrentThread()) {
// Note that Unretained is safe here because this object is deleted
// exclusively by posting a task to the same task runner, after its owner
// has dropped the only reference to it.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&StreamTextureProxy::UpdateRotatedVisibleSize,
base::Unretained(this), size));
return;
}
host_->UpdateRotatedVisibleSize(size);
}
// static
scoped_refptr<StreamTextureFactory> StreamTextureFactory::Create(
scoped_refptr<gpu::GpuChannelHost> channel) {
return new StreamTextureFactory(std::move(channel));
}
StreamTextureFactory::StreamTextureFactory(
scoped_refptr<gpu::GpuChannelHost> channel)
: channel_(std::move(channel)) {
DCHECK(channel_);
}
StreamTextureFactory::~StreamTextureFactory() = default;
ScopedStreamTextureProxy StreamTextureFactory::CreateProxy() {
// Send a StreamTexure receiver down to the GPU process. This will be bound to
// a concrete StreamTexture impl there.
int32_t stream_id = channel_->GenerateRouteID();
bool succeeded = false;
mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync;
mojo::PendingAssociatedRemote<gpu::mojom::StreamTexture> remote;
channel_->GetGpuChannel().CreateStreamTexture(
stream_id, remote.InitWithNewEndpointAndPassReceiver(), &succeeded);
if (!succeeded) {
DLOG(ERROR) << "CreateStreamTexture failed";
return ScopedStreamTextureProxy();
}
// Now instantiate a new StreamTextureHost here to remotely control the new
// GPU-side StreamTexture instance.
return ScopedStreamTextureProxy(
new StreamTextureProxy(std::make_unique<StreamTextureHost>(
channel_, stream_id, std::move(remote))));
}
bool StreamTextureFactory::IsLost() const {
return channel_->IsLost();
}
gpu::SharedImageInterface* StreamTextureFactory::SharedImageInterface() {
if (shared_image_interface_)
return shared_image_interface_.get();
shared_image_interface_ = channel_->CreateClientSharedImageInterface();
DCHECK(shared_image_interface_);
return shared_image_interface_.get();
}
} // namespace content