blob: ee556d686cda24442d8cce7be58548a4c4f6183a [file] [log] [blame]
// Copyright 2019 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/android/frame_info_helper.h"
#include "base/threading/sequence_bound.h"
#include "gpu/command_buffer/service/shared_image_video.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "media/base/bind_to_current_loop.h"
#include "media/gpu/android/codec_output_buffer_renderer.h"
namespace media {
FrameInfoHelper::FrameInfo::FrameInfo() = default;
FrameInfoHelper::FrameInfo::~FrameInfo() = default;
FrameInfoHelper::FrameInfo::FrameInfo(FrameInfo&&) = default;
FrameInfoHelper::FrameInfo::FrameInfo(const FrameInfoHelper::FrameInfo&) =
default;
FrameInfoHelper::FrameInfo& FrameInfoHelper::FrameInfo::operator=(
const FrameInfoHelper::FrameInfo&) = default;
// Concrete implementation of FrameInfoHelper that renders output buffers and
// gets the FrameInfo they need.
class FrameInfoHelperImpl : public FrameInfoHelper,
public gpu::RefCountedLockHelperDrDc {
public:
FrameInfoHelperImpl(scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb,
scoped_refptr<gpu::RefCountedLock> drdc_lock)
: gpu::RefCountedLockHelperDrDc(std::move(drdc_lock)) {
on_gpu_ = base::SequenceBound<OnGpu>(std::move(gpu_task_runner),
std::move(get_stub_cb));
}
~FrameInfoHelperImpl() override = default;
void GetFrameInfo(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoReadyCB callback) override {
Request request = {.buffer_renderer = std::move(buffer_renderer),
.callback = std::move(callback)};
requests_.push(std::move(request));
// If there were no pending requests start processing queue now.
if (requests_.size() == 1)
ProcessRequestsQueue();
}
private:
struct Request {
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer;
FrameInfoReadyCB callback;
};
class OnGpu : public gpu::CommandBufferStub::DestructionObserver {
public:
OnGpu(SharedImageVideoProvider::GetStubCB get_stub_cb) {
stub_ = get_stub_cb.Run();
if (stub_)
stub_->AddDestructionObserver(this);
}
~OnGpu() override {
if (stub_)
stub_->RemoveDestructionObserver(this);
}
void OnWillDestroyStub(bool have_context) override {
DCHECK(stub_);
stub_ = nullptr;
}
void GetFrameInfoImpl(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
absl::optional<FrameInfo>)> cb,
std::unique_ptr<base::AutoLockMaybe> scoped_drdc_lock) {
DCHECK(buffer_renderer);
auto texture_owner = buffer_renderer->texture_owner();
DCHECK(texture_owner);
absl::optional<FrameInfo> info;
if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
CodecOutputBufferRenderer::BindingsMode::kDontBindImage, 0)) {
gfx::Size coded_size;
gfx::Rect visible_rect;
if (texture_owner->GetCodedSizeAndVisibleRect(
buffer_renderer->size(), &coded_size, &visible_rect)) {
info.emplace();
info->coded_size = coded_size;
info->visible_rect = visible_rect;
info->ycbcr_info = GetYCbCrInfo(texture_owner.get());
}
}
// Release the lock here since we already got the frame info.
scoped_drdc_lock.reset();
std::move(cb).Run(std::move(buffer_renderer), info);
}
void GetFrameInfo(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
absl::optional<FrameInfo>)> cb,
scoped_refptr<gpu::RefCountedLock> drdc_lock) {
// Note that we need to ensure that no other thread renders another buffer
// in between while we are getting frame info here. Otherwise we will get
// wrong frame info. This is ensured by holding |lock| here until
// GetFrameInfoImpl() ends.
auto scoped_drdc_lock = std::make_unique<base::AutoLockMaybe>(
drdc_lock ? drdc_lock->GetDrDcLockPtr() : nullptr);
DCHECK(buffer_renderer);
auto texture_owner = buffer_renderer->texture_owner();
DCHECK(texture_owner);
auto buffer_available_cb =
base::BindOnce(&OnGpu::GetFrameInfoImpl, weak_factory_.GetWeakPtr(),
std::move(buffer_renderer), std::move(cb),
std::move(scoped_drdc_lock));
texture_owner->RunWhenBufferIsAvailable(std::move(buffer_available_cb));
}
private:
// Gets YCbCrInfo from last rendered frame.
absl::optional<gpu::VulkanYCbCrInfo> GetYCbCrInfo(
gpu::TextureOwner* texture_owner) {
gpu::ContextResult result;
if (!stub_)
return absl::nullopt;
auto shared_context =
stub_->channel()->gpu_channel_manager()->GetSharedContextState(
&result);
auto context_provider =
(result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
if (!context_provider)
return absl::nullopt;
return gpu::SharedImageVideo::GetYcbcrInfo(texture_owner,
context_provider);
}
gpu::CommandBufferStub* stub_ = nullptr;
base::WeakPtrFactory<OnGpu> weak_factory_{this};
};
FrameInfo GetFrameInfoWithVisibleSize(const gfx::Size& visible_size) {
FrameInfo info;
info.coded_size = visible_size;
info.visible_rect = gfx::Rect(visible_size);
return info;
}
void OnFrameInfoReady(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
absl::optional<FrameInfo> frame_info) {
DCHECK(buffer_renderer);
DCHECK(!requests_.empty());
auto& request = requests_.front();
if (frame_info) {
visible_size_ = buffer_renderer->size();
frame_info_ = *frame_info;
std::move(request.callback).Run(std::move(buffer_renderer), frame_info_);
} else {
// It's possible that we will fail to render frame and so weren't able to
// obtain FrameInfo. In this case we don't cache new values and complete
// current request with visible size, we will attempt to render next frame
// with next request.
auto info = GetFrameInfoWithVisibleSize(buffer_renderer->size());
std::move(request.callback)
.Run(std::move(buffer_renderer), std::move(info));
}
requests_.pop();
ProcessRequestsQueue();
}
void ProcessRequestsQueue() {
while (!requests_.empty()) {
auto& request = requests_.front();
if (!request.buffer_renderer) {
// If we don't have buffer_renderer we can Run callback immediately.
std::move(request.callback).Run(nullptr, FrameInfo());
} else if (!request.buffer_renderer->texture_owner()) {
// If there is no texture_owner (SurfaceView case), we can't render
// frame and get proper size. But as Display Compositor won't render
// this frame the actual size is not important, assume coded_size =
// visible_size.
auto info =
GetFrameInfoWithVisibleSize(request.buffer_renderer->size());
std::move(request.callback)
.Run(std::move(request.buffer_renderer), std::move(info));
} else if (visible_size_ == request.buffer_renderer->size()) {
// We have cached the results of last frame info request with the same
// size. We assume that coded_size doesn't change if the visible_size
// stays the same.
std::move(request.callback)
.Run(std::move(request.buffer_renderer), frame_info_);
} else {
// We have texture_owner and we don't have cached value, so we need to
// hop to GPU thread and render the frame to get proper size.
auto cb = BindToCurrentLoop(
base::BindOnce(&FrameInfoHelperImpl::OnFrameInfoReady,
weak_factory_.GetWeakPtr()));
on_gpu_.AsyncCall(&OnGpu::GetFrameInfo)
.WithArgs(std::move(request.buffer_renderer), std::move(cb),
GetDrDcLock());
// We didn't complete this request quite yet, so we can't process queue
// any further.
break;
}
requests_.pop();
}
}
base::SequenceBound<OnGpu> on_gpu_;
std::queue<Request> requests_;
// Cached values.
FrameInfo frame_info_;
gfx::Size visible_size_;
base::WeakPtrFactory<FrameInfoHelperImpl> weak_factory_{this};
};
// static
std::unique_ptr<FrameInfoHelper> FrameInfoHelper::Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb,
scoped_refptr<gpu::RefCountedLock> drdc_lock) {
return std::make_unique<FrameInfoHelperImpl>(
std::move(gpu_task_runner), std::move(get_stub_cb), std::move(drdc_lock));
}
} // namespace media