Commit 83134b01 authored by Dan Sanders's avatar Dan Sanders Committed by Commit Bot

[media] Reland VdaVideoDecoder

This adds VdaVideoDecoder, an adapter that implements the media::VideoDecoder interface using a
media::VideoDecodeAccelerator. VdaVideoDecoder expects to run in the GPU process, with access
to a command buffer stub, as will be the case for decoders created by MojoVideoDecoder.

VdaVideoDecoder runs on the (mojo) IO thread as much as possible, but the VDA interface is
inherently tied to the GPU thread.

Compared to the original landing (commit e192695d), this change
casts kMaxPlanes to uint32_t before using it as a parameter to DCHECK_LE() in
PictureBufferManager::CreatePictureBuffers().

Bug: 522298
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: Ie3e1b346f9b88692bcb2be12434fb0b6b81ef025
Reviewed-on: https://chromium-review.googlesource.com/1012936
Commit-Queue: Dan Sanders <sandersd@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551125}
parent c53bedf6
...@@ -124,6 +124,7 @@ source_set("test_support") { ...@@ -124,6 +124,7 @@ source_set("test_support") {
"//media/base/android:test_support", "//media/base/android:test_support",
"//media/filters:test_support", "//media/filters:test_support",
"//media/formats:test_support", "//media/formats:test_support",
"//media/gpu:test_support",
"//media/video:test_support", "//media/video:test_support",
] ]
} }
......
...@@ -87,6 +87,8 @@ component("gpu") { ...@@ -87,6 +87,8 @@ component("gpu") {
defines = [ "MEDIA_GPU_IMPLEMENTATION" ] defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
sources = [ sources = [
"command_buffer_helper.cc",
"command_buffer_helper.h",
"fake_jpeg_decode_accelerator.cc", "fake_jpeg_decode_accelerator.cc",
"fake_jpeg_decode_accelerator.h", "fake_jpeg_decode_accelerator.h",
"fake_video_decode_accelerator.cc", "fake_video_decode_accelerator.cc",
...@@ -111,6 +113,7 @@ component("gpu") { ...@@ -111,6 +113,7 @@ component("gpu") {
"//base", "//base",
"//gpu", "//gpu",
"//media", "//media",
"//third_party/mesa:mesa_headers",
"//ui/gfx/geometry", "//ui/gfx/geometry",
] ]
deps = [ deps = [
...@@ -498,7 +501,6 @@ if (use_vaapi) { ...@@ -498,7 +501,6 @@ if (use_vaapi) {
"//base/test:test_support", "//base/test:test_support",
"//media:test_support", "//media:test_support",
"//media/gpu", "//media/gpu",
"//media/gpu/ipc/service",
"//testing/gtest", "//testing/gtest",
"//third_party:jpeg", "//third_party:jpeg",
"//third_party/libyuv", "//third_party/libyuv",
...@@ -529,7 +531,6 @@ if (use_v4l2_codec || use_vaapi) { ...@@ -529,7 +531,6 @@ if (use_v4l2_codec || use_vaapi) {
"//base", "//base",
"//media:test_support", "//media:test_support",
"//media/gpu", "//media/gpu",
"//media/gpu/ipc/service",
"//media/mojo/services", "//media/mojo/services",
"//testing/gtest", "//testing/gtest",
"//third_party/libyuv", "//third_party/libyuv",
...@@ -554,12 +555,30 @@ if (use_v4l2_codec || use_vaapi) { ...@@ -554,12 +555,30 @@ if (use_v4l2_codec || use_vaapi) {
} }
} }
static_library("test_support") {
visibility = [ "//media:test_support" ]
testonly = true
sources = [
"fake_command_buffer_helper.cc",
"fake_command_buffer_helper.h",
]
configs += [ "//media:media_config" ]
deps = [
":gpu",
]
public_deps = [
"//base",
"//media",
]
}
source_set("unit_tests") { source_set("unit_tests") {
testonly = true testonly = true
deps = [ deps = [
"//base", "//base",
"//media:test_support", "//media:test_support",
"//media/gpu", "//media/gpu",
"//media/gpu/ipc/service:unit_tests",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
] ]
......
// Copyright 2018 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/command_buffer_helper.h"
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/scheduling_priority.h"
#include "gpu/command_buffer/service/decoder_context.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "media/gpu/gles2_decoder_helper.h"
#include "ui/gl/gl_context.h"
namespace media {
namespace {
class CommandBufferHelperImpl
: public CommandBufferHelper,
public gpu::CommandBufferStub::DestructionObserver {
public:
explicit CommandBufferHelperImpl(gpu::CommandBufferStub* stub) : stub_(stub) {
DVLOG(1) << __func__;
DCHECK(stub_);
DCHECK(stub_->channel()->task_runner()->BelongsToCurrentThread());
stub_->AddDestructionObserver(this);
wait_sequence_id_ = stub_->channel()->scheduler()->CreateSequence(
gpu::SchedulingPriority::kNormal);
decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
}
bool MakeContextCurrent() override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return decoder_helper_ && decoder_helper_->MakeContextCurrent();
}
GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr));
scoped_refptr<gpu::gles2::TextureRef> texture_ref =
decoder_helper_->CreateTexture(target, internal_format, width, height,
format, type);
GLuint service_id = texture_ref->service_id();
texture_refs_[service_id] = std::move(texture_ref);
return service_id;
}
void DestroyTexture(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr));
DCHECK(texture_refs_.count(service_id));
texture_refs_.erase(service_id);
}
gpu::Mailbox CreateMailbox(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!decoder_helper_)
return gpu::Mailbox();
DCHECK(texture_refs_.count(service_id));
return decoder_helper_->CreateMailbox(texture_refs_[service_id].get());
}
void SetCleared(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!decoder_helper_)
return;
DCHECK(texture_refs_.count(service_id));
decoder_helper_->SetCleared(texture_refs_[service_id].get());
}
void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!stub_)
return;
// TODO(sandersd): Do we need to keep a ref to |this| while there are
// pending waits? If we destruct while they are pending, they will never
// run.
stub_->channel()->scheduler()->ScheduleTask(
gpu::Scheduler::Task(wait_sequence_id_, std::move(done_cb),
std::vector<gpu::SyncToken>({sync_token})));
}
private:
~CommandBufferHelperImpl() override {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!stub_)
return;
// Try to drop TextureRefs with the context current, so that the platform
// textures can be deleted.
//
// Note: Since we don't know what stack we are on, it might not be safe to
// change the context. In practice we can be reasonably sure that our last
// owner isn't doing work in a different context.
//
// TODO(sandersd): We should restore the previous context.
if (!texture_refs_.empty() && MakeContextCurrent())
texture_refs_.clear();
DestroyStub();
}
void OnWillDestroyStub() override {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// OnWillDestroyStub() is called with the context current if possible. Drop
// the TextureRefs now while the platform textures can still be deleted.
texture_refs_.clear();
DestroyStub();
}
void DestroyStub() {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
decoder_helper_ = nullptr;
// If the last reference to |this| is in a |done_cb|, destroying the wait
// sequence can delete |this|. Clearing |stub_| first prevents DestroyStub()
// being called twice.
gpu::CommandBufferStub* stub = stub_;
stub_ = nullptr;
stub->RemoveDestructionObserver(this);
stub->channel()->scheduler()->DestroySequence(wait_sequence_id_);
}
gpu::CommandBufferStub* stub_;
// Wait tasks are scheduled on our own sequence so that we can't inadvertently
// block the command buffer.
gpu::SequenceId wait_sequence_id_;
// TODO(sandersd): Merge GLES2DecoderHelper implementation into this class.
std::unique_ptr<GLES2DecoderHelper> decoder_helper_;
std::map<GLuint, scoped_refptr<gpu::gles2::TextureRef>> texture_refs_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(CommandBufferHelperImpl);
};
} // namespace
// static
scoped_refptr<CommandBufferHelper> CommandBufferHelper::Create(
gpu::CommandBufferStub* stub) {
return base::MakeRefCounted<CommandBufferHelperImpl>(stub);
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
#define MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "media/gpu/media_gpu_export.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media {
// TODO(sandersd): CommandBufferHelper does not inherently need to be ref
// counted, but some clients want that (VdaVideoDecoder and PictureBufferManager
// both hold a ref to the same CommandBufferHelper). Consider making an owned
// variant.
class MEDIA_GPU_EXPORT CommandBufferHelper
: public base::RefCountedThreadSafe<CommandBufferHelper> {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
// TODO(sandersd): Consider adding an Initialize(stub) method so that
// CommandBufferHelpers can be created before a stub is available.
static scoped_refptr<CommandBufferHelper> Create(
gpu::CommandBufferStub* stub);
virtual bool MakeContextCurrent() = 0;
// Creates a texture and returns its |service_id|.
//
// See glTexImage2D() for argument definitions.
//
// The texture will be configured as a video frame: linear filtering, clamp to
// edge, no mipmaps. If |target| is GL_TEXTURE_2D, storage will be allocated
// but not initialized.
//
// The context must be current.
//
// TODO(sandersd): Is really necessary to allocate storage? GpuVideoDecoder
// does this, but it's not clear that any clients require it.
virtual GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) = 0;
// Destroys a texture.
//
// The context must be current.
virtual void DestroyTexture(GLuint service_id) = 0;
// Creates a mailbox for a texture.
//
// TODO(sandersd): Specify the behavior when the stub has been destroyed. The
// current implementation returns an empty (zero) mailbox. One solution would
// be to add a HasStub() method, and not define behavior when it is false.
virtual gpu::Mailbox CreateMailbox(GLuint service_id) = 0;
// Marks layer 0 of the texture as cleared.
virtual void SetCleared(GLuint service_id) = 0;
// Waits for a SyncToken, then runs |done_cb|.
//
// |done_cb| may be destructed without running if the stub is destroyed.
//
// TODO(sandersd): Currently it is possible to lose the stub while
// PictureBufferManager is waiting for all picture buffers, which results in a
// decoding softlock. Notification of wait failure (or just context/stub lost)
// is probably necessary.
virtual void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) = 0;
protected:
CommandBufferHelper() = default;
// TODO(sandersd): Deleting remaining textures upon destruction requires
// making the context current, which may be undesireable. Consider adding an
// explicit DestroyWithContext() API.
virtual ~CommandBufferHelper() = default;
private:
friend class base::RefCountedThreadSafe<CommandBufferHelper>;
DISALLOW_COPY_AND_ASSIGN(CommandBufferHelper);
};
} // namespace media
#endif // MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
// Copyright 2018 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/fake_command_buffer_helper.h"
namespace media {
FakeCommandBufferHelper::FakeCommandBufferHelper(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
FakeCommandBufferHelper::~FakeCommandBufferHelper() = default;
void FakeCommandBufferHelper::StubLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
has_stub_ = false;
is_context_lost_ = true;
is_context_current_ = false;
service_ids_.clear();
waits_.clear();
}
void FakeCommandBufferHelper::ContextLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_lost_ = true;
is_context_current_ = false;
}
void FakeCommandBufferHelper::CurrentContextLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_current_ = false;
}
bool FakeCommandBufferHelper::HasTexture(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
return service_ids_.count(service_id);
}
void FakeCommandBufferHelper::ReleaseSyncToken(gpu::SyncToken sync_token) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(waits_.count(sync_token));
task_runner_->PostTask(FROM_HERE, std::move(waits_[sync_token]));
waits_.erase(sync_token);
}
bool FakeCommandBufferHelper::MakeContextCurrent() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_current_ = !is_context_lost_;
return is_context_current_;
}
GLuint FakeCommandBufferHelper::CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(is_context_current_);
GLuint service_id = next_service_id_++;
service_ids_.insert(service_id);
return service_id;
}
void FakeCommandBufferHelper::DestroyTexture(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(is_context_current_);
DCHECK(service_ids_.count(service_id));
service_ids_.erase(service_id);
}
gpu::Mailbox FakeCommandBufferHelper::CreateMailbox(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(service_ids_.count(service_id));
return gpu::Mailbox::Generate();
}
void FakeCommandBufferHelper::SetCleared(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(service_ids_.count(service_id));
}
void FakeCommandBufferHelper::WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!waits_.count(sync_token));
waits_.emplace(sync_token, std::move(done_cb));
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
#define MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
#include <map>
#include <set>
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "media/gpu/command_buffer_helper.h"
namespace media {
class FakeCommandBufferHelper : public CommandBufferHelper {
public:
explicit FakeCommandBufferHelper(
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
// Signal stub destruction. All textures will be deleted.
void StubLost();
// Signal context loss. MakeContextCurrent() fails after this.
void ContextLost();
// Signal that the context is no longer current.
void CurrentContextLost();
// Complete a pending SyncToken wait.
void ReleaseSyncToken(gpu::SyncToken sync_token);
// Test whether a texture exists (has not been destroyed).
bool HasTexture(GLuint service_id);
// CommandBufferHelper implementation.
bool MakeContextCurrent() override;
GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) override;
void DestroyTexture(GLuint service_id) override;
gpu::Mailbox CreateMailbox(GLuint service_id) override;
void SetCleared(GLuint service_id) override;
void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) override;
private:
~FakeCommandBufferHelper() override;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool has_stub_ = true;
bool is_context_lost_ = false;
bool is_context_current_ = false;
GLuint next_service_id_ = 1;
std::set<GLuint> service_ids_;
std::map<gpu::SyncToken, base::OnceClosure> waits_;
DISALLOW_COPY_AND_ASSIGN(FakeCommandBufferHelper);
};
} // namespace media
#endif // MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/mailbox.h"
...@@ -20,7 +21,15 @@ namespace media { ...@@ -20,7 +21,15 @@ namespace media {
class GLES2DecoderHelperImpl : public GLES2DecoderHelper { class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
public: public:
explicit GLES2DecoderHelperImpl(gpu::DecoderContext* decoder) explicit GLES2DecoderHelperImpl(gpu::DecoderContext* decoder)
: decoder_(decoder) {} : decoder_(decoder) {
DCHECK(decoder_);
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
texture_manager_ = group->texture_manager();
mailbox_manager_ = group->mailbox_manager();
// TODO(sandersd): Support GLES2DecoderPassthroughImpl.
DCHECK(texture_manager_);
DCHECK(mailbox_manager_);
}
bool MakeContextCurrent() override { bool MakeContextCurrent() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
...@@ -35,10 +44,6 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper { ...@@ -35,10 +44,6 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
GLenum type) override { GLenum type) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(decoder_->GetGLContext()->IsCurrent(nullptr)); DCHECK(decoder_->GetGLContext()->IsCurrent(nullptr));
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
gpu::gles2::TextureManager* texture_manager = group->texture_manager();
// TODO(sandersd): Support GLES2DecoderPassthroughImpl.
DCHECK(texture_manager);
// We can't use texture_manager->CreateTexture(), since it requires a unique // We can't use texture_manager->CreateTexture(), since it requires a unique
// |client_id|. Instead we create the texture directly, and create our own // |client_id|. Instead we create the texture directly, and create our own
...@@ -48,38 +53,39 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper { ...@@ -48,38 +53,39 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
glBindTexture(target, texture_id); glBindTexture(target, texture_id);
scoped_refptr<gpu::gles2::TextureRef> texture_ref = scoped_refptr<gpu::gles2::TextureRef> texture_ref =
gpu::gles2::TextureRef::Create(texture_manager, 0, texture_id); gpu::gles2::TextureRef::Create(texture_manager_, 0, texture_id);
texture_manager->SetTarget(texture_ref.get(), target); texture_manager_->SetTarget(texture_ref.get(), target);
texture_manager->SetLevelInfo(texture_ref.get(), // ref texture_manager_->SetLevelInfo(texture_ref.get(), // ref
target, // target target, // target
0, // level 0, // level
internal_format, // internal_format internal_format, // internal_format
width, // width width, // width
height, // height height, // height
1, // depth 1, // depth
0, // border 0, // border
format, // format format, // format
type, // type type, // type
gfx::Rect()); // cleared_rect gfx::Rect()); // cleared_rect
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAG_FILTER, texture_ref.get(), GL_TEXTURE_MAG_FILTER,
GL_LINEAR); GL_LINEAR);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MIN_FILTER, texture_ref.get(), GL_TEXTURE_MIN_FILTER,
GL_LINEAR); GL_LINEAR);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_S, texture_ref.get(), GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_T, texture_ref.get(), GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_BASE_LEVEL, 0); texture_ref.get(), GL_TEXTURE_BASE_LEVEL,
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(), 0);
texture_ref.get(), GL_TEXTURE_MAX_LEVEL, 0); texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAX_LEVEL, 0);
// TODO(sandersd): Do we always want to allocate for GL_TEXTURE_2D? // TODO(sandersd): Do we always want to allocate for GL_TEXTURE_2D?
if (target == GL_TEXTURE_2D) { if (target == GL_TEXTURE_2D) {
...@@ -98,17 +104,23 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper { ...@@ -98,17 +104,23 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
return texture_ref; return texture_ref;
} }
void SetCleared(gpu::gles2::TextureRef* texture_ref) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
texture_manager_->SetLevelCleared(
texture_ref, texture_ref->texture()->target(), 0, true);
}
gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) override { gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
gpu::MailboxManager* mailbox_manager = group->mailbox_manager();
gpu::Mailbox mailbox = gpu::Mailbox::Generate(); gpu::Mailbox mailbox = gpu::Mailbox::Generate();
mailbox_manager->ProduceTexture(mailbox, texture_ref->texture()); mailbox_manager_->ProduceTexture(mailbox, texture_ref->texture());
return mailbox; return mailbox;
} }
private: private:
gpu::DecoderContext* decoder_; gpu::DecoderContext* decoder_;
gpu::gles2::TextureManager* texture_manager_;
gpu::MailboxManager* mailbox_manager_;
THREAD_CHECKER(thread_checker_); THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(GLES2DecoderHelperImpl); DISALLOW_COPY_AND_ASSIGN(GLES2DecoderHelperImpl);
...@@ -117,8 +129,6 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper { ...@@ -117,8 +129,6 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
// static // static
std::unique_ptr<GLES2DecoderHelper> GLES2DecoderHelper::Create( std::unique_ptr<GLES2DecoderHelper> GLES2DecoderHelper::Create(
gpu::DecoderContext* decoder) { gpu::DecoderContext* decoder) {
if (!decoder)
return nullptr;
return std::make_unique<GLES2DecoderHelperImpl>(decoder); return std::make_unique<GLES2DecoderHelperImpl>(decoder);
} }
......
...@@ -50,6 +50,9 @@ class MEDIA_GPU_EXPORT GLES2DecoderHelper { ...@@ -50,6 +50,9 @@ class MEDIA_GPU_EXPORT GLES2DecoderHelper {
GLenum format, GLenum format,
GLenum type) = 0; GLenum type) = 0;
// Sets the cleared flag on level 0 of the texture.
virtual void SetCleared(gpu::gles2::TextureRef* texture_ref) = 0;
// Creates a mailbox for a texture. // Creates a mailbox for a texture.
virtual gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) = 0; virtual gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) = 0;
}; };
......
...@@ -22,6 +22,10 @@ target(link_target_type, "service") { ...@@ -22,6 +22,10 @@ target(link_target_type, "service") {
"media_gpu_channel.h", "media_gpu_channel.h",
"media_gpu_channel_manager.cc", "media_gpu_channel_manager.cc",
"media_gpu_channel_manager.h", "media_gpu_channel_manager.h",
"picture_buffer_manager.cc",
"picture_buffer_manager.h",
"vda_video_decoder.cc",
"vda_video_decoder.h",
] ]
include_dirs = [ "//third_party/mesa/src/include" ] include_dirs = [ "//third_party/mesa/src/include" ]
...@@ -37,6 +41,7 @@ target(link_target_type, "service") { ...@@ -37,6 +41,7 @@ target(link_target_type, "service") {
"//gpu/command_buffer/service:gles2", "//gpu/command_buffer/service:gles2",
"//gpu/ipc/service", "//gpu/ipc/service",
"//media:media_buildflags", "//media:media_buildflags",
"//media/gpu",
"//media/gpu:buildflags", "//media/gpu:buildflags",
"//media/gpu/ipc/common", "//media/gpu/ipc/common",
"//third_party/mesa:mesa_headers", "//third_party/mesa:mesa_headers",
...@@ -51,3 +56,19 @@ target(link_target_type, "service") { ...@@ -51,3 +56,19 @@ target(link_target_type, "service") {
deps += [ "//third_party/webrtc/common_video:common_video" ] deps += [ "//third_party/webrtc/common_video:common_video" ]
} }
} }
source_set("unit_tests") {
testonly = true
sources = [
"picture_buffer_manager_unittest.cc",
"vda_video_decoder_unittest.cc",
]
deps = [
":service",
"//base",
"//base/test:test_support",
"//media:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
This diff is collapsed.
// Copyright 2018 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.
#ifndef MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
#define MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
#include <stdint.h>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/video/picture.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class PictureBufferManager
: public base::RefCountedThreadSafe<PictureBufferManager> {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
using ReusePictureBufferCB = base::RepeatingCallback<void(int32_t)>;
// Creates a PictureBufferManager.
//
// |reuse_picture_buffer_cb|: Called when a picture is returned to the pool
// after its VideoFrame has been destructed.
static scoped_refptr<PictureBufferManager> Create(
ReusePictureBufferCB reuse_picture_buffer_cb);
// Provides access to a CommandBufferHelper. This must be done before calling
// CreatePictureBuffers().
//
// TODO(sandersd): It would be convenient to set this up at creation time.
// Consider changes to CommandBufferHelper that would enable that.
virtual void Initialize(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
scoped_refptr<CommandBufferHelper> command_buffer_helper) = 0;
// Predicts whether the VDA can output a picture without reusing one first.
//
// Implementations should be pessimistic; it is better to incorrectly skip
// preroll than to hang waiting for an output that can never come.
virtual bool CanReadWithoutStalling() = 0;
// Creates and returns a vector of picture buffers, or an empty vector on
// failure.
//
// |count|: Number of picture buffers to create.
// |pixel_format|: Describes the arrangement of image data in the picture's
// textures and is surfaced by VideoFrames.
// |planes|: Number of image planes (textures) in the picture.
// |texture_size|: Size of textures to create.
// |texture_target|: Type of textures to create.
//
// Must be called on the GPU thread.
//
// TODO(sandersd): For many subsampled pixel formats, it doesn't make sense to
// allocate all planes with the same size.
// TODO(sandersd): Surface control over allocation for GL_TEXTURE_2D. Right
// now such textures are allocated as RGBA textures. (Other texture targets
// are not automatically allocated.)
// TODO(sandersd): The current implementation makes the context current.
// Consider requiring that the context is already current.
virtual std::vector<PictureBuffer> CreatePictureBuffers(
uint32_t count,
VideoPixelFormat pixel_format,
uint32_t planes,
gfx::Size texture_size,
uint32_t texture_target) = 0;
// Dismisses a picture buffer from the pool.
//
// A picture buffer may be dismissed even if it is bound to a VideoFrame; its
// backing textures will be maintained until the VideoFrame is destroyed.
//
// Must be called on the GPU thread.
virtual bool DismissPictureBuffer(int32_t picture_buffer_id) = 0;
// Creates and returns a VideoFrame bound to a picture buffer, or nullptr on
// failure.
//
// |picture|: Identifies the picture buffer and provides some metadata about
// the desired binding. Not all Picture features are supported.
// |timestamp|: Presentation timestamp of the VideoFrame.
// |visible_rect|: Visible region of the VideoFrame.
// |natural_size|: Natural size of the VideoFrame.
//
// TODO(sandersd): Specify which Picture features are supported.
virtual scoped_refptr<VideoFrame> CreateVideoFrame(
Picture picture,
base::TimeDelta timestamp,
gfx::Rect visible_rect,
gfx::Size natural_size) = 0;
protected:
PictureBufferManager() = default;
// Must be called on the GPU thread if Initialize() was called.
virtual ~PictureBufferManager() = default;
private:
friend class base::RefCountedThreadSafe<PictureBufferManager>;
DISALLOW_COPY_AND_ASSIGN(PictureBufferManager);
};
} // namespace media
#endif // MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
// Copyright 2018 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 <stdint.h>
#include "media/gpu/ipc/service/picture_buffer_manager.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "media/gpu/fake_command_buffer_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
// TODO(sandersd): Should be part of //media, as it is used by
// MojoVideoDecoderService (production code) as well.
class StaticSyncTokenClient : public VideoFrame::SyncTokenClient {
public:
explicit StaticSyncTokenClient(const gpu::SyncToken& sync_token)
: sync_token_(sync_token) {}
void GenerateSyncToken(gpu::SyncToken* sync_token) final {
*sync_token = sync_token_;
}
void WaitSyncToken(const gpu::SyncToken& sync_token) final {}
private:
gpu::SyncToken sync_token_;
DISALLOW_COPY_AND_ASSIGN(StaticSyncTokenClient);
};
} // namespace
class PictureBufferManagerImplTest : public testing::Test {
public:
explicit PictureBufferManagerImplTest() {
// TODO(sandersd): Use a separate thread for the GPU task runner.
cbh_ = base::MakeRefCounted<FakeCommandBufferHelper>(
environment_.GetMainThreadTaskRunner());
pbm_ = PictureBufferManager::Create(reuse_cb_.Get());
}
~PictureBufferManagerImplTest() override {}
protected:
void Initialize() {
pbm_->Initialize(environment_.GetMainThreadTaskRunner(), cbh_);
}
std::vector<PictureBuffer> CreateARGBPictureBuffers(uint32_t count) {
return pbm_->CreatePictureBuffers(count, PIXEL_FORMAT_ARGB, 1,
gfx::Size(320, 240), GL_TEXTURE_2D);
}
PictureBuffer CreateARGBPictureBuffer() {
std::vector<PictureBuffer> picture_buffers = CreateARGBPictureBuffers(1);
DCHECK_EQ(picture_buffers.size(), 1U);
return picture_buffers[0];
}
scoped_refptr<VideoFrame> CreateVideoFrame(int32_t picture_buffer_id) {
return pbm_->CreateVideoFrame(
Picture(picture_buffer_id, // picture_buffer_id
0, // bitstream_buffer_id
gfx::Rect(), // visible_rect (ignored)
gfx::ColorSpace::CreateSRGB(), // color_space
false), // allow_overlay
base::TimeDelta(), // timestamp
gfx::Rect(), // visible_rect
gfx::Size()); // natural_size
}
gpu::SyncToken GenerateSyncToken(scoped_refptr<VideoFrame> video_frame) {
gpu::SyncToken sync_token(gpu::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(1),
next_release_count_++);
StaticSyncTokenClient sync_token_client(sync_token);
video_frame->UpdateReleaseSyncToken(&sync_token_client);
return sync_token;
}
base::test::ScopedTaskEnvironment environment_;
uint64_t next_release_count_ = 1;
testing::StrictMock<
base::MockCallback<PictureBufferManager::ReusePictureBufferCB>>
reuse_cb_;
scoped_refptr<FakeCommandBufferHelper> cbh_;
scoped_refptr<PictureBufferManager> pbm_;
DISALLOW_COPY_AND_ASSIGN(PictureBufferManagerImplTest);
};
TEST_F(PictureBufferManagerImplTest, CreateAndDestroy) {}
TEST_F(PictureBufferManagerImplTest, Initialize) {
Initialize();
}
TEST_F(PictureBufferManagerImplTest, CreatePictureBuffer) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, CreatePictureBuffer_ContextLost) {
Initialize();
cbh_->ContextLost();
std::vector<PictureBuffer> pbs = CreateARGBPictureBuffers(1);
EXPECT_TRUE(pbs.empty());
}
TEST_F(PictureBufferManagerImplTest, ReusePictureBuffer) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
// Dropping the frame does not immediately trigger reuse.
gpu::SyncToken sync_token = GenerateSyncToken(frame);
frame = nullptr;
environment_.RunUntilIdle();
// Completing the SyncToken wait does.
EXPECT_CALL(reuse_cb_, Run(pb.id()));
cbh_->ReleaseSyncToken(sync_token);
environment_.RunUntilIdle();
}
TEST_F(PictureBufferManagerImplTest, DismissPictureBuffer_Available) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
pbm_->DismissPictureBuffer(pb.id());
// Allocated textures should be deleted soon.
environment_.RunUntilIdle();
EXPECT_FALSE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, DismissPictureBuffer_Output) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
pbm_->DismissPictureBuffer(pb.id());
// Allocated textures should not be deleted while the VideoFrame exists.
environment_.RunUntilIdle();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
// Or after it has been returned.
gpu::SyncToken sync_token = GenerateSyncToken(frame);
frame = nullptr;
environment_.RunUntilIdle();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
// Until the SyncToken has been waited for. (Reuse callback should not be
// called for a dismissed picture buffer.)
cbh_->ReleaseSyncToken(sync_token);
environment_.RunUntilIdle();
EXPECT_FALSE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, CanReadWithoutStalling) {
// Works before Initialize().
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True before any picture buffers are allocated.
Initialize();
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True when a picture buffer is available.
PictureBuffer pb = CreateARGBPictureBuffer();
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// False when all picture buffers are used.
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
EXPECT_FALSE(pbm_->CanReadWithoutStalling());
// True once a picture buffer is returned.
frame = nullptr;
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True after all picture buffers have been dismissed.
pbm_->DismissPictureBuffer(pb.id());
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
}
} // namespace media
This diff is collapsed.
// Copyright 2018 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.
#ifndef MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
#define MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
#include <stdint.h>
#include <map>
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/mru_cache.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/base/video_decoder.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/ipc/service/picture_buffer_manager.h"
#include "media/video/video_decode_accelerator.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media {
// Implements the VideoDecoder interface backed by a VideoDecodeAccelerator.
// This class expects to run in the GPU process via MojoVideoDecoder.
class VdaVideoDecoder : public VideoDecoder,
public VideoDecodeAccelerator::Client {
public:
using GetStubCB = base::RepeatingCallback<gpu::CommandBufferStub*()>;
using AllocateShmCB =
base::RepeatingCallback<std::unique_ptr<base::SharedMemory>(size_t)>;
using CreatePictureBufferManagerCB =
base::OnceCallback<scoped_refptr<PictureBufferManager>(
PictureBufferManager::ReusePictureBufferCB)>;
using CreateCommandBufferHelperCB =
base::OnceCallback<scoped_refptr<CommandBufferHelper>()>;
using CreateVdaCB =
base::OnceCallback<std::unique_ptr<VideoDecodeAccelerator>(
scoped_refptr<CommandBufferHelper>)>;
using GetVdaCapabilitiesCB =
base::RepeatingCallback<VideoDecodeAccelerator::Capabilities()>;
// Creates a VdaVideoDecoder. The returned unique_ptr can be safely upcast to
// unique_ptr<VideoDecoder>.
//
// |get_stub_cb|: Callback to retrieve the CommandBufferStub that should be
// used for allocating textures and mailboxes. This callback will be
// called on the GPU thread.
//
// See VdaVideoDecoder() for other arguments.
static std::unique_ptr<VdaVideoDecoder, std::default_delete<VideoDecoder>>
Create(scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetStubCB get_stub_cb);
// |parent_task_runner|: Task runner that |this| should operate on. All
// methods must be called on |parent_task_runner| (should be the Mojo
// MediaService task runner).
// |gpu_task_runner|: Task runner that |get_stub_cb| and GPU command buffer
// methods must be called on (should be the GPU main thread).
// |create_picture_buffer_manager_cb|: PictureBufferManager factory.
// |create_command_buffer_helper_cb|: CommandBufferHelper factory.
// |create_vda_cb|: VideoDecodeAccelerator factory.
// |get_vda_capabilities_cb|: VideDecodeAccelerator::Capabilities provider.
VdaVideoDecoder(
scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
CreatePictureBufferManagerCB create_picture_buffer_manager_cb,
CreateCommandBufferHelperCB create_command_buffer_helper_cb,
CreateVdaCB create_vda_cb,
GetVdaCapabilitiesCB get_vda_capabilities_cb);
// media::VideoDecoder implementation.
std::string GetDisplayName() const override;
void Initialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) override;
void Reset(const base::RepeatingClosure& reset_cb) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
private:
void Destroy() override;
protected:
// Owners should call Destroy(). This is automatic via
// std::default_delete<media::VideoDecoder> when held by a
// std::unique_ptr<media::VideoDecoder>.
~VdaVideoDecoder() override;
private:
// media::VideoDecodeAccelerator::Client implementation.
void NotifyInitializationComplete(bool success) override;
void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
VideoPixelFormat format,
uint32_t textures_per_buffer,
const gfx::Size& dimensions,
uint32_t texture_target) override;
void DismissPictureBuffer(int32_t picture_buffer_id) override;
void PictureReady(const Picture& picture) override;
void NotifyEndOfBitstreamBuffer(int32_t bitstream_buffer_id) override;
void NotifyFlushDone() override;
void NotifyResetDone() override;
void NotifyError(VideoDecodeAccelerator::Error error) override;
// Tasks and thread hopping.
void DestroyOnGpuThread();
void InitializeOnGpuThread();
void InitializeDone(bool status);
void DecodeOnGpuThread(BitstreamBuffer bitstream_buffer);
void PictureReadyOnParentThread(Picture picture);
void NotifyEndOfBitstreamBufferOnParentThread(int32_t bitstream_buffer_id);
void NotifyFlushDoneOnParentThread();
void NotifyResetDoneOnParentThread();
void ProvidePictureBuffersAsync(uint32_t count,
VideoPixelFormat pixel_format,
uint32_t planes,
gfx::Size texture_size,
GLenum texture_target);
void ReusePictureBuffer(int32_t picture_buffer_id);
// Error handling.
void EnterErrorState();
void DestroyCallbacks();
//
// Constant after construction, safe to read on any thread.
//
scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
scoped_refptr<CommandBufferHelper> command_buffer_helper_;
scoped_refptr<PictureBufferManager> picture_buffer_manager_;
CreateCommandBufferHelperCB create_command_buffer_helper_cb_;
CreateVdaCB create_vda_cb_;
GetVdaCapabilitiesCB get_vda_capabilities_cb_;
//
// Parent thread state.
//
bool has_error_ = false;
InitCB init_cb_;
OutputCB output_cb_;
DecodeCB flush_cb_;
base::RepeatingClosure reset_cb_;
int32_t bitstream_buffer_id_ = 0;
std::map<int32_t, DecodeCB> decode_cbs_;
// Records timestamps so that they can be mapped to output pictures. Must be
// large enough to account for any amount of frame reordering.
base::MRUCache<int32_t, base::TimeDelta> timestamps_;
//
// GPU thread state.
//
std::unique_ptr<VideoDecodeAccelerator> vda_;
//
// Shared state.
//
VideoDecoderConfig config_;
//
// Weak pointers, prefixed by bound thread.
//
// |gpu_weak_vda_| is invalidated when the VDA has notified about an error, or
// has been destroyed. It is not valid to call VDA methods in those cases.
base::WeakPtr<VideoDecodeAccelerator> gpu_weak_vda_;
std::unique_ptr<base::WeakPtrFactory<VideoDecodeAccelerator>>
gpu_weak_vda_factory_;
// |gpu_weak_this_| is never explicitly invalidated.
// |parent_weak_this_| is invalidated when the client calls Destroy(), and
// indicates that we should not make any new client callbacks.
base::WeakPtr<VdaVideoDecoder> gpu_weak_this_;
base::WeakPtr<VdaVideoDecoder> parent_weak_this_;
base::WeakPtrFactory<VdaVideoDecoder> gpu_weak_this_factory_;
base::WeakPtrFactory<VdaVideoDecoder> parent_weak_this_factory_;
DISALLOW_COPY_AND_ASSIGN(VdaVideoDecoder);
};
} // namespace media
#endif // MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
This diff is collapsed.
...@@ -198,7 +198,7 @@ void MojoVideoDecoderService::Reset(ResetCallback callback) { ...@@ -198,7 +198,7 @@ void MojoVideoDecoderService::Reset(ResetCallback callback) {
return; return;
} }
// Flush the reader so that pending decodes will be dispatches first. // Flush the reader so that pending decodes will be dispatched first.
mojo_decoder_buffer_reader_->Flush( mojo_decoder_buffer_reader_->Flush(
base::Bind(&MojoVideoDecoderService::OnReaderFlushed, weak_this_, base::Bind(&MojoVideoDecoderService::OnReaderFlushed, weak_this_,
base::Passed(&callback))); base::Passed(&callback)));
...@@ -254,6 +254,11 @@ void MojoVideoDecoderService::OnDecoderOutput( ...@@ -254,6 +254,11 @@ void MojoVideoDecoderService::OnDecoderOutput(
DCHECK(client_); DCHECK(client_);
DCHECK(decoder_); DCHECK(decoder_);
// All MojoVideoDecoder-based decoders are hardware decoders. If you're the
// first to implement an out-of-process decoder that is not power efficent,
// you can remove this DCHECK.
DCHECK(frame->metadata()->IsTrue(VideoFrameMetadata::POWER_EFFICIENT));
base::Optional<base::UnguessableToken> release_token; base::Optional<base::UnguessableToken> release_token;
if (frame->HasReleaseMailboxCB() && video_frame_handle_releaser_) { if (frame->HasReleaseMailboxCB() && video_frame_handle_releaser_) {
// |video_frame_handle_releaser_| is explicitly constructed with a // |video_frame_handle_releaser_| is explicitly constructed with a
......
...@@ -116,10 +116,12 @@ class MockVideoDecoder : public VideoDecoder { ...@@ -116,10 +116,12 @@ class MockVideoDecoder : public VideoDecoder {
if (!buffer->end_of_stream()) { if (!buffer->end_of_stream()) {
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes]; gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0].mailbox.name[0] = 1; mailbox_holders[0].mailbox.name[0] = 1;
output_cb_.Run(VideoFrame::WrapNativeTextures( scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
PIXEL_FORMAT_ARGB, mailbox_holders, GetReleaseMailboxCB(), PIXEL_FORMAT_ARGB, mailbox_holders, GetReleaseMailboxCB(),
config_.coded_size(), config_.visible_rect(), config_.natural_size(), config_.coded_size(), config_.visible_rect(), config_.natural_size(),
buffer->timestamp())); buffer->timestamp());
frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
output_cb_.Run(frame);
} }
// |decode_cb| must not be called from the same stack. // |decode_cb| must not be called from the same stack.
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
......
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