blob: fa668ac56fcda7619eb42ffbb65ba129b73c52c9 [file] [log] [blame]
// Copyright (c) 2012 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 "media/gpu/ipc/client/gpu_video_decode_accelerator_host.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "ipc/ipc_mojo_bootstrap.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
namespace media {
GpuVideoDecodeAcceleratorHost::GpuVideoDecodeAcceleratorHost(
gpu::CommandBufferProxyImpl* impl)
: client_(nullptr),
impl_(impl),
media_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
DCHECK(impl_);
weak_this_ = weak_this_factory_.GetWeakPtr();
impl_->AddDeletionObserver(this);
}
GpuVideoDecodeAcceleratorHost::~GpuVideoDecodeAcceleratorHost() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock lock(impl_lock_);
if (impl_)
impl_->RemoveDeletionObserver(this);
}
void GpuVideoDecodeAcceleratorHost::OnDisconnectedFromGpuProcess() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
decoder_.reset();
client_receiver_.reset();
DLOG(ERROR) << "OnDisconnectedFromGpuProcess()";
PostNotifyError(PLATFORM_FAILURE);
}
bool GpuVideoDecodeAcceleratorHost::Initialize(const Config& config,
Client* client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
client_ = client;
base::AutoLock lock(impl_lock_);
if (!impl_)
return false;
// Mojo will ignore our request to bind to a different thread than the main or
// IO thread unless we construct this object. It does this to avoid breaking
// use cases that depend on the behavior of ignoring other bindings, as
// detailed in the documentation for
// IPC::ScopedAllowOffSequenceChannelAssociatedBindings.
IPC::ScopedAllowOffSequenceChannelAssociatedBindings allow_binding;
const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
impl_->channel()->io_task_runner();
bool succeeded = false;
mojo::SharedAssociatedRemote<mojom::GpuAcceleratedVideoDecoderProvider>
provider;
impl_->BindMediaReceiver(
provider.BindNewEndpointAndPassReceiver(io_task_runner));
provider->CreateAcceleratedVideoDecoder(
config, decoder_.BindNewEndpointAndPassReceiver(io_task_runner),
client_receiver_.BindNewEndpointAndPassRemote(), &succeeded);
if (!succeeded) {
DLOG(ERROR) << "CreateAcceleratedVideoDecoder() failed";
PostNotifyError(PLATFORM_FAILURE);
decoder_.reset();
client_receiver_.reset();
return false;
}
decoder_.set_disconnect_handler(
base::BindOnce(
&GpuVideoDecodeAcceleratorHost::OnDisconnectedFromGpuProcess,
weak_this_),
base::SequencedTaskRunnerHandle::Get());
return true;
}
void GpuVideoDecodeAcceleratorHost::Decode(BitstreamBuffer bitstream_buffer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
decoder_->Decode(std::move(bitstream_buffer));
}
void GpuVideoDecodeAcceleratorHost::AssignPictureBuffers(
const std::vector<PictureBuffer>& buffers) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
std::vector<mojom::PictureBufferAssignmentPtr> assignments;
for (const auto& buffer : buffers) {
if (buffer.size() != picture_buffer_dimensions_) {
DLOG(ERROR) << "buffer.size() invalid: expected "
<< picture_buffer_dimensions_.ToString() << ", got "
<< buffer.size().ToString();
PostNotifyError(INVALID_ARGUMENT);
return;
}
assignments.push_back(mojom::PictureBufferAssignment::New(
buffer.id(), buffer.client_texture_ids()));
}
decoder_->AssignPictureBuffers(std::move(assignments));
}
void GpuVideoDecodeAcceleratorHost::ReusePictureBuffer(
int32_t picture_buffer_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
decoder_->ReusePictureBuffer(picture_buffer_id);
}
void GpuVideoDecodeAcceleratorHost::Flush() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
decoder_->Flush(base::BindOnce(&GpuVideoDecodeAcceleratorHost::OnFlushDone,
base::Unretained(this)));
}
void GpuVideoDecodeAcceleratorHost::Reset() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
decoder_->Flush(base::BindOnce(&GpuVideoDecodeAcceleratorHost::OnResetDone,
base::Unretained(this)));
}
void GpuVideoDecodeAcceleratorHost::SetOverlayInfo(
const OverlayInfo& overlay_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decoder_)
return;
decoder_->SetOverlayInfo(overlay_info);
}
void GpuVideoDecodeAcceleratorHost::Destroy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
client_ = nullptr;
delete this;
}
void GpuVideoDecodeAcceleratorHost::OnWillDeleteImpl() {
base::AutoLock lock(impl_lock_);
impl_ = nullptr;
// The gpu::CommandBufferProxyImpl is going away; error out this VDA.
media_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&GpuVideoDecodeAcceleratorHost::OnDisconnectedFromGpuProcess,
weak_this_));
}
void GpuVideoDecodeAcceleratorHost::PostNotifyError(Error error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "PostNotifyError(): error=" << error;
media_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&GpuVideoDecodeAcceleratorHost::OnError,
weak_this_, error));
}
// TODO(tmathmeyer) This needs to accept a Status at some point
void GpuVideoDecodeAcceleratorHost::OnInitializationComplete(bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_) {
client_->NotifyInitializationComplete(
success ? OkStatus() : StatusCode::kInitializationUnspecifiedFailure);
}
}
void GpuVideoDecodeAcceleratorHost::OnBitstreamBufferProcessed(
int32_t bitstream_buffer_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_)
client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id);
}
void GpuVideoDecodeAcceleratorHost::OnProvidePictureBuffers(
uint32_t num_requested_buffers,
VideoPixelFormat format,
uint32_t textures_per_buffer,
const gfx::Size& dimensions,
uint32_t texture_target) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
picture_buffer_dimensions_ = dimensions;
const int kMaxVideoPlanes = 4;
if (textures_per_buffer > kMaxVideoPlanes) {
PostNotifyError(PLATFORM_FAILURE);
return;
}
if (client_) {
client_->ProvidePictureBuffers(num_requested_buffers, format,
textures_per_buffer, dimensions,
texture_target);
}
}
void GpuVideoDecodeAcceleratorHost::OnDismissPictureBuffer(
int32_t picture_buffer_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_)
client_->DismissPictureBuffer(picture_buffer_id);
}
void GpuVideoDecodeAcceleratorHost::OnPictureReady(
mojom::PictureReadyParamsPtr params) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!client_)
return;
Picture picture(params->picture_buffer_id, params->bitstream_buffer_id,
params->visible_rect, params->color_space,
params->allow_overlay);
picture.set_read_lock_fences_enabled(params->read_lock_fences_enabled);
picture.set_size_changed(params->size_changed);
picture.set_texture_owner(params->surface_texture);
picture.set_wants_promotion_hint(params->wants_promotion_hint);
client_->PictureReady(picture);
}
void GpuVideoDecodeAcceleratorHost::OnFlushDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_)
client_->NotifyFlushDone();
}
void GpuVideoDecodeAcceleratorHost::OnResetDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_)
client_->NotifyResetDone();
}
void GpuVideoDecodeAcceleratorHost::OnError(uint32_t error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!client_)
return;
weak_this_factory_.InvalidateWeakPtrs();
// Client::NotifyError() may Destroy() |this|, so calling it needs to be the
// last thing done on this stack!
VideoDecodeAccelerator::Client* client = client_;
client_ = nullptr;
client->NotifyError(static_cast<VideoDecodeAccelerator::Error>(error));
}
} // namespace media