Commit 6f6776f4 authored by Miguel Casas's avatar Miguel Casas Committed by Commit Bot

media/gpu/linux: convert MailboxVideoFrameConverter to SharedImages

This CL migrates MailboxVideoFrameConverter from using Textures to
using SharedImages.  No external functionality changes are expected.

Couple of things need to be added:

- ScopedSharedImage: an internal class to scope a SharedImage lifetime.
 SharedImages are kept in |shared_images_|, indexed by a unique id.

- Added GetGpuChannelCB that can be derived from the GetStubCB and that
 is needed for SharedImage creation and destruction. They are generated
 on Create(), so this interface doesn't change.

- This CL uses a WeakPtr to the GpuChannel, accessing its SharedImageStub
 and the gpu::Scheduler that way.

Most of the work goes in:
 - InitializeOnGpuTaskRunner() and then
 - the sequence ConvertFrameOnGPUThread() (calls either
  GenerateSharedImageOnGPUThread() or UpdateSharedImageOnGPUThread()) then
  jumps back to WrapMailboxAndVideoFrameAndOutput().

Test: crosvideo.appspot.com resolution-changing videos  (vp9, h264, vp8)
of course with the new VD decoder, on kohaku and scarlet/dru.

Bug: 998279
Change-Id: Ic9b5b2270d1b488562bc2725bba89dc2a0350012
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1772642
Commit-Queue: Miguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Reviewed-by: default avatarEric Karl <ericrk@chromium.org>
Reviewed-by: default avatarChih-Yu Huang <akahuang@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Reviewed-by: default avatarAndres Calderon Jaramillo <andrescj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698666}
parent fca05b6c
......@@ -430,6 +430,10 @@ bool Scheduler::ShouldYield(SequenceId sequence_id) {
return running_sequence->ShouldYieldTo(next_sequence);
}
base::WeakPtr<Scheduler> Scheduler::AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void Scheduler::SyncTokenFenceReleased(const SyncToken& sync_token,
uint32_t order_num,
SequenceId release_sequence_id,
......
......@@ -92,6 +92,8 @@ class GPU_EXPORT Scheduler {
// If the sequence should yield so that a higher priority sequence may run.
bool ShouldYield(SequenceId sequence_id);
base::WeakPtr<Scheduler> AsWeakPtr();
private:
struct SchedulingState {
......
......@@ -7,20 +7,19 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/shared_image_stub.h"
#include "media/base/format_utils.h"
#include "media/gpu/linux/platform_video_frame_utils.h"
#include "media/gpu/macros.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/native_pixmap.h"
#include "ui/gl/gl_image_native_pixmap.h"
#include "ui/gl/scoped_binders.h"
#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#endif // defined(USE_OZONE)
#include "ui/gl/gl_bindings.h"
namespace media {
......@@ -28,47 +27,41 @@ namespace {
constexpr GLenum kTextureTarget = GL_TEXTURE_EXTERNAL_OES;
// Destroy the GL texture. This is called when the origin DMA-buf VideoFrame
// is destroyed.
void DestroyTexture(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
scoped_refptr<CommandBufferHelper> command_buffer_helper,
GLuint service_id) {
DVLOGF(4);
if (!gpu_task_runner->BelongsToCurrentThread()) {
gpu_task_runner->PostTask(
} // anonymous namespace
// A SharedImage wrapper that calls |destroy_shared_image_cb| in dtor on
// |gpu_task_runner|.
class MailboxVideoFrameConverter::ScopedSharedImage {
public:
using DestroySharedImageCB =
gpu::SharedImageStub::SharedImageDestructionCallback;
ScopedSharedImage(
const gpu::Mailbox& mailbox,
const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner,
DestroySharedImageCB destroy_shared_image_cb)
: mailbox_(mailbox),
destroy_shared_image_cb_(std::move(destroy_shared_image_cb)),
destruction_task_runner_(gpu_task_runner) {}
~ScopedSharedImage() {
if (destruction_task_runner_->RunsTasksInCurrentSequence()) {
std::move(destroy_shared_image_cb_).Run(gpu::SyncToken());
return;
}
destruction_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DestroyTexture, std::move(gpu_task_runner),
std::move(command_buffer_helper), service_id));
return;
base::BindOnce(std::move(destroy_shared_image_cb_), gpu::SyncToken()));
}
if (!command_buffer_helper->MakeContextCurrent()) {
VLOGF(1) << "Failed to make context current";
return;
}
command_buffer_helper->DestroyTexture(service_id);
}
const gpu::Mailbox& mailbox() const { return mailbox_; }
// ReleaseMailbox callback of the mailbox VideoFrame.
// Keep the wrapped DMA-buf VideoFrame until WaitForSyncToken() is done.
void WaitForSyncToken(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
scoped_refptr<CommandBufferHelper> command_buffer_helper,
scoped_refptr<VideoFrame> frame,
const gpu::SyncToken& sync_token) {
DVLOGF(4);
gpu_task_runner->PostTask(
FROM_HERE,
base::BindOnce(
&CommandBufferHelper::WaitForSyncToken,
std::move(command_buffer_helper), sync_token,
base::BindOnce(base::DoNothing::Once<scoped_refptr<VideoFrame>>(),
std::move(frame))));
}
private:
const gpu::Mailbox mailbox_;
DestroySharedImageCB destroy_shared_image_cb_;
const scoped_refptr<base::SequencedTaskRunner> destruction_task_runner_;
} // namespace
DISALLOW_COPY_AND_ASSIGN(ScopedSharedImage);
};
// static
std::unique_ptr<VideoFrameConverter> MailboxVideoFrameConverter::Create(
......@@ -78,18 +71,28 @@ std::unique_ptr<VideoFrameConverter> MailboxVideoFrameConverter::Create(
if (!unwrap_frame_cb || !gpu_task_runner || !get_stub_cb)
return nullptr;
auto get_gpu_channel_cb = base::BindRepeating(
[](base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb) {
gpu::CommandBufferStub* stub = get_stub_cb.Run();
if (!stub)
return base::WeakPtr<gpu::GpuChannel>();
DCHECK(stub->channel());
return stub->channel()->AsWeakPtr();
},
get_stub_cb);
return base::WrapUnique<VideoFrameConverter>(new MailboxVideoFrameConverter(
std::move(unwrap_frame_cb), std::move(gpu_task_runner),
std::move(get_stub_cb)));
get_gpu_channel_cb));
}
MailboxVideoFrameConverter::MailboxVideoFrameConverter(
UnwrapFrameCB unwrap_frame_cb,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetCommandBufferStubCB get_stub_cb)
GetGpuChannelCB get_gpu_channel_cb)
: unwrap_frame_cb_(std::move(unwrap_frame_cb)),
gpu_task_runner_(std::move(gpu_task_runner)),
get_stub_cb_(std::move(get_stub_cb)) {
get_gpu_channel_cb_(get_gpu_channel_cb) {
DVLOGF(2);
parent_weak_this_ = parent_weak_this_factory_.GetWeakPtr();
......@@ -121,19 +124,16 @@ MailboxVideoFrameConverter::~MailboxVideoFrameConverter() {
DVLOGF(2);
}
bool MailboxVideoFrameConverter::CreateCommandBufferHelper() {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
DCHECK(get_stub_cb_);
bool MailboxVideoFrameConverter::InitializeOnGPUThread() {
DVLOGF(4);
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
gpu::CommandBufferStub* stub = std::move(get_stub_cb_).Run();
if (!stub) {
VLOGF(1) << "Failed to obtain command buffer stub";
return false;
}
// Use |gpu_channel_| as a marker that we have been initialized already.
if (gpu_channel_)
return true;
command_buffer_helper_ = CommandBufferHelper::Create(stub);
return command_buffer_helper_ != nullptr;
gpu_channel_ = get_gpu_channel_cb_.Run();
return !!gpu_channel_;
}
void MailboxVideoFrameConverter::ConvertFrame(scoped_refptr<VideoFrame> frame) {
......@@ -147,165 +147,243 @@ void MailboxVideoFrameConverter::ConvertFrame(scoped_refptr<VideoFrame> frame) {
if (!origin_frame)
return OnError("Failed to get origin frame.");
// Generate mailbox at gpu thread if we haven't.
gpu::Mailbox mailbox;
const int origin_frame_id = origin_frame->unique_id();
if (mailbox_table_.find(origin_frame_id) == mailbox_table_.end()) {
DVLOGF(4) << "Generate mailbox for frame: " << origin_frame_id;
// Set an empty mailbox first to prevent generating mailbox multiple times.
mailbox_table_.emplace(origin_frame_id, gpu::Mailbox());
// |frame| keeps a refptr of |origin_frame|. |origin_frame| is guaranteed
// alive by carrying |frame|. So it's safe to use base::Unretained here.
gpu_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::GenerateMailbox,
gpu_weak_this_, base::Unretained(origin_frame), frame));
}
if (shared_images_.find(origin_frame_id) != shared_images_.end())
mailbox = shared_images_[origin_frame_id]->mailbox();
input_frame_queue_.emplace(frame, origin_frame_id);
input_frame_queue_.emplace(std::move(frame), origin_frame_id);
TryOutputFrames();
// |frame| keeps a refptr of |origin_frame|. |origin_frame| is guaranteed
// alive by carrying |frame|. So it's safe to use base::Unretained here.
gpu_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::ConvertFrameOnGPUThread,
gpu_weak_this_, base::Unretained(origin_frame),
std::move(frame), mailbox));
}
void MailboxVideoFrameConverter::TryOutputFrames() {
void MailboxVideoFrameConverter::WrapMailboxAndVideoFrameAndOutput(
VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame,
const gpu::Mailbox& mailbox) {
DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
DVLOGF(4) << "input_frame_queue_ size: " << input_frame_queue_.size();
while (!input_frame_queue_.empty()) {
const int origin_frame_id = input_frame_queue_.front().second;
const gpu::Mailbox& mailbox = mailbox_table_[origin_frame_id];
if (mailbox.IsZero()) {
DVLOGF(4) << "Mailbox for frame: " << origin_frame_id
<< " is not generated yet.";
return;
}
auto frame = std::move(input_frame_queue_.front().first);
input_frame_queue_.pop();
DCHECK(!mailbox.IsZero());
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0] =
gpu::MailboxHolder(mailbox, gpu::SyncToken(), kTextureTarget);
scoped_refptr<VideoFrame> mailbox_frame = VideoFrame::WrapNativeTextures(
frame->format(), mailbox_holders,
base::BindOnce(&WaitForSyncToken, gpu_task_runner_,
command_buffer_helper_, frame),
frame->coded_size(), frame->visible_rect(), frame->natural_size(),
frame->timestamp());
mailbox_frame->metadata()->MergeMetadataFrom(frame->metadata());
const int origin_frame_id = origin_frame->unique_id();
DCHECK(base::Contains(shared_images_, origin_frame_id));
mailbox_frame->metadata()->SetBoolean(
VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
// While we were on |gpu_task_runner_|, AbortPendingFrames() might have been
// called and/or possibly different frames enqueued in |input_frame_queue_|.
if (input_frame_queue_.empty())
return;
if (input_frame_queue_.front().second != origin_frame_id)
return;
input_frame_queue_.pop();
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0] =
gpu::MailboxHolder(mailbox, gpu::SyncToken(), kTextureTarget);
VideoFrame::ReleaseMailboxCB release_mailbox_cb = base::BindOnce(
[](scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
base::WeakPtr<MailboxVideoFrameConverter> gpu_weak_ptr,
scoped_refptr<VideoFrame> frame, const gpu::SyncToken& sync_token) {
if (gpu_task_runner->RunsTasksInCurrentSequence()) {
if (gpu_weak_ptr) {
gpu_weak_ptr->WaitOnSyncTokenAndReleaseFrameOnGPUThread(
std::move(frame), sync_token);
}
return;
}
gpu_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::
WaitOnSyncTokenAndReleaseFrameOnGPUThread,
gpu_weak_ptr, std::move(frame), sync_token));
},
gpu_task_runner_, gpu_weak_this_, frame);
scoped_refptr<VideoFrame> mailbox_frame = VideoFrame::WrapNativeTextures(
frame->format(), mailbox_holders, std::move(release_mailbox_cb),
frame->coded_size(), frame->visible_rect(), frame->natural_size(),
frame->timestamp());
mailbox_frame->metadata()->MergeMetadataFrom(frame->metadata());
mailbox_frame->metadata()->SetBoolean(
VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
// TODO(crbug.com/998547): Remove this specific statement when not needed.
mailbox_frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT,
true);
output_cb_.Run(mailbox_frame);
}
output_cb_.Run(mailbox_frame);
void MailboxVideoFrameConverter::ConvertFrameOnGPUThread(
VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame,
gpu::Mailbox stored_mailbox) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
TRACE_EVENT1("media,gpu", "ConvertFrameOnGPUThread", "VideoFrame id",
origin_frame->unique_id());
// |origin_frame| is kept alive by |frame|.
auto wrap_mailbox_and_video_frame_and_output_cb = base::BindOnce(
&MailboxVideoFrameConverter::WrapMailboxAndVideoFrameAndOutput,
parent_weak_this_, base::Unretained(origin_frame), std::move(frame));
// If there's a |stored_mailbox| associated with |origin_frame|, update it and
// call the continuation callback, otherwise create a Mailbox and register it.
if (!stored_mailbox.IsZero()) {
if (!UpdateSharedImageOnGPUThread(stored_mailbox))
return;
parent_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(wrap_mailbox_and_video_frame_and_output_cb),
stored_mailbox));
return;
}
std::unique_ptr<ScopedSharedImage> scoped_shared_image;
scoped_shared_image = GenerateSharedImageOnGPUThread(origin_frame);
if (!scoped_shared_image)
return;
gpu::Mailbox mailbox = scoped_shared_image->mailbox();
// |origin_frame| is kept alive by |frame| in
// |wrap_mailbox_and_video_frame_and_output_cb|.
parent_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::RegisterSharedImage,
parent_weak_this_, base::Unretained(origin_frame),
std::move(scoped_shared_image)));
parent_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(wrap_mailbox_and_video_frame_and_output_cb),
mailbox));
}
void MailboxVideoFrameConverter::GenerateMailbox(
VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame) {
std::unique_ptr<MailboxVideoFrameConverter::ScopedSharedImage>
MailboxVideoFrameConverter::GenerateSharedImageOnGPUThread(
VideoFrame* video_frame) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
DVLOGF(4) << "frame: " << origin_frame->unique_id();
DVLOGF(4) << "frame: " << video_frame->unique_id();
// CreateCommandBufferHelper() should be called on |gpu_task_runner_| so we
// call it here lazily instead of at constructor.
if (!command_buffer_helper_ && !CreateCommandBufferHelper())
return OnError("Failed to create command buffer helper.");
// TODO(crbug.com/998279): consider eager initialization.
if (!InitializeOnGPUThread()) {
OnError("InitializeOnGPUThread failed");
return nullptr;
}
// Get NativePixmap.
scoped_refptr<gfx::NativePixmap> pixmap;
auto buffer_format =
VideoPixelFormatToGfxBufferFormat(origin_frame->format());
const auto buffer_format =
VideoPixelFormatToGfxBufferFormat(video_frame->format());
if (!buffer_format) {
return OnError("Unsupported format: " +
VideoPixelFormatToString(origin_frame->format()));
OnError("Unsupported format: " +
VideoPixelFormatToString(video_frame->format()));
return nullptr;
}
#if defined(USE_OZONE)
gfx::GpuMemoryBufferHandle handle = CreateGpuMemoryBufferHandle(origin_frame);
DCHECK(!handle.is_null());
pixmap = ui::OzonePlatform::GetInstance()
->GetSurfaceFactoryOzone()
->CreateNativePixmapFromHandle(
gfx::kNullAcceleratedWidget, origin_frame->coded_size(),
*buffer_format, std::move(handle.native_pixmap_handle));
#endif // defined(USE_OZONE)
if (!pixmap)
return OnError("Cannot create NativePixmap.");
// Create GLImage.
auto image = base::MakeRefCounted<gl::GLImageNativePixmap>(
origin_frame->coded_size(), *buffer_format);
if (!image->Initialize(std::move(pixmap)))
return OnError("Failed to initialize GLImage.");
// Create texture and bind image to texture.
if (!command_buffer_helper_->MakeContextCurrent())
return OnError("Failed to make context current.");
GLuint service_id = command_buffer_helper_->CreateTexture(
kTextureTarget, GL_RGBA, origin_frame->coded_size().width(),
origin_frame->coded_size().height(), GL_RGBA, GL_UNSIGNED_BYTE);
DCHECK(service_id);
gl::ScopedTextureBinder bind_restore(kTextureTarget, service_id);
bool ret = image->BindTexImage(kTextureTarget);
DCHECK(ret);
command_buffer_helper_->BindImage(service_id, image.get(), true);
command_buffer_helper_->SetCleared(service_id);
gpu::Mailbox mailbox = command_buffer_helper_->CreateMailbox(service_id);
// Destroy the texture after the DMA-buf VideoFrame is destructed.
origin_frame->AddDestructionObserver(base::BindOnce(
&DestroyTexture, gpu_task_runner_, command_buffer_helper_, service_id));
auto gpu_memory_buffer_handle = CreateGpuMemoryBufferHandle(video_frame);
DCHECK(!gpu_memory_buffer_handle.is_null());
DCHECK_EQ(gpu_memory_buffer_handle.type, gfx::NATIVE_PIXMAP);
// |frame| keeps a refptr of |origin_frame|. |origin_frame| is guaranteed
// alive by carrying |frame|. So it's safe to use base::Unretained here.
parent_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::RegisterMailbox,
parent_weak_this_, base::Unretained(origin_frame), mailbox,
std::move(frame)));
gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
if (!gpu_channel_) {
OnError("GpuChannel is gone!");
return nullptr;
}
gpu::SharedImageStub* shared_image_stub = gpu_channel_->shared_image_stub();
DCHECK(shared_image_stub);
// The allocated SharedImages should be usable for the (Display) compositor
// and, potentially, for overlays (Scanout).
const uint32_t shared_image_usage =
gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
const bool success = shared_image_stub->CreateSharedImage(
mailbox, shared_image_stub->channel()->client_id(),
std::move(gpu_memory_buffer_handle), *buffer_format,
gpu::kNullSurfaceHandle, video_frame->coded_size(),
video_frame->ColorSpace(), shared_image_usage);
if (!success) {
OnError("Failed to create shared image.");
return nullptr;
}
// There's no need to UpdateSharedImage() after CreateSharedImage().
return std::make_unique<ScopedSharedImage>(
mailbox, gpu_task_runner_,
shared_image_stub->GetSharedImageDestructionCallback(mailbox));
}
void MailboxVideoFrameConverter::RegisterMailbox(
void MailboxVideoFrameConverter::RegisterSharedImage(
VideoFrame* origin_frame,
const gpu::Mailbox& mailbox,
scoped_refptr<VideoFrame> frame) {
DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!mailbox.IsZero());
std::unique_ptr<ScopedSharedImage> scoped_shared_image) {
DVLOGF(4) << "frame: " << origin_frame->unique_id();
DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
DCHECK(scoped_shared_image);
DCHECK(!scoped_shared_image->mailbox().IsZero());
DCHECK(!base::Contains(shared_images_, origin_frame->unique_id()));
mailbox_table_[origin_frame->unique_id()] = mailbox;
shared_images_[origin_frame->unique_id()] = std::move(scoped_shared_image);
origin_frame->AddDestructionObserver(base::BindOnce(
&MailboxVideoFrameConverter::UnregisterMailboxThunk, parent_task_runner_,
parent_weak_this_, origin_frame->unique_id()));
[](scoped_refptr<base::SequencedTaskRunner> parent_task_runner,
base::WeakPtr<MailboxVideoFrameConverter> parent_weak_ptr,
int origin_frame_id) {
if (parent_task_runner->RunsTasksInCurrentSequence()) {
if (parent_weak_ptr)
parent_weak_ptr->UnregisterSharedImage(origin_frame_id);
return;
}
parent_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&MailboxVideoFrameConverter::UnregisterSharedImage,
parent_weak_ptr, origin_frame_id));
},
parent_task_runner_, parent_weak_this_, origin_frame->unique_id()));
}
// Mailbox has been generated. It's time to convert frame again.
TryOutputFrames();
bool MailboxVideoFrameConverter::UpdateSharedImageOnGPUThread(
const gpu::Mailbox& mailbox) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
if (!gpu_channel_) {
OnError("GpuChannel is gone!");
return false;
}
gpu::SharedImageStub* shared_image_stub = gpu_channel_->shared_image_stub();
DCHECK(shared_image_stub);
if (!shared_image_stub->UpdateSharedImage(mailbox, gfx::GpuFenceHandle())) {
OnError("Could not update shared image");
return false;
}
return true;
}
// static
void MailboxVideoFrameConverter::UnregisterMailboxThunk(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Optional<base::WeakPtr<MailboxVideoFrameConverter>> converter,
const int origin_frame_id) {
DCHECK(converter);
DVLOGF(4) << "frame: " << origin_frame_id;
// MailboxVideoFrameConverter might have already been destroyed when this
// method is called. In this case, the WeakPtr will have been invalidated at
// |parent_task_runner_|, and UnregisterMailbox() will not get executed.
task_runner->PostTask(
FROM_HERE, base::BindOnce(&MailboxVideoFrameConverter::UnregisterMailbox,
*converter, origin_frame_id));
void MailboxVideoFrameConverter::WaitOnSyncTokenAndReleaseFrameOnGPUThread(
scoped_refptr<VideoFrame> frame,
const gpu::SyncToken& sync_token) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
if (!gpu_channel_)
return OnError("GpuChannel is gone!");
gpu::SharedImageStub* shared_image_stub = gpu_channel_->shared_image_stub();
DCHECK(shared_image_stub);
auto keep_video_frame_alive = base::BindOnce(
base::DoNothing::Once<scoped_refptr<VideoFrame>>(), std::move(frame));
auto* scheduler = gpu_channel_->scheduler();
DCHECK(scheduler);
scheduler->ScheduleTask(gpu::Scheduler::Task(
shared_image_stub->sequence(), std::move(keep_video_frame_alive),
std::vector<gpu::SyncToken>({sync_token})));
}
void MailboxVideoFrameConverter::UnregisterMailbox(const int origin_frame_id) {
void MailboxVideoFrameConverter::UnregisterSharedImage(int origin_frame_id) {
DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
DVLOGF(4);
auto it = mailbox_table_.find(origin_frame_id);
DCHECK(it != mailbox_table_.end());
mailbox_table_.erase(it);
auto it = shared_images_.find(origin_frame_id);
DCHECK(it != shared_images_.end());
shared_images_.erase(it);
}
void MailboxVideoFrameConverter::AbortPendingFrames() {
......
......@@ -13,91 +13,104 @@
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "media/base/video_decoder.h"
#include "media/base/video_frame.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/video_frame_converter.h"
namespace gpu {
class GpuChannel;
class CommandBufferStub;
} // namespace gpu
namespace media {
// The linux VideoDecoder implementations request DMA-buf VideoFrame from the
// DmabufVideoFramePool, and store the decoded data into DMA-buf. However the
// client of the VideoDecoder may only accept mailbox VideoFrame.
// This class is used for converting DMA-buf VideoFrame to mailbox VideoFrame.
// This class is used for converting DMA-buf backed VideoFrames to mailbox-based
// VideoFrames. See ConvertFrame() for more details.
// After conversion, the mailbox VideoFrame will retain a reference of the
// VideoFrame passed to ConvertFrame().
class MEDIA_GPU_EXPORT MailboxVideoFrameConverter : public VideoFrameConverter {
public:
using UnwrapFrameCB =
base::RepeatingCallback<VideoFrame*(const VideoFrame& wrapped_frame)>;
using GetCommandBufferStubCB = base::OnceCallback<gpu::CommandBufferStub*()>;
// Create a MailboxVideoFrameConverter instance. Return nullptr if any
// argument is invalid.
using GetCommandBufferStubCB =
base::RepeatingCallback<gpu::CommandBufferStub*()>;
using GetGpuChannelCB =
base::RepeatingCallback<base::WeakPtr<gpu::GpuChannel>()>;
// Creates a MailboxVideoFrameConverter instance. The callers will send
// wrapped VideoFrames to ConvertFrame(), |unwrap_frame_cb| is the callback
// used to get the original, unwrapped, VideoFrame. |gpu_task_runner| is the
// task runner of the GPU main thread. Returns nullptr if any argument is
// invalid.
static std::unique_ptr<VideoFrameConverter> Create(
UnwrapFrameCB unwrap_frame_cb,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetCommandBufferStubCB get_stub_cb);
// Convert DMA-buf VideoFrame to mailbox VideoFrame.
// For each frame, we bind DMA-buf to GL texture and create mailbox on the GPU
// main thread.
// The mailbox of each frame will be stored at |mailbox_table_|. When
// converting a frame second time, we just lookup the table instead of
// creating texture and mailbox.
// Enqueues |frame| to be converted to a gpu::Mailbox backed VideoFrame.
// |frame| must wrap a DMA-buf backed VideoFrame that is retrieved via
// |unwrap_frame_cb_|. The generated gpu::Mailbox-based VideoFrame is kept
// alive until the original (i.e. the unwrapped) DMA-Buf based VideoFrame one
// goes out of scope.
void ConvertFrame(scoped_refptr<VideoFrame> frame) override;
void AbortPendingFrames() override;
bool HasPendingFrames() const override;
private:
// In order to recycle VideoFrame, the DmabufVideoFramePool implementation may
// wrap the frame. We want to create texture only once for the same buffer, so
// we need to get the original frame at ConvertFrame(). |unwrap_frame_cb| is
// the callback used to get the original frame.
// |gpu_task_runner| is the task runner of the GPU main thread. We generate
// mailbox on it.
// |get_stub_cb| is the callback used to get the CommandBufferStub, which is
// used to create CommandBufferHelper.
// A self-cleaning SharedImage, with move-only semantics.
class ScopedSharedImage;
MailboxVideoFrameConverter(
UnwrapFrameCB unwrap_frame_cb,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetCommandBufferStubCB get_stub_cb);
GetGpuChannelCB get_gpu_channel_cb);
// Destructor runs on the GPU main thread.
~MailboxVideoFrameConverter() override;
void Destroy() override;
void DestroyOnGPUThread();
bool CreateCommandBufferHelper();
// Try to convert frames in |input_frame_queue_| and output the converted
// frames to client.
void TryOutputFrames();
// Generate mailbox for the DMA-buf VideoFrame. This method runs on the GPU
// main thread.
// |origin_frame| is unwrapped from |frame| passed from ConvertFrame().
// |frame| is passed only for keeping |origin_frame| alive.
void GenerateMailbox(VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame);
// Register the mapping between DMA-buf VideoFrame and the mailbox.
// |frame| is passed only for keeping |origin_frame| alive.
void RegisterMailbox(VideoFrame* origin_frame,
const gpu::Mailbox& mailbox,
scoped_refptr<VideoFrame> frame);
// Thunk for calling UnregisterMailbox() on |task_runner|.
// Because this thunk may be called in any thread, We cannot dereference
// WeakPtr. Therefore we wrap the WeakPtr by base::Optional to avoid task
// runner defererence the WeakPtr.
static void UnregisterMailboxThunk(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Optional<base::WeakPtr<MailboxVideoFrameConverter>> converter,
const int origin_frame_id);
// Remove the mapping between the frame whose unique id is |origin_frame_id|
// and the mailbox.
void UnregisterMailbox(const int origin_frame_id);
// TODO(crbug.com/998279): replace s/OnGPUThread/OnGPUTaskRunner/.
bool InitializeOnGPUThread();
// Wraps |mailbox| and |frame| into a new VideoFrame and sends it via
// |output_cb_|.
void WrapMailboxAndVideoFrameAndOutput(VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame,
const gpu::Mailbox& mailbox);
// ConvertFrame() delegates to this method to GenerateSharedImageOnGPUThread()
// or just UpdateSharedImageOnGPUThread(), then to jump back to
// WrapMailboxAndVideoFrameAndOutput().
void ConvertFrameOnGPUThread(VideoFrame* origin_frame,
scoped_refptr<VideoFrame> frame,
gpu::Mailbox mailbox);
// Generates a ScopedSharedImage from a DMA-buf backed |video_frame|, and
// returns it or nullptr if that could not be done. |video_frame| must be kept
// alive for the duration of this method. This method runs on
// |gpu_task_runner_|.
std::unique_ptr<ScopedSharedImage> GenerateSharedImageOnGPUThread(
VideoFrame* video_frame);
// Registers the mapping between a DMA-buf VideoFrame and the SharedImage.
// |origin_frame| must be kept alive for the duration of this method.
void RegisterSharedImage(
VideoFrame* origin_frame,
std::unique_ptr<ScopedSharedImage> scoped_shared_image);
// Unregisters the |origin_frame_id| and associated SharedImage.
void UnregisterSharedImage(int origin_frame_id);
// Updates the SharedImage associated to |mailbox|. Returns true if the update
// could be carried out, false otherwise.
bool UpdateSharedImageOnGPUThread(const gpu::Mailbox& mailbox);
// Waits on |sync_token|, keeping |frame| alive until it is signalled. It
// trampolines threads to |gpu_task_runner| if necessary.
void WaitOnSyncTokenAndReleaseFrameOnGPUThread(
scoped_refptr<VideoFrame> frame,
const gpu::SyncToken& sync_token);
// Invoked when any error occurs. |msg| is the error message.
void OnError(const std::string& msg);
......@@ -112,24 +125,29 @@ class MEDIA_GPU_EXPORT MailboxVideoFrameConverter : public VideoFrameConverter {
UnwrapFrameCB unwrap_frame_cb_;
const scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
GetCommandBufferStubCB get_stub_cb_;
// The interface to communicate with command buffer. We use this to create and
// destroy texture, wait for SyncToken, and generate mailbox.
scoped_refptr<CommandBufferHelper> command_buffer_helper_;
const GetGpuChannelCB get_gpu_channel_cb_;
// |gpu_channel_| will outlive CommandBufferStub, keep the former as a WeakPtr
// to guarantee proper resource cleanup. To be dereferenced on
// |gpu_task_runner_| only.
base::WeakPtr<gpu::GpuChannel> gpu_channel_;
// Mapping from the unique id of the frame to its corresponding mailbox.
// Mapping from the unique id of the frame to its corresponding SharedImage.
// Accessed only on |parent_task_runner_|.
std::map<int, gpu::Mailbox> mailbox_table_;
// TODO(crbug.com/998279): use base::small_map.
// TODO(crbug.com/998279): use VideoFrame::unique_id() return type.
std::map<int, std::unique_ptr<ScopedSharedImage>> shared_images_;
// The queue of input frames and the unique_id of their origin frame.
// Accessed only on |parent_task_runner_|.
// TODO(crbug.com/998279): remove this member entirely.
base::queue<std::pair<scoped_refptr<VideoFrame>, int>> input_frame_queue_;
// The weak pointer of this, bound to |parent_task_runner_|.
// Used at the VideoFrame destruction callback.
base::WeakPtr<MailboxVideoFrameConverter> parent_weak_this_;
// The weak pointer of this, bound to |gpu_task_runner_|.
// Used to generate mailbox on the GPU main thread.
// Used to generate SharedImages on the GPU main thread.
base::WeakPtr<MailboxVideoFrameConverter> gpu_weak_this_;
base::WeakPtrFactory<MailboxVideoFrameConverter> parent_weak_this_factory_{
this};
......
......@@ -235,10 +235,10 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
base::BindRepeating(&DmabufVideoFramePool::UnwrapFrame,
base::Unretained(frame_pool.get())),
gpu_task_runner_,
base::BindOnce(&GetCommandBufferStub, gpu_task_runner_,
media_gpu_channel_manager_,
command_buffer_id->channel_token,
command_buffer_id->route_id));
base::BindRepeating(&GetCommandBufferStub, gpu_task_runner_,
media_gpu_channel_manager_,
command_buffer_id->channel_token,
command_buffer_id->route_id));
video_decoder = ChromeosVideoDecoderFactory::Create(
task_runner, std::move(frame_pool), std::move(frame_converter));
#endif // BUILDFLAG(USE_V4L2_CODEC) || BUILDFLAG(USE_VAAPI)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment