Commit 9aa45129 authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

[Fuchsia] Update FuchsiaVideoDecoder to use sysmem for output buffers.

Now FuchsiaVideoDecoder uses sysmem to allocate output buffers instead
of creating raw VMOs. The sysmem collection is shared with the GPU
where it's passed to Vulkan. This means that decoded frames are now
passed from mediacodec to Vulkan directly, without being touched by
the renderer or the GPU process.

Bug: 981026
Change-Id: I7dc8b48ceb0621966034e554897efd542af63a6e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1752918Reviewed-by: default avatarenne <enne@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#689065}
parent dc068eae
...@@ -40,6 +40,9 @@ specific_include_rules = { ...@@ -40,6 +40,9 @@ specific_include_rules = {
"cras_unified_unittest.cc": [ "cras_unified_unittest.cc": [
"+chromeos/dbus" "+chromeos/dbus"
], ],
"fuchsia_video_decoder_unittest.cc": [
"+components/viz/test/test_context_support.h",
],
"gpu_memory_buffer_video_frame_pool_unittest.cc": [ "gpu_memory_buffer_video_frame_pool_unittest.cc": [
"+components/viz/test/test_context_provider.h", "+components/viz/test/test_context_provider.h",
], ],
......
...@@ -209,8 +209,14 @@ jumbo_source_set("filters") { ...@@ -209,8 +209,14 @@ jumbo_source_set("filters") {
"fuchsia/fuchsia_video_decoder.h", "fuchsia/fuchsia_video_decoder.h",
] ]
deps += [ deps += [
"//gpu/command_buffer/client",
"//gpu/command_buffer/common",
"//gpu/ipc/common",
"//third_party/fuchsia-sdk/sdk:media", "//third_party/fuchsia-sdk/sdk:media",
"//third_party/fuchsia-sdk/sdk:mediacodec", "//third_party/fuchsia-sdk/sdk:mediacodec",
"//third_party/fuchsia-sdk/sdk:sys_cpp",
"//third_party/fuchsia-sdk/sdk:sysmem",
"//ui/ozone",
] ]
} }
} }
...@@ -311,6 +317,11 @@ source_set("unit_tests") { ...@@ -311,6 +317,11 @@ source_set("unit_tests") {
if (is_fuchsia) { if (is_fuchsia) {
sources += [ "fuchsia/fuchsia_video_decoder_unittest.cc" ] sources += [ "fuchsia/fuchsia_video_decoder_unittest.cc" ]
deps += [
"//components/viz/test:test_support",
"//third_party/fuchsia-sdk/sdk:sys_cpp",
"//third_party/fuchsia-sdk/sdk:sysmem",
]
} }
# libvpx for running vpx test on chromecast doesn't support high bit depth. # libvpx for running vpx test on chromecast doesn't support high bit depth.
......
...@@ -6,13 +6,15 @@ ...@@ -6,13 +6,15 @@
#include <fuchsia/media/cpp/fidl.h> #include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/mediacodec/cpp/fidl.h> #include <fuchsia/mediacodec/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <zircon/rights.h> #include <zircon/rights.h>
#include "base/bind.h" #include "base/bind.h"
#include "base/bits.h" #include "base/bits.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/service_directory_client.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -20,6 +22,10 @@ ...@@ -20,6 +22,10 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/process/process_metrics.h" #include "base/process/process_metrics.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h"
#include "media/base/bind_to_current_loop.h" #include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h" #include "media/base/cdm_context.h"
#include "media/base/decryptor.h" #include "media/base/decryptor.h"
...@@ -28,15 +34,14 @@ ...@@ -28,15 +34,14 @@
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/base/video_util.h" #include "media/base/video_util.h"
#include "third_party/libyuv/include/libyuv/video_common.h" #include "third_party/libyuv/include/libyuv/video_common.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/client_native_pixmap_factory.h"
#include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
namespace media { namespace media {
namespace { namespace {
const zx_rights_t kReadOnlyVmoRights =
ZX_DEFAULT_VMO_RIGHTS &
~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY);
// Value passed to the codec as packet_count_for_client. It's number of output // Value passed to the codec as packet_count_for_client. It's number of output
// buffers that we expect to hold on to in the renderer. // buffers that we expect to hold on to in the renderer.
// //
...@@ -45,35 +50,6 @@ const zx_rights_t kReadOnlyVmoRights = ...@@ -45,35 +50,6 @@ const zx_rights_t kReadOnlyVmoRights =
// works properly when the client holds to more than that. // works properly when the client holds to more than that.
const uint32_t kMaxUsedOutputFrames = 8; const uint32_t kMaxUsedOutputFrames = 8;
zx::vmo CreateContiguousVmo(size_t size, const zx::handle& bti_handle) {
zx::vmo vmo;
zx_status_t status =
zx_vmo_create_contiguous(bti_handle.get(), size, /*alignment_log2=*/0,
vmo.reset_and_get_address());
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmo_create_contiguous";
return zx::vmo();
}
return vmo;
}
zx::vmo CreateVmo(size_t size) {
zx::vmo vmo;
zx_status_t status = zx::vmo::create(size, 0, &vmo);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmo_create";
return zx::vmo();
}
static const char kVmoName[] = "cr-codec-output";
status = vmo.set_property(ZX_PROP_NAME, kVmoName, strlen(kVmoName));
ZX_DCHECK(status == ZX_OK, status);
return vmo;
}
class PendingDecode { class PendingDecode {
public: public:
PendingDecode(scoped_refptr<DecoderBuffer> buffer, PendingDecode(scoped_refptr<DecoderBuffer> buffer,
...@@ -108,65 +84,6 @@ class PendingDecode { ...@@ -108,65 +84,6 @@ class PendingDecode {
DISALLOW_COPY_AND_ASSIGN(PendingDecode); DISALLOW_COPY_AND_ASSIGN(PendingDecode);
}; };
class CodecBuffer {
public:
CodecBuffer() = default;
bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) {
if (!constraints.has_per_packet_buffer_bytes_recommended()) {
return false;
}
size_ = constraints.per_packet_buffer_bytes_recommended();
if (constraints.has_is_physically_contiguous_required() &&
constraints.is_physically_contiguous_required()) {
if (!constraints.has_very_temp_kludge_bti_handle()) {
return false;
}
vmo_ =
CreateContiguousVmo(size_, constraints.very_temp_kludge_bti_handle());
} else {
vmo_ = CreateVmo(size_);
}
return vmo_.is_valid();
}
const zx::vmo& vmo() const { return vmo_; }
size_t size() const { return size_; }
bool ToFidlCodecBuffer(uint64_t buffer_lifetime_ordinal,
uint32_t buffer_index,
bool read_only,
fuchsia::media::StreamBuffer* buffer) {
zx::vmo vmo_dup;
zx_status_t status = vmo_.duplicate(
read_only ? kReadOnlyVmoRights : ZX_RIGHT_SAME_RIGHTS, &vmo_dup);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_handle_duplicate";
return false;
}
fuchsia::media::StreamBufferDataVmo buf_data;
buf_data.set_vmo_handle(std::move(vmo_dup));
buf_data.set_vmo_usable_start(0);
buf_data.set_vmo_usable_size(size_);
buffer->mutable_data()->set_vmo(std::move(buf_data));
buffer->set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
buffer->set_buffer_index(buffer_index);
return true;
}
private:
zx::vmo vmo_;
size_t size_ = 0;
DISALLOW_COPY_AND_ASSIGN(CodecBuffer);
};
class InputBuffer { class InputBuffer {
public: public:
InputBuffer() {} InputBuffer() {}
...@@ -268,63 +185,110 @@ class InputBuffer { ...@@ -268,63 +185,110 @@ class InputBuffer {
VideoDecoder::DecodeCB decode_cb_; VideoDecoder::DecodeCB decode_cb_;
}; };
// Output buffer used to pass decoded frames from the decoder. Ref-counted // Helper used to hold mailboxes for the output textures. OutputMailbox may
// to make it possible to share the buffers with VideoFrames, in case when a // outlive FuchsiaVideoDecoder if is referenced by a VideoFrame.
// frame outlives the decoder.UnsafeSharedMemoryRegion class OutputMailbox {
class OutputBuffer : public base::RefCountedThreadSafe<OutputBuffer> {
public: public:
OutputBuffer() = default; OutputMailbox(gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support,
std::unique_ptr<gfx::GpuMemoryBuffer> gmb)
: shared_image_interface_(shared_image_interface),
gpu_context_support_(gpu_context_support),
weak_factory_(this) {
uint32_t usage = gpu::SHARED_IMAGE_USAGE_RASTER |
gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION |
gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
mailbox_ = shared_image_interface_->CreateSharedImage(
gmb.get(), nullptr, gfx::ColorSpace(), usage);
}
~OutputMailbox() {
shared_image_interface_->DestroySharedImage(sync_token_, mailbox_);
}
bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) { const gpu::Mailbox& mailbox() { return mailbox_; }
if (!buffer_.Initialize(constraints)) {
return false;
}
zx_status_t status = zx::vmar::root_self()->map( // Create a new video frame that wraps the mailbox. |reuse_callback| will be
/*vmar_offset=*/0, buffer_.vmo(), 0, buffer_.size(), // called when the mailbox can be reused.
ZX_VM_REQUIRE_NON_RESIZABLE | ZX_VM_PERM_READ, &mapped_memory_); scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat pixel_format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp,
base::OnceClosure reuse_callback) {
DCHECK(!is_used_);
is_used_ = true;
reuse_callback_ = std::move(reuse_callback);
if (status != ZX_OK) { gpu::MailboxHolder mailboxes[VideoFrame::kMaxPlanes];
ZX_DLOG(ERROR, status) << "zx_vmar_map"; mailboxes[0].mailbox = mailbox_;
mapped_memory_ = 0; mailboxes[0].sync_token = shared_image_interface_->GenUnverifiedSyncToken();
return false;
}
return true; return VideoFrame::WrapNativeTextures(
pixel_format, mailboxes,
BindToCurrentLoop(base::BindOnce(&OutputMailbox::OnFrameDestroyed,
base::Unretained(this))),
coded_size, visible_rect, natural_size, timestamp);
} }
CodecBuffer& buffer() { return buffer_; } // Called by FuchsiaVideoDecoder when it no longer needs this mailbox.
void Release() {
const uint8_t* mapped_memory() { if (is_used_) {
DCHECK(mapped_memory_); // The mailbox is referenced by a VideoFrame. It will be deleted as soon
return reinterpret_cast<uint8_t*>(mapped_memory_); // as the frame is destroyed.
DCHECK(reuse_callback_);
reuse_callback_ = base::Closure();
} else {
delete this;
}
} }
private: private:
friend class RefCountedThreadSafe<OutputBuffer>; void OnFrameDestroyed(const gpu::SyncToken& sync_token) {
DCHECK(is_used_);
~OutputBuffer() { is_used_ = false;
if (mapped_memory_) { sync_token_ = sync_token;
zx_status_t status =
zx::vmar::root_self()->unmap(mapped_memory_, buffer_.size()); if (!reuse_callback_) {
if (status != ZX_OK) { // If the mailbox cannot be reused then we can just delete it.
ZX_LOG(FATAL, status) << "zx_vmar_unmap"; delete this;
} return;
} }
gpu_context_support_->SignalSyncToken(
sync_token_,
BindToCurrentLoop(base::BindOnce(&OutputMailbox::OnSyncTokenSignaled,
weak_factory_.GetWeakPtr())));
}
void OnSyncTokenSignaled() {
sync_token_.Clear();
std::move(reuse_callback_).Run();
} }
CodecBuffer buffer_; gpu::SharedImageInterface* const shared_image_interface_;
gpu::ContextSupport* const gpu_context_support_;
uintptr_t mapped_memory_ = 0; gpu::Mailbox mailbox_;
gpu::SyncToken sync_token_;
DISALLOW_COPY_AND_ASSIGN(OutputBuffer); // Set to true when the mailbox is referenced by a video frame.
bool is_used_ = false;
base::OnceClosure reuse_callback_;
base::WeakPtrFactory<OutputMailbox> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(OutputMailbox);
}; };
} // namespace } // namespace
class FuchsiaVideoDecoder : public VideoDecoder { class FuchsiaVideoDecoder : public VideoDecoder {
public: public:
explicit FuchsiaVideoDecoder(bool enable_sw_decoding); FuchsiaVideoDecoder(gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support,
bool enable_sw_decoding);
~FuchsiaVideoDecoder() override; ~FuchsiaVideoDecoder() override;
// VideoDecoder implementation. // VideoDecoder implementation.
...@@ -357,6 +321,7 @@ class FuchsiaVideoDecoder : public VideoDecoder { ...@@ -357,6 +321,7 @@ class FuchsiaVideoDecoder : public VideoDecoder {
void OnOutputEndOfStream(uint64_t stream_lifetime_ordinal, void OnOutputEndOfStream(uint64_t stream_lifetime_ordinal,
bool error_detected_before); bool error_detected_before);
// Called on errors to shutdown the decoder and notify the client.
void OnError(); void OnError();
// Called by OnInputConstraints() to initialize input buffers. // Called by OnInputConstraints() to initialize input buffers.
...@@ -373,15 +338,21 @@ class FuchsiaVideoDecoder : public VideoDecoder { ...@@ -373,15 +338,21 @@ class FuchsiaVideoDecoder : public VideoDecoder {
// Pumps |pending_decodes_| to the decoder. // Pumps |pending_decodes_| to the decoder.
void PumpInput(); void PumpInput();
// Called by OnInputConstraints() to initialize input buffers. // Called by OnOutputConstraints() to initialize input buffers.
bool InitializeOutputBuffers( void InitializeOutputBufferCollection(
fuchsia::media::StreamBufferConstraints constraints); fuchsia::media::StreamBufferConstraints constraints,
fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_codec,
fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_gpu);
// Called by OutputMailbox to signal that the mailbox and buffer can be
// reused.
void OnReuseMailbox(uint32_t buffer_index, uint32_t packet_index);
// Destruction callback for the output VideoFrame instances. // Releases BufferCollection currently used for output buffers if any.
void OnFrameDestroyed(scoped_refptr<OutputBuffer> buffer, void ReleaseOutputBuffers();
uint64_t buffer_lifetime_ordinal,
uint32_t packet_index);
gpu::SharedImageInterface* const shared_image_interface_;
gpu::ContextSupport* const gpu_context_support_;
const bool enable_sw_decoding_; const bool enable_sw_decoding_;
OutputCB output_cb_; OutputCB output_cb_;
...@@ -392,6 +363,7 @@ class FuchsiaVideoDecoder : public VideoDecoder { ...@@ -392,6 +363,7 @@ class FuchsiaVideoDecoder : public VideoDecoder {
fuchsia::media::StreamProcessorPtr codec_; fuchsia::media::StreamProcessorPtr codec_;
fuchsia::sysmem::AllocatorPtr sysmem_allocator_; fuchsia::sysmem::AllocatorPtr sysmem_allocator_;
std::unique_ptr<gfx::ClientNativePixmapFactory> client_native_pixmap_factory_;
uint64_t stream_lifetime_ordinal_ = 1; uint64_t stream_lifetime_ordinal_ = 1;
...@@ -402,14 +374,17 @@ class FuchsiaVideoDecoder : public VideoDecoder { ...@@ -402,14 +374,17 @@ class FuchsiaVideoDecoder : public VideoDecoder {
// Input buffers. // Input buffers.
std::list<PendingDecode> pending_decodes_; std::list<PendingDecode> pending_decodes_;
uint64_t input_buffer_lifetime_ordinal_ = 1; uint64_t input_buffer_lifetime_ordinal_ = 1;
fuchsia::sysmem::BufferCollectionPtr input_buffers_collection_; fuchsia::sysmem::BufferCollectionPtr input_buffer_collection_;
std::vector<InputBuffer> input_buffers_; std::vector<InputBuffer> input_buffers_;
int num_used_input_buffers_ = 0; int num_used_input_buffers_ = 0;
// Output buffers. // Output buffers.
fuchsia::media::VideoUncompressedFormat output_format_; fuchsia::media::VideoUncompressedFormat output_format_;
uint64_t output_buffer_lifetime_ordinal_ = 1; uint64_t output_buffer_lifetime_ordinal_ = 1;
std::vector<scoped_refptr<OutputBuffer>> output_buffers_; fuchsia::sysmem::BufferCollectionPtr output_buffer_collection_;
gfx::SysmemBufferCollectionId output_buffer_collection_id_;
std::vector<OutputMailbox*> output_mailboxes_;
int num_used_output_buffers_ = 0; int num_used_output_buffers_ = 0;
int max_used_output_buffers_ = 0; int max_used_output_buffers_ = 0;
...@@ -422,12 +397,22 @@ class FuchsiaVideoDecoder : public VideoDecoder { ...@@ -422,12 +397,22 @@ class FuchsiaVideoDecoder : public VideoDecoder {
DISALLOW_COPY_AND_ASSIGN(FuchsiaVideoDecoder); DISALLOW_COPY_AND_ASSIGN(FuchsiaVideoDecoder);
}; };
FuchsiaVideoDecoder::FuchsiaVideoDecoder(bool enable_sw_decoding) FuchsiaVideoDecoder::FuchsiaVideoDecoder(
: enable_sw_decoding_(enable_sw_decoding), weak_factory_(this) { gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support,
bool enable_sw_decoding)
: shared_image_interface_(shared_image_interface),
gpu_context_support_(gpu_context_support),
enable_sw_decoding_(enable_sw_decoding),
client_native_pixmap_factory_(ui::CreateClientNativePixmapFactoryOzone()),
weak_factory_(this) {
DCHECK(shared_image_interface_);
weak_this_ = weak_factory_.GetWeakPtr(); weak_this_ = weak_factory_.GetWeakPtr();
} }
FuchsiaVideoDecoder::~FuchsiaVideoDecoder() = default; FuchsiaVideoDecoder::~FuchsiaVideoDecoder() {
ReleaseOutputBuffers();
}
std::string FuchsiaVideoDecoder::GetDisplayName() const { std::string FuchsiaVideoDecoder::GetDisplayName() const {
return "FuchsiaVideoDecoder"; return "FuchsiaVideoDecoder";
...@@ -463,13 +448,13 @@ void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config, ...@@ -463,13 +448,13 @@ void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config,
output_cb_ = output_cb; output_cb_ = output_cb;
container_pixel_aspect_ratio_ = config.GetPixelAspectRatio(); container_pixel_aspect_ratio_ = config.GetPixelAspectRatio();
sysmem_allocator_ = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() sysmem_allocator_ = base::fuchsia::ComponentContextForCurrentProcess()
->ConnectToService<fuchsia::sysmem::Allocator>(); ->svc()
->Connect<fuchsia::sysmem::Allocator>();
sysmem_allocator_.set_error_handler([](zx_status_t status) { sysmem_allocator_.set_error_handler([](zx_status_t status) {
// Just log a warning. We will handle BufferCollection the failure when // Just log a warning. We will handle BufferCollection the failure when
// trying to create a new BufferCollection. // trying to create a new BufferCollection.
ZX_DLOG(WARNING, status) ZX_DLOG(WARNING, status) << "fuchsia.sysmem.Allocator disconnected.";
<< "The fuchsia.sysmem.Allocator channel was terminated.";
}); });
fuchsia::mediacodec::CreateDecoder_Params codec_params; fuchsia::mediacodec::CreateDecoder_Params codec_params;
...@@ -500,17 +485,15 @@ void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config, ...@@ -500,17 +485,15 @@ void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config,
codec_params.set_promise_separate_access_units_on_input(true); codec_params.set_promise_separate_access_units_on_input(true);
codec_params.set_require_hw(!enable_sw_decoding_); codec_params.set_require_hw(!enable_sw_decoding_);
auto codec_factory = auto codec_factory = base::fuchsia::ComponentContextForCurrentProcess()
base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->svc()
->ConnectToService<fuchsia::mediacodec::CodecFactory>(); ->Connect<fuchsia::mediacodec::CodecFactory>();
codec_factory->CreateDecoder(std::move(codec_params), codec_.NewRequest()); codec_factory->CreateDecoder(std::move(codec_params), codec_.NewRequest());
codec_.set_error_handler( codec_.set_error_handler([this](zx_status_t status) {
[this](zx_status_t status) { ZX_LOG(ERROR, status) << "fuchsia.mediacodec.Codec disconnected.";
ZX_LOG(ERROR, status) OnError();
<< "The fuchsia.mediacodec.Codec channel was terminated."; });
OnError();
});
codec_.events().OnStreamFailed = codec_.events().OnStreamFailed =
fit::bind_member(this, &FuchsiaVideoDecoder::OnStreamFailed); fit::bind_member(this, &FuchsiaVideoDecoder::OnStreamFailed);
...@@ -602,16 +585,16 @@ void FuchsiaVideoDecoder::OnInputConstraints( ...@@ -602,16 +585,16 @@ void FuchsiaVideoDecoder::OnInputConstraints(
if (!constraints.has_default_settings() || if (!constraints.has_default_settings() ||
!constraints.default_settings().has_packet_count_for_server() || !constraints.default_settings().has_packet_count_for_server() ||
!constraints.default_settings().has_packet_count_for_client()) { !constraints.default_settings().has_packet_count_for_client()) {
DLOG(ERROR) << "Received InitializeInputBufferCollection() with missing " DLOG(ERROR)
"required fields."; << "Received OnInputConstraints() with missing required fields.";
OnError(); OnError();
return; return;
} }
input_buffers_collection_.Unbind(); input_buffer_collection_.Unbind();
input_buffers_.clear(); input_buffers_.clear();
// Create a new sysmem buffer collection token for the output buffers. // Create a new sysmem buffer collection token for the input buffers.
fuchsia::sysmem::BufferCollectionTokenPtr collection_token; fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest()); sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest());
...@@ -622,20 +605,18 @@ void FuchsiaVideoDecoder::OnInputConstraints( ...@@ -622,20 +605,18 @@ void FuchsiaVideoDecoder::OnInputConstraints(
// Convert the token to a BufferCollection connection. // Convert the token to a BufferCollection connection.
sysmem_allocator_->BindSharedCollection( sysmem_allocator_->BindSharedCollection(
std::move(collection_token), input_buffers_collection_.NewRequest()); std::move(collection_token), input_buffer_collection_.NewRequest());
input_buffers_collection_.set_error_handler( input_buffer_collection_.set_error_handler([this](zx_status_t status) {
[this](zx_status_t status) { ZX_LOG(ERROR, status) << "fuchsia.sysmem.BufferCollection disconnected.";
ZX_LOG(ERROR, status) OnError();
<< "The fuchsia.sysmem.BufferCollection channel was terminated."; });
OnError();
});
// BufferCollection needs to be synchronized to ensure that all token // BufferCollection needs to be synchronized to ensure that all token
// duplicate requests have been processed and sysmem knows about all clients // duplicate requests have been processed and sysmem knows about all clients
// that will be using this buffer collection. // that will be using this buffer collection.
input_buffers_collection_->Sync([this, constraints = std::move(constraints), input_buffer_collection_->Sync([this, constraints = std::move(constraints),
collection_token_for_codec = std::move( collection_token_for_codec = std::move(
collection_token_for_codec)]() mutable { collection_token_for_codec)]() mutable {
InitializeInputBufferCollection(std::move(constraints), InitializeInputBufferCollection(std::move(constraints),
std::move(collection_token_for_codec)); std::move(collection_token_for_codec));
}); });
...@@ -676,8 +657,8 @@ void FuchsiaVideoDecoder::OnFreeInputPacket( ...@@ -676,8 +657,8 @@ void FuchsiaVideoDecoder::OnFreeInputPacket(
void FuchsiaVideoDecoder::OnOutputConstraints( void FuchsiaVideoDecoder::OnOutputConstraints(
fuchsia::media::StreamOutputConstraints output_constraints) { fuchsia::media::StreamOutputConstraints output_constraints) {
if (!output_constraints.has_stream_lifetime_ordinal()) { if (!output_constraints.has_stream_lifetime_ordinal()) {
DLOG(ERROR) << "Received OnOutputConstraints() with missing required " DLOG(ERROR)
"fields."; << "Received OnOutputConstraints() with missing required fields.";
OnError(); OnError();
return; return;
} }
...@@ -687,21 +668,72 @@ void FuchsiaVideoDecoder::OnOutputConstraints( ...@@ -687,21 +668,72 @@ void FuchsiaVideoDecoder::OnOutputConstraints(
return; return;
} }
if (output_constraints.has_buffer_constraints_action_required() && if (!output_constraints.has_buffer_constraints_action_required() ||
output_constraints.buffer_constraints_action_required()) { !output_constraints.buffer_constraints_action_required()) {
if (!output_constraints.has_buffer_constraints()) { return;
DLOG(ERROR) << "Received OnOutputConstraints() which requires buffer " }
"constraints action, but without buffer constraints.";
OnError(); if (!output_constraints.has_buffer_constraints()) {
return; DLOG(ERROR) << "Received OnOutputConstraints() which requires buffer "
} "constraints action, but without buffer constraints.";
if (!InitializeOutputBuffers( OnError();
std::move(*output_constraints.mutable_buffer_constraints()))) { return;
DLOG(ERROR) << "Failed to initialize output buffers."; }
OnError();
return; const fuchsia::media::StreamBufferConstraints& buffer_constraints =
} output_constraints.buffer_constraints();
if (!buffer_constraints.has_default_settings() ||
!buffer_constraints.has_packet_count_for_client_max() ||
!buffer_constraints.default_settings().has_packet_count_for_server() ||
!buffer_constraints.default_settings().has_packet_count_for_client()) {
DLOG(ERROR)
<< "Received OnOutputConstraints() with missing required fields.";
OnError();
return;
} }
ReleaseOutputBuffers();
// mediacodec API expects odd buffer lifetime ordinal, which is incremented by
// 2 for each buffer generation.
output_buffer_lifetime_ordinal_ += 2;
max_used_output_buffers_ = std::min(
kMaxUsedOutputFrames, buffer_constraints.packet_count_for_client_max());
// Create a new sysmem buffer collection token for the output buffers.
fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest());
// Create sysmem tokens for the gpu process and the codec.
fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_codec;
collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
collection_token_for_codec.NewRequest());
fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_gpu;
collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
collection_token_for_gpu.NewRequest());
// Convert the token to a BufferCollection connection.
sysmem_allocator_->BindSharedCollection(
std::move(collection_token), output_buffer_collection_.NewRequest());
output_buffer_collection_.set_error_handler([this](zx_status_t status) {
ZX_LOG(ERROR, status) << "fuchsia.sysmem.BufferCollection disconnected.";
OnError();
});
// BufferCollection needs to be synchronized before we can use it.
output_buffer_collection_->Sync(
[this,
buffer_constraints = std::move(
std::move(*output_constraints.mutable_buffer_constraints())),
collection_token_for_codec = std::move(collection_token_for_codec),
collection_token_for_gpu =
std::move(collection_token_for_gpu)]() mutable {
InitializeOutputBufferCollection(std::move(buffer_constraints),
std::move(collection_token_for_codec),
std::move(collection_token_for_gpu));
});
} }
void FuchsiaVideoDecoder::OnOutputFormat( void FuchsiaVideoDecoder::OnOutputFormat(
...@@ -718,7 +750,6 @@ void FuchsiaVideoDecoder::OnOutputFormat( ...@@ -718,7 +750,6 @@ void FuchsiaVideoDecoder::OnOutputFormat(
} }
auto* format = output_format.mutable_format_details(); auto* format = output_format.mutable_format_details();
if (!format->has_domain() || !format->domain().is_video() || if (!format->has_domain() || !format->domain().is_video() ||
!format->domain().video().is_uncompressed()) { !format->domain().video().is_uncompressed()) {
DLOG(ERROR) << "Received OnOutputFormat() with invalid format."; DLOG(ERROR) << "Received OnOutputFormat() with invalid format.";
...@@ -746,72 +777,64 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet, ...@@ -746,72 +777,64 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet,
return; return;
} }
auto coded_size = gfx::Size(output_format_.primary_width_pixels, fuchsia::sysmem::PixelFormatType sysmem_pixel_format =
output_format_.primary_height_pixels); output_format_.image_format.pixel_format.type;
base::Optional<VideoFrameLayout> layout; VideoPixelFormat pixel_format;
switch (output_format_.fourcc) { gfx::BufferFormat buffer_format;
case libyuv::FOURCC_NV12: switch (sysmem_pixel_format) {
layout = VideoFrameLayout::CreateWithPlanes( case fuchsia::sysmem::PixelFormatType::NV12:
PIXEL_FORMAT_NV12, coded_size, pixel_format = PIXEL_FORMAT_NV12;
std::vector<VideoFrameLayout::Plane>{ buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR;
VideoFrameLayout::Plane(output_format_.primary_line_stride_bytes,
output_format_.primary_start_offset,
output_format_.primary_line_stride_bytes *
output_format_.primary_height_pixels),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.secondary_start_offset,
output_format_.secondary_line_stride_bytes *
output_format_.secondary_height_pixels)});
DCHECK(layout);
break; break;
case libyuv::FOURCC_YV12: case fuchsia::sysmem::PixelFormatType::I420:
layout = VideoFrameLayout::CreateWithPlanes( case fuchsia::sysmem::PixelFormatType::YV12:
PIXEL_FORMAT_YV12, coded_size, pixel_format = PIXEL_FORMAT_I420;
std::vector<VideoFrameLayout::Plane>{ buffer_format = gfx::BufferFormat::YVU_420;
VideoFrameLayout::Plane(output_format_.primary_line_stride_bytes,
output_format_.primary_start_offset,
output_format_.primary_line_stride_bytes *
output_format_.primary_height_pixels),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.secondary_start_offset,
output_format_.secondary_line_stride_bytes *
output_format_.secondary_height_pixels),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.tertiary_start_offset,
output_format_.secondary_line_stride_bytes *
output_format_.secondary_height_pixels)});
DCHECK(layout);
break; break;
default: default:
LOG(ERROR) << "unknown fourcc: " DLOG(ERROR) << "Unsupported pixel format: "
<< std::string(reinterpret_cast<char*>(&output_format_.fourcc), << static_cast<int>(sysmem_pixel_format);
4); OnError();
return;
} }
if (!layout) { size_t buffer_index = output_packet.buffer_index();
codec_->RecycleOutputPacket(fidl::Clone(output_packet.header())); if (buffer_index >= output_mailboxes_.size()) {
DLOG(ERROR)
<< "mediacodec generated output packet with an invalid buffer_index="
<< buffer_index << " for output buffer collection with only "
<< output_mailboxes_.size() << " packets.";
OnError();
return; return;
} }
base::TimeDelta timestamp; auto coded_size = gfx::Size(output_format_.primary_width_pixels,
if (output_packet.has_timestamp_ish()) { output_format_.primary_height_pixels);
timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
}
auto packet_index = output_packet.header().packet_index(); if (!output_mailboxes_[buffer_index]) {
auto buffer_index = output_packet.buffer_index(); gfx::GpuMemoryBufferHandle gmb_handle;
auto& buffer = output_buffers_[buffer_index]; gmb_handle.type = gfx::NATIVE_PIXMAP;
gmb_handle.native_pixmap_handle.buffer_collection_id =
output_buffer_collection_id_;
gmb_handle.native_pixmap_handle.buffer_index = buffer_index;
// We're not using single buffer mode, so packet count will be equal to buffer auto gmb = gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle(
// count. client_native_pixmap_factory_.get(), std::move(gmb_handle), coded_size,
DCHECK_LT(num_used_output_buffers_, static_cast<int>(output_buffers_.size())); buffer_format, gfx::BufferUsage::GPU_READ,
num_used_output_buffers_++; gpu::GpuMemoryBufferImpl::DestructionCallback());
output_mailboxes_[buffer_index] = new OutputMailbox(
shared_image_interface_, gpu_context_support_, std::move(gmb));
} else {
shared_image_interface_->UpdateSharedImage(
gpu::SyncToken(), output_mailboxes_[buffer_index]->mailbox());
}
auto display_rect = gfx::Rect(output_format_.primary_display_width_pixels,
output_format_.primary_display_height_pixels);
float pixel_aspect_ratio; float pixel_aspect_ratio;
if (output_format_.has_pixel_aspect_ratio) { if (output_format_.has_pixel_aspect_ratio) {
...@@ -822,22 +845,19 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet, ...@@ -822,22 +845,19 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet,
pixel_aspect_ratio = container_pixel_aspect_ratio_; pixel_aspect_ratio = container_pixel_aspect_ratio_;
} }
auto display_rect = gfx::Rect(output_format_.primary_display_width_pixels, base::TimeDelta timestamp;
output_format_.primary_display_height_pixels); if (output_packet.has_timestamp_ish()) {
timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
}
// TODO(sergeyu): Create ReadOnlySharedMemoryRegion for the VMO and pass num_used_output_buffers_++;
// it to the frame.
auto frame = VideoFrame::WrapExternalDataWithLayout(
*layout, display_rect, GetNaturalSize(display_rect, pixel_aspect_ratio),
const_cast<uint8_t*>(buffer->mapped_memory()) +
output_format_.primary_start_offset,
buffer->buffer().size() - output_format_.primary_start_offset, timestamp);
// Pass a reference to the buffer to the destruction callback to ensure it's auto frame = output_mailboxes_[buffer_index]->CreateFrame(
// not destroyed while the frame is being used. pixel_format, coded_size, display_rect,
frame->AddDestructionObserver(BindToCurrentLoop( GetNaturalSize(display_rect, pixel_aspect_ratio), timestamp,
base::BindOnce(&FuchsiaVideoDecoder::OnFrameDestroyed, weak_this_, buffer, base::BindOnce(&FuchsiaVideoDecoder::OnReuseMailbox,
output_buffer_lifetime_ordinal_, packet_index))); base::Unretained(this), buffer_index,
output_packet.header().packet_index()));
output_cb_.Run(std::move(frame)); output_cb_.Run(std::move(frame));
} }
...@@ -882,12 +902,11 @@ void FuchsiaVideoDecoder::OnError() { ...@@ -882,12 +902,11 @@ void FuchsiaVideoDecoder::OnError() {
pending_decodes_.clear(); pending_decodes_.clear();
input_buffers_collection_.Unbind(); input_buffer_collection_.Unbind();
num_used_input_buffers_ = 0; num_used_input_buffers_ = 0;
input_buffers_.clear(); input_buffers_.clear();
num_used_output_buffers_ = 0; ReleaseOutputBuffers();
output_buffers_.clear();
} }
void FuchsiaVideoDecoder::InitializeInputBufferCollection( void FuchsiaVideoDecoder::InitializeInputBufferCollection(
...@@ -920,10 +939,10 @@ void FuchsiaVideoDecoder::InitializeInputBufferCollection( ...@@ -920,10 +939,10 @@ void FuchsiaVideoDecoder::InitializeInputBufferCollection(
constraints.has_per_packet_buffer_bytes_recommended(); constraints.has_per_packet_buffer_bytes_recommended();
buffer_constraints.buffer_memory_constraints.ram_domain_supported = true; buffer_constraints.buffer_memory_constraints.ram_domain_supported = true;
buffer_constraints.buffer_memory_constraints.cpu_domain_supported = true; buffer_constraints.buffer_memory_constraints.cpu_domain_supported = true;
input_buffers_collection_->SetConstraints( input_buffer_collection_->SetConstraints(
/*has_constraints=*/true, std::move(buffer_constraints)); /*has_constraints=*/true, std::move(buffer_constraints));
input_buffers_collection_->WaitForBuffersAllocated( input_buffer_collection_->WaitForBuffersAllocated(
fit::bind_member(this, &FuchsiaVideoDecoder::OnInputBuffersAllocated)); fit::bind_member(this, &FuchsiaVideoDecoder::OnInputBuffersAllocated));
} }
...@@ -1010,77 +1029,92 @@ void FuchsiaVideoDecoder::PumpInput() { ...@@ -1010,77 +1029,92 @@ void FuchsiaVideoDecoder::PumpInput() {
} }
} }
bool FuchsiaVideoDecoder::InitializeOutputBuffers( void FuchsiaVideoDecoder::InitializeOutputBufferCollection(
fuchsia::media::StreamBufferConstraints constraints) { fuchsia::media::StreamBufferConstraints constraints,
if (!constraints.has_default_settings() || fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_codec,
!constraints.has_packet_count_for_client_max() || fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_gpu) {
!constraints.default_settings().has_packet_count_for_server() || fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
!constraints.default_settings().has_packet_count_for_client()) { buffer_constraints.usage.none = fuchsia::sysmem::noneUsage;
DLOG(ERROR) buffer_constraints.min_buffer_count_for_camping = max_used_output_buffers_;
<< "Received InitializeOutputBuffers() with missing required fields."; output_buffer_collection_->SetConstraints(
OnError(); /*has_constraints=*/true, std::move(buffer_constraints));
return false;
}
// mediacodec API expects odd buffer lifetime ordinal, which is incremented by // Register the new collection with the GPU process.
// 2 for each buffer generation. DCHECK(!output_buffer_collection_id_);
output_buffer_lifetime_ordinal_ += 2; output_buffer_collection_id_ = gfx::SysmemBufferCollectionId::Create();
shared_image_interface_->RegisterSysmemBufferCollection(
output_buffer_collection_id_,
collection_token_for_gpu.Unbind().TakeChannel());
auto settings = fidl::Clone(constraints.default_settings()); // Pass new output buffer settings to the codec.
fuchsia::media::StreamBufferPartialSettings settings;
settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_); settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
settings.set_buffer_constraints_version_ordinal(
max_used_output_buffers_ = constraints.buffer_constraints_version_ordinal());
std::min(kMaxUsedOutputFrames, constraints.packet_count_for_client_max());
settings.set_packet_count_for_client(max_used_output_buffers_); settings.set_packet_count_for_client(max_used_output_buffers_);
settings.set_packet_count_for_server(
constraints.packet_count_for_server_recommended());
settings.set_sysmem_token(std::move(collection_token_for_codec));
codec_->SetOutputBufferPartialSettings(std::move(settings));
codec_->CompleteOutputBufferPartialSettings(output_buffer_lifetime_ordinal_);
DCHECK(output_mailboxes_.empty());
output_mailboxes_.resize(
max_used_output_buffers_ +
constraints.packet_count_for_server_recommended(),
nullptr);
}
codec_->SetOutputBufferSettings(fidl::Clone(settings)); void FuchsiaVideoDecoder::ReleaseOutputBuffers() {
// Release the buffer collection.
int total_buffers = num_used_output_buffers_ = 0;
settings.packet_count_for_server() + settings.packet_count_for_client(); if (output_buffer_collection_) {
std::vector<scoped_refptr<OutputBuffer>> new_buffers(total_buffers); output_buffer_collection_->Close();
output_buffer_collection_.Unbind();
for (int i = 0; i < total_buffers; ++i) {
fuchsia::media::StreamBuffer codec_buffer;
new_buffers[i] = new OutputBuffer();
if (!new_buffers[i]->Initialize(constraints) ||
!new_buffers[i]->buffer().ToFidlCodecBuffer(
output_buffer_lifetime_ordinal_, i, /*read_only=*/false,
&codec_buffer)) {
return false;
}
codec_->AddOutputBuffer(std::move(codec_buffer));
} }
num_used_output_buffers_ = 0; // Release all output mailboxes.
output_buffers_ = std::move(new_buffers); for (OutputMailbox* mailbox : output_mailboxes_) {
if (mailbox)
mailbox->Release();
}
output_mailboxes_.clear();
return true; // Tell the GPU process to drop the buffer collection.
if (output_buffer_collection_id_) {
shared_image_interface_->ReleaseSysmemBufferCollection(
output_buffer_collection_id_);
output_buffer_collection_id_ = {};
}
} }
void FuchsiaVideoDecoder::OnFrameDestroyed(scoped_refptr<OutputBuffer> buffer, void FuchsiaVideoDecoder::OnReuseMailbox(uint32_t buffer_index,
uint64_t buffer_lifetime_ordinal, uint32_t packet_index) {
uint32_t packet_index) { DCHECK(codec_);
if (!codec_)
return;
if (buffer_lifetime_ordinal == output_buffer_lifetime_ordinal_) { DCHECK_GT(num_used_output_buffers_, 0);
DCHECK_GT(num_used_output_buffers_, 0); num_used_output_buffers_--;
num_used_output_buffers_--;
fuchsia::media::PacketHeader header; fuchsia::media::PacketHeader header;
header.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal); header.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
header.set_packet_index(packet_index); header.set_packet_index(packet_index);
codec_->RecycleOutputPacket(std::move(header)); codec_->RecycleOutputPacket(std::move(header));
}
} }
std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoder() { std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoder(
return std::make_unique<FuchsiaVideoDecoder>(/*enable_sw_decoding=*/false); gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support) {
return std::make_unique<FuchsiaVideoDecoder>(shared_image_interface,
gpu_context_support,
/*enable_sw_decoding=*/false);
} }
std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoderForTests( std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoderForTests(
gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support,
bool enable_sw_decoding) { bool enable_sw_decoding) {
return std::make_unique<FuchsiaVideoDecoder>(enable_sw_decoding); return std::make_unique<FuchsiaVideoDecoder>(
shared_image_interface, gpu_context_support, enable_sw_decoding);
} }
} // namespace media } // namespace media
...@@ -9,18 +9,28 @@ ...@@ -9,18 +9,28 @@
#include "media/base/media_export.h" #include "media/base/media_export.h"
namespace gpu {
class ContextSupport;
class SharedImageInterface;
} // namespace gpu
namespace media { namespace media {
class VideoDecoder; class VideoDecoder;
// Creates VideoDecoder that uses fuchsia.mediacodec API. The returned // Creates VideoDecoder that uses fuchsia.mediacodec API. The returned
// VideoDecoder instance will only try to use hardware video codecs. // VideoDecoder instance will only try to use hardware video codecs.
MEDIA_EXPORT std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoder(); // |shared_image_interface| and |gpu_context_support| must outlive the decoder.
MEDIA_EXPORT std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoder(
gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support);
// Same as above, but also allows to enable software codecs. This is useful for // Same as above, but also allows to enable software codecs. This is useful for
// FuchsiaVideoDecoder tests that run on systems that don't have hardware // FuchsiaVideoDecoder tests that run on systems that don't have hardware
// decoder support. // decoder support.
MEDIA_EXPORT std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoderForTests( MEDIA_EXPORT std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoderForTests(
gpu::SharedImageInterface* shared_image_interface,
gpu::ContextSupport* gpu_context_support,
bool enable_sw_decoding); bool enable_sw_decoding);
} // namespace media } // namespace media
......
...@@ -4,21 +4,188 @@ ...@@ -4,21 +4,188 @@
#include "media/filters/fuchsia/fuchsia_video_decoder.h" #include "media/filters/fuchsia/fuchsia_video_decoder.h"
#include <fuchsia/sysmem/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "components/viz/test/test_context_support.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "media/base/test_data_util.h" #include "media/base/test_data_util.h"
#include "media/base/test_helpers.h" #include "media/base/test_helpers.h"
#include "media/base/video_decoder.h" #include "media/base/video_decoder.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace media { namespace media {
namespace {
class TestBufferCollection {
public:
explicit TestBufferCollection(zx::channel collection_token) {
sysmem_allocator_ = base::fuchsia::ComponentContextForCurrentProcess()
->svc()
->Connect<fuchsia::sysmem::Allocator>();
sysmem_allocator_.set_error_handler([](zx_status_t status) {
ZX_LOG(FATAL, status)
<< "The fuchsia.sysmem.Allocator channel was terminated.";
});
sysmem_allocator_->BindSharedCollection(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>(
std::move(collection_token)),
buffers_collection_.NewRequest());
fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
buffer_constraints.usage.cpu = fuchsia::sysmem::cpuUsageRead;
zx_status_t status = buffers_collection_->SetConstraints(
/*has_constraints=*/true, std::move(buffer_constraints));
ZX_CHECK(status == ZX_OK, status) << "BufferCollection::SetConstraints()";
}
~TestBufferCollection() { buffers_collection_->Close(); }
size_t GetNumBuffers() {
if (!buffer_collection_info_) {
zx_status_t wait_status;
fuchsia::sysmem::BufferCollectionInfo_2 info;
zx_status_t status =
buffers_collection_->WaitForBuffersAllocated(&wait_status, &info);
ZX_CHECK(status == ZX_OK, status)
<< "BufferCollection::WaitForBuffersAllocated()";
ZX_CHECK(wait_status == ZX_OK, wait_status)
<< "BufferCollection::WaitForBuffersAllocated()";
buffer_collection_info_ = std::move(info);
}
return buffer_collection_info_->buffer_count;
}
private:
fuchsia::sysmem::AllocatorPtr sysmem_allocator_;
fuchsia::sysmem::BufferCollectionSyncPtr buffers_collection_;
base::Optional<fuchsia::sysmem::BufferCollectionInfo_2>
buffer_collection_info_;
DISALLOW_COPY_AND_ASSIGN(TestBufferCollection);
};
class TestSharedImageInterface : public gpu::SharedImageInterface {
public:
TestSharedImageInterface() = default;
~TestSharedImageInterface() override = default;
gpu::Mailbox CreateSharedImage(viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
NOTREACHED();
return gpu::Mailbox();
}
gpu::Mailbox CreateSharedImage(
viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage,
base::span<const uint8_t> pixel_data) override {
NOTREACHED();
return gpu::Mailbox();
}
gpu::Mailbox CreateSharedImage(
gfx::GpuMemoryBuffer* gpu_memory_buffer,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->CloneHandle();
CHECK_EQ(handle.type, gfx::GpuMemoryBufferType::NATIVE_PIXMAP);
auto collection_it = sysmem_buffer_collections_.find(
handle.native_pixmap_handle.buffer_collection_id);
CHECK(collection_it != sysmem_buffer_collections_.end());
CHECK_LT(handle.native_pixmap_handle.buffer_index,
collection_it->second->GetNumBuffers());
auto result = gpu::Mailbox::Generate();
mailoxes_.insert(result);
return result;
}
void UpdateSharedImage(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
NOTREACHED();
}
void UpdateSharedImage(const gpu::SyncToken& sync_token,
std::unique_ptr<gfx::GpuFence> acquire_fence,
const gpu::Mailbox& mailbox) override {
NOTREACHED();
}
void DestroySharedImage(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
CHECK_EQ(mailoxes_.erase(mailbox), 1U);
}
SwapChainMailboxes CreateSwapChain(viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
NOTREACHED();
return SwapChainMailboxes();
}
void PresentSwapChain(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
NOTREACHED();
}
void RegisterSysmemBufferCollection(gfx::SysmemBufferCollectionId id,
zx::channel token) override {
std::unique_ptr<TestBufferCollection>& collection =
sysmem_buffer_collections_[id];
EXPECT_FALSE(collection);
collection = std::make_unique<TestBufferCollection>(std::move(token));
}
void ReleaseSysmemBufferCollection(
gfx::SysmemBufferCollectionId id) override {
EXPECT_EQ(sysmem_buffer_collections_.erase(id), 1U);
}
gpu::SyncToken GenVerifiedSyncToken() override {
NOTREACHED();
return gpu::SyncToken();
}
gpu::SyncToken GenUnverifiedSyncToken() override {
return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId(33), 1);
}
void Flush() override { NOTREACHED(); }
private:
base::flat_map<gfx::SysmemBufferCollectionId,
std::unique_ptr<TestBufferCollection>>
sysmem_buffer_collections_;
base::flat_set<gpu::Mailbox> mailoxes_;
};
} // namespace
class FuchsiaVideoDecoderTest : public testing::Test { class FuchsiaVideoDecoderTest : public testing::Test {
public: public:
FuchsiaVideoDecoderTest() { FuchsiaVideoDecoderTest() {
decoder_ = CreateFuchsiaVideoDecoderForTests(/*enable_sw_decoding=*/true); decoder_ = CreateFuchsiaVideoDecoderForTests(&shared_image_interface_,
&gpu_context_support_,
/*enable_sw_decoding=*/true);
} }
~FuchsiaVideoDecoderTest() override = default; ~FuchsiaVideoDecoderTest() override = default;
...@@ -43,6 +210,7 @@ class FuchsiaVideoDecoderTest : public testing::Test { ...@@ -43,6 +210,7 @@ class FuchsiaVideoDecoderTest : public testing::Test {
void OnVideoFrame(scoped_refptr<VideoFrame> frame) { void OnVideoFrame(scoped_refptr<VideoFrame> frame) {
num_output_frames_++; num_output_frames_++;
CHECK(frame->HasTextures());
output_frames_.push_back(std::move(frame)); output_frames_.push_back(std::move(frame));
while (output_frames_.size() > frames_to_keep_) { while (output_frames_.size() > frames_to_keep_) {
output_frames_.pop_front(); output_frames_.pop_front();
...@@ -76,6 +244,10 @@ class FuchsiaVideoDecoderTest : public testing::Test { ...@@ -76,6 +244,10 @@ class FuchsiaVideoDecoderTest : public testing::Test {
base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
base::test::TaskEnvironment::MainThreadType::IO}; base::test::TaskEnvironment::MainThreadType::IO};
TestSharedImageInterface shared_image_interface_;
viz::TestContextSupport gpu_context_support_;
std::unique_ptr<VideoDecoder> decoder_; std::unique_ptr<VideoDecoder> decoder_;
std::list<scoped_refptr<VideoFrame>> output_frames_; std::list<scoped_refptr<VideoFrame>> output_frames_;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "build/buildflag.h" #include "build/buildflag.h"
#include "components/viz/common/gpu/context_provider.h"
#include "media/base/decoder_factory.h" #include "media/base/decoder_factory.h"
#include "media/base/media_switches.h" #include "media/base/media_switches.h"
#include "media/media_buildflags.h" #include "media/media_buildflags.h"
...@@ -104,7 +105,11 @@ void DefaultDecoderFactory::CreateVideoDecoders( ...@@ -104,7 +105,11 @@ void DefaultDecoderFactory::CreateVideoDecoders(
} }
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
video_decoders->push_back(CreateFuchsiaVideoDecoder()); if (gpu_factories) {
video_decoders->push_back(CreateFuchsiaVideoDecoder(
gpu_factories->SharedImageInterface(),
gpu_factories->GetMediaContextProvider()->ContextSupport()));
}
#endif #endif
#if BUILDFLAG(ENABLE_LIBVPX) #if BUILDFLAG(ENABLE_LIBVPX)
......
...@@ -87,26 +87,28 @@ VideoFrameResourceType ExternalResourceTypeForHardwarePlanes( ...@@ -87,26 +87,28 @@ VideoFrameResourceType ExternalResourceTypeForHardwarePlanes(
} }
break; break;
case PIXEL_FORMAT_I420: case PIXEL_FORMAT_I420:
DCHECK(num_textures == 3); DCHECK_EQ(num_textures, 3);
buffer_formats[0] = gfx::BufferFormat::R_8; buffer_formats[0] = gfx::BufferFormat::R_8;
buffer_formats[1] = gfx::BufferFormat::R_8; buffer_formats[1] = gfx::BufferFormat::R_8;
buffer_formats[2] = gfx::BufferFormat::R_8; buffer_formats[2] = gfx::BufferFormat::R_8;
return VideoFrameResourceType::YUV; return VideoFrameResourceType::YUV;
case PIXEL_FORMAT_NV12: case PIXEL_FORMAT_NV12:
DCHECK(target == GL_TEXTURE_EXTERNAL_OES || target == GL_TEXTURE_2D || // |target| is set to 0 for Vulkan textures.
target == GL_TEXTURE_RECTANGLE_ARB) DCHECK(target == 0 || target == GL_TEXTURE_EXTERNAL_OES ||
target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ARB)
<< "Unsupported target " << gl::GLEnums::GetStringEnum(target); << "Unsupported target " << gl::GLEnums::GetStringEnum(target);
DCHECK(num_textures <= 2);
// Single plane textures can be sampled as RGB. if (num_textures == 1) {
if (num_textures == 2) { // Single-texture multi-planar frames can be sampled as RGB.
buffer_formats[0] = gfx::BufferFormat::R_8; buffer_formats[0] = gfx::BufferFormat::YUV_420_BIPLANAR;
buffer_formats[1] = gfx::BufferFormat::RG_88; return VideoFrameResourceType::RGB;
return VideoFrameResourceType::YUV;
} }
buffer_formats[0] = gfx::BufferFormat::YUV_420_BIPLANAR; buffer_formats[0] = gfx::BufferFormat::R_8;
return VideoFrameResourceType::RGB; buffer_formats[1] = gfx::BufferFormat::RG_88;
return VideoFrameResourceType::YUV;
case PIXEL_FORMAT_YV12: case PIXEL_FORMAT_YV12:
case PIXEL_FORMAT_I422: case PIXEL_FORMAT_I422:
case PIXEL_FORMAT_I444: case PIXEL_FORMAT_I444:
......
...@@ -20,7 +20,6 @@ class ClientNativePixmapFuchsia : public gfx::ClientNativePixmap { ...@@ -20,7 +20,6 @@ class ClientNativePixmapFuchsia : public gfx::ClientNativePixmap {
public: public:
explicit ClientNativePixmapFuchsia(gfx::NativePixmapHandle handle) explicit ClientNativePixmapFuchsia(gfx::NativePixmapHandle handle)
: handle_(std::move(handle)) { : handle_(std::move(handle)) {
DCHECK(!handle_.planes.empty());
} }
~ClientNativePixmapFuchsia() override { ~ClientNativePixmapFuchsia() override {
...@@ -32,10 +31,8 @@ class ClientNativePixmapFuchsia : public gfx::ClientNativePixmap { ...@@ -32,10 +31,8 @@ class ClientNativePixmapFuchsia : public gfx::ClientNativePixmap {
if (mapping_) if (mapping_)
return true; return true;
if (!handle_.planes[0].vmo) { if (handle_.planes.empty() || !handle_.planes[0].vmo)
NOTREACHED();
return false; return false;
}
uintptr_t addr; uintptr_t addr;
...@@ -110,8 +107,6 @@ class ScenicClientNativePixmapFactory : public gfx::ClientNativePixmapFactory { ...@@ -110,8 +107,6 @@ class ScenicClientNativePixmapFactory : public gfx::ClientNativePixmapFactory {
const gfx::Size& size, const gfx::Size& size,
gfx::BufferFormat format, gfx::BufferFormat format,
gfx::BufferUsage usage) override { gfx::BufferUsage usage) override {
if (handle.planes.empty())
return nullptr;
return std::make_unique<ClientNativePixmapFuchsia>(std::move(handle)); return std::make_unique<ClientNativePixmapFuchsia>(std::move(handle));
} }
......
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