summaryrefslogtreecommitdiff
path: root/chromium/media/gpu/linux/mailbox_video_frame_converter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/gpu/linux/mailbox_video_frame_converter.cc')
-rw-r--r--chromium/media/gpu/linux/mailbox_video_frame_converter.cc260
1 files changed, 260 insertions, 0 deletions
diff --git a/chromium/media/gpu/linux/mailbox_video_frame_converter.cc b/chromium/media/gpu/linux/mailbox_video_frame_converter.cc
new file mode 100644
index 00000000000..6b85bafc30b
--- /dev/null
+++ b/chromium/media/gpu/linux/mailbox_video_frame_converter.cc
@@ -0,0 +1,260 @@
+// 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/linux/mailbox_video_frame_converter.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/optional.h"
+#include "base/task/post_task.h"
+#include "media/gpu/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)
+
+namespace media {
+
+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(
+ FROM_HERE,
+ base::BindOnce(&DestroyTexture, std::move(gpu_task_runner),
+ std::move(command_buffer_helper), service_id));
+ return;
+ }
+
+ if (!command_buffer_helper->MakeContextCurrent()) {
+ VLOGF(1) << "Failed to make context current";
+ return;
+ }
+ command_buffer_helper->DestroyTexture(service_id);
+}
+
+// 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))));
+}
+
+} // namespace
+
+MailboxVideoFrameConverter::MailboxVideoFrameConverter(
+ UnwrapFrameCB unwrap_frame_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferStubCB get_stub_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)),
+ weak_this_factory_(this) {
+ weak_this_ = weak_this_factory_.GetWeakPtr();
+}
+
+MailboxVideoFrameConverter::~MailboxVideoFrameConverter() {
+ DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
+
+ weak_this_factory_.InvalidateWeakPtrs();
+}
+
+bool MailboxVideoFrameConverter::CreateCommandBufferHelper() {
+ DCHECK(gpu_task_runner_->BelongsToCurrentThread());
+ DCHECK(get_stub_cb_);
+ DVLOGF(4);
+
+ gpu::CommandBufferStub* stub = std::move(get_stub_cb_).Run();
+ if (!stub) {
+ VLOGF(1) << "Failed to obtain command buffer stub";
+ return false;
+ }
+
+ command_buffer_helper_ = CommandBufferHelper::Create(stub);
+ return command_buffer_helper_ != nullptr;
+}
+
+scoped_refptr<VideoFrame> MailboxVideoFrameConverter::ConvertFrame(
+ scoped_refptr<VideoFrame> frame) {
+ DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
+ DVLOGF(4);
+
+ if (!frame) {
+ DVLOGF(1) << "nullptr input.";
+ return nullptr;
+ }
+ if (!frame->HasDmaBufs()) {
+ DVLOGF(1) << "Only converting DMA-buf frames is supported.";
+ return nullptr;
+ }
+
+ VideoFrame* origin_frame = unwrap_frame_cb_.Run(*frame);
+ gpu::Mailbox mailbox;
+ auto it = mailbox_table_.find(origin_frame->unique_id());
+ if (it != mailbox_table_.end())
+ mailbox = it->second;
+
+ if (mailbox.IsZero()) {
+ base::WaitableEvent event;
+ // We wait until GenerateMailbox() finished, so base::Unretained(this) is
+ // safe.
+ gpu_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MailboxVideoFrameConverter::GenerateMailbox,
+ base::Unretained(this), base::Unretained(origin_frame),
+ base::Unretained(&mailbox), base::Unretained(&event)));
+ event.Wait();
+
+ if (mailbox.IsZero()) {
+ VLOGF(1) << "Failed to create mailbox.";
+ return nullptr;
+ }
+
+ RegisterMailbox(origin_frame, mailbox);
+ }
+
+ 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());
+ return mailbox_frame;
+}
+
+void MailboxVideoFrameConverter::GenerateMailbox(VideoFrame* origin_frame,
+ gpu::Mailbox* mailbox,
+ base::WaitableEvent* event) {
+ DCHECK(gpu_task_runner_->BelongsToCurrentThread());
+ DVLOGF(4);
+
+ // Signal the event when leaving the method.
+ base::ScopedClosureRunner signal_event(
+ base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
+
+ // CreateCommandBufferHelper() should be called on |gpu_task_runner_| so we
+ // call it here lazily instead of at constructor.
+ if (!command_buffer_helper_ && !CreateCommandBufferHelper()) {
+ VLOGF(1) << "Failed to create command buffer helper.";
+ return;
+ }
+
+ // Get NativePixmap.
+ scoped_refptr<gfx::NativePixmap> pixmap;
+ gfx::BufferFormat buffer_format =
+ VideoPixelFormatToGfxBufferFormat(origin_frame->format());
+#if defined(USE_OZONE)
+ gfx::GpuMemoryBufferHandle handle = CreateGpuMemoryBufferHandle(origin_frame);
+ 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) {
+ VLOGF(1) << "Cannot create NativePixmap.";
+ return;
+ }
+
+ // Create GLImage.
+ auto image = base::MakeRefCounted<gl::GLImageNativePixmap>(
+ origin_frame->coded_size(), buffer_format);
+ if (!image->Initialize(std::move(pixmap))) {
+ VLOGF(1) << "Failed to initialize GLImage.";
+ return;
+ }
+
+ // Create texture and bind image to texture.
+ if (!command_buffer_helper_->MakeContextCurrent()) {
+ VLOGF(1) << "Failed to make context current.";
+ return;
+ }
+ 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);
+ *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));
+ return;
+}
+
+void MailboxVideoFrameConverter::RegisterMailbox(VideoFrame* origin_frame,
+ const gpu::Mailbox& mailbox) {
+ DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!mailbox.IsZero());
+ DVLOGF(4);
+
+ auto ret =
+ mailbox_table_.insert(std::make_pair(origin_frame->unique_id(), mailbox));
+ DCHECK(ret.second);
+ origin_frame->AddDestructionObserver(base::BindOnce(
+ &MailboxVideoFrameConverter::UnregisterMailboxThunk, parent_task_runner_,
+ weak_this_, origin_frame->unique_id()));
+}
+
+// static
+void MailboxVideoFrameConverter::UnregisterMailboxThunk(
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ base::Optional<base::WeakPtr<MailboxVideoFrameConverter>> converter,
+ int origin_frame_id) {
+ DCHECK(converter);
+ DVLOGF(4);
+
+ // 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::UnregisterMailbox(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);
+}
+
+} // namespace media