#include "media/gpu/chromeos/mailbox_video_frame_converter.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_shared_image_interface.h"
#include "media/base/format_utils.h"
#include "media/base/video_util.h"
#include "media/gpu/macros.h"
#include "ui/gfx/gpu_memory_buffer_handle.h"
namespace media {
namespace {
inline gfx::Size to_shared_image_size(FrameResource* origin_frame,
scoped_refptr<FrameResource> frame) {
return origin_frame->metadata().needs_detiling
? origin_frame->coded_size()
: GetRectSizeFromOrigin(frame->visible_rect());
}
inline gfx::Size to_coded_size(scoped_refptr<FrameResource> frame) {
return frame->metadata().needs_detiling
? frame->coded_size()
: GetRectSizeFromOrigin(frame->visible_rect());
}
}
std::unique_ptr<FrameResourceConverter> MailboxVideoFrameConverter::Create(
scoped_refptr<gpu::SharedImageInterface> sii) {
return base::WrapUnique<FrameResourceConverter>(
new MailboxVideoFrameConverter(std::move(sii)));
}
std::unique_ptr<FrameResourceConverter> MailboxVideoFrameConverter::Create(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetCommandBufferStubCB get_stub_cb) {
DCHECK(gpu_task_runner);
DCHECK(get_stub_cb);
scoped_refptr<gpu::SharedImageInterface> sii;
base::WaitableEvent wait;
bool success = gpu_task_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](GetCommandBufferStubCB get_stub_cb,
scoped_refptr<gpu::SharedImageInterface>* sii,
base::WaitableEvent* wait) {
auto* cb_stub = get_stub_cb.Run();
if (cb_stub) {
DCHECK(cb_stub->channel());
*sii = cb_stub->channel()
->shared_image_stub()
->shared_image_interface();
}
wait->Signal();
},
get_stub_cb, &sii, &wait));
if (success) {
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
wait.Wait();
}
return Create(std::move(sii));
}
MailboxVideoFrameConverter::MailboxVideoFrameConverter(
scoped_refptr<gpu::SharedImageInterface> sii)
: shared_image_interface_(std::move(sii)) {
DVLOGF(2);
}
void MailboxVideoFrameConverter::Destroy() {
DCHECK(!parent_task_runner() ||
parent_task_runner()->RunsTasksInCurrentSequence());
DVLOGF(2);
weak_this_factory_.InvalidateWeakPtrs();
delete this;
}
MailboxVideoFrameConverter::~MailboxVideoFrameConverter() {
DVLOGF(2);
}
void MailboxVideoFrameConverter::ConvertFrameImpl(
scoped_refptr<FrameResource> frame) {
DVLOGF(4);
if (!shared_image_interface_) {
return OnError(FROM_HERE, "Initialized without SharedImageInterface");
}
if (!frame ||
(frame->storage_type() != VideoFrame::STORAGE_DMABUFS &&
frame->storage_type() != VideoFrame::STORAGE_GPU_MEMORY_BUFFER)) {
return OnError(FROM_HERE, "Invalid frame.");
}
FrameResource* origin_frame = GetOriginalFrame(*frame);
if (!origin_frame) {
return OnError(FROM_HERE, "Failed to get origin frame.");
}
TRACE_EVENT1("media,gpu", "ConvertFrameImpl", "FrameResource id",
origin_frame->unique_id());
auto shared_image_it = shared_images_.find(origin_frame->unique_id());
if (shared_image_it != shared_images_.end()) {
auto stored_shared_image = shared_image_it->second;
if (stored_shared_image &&
stored_shared_image->size() ==
to_shared_image_size(origin_frame, frame) &&
stored_shared_image->color_space() == frame->ColorSpace()) {
shared_image_interface_->UpdateSharedImage(
gpu::SyncToken(), stored_shared_image->mailbox());
WrapSharedImageAndVideoFrameAndOutput(
origin_frame, std::move(frame), std::move(stored_shared_image),
shared_image_interface_->GenVerifiedSyncToken());
return;
}
}
scoped_refptr<gpu::ClientSharedImage> new_client_shared_image =
GenerateSharedImage(origin_frame, frame);
if (!new_client_shared_image) {
return;
}
auto sync_token = new_client_shared_image->creation_sync_token();
shared_image_interface_->VerifySyncToken(sync_token);
WrapSharedImageAndVideoFrameAndOutput(origin_frame, std::move(frame),
std::move(new_client_shared_image),
sync_token);
}
void MailboxVideoFrameConverter::WrapSharedImageAndVideoFrameAndOutput(
FrameResource* origin_frame,
scoped_refptr<FrameResource> frame,
scoped_refptr<gpu::ClientSharedImage> shared_image,
const gpu::SyncToken& shared_image_sync_token) {
DCHECK(parent_task_runner()->RunsTasksInCurrentSequence());
DCHECK(shared_image);
const UniqueID origin_frame_id = origin_frame->unique_id();
DCHECK(base::Contains(shared_images_, origin_frame_id));
CHECK_EQ(frame->format(), origin_frame->format());
const gfx::Size coded_size = to_coded_size(frame);
scoped_refptr<VideoFrame> mailbox_frame = VideoFrame::WrapSharedImage(
frame->format(), shared_image, shared_image_sync_token,
{}, coded_size, frame->visible_rect(),
frame->natural_size(), frame->timestamp());
mailbox_frame->set_color_space(shared_image->color_space());
mailbox_frame->set_hdr_metadata(frame->hdr_metadata());
mailbox_frame->set_metadata(frame->metadata());
mailbox_frame->metadata().read_lock_fences_enabled = true;
mailbox_frame->metadata().is_webgpu_compatible =
frame->metadata().is_webgpu_compatible;
mailbox_frame->AddDestructionObserver(
base::DoNothingWithBoundArgs(std::move(frame)));
Output(std::move(mailbox_frame));
}
scoped_refptr<gpu::ClientSharedImage>
MailboxVideoFrameConverter::GenerateSharedImage(
FrameResource* origin_frame,
scoped_refptr<FrameResource> frame) {
auto si_format = VideoPixelFormatToSharedImageFormat(origin_frame->format());
if (!si_format) {
OnError(FROM_HERE, "Unsupported format: " +
VideoPixelFormatToString(origin_frame->format()));
return nullptr;
}
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
if (si_format->is_multi_plane()) {
si_format->SetPrefersExternalSampler();
}
#endif
auto gpu_memory_buffer_handle = origin_frame->CreateGpuMemoryBufferHandle();
DCHECK(!gpu_memory_buffer_handle.is_null());
DCHECK_EQ(gpu_memory_buffer_handle.type, gfx::NATIVE_PIXMAP);
const gfx::Size shared_image_size = to_shared_image_size(origin_frame, frame);
gpu::SharedImageUsageSet shared_image_usage =
gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_SCANOUT |
gpu::SHARED_IMAGE_USAGE_GLES2_READ | gpu::SHARED_IMAGE_USAGE_RASTER_READ;
if (origin_frame->metadata().is_webgpu_compatible &&
!shared_image_interface_->GetCapabilities()
.disable_webgpu_shared_images) {
shared_image_usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU_READ;
}
scoped_refptr<gpu::ClientSharedImage> client_shared_image =
shared_image_interface_->CreateSharedImage(
{*si_format, shared_image_size, frame->ColorSpace(),
shared_image_usage, "MailboxVideoFrameConverter"},
std::move(gpu_memory_buffer_handle));
if (!client_shared_image) {
OnError(FROM_HERE, "Failed to create shared image.");
return nullptr;
}
RegisterSharedImage(origin_frame, client_shared_image);
return client_shared_image;
}
void MailboxVideoFrameConverter::RegisterSharedImage(
FrameResource* origin_frame,
scoped_refptr<gpu::ClientSharedImage> client_shared_image) {
DVLOGF(4) << "frame: " << origin_frame->unique_id();
DCHECK(parent_task_runner()->RunsTasksInCurrentSequence());
DCHECK(client_shared_image);
DCHECK(!base::Contains(shared_images_, origin_frame->unique_id()) ||
shared_images_.find(origin_frame->unique_id())->second !=
client_shared_image);
shared_images_[origin_frame->unique_id()] = client_shared_image;
origin_frame->AddDestructionObserver(base::BindOnce(
[](scoped_refptr<gpu::ClientSharedImage> shared_image,
scoped_refptr<base::SequencedTaskRunner> parent_task_runner,
base::WeakPtr<MailboxVideoFrameConverter> parent_weak_ptr,
UniqueID origin_frame_id) {
if (parent_task_runner->RunsTasksInCurrentSequence()) {
if (parent_weak_ptr)
parent_weak_ptr->UnregisterSharedImage(origin_frame_id,
std::move(shared_image));
return;
}
parent_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::UnregisterSharedImage,
parent_weak_ptr, origin_frame_id,
std::move(shared_image)));
},
std::move(client_shared_image), parent_task_runner(),
weak_this_factory_.GetWeakPtr(), origin_frame->unique_id()));
}
void MailboxVideoFrameConverter::UnregisterSharedImage(
UniqueID origin_frame_id,
scoped_refptr<gpu::ClientSharedImage> client_shared_image) {
DCHECK(parent_task_runner()->RunsTasksInCurrentSequence());
DVLOGF(4);
auto it = shared_images_.find(origin_frame_id);
CHECK(it != shared_images_.end());
if (it->second == client_shared_image.get()) {
shared_images_.erase(it);
}
}
bool MailboxVideoFrameConverter::UsesGetOriginalFrameCBImpl() const {
return true;
}
void MailboxVideoFrameConverter::OnError(const base::Location& location,
const std::string& msg) {
DCHECK(parent_task_runner()->RunsTasksInCurrentSequence());
FrameResourceConverter::AbortPendingFrames();
FrameResourceConverter::OnError(location, msg);
}
}