blob: 9084899e6b633af8b31eaa965be2c7977b84de46 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <fake_codec_adapter.h>
#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl_test_base.h>
#include <fuchsia/sysmem2/cpp/fidl_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <gtest/gtest.h>
#include "lib/media/codec_impl/codec_impl.h"
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
namespace {
constexpr uint32_t kInputMinBufferCountForCamping = 3;
auto CreateDecoderParams() {
fuchsia::mediacodec::CreateDecoder_Params params;
params.mutable_input_details()->set_format_details_version_ordinal(0);
return params;
}
auto CreateStreamBufferPartialSettings(
uint64_t buffer_lifetime_ordinal, const fuchsia::media::StreamBufferConstraints& constraints,
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
constexpr uint64_t kBufferConstraintsVersionOrdinal = 1;
fuchsia::media::StreamBufferPartialSettings settings;
settings.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal)
.set_buffer_constraints_version_ordinal(kBufferConstraintsVersionOrdinal)
.set_sysmem_token(std::move(token));
return settings;
}
auto CreateValidInputBufferCollectionConstraints() {
fuchsia_sysmem2::BufferCollectionConstraints result;
result.usage().emplace().cpu() =
fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageReadOften;
result.min_buffer_count_for_camping() = kInputMinBufferCountForCamping;
// Must emplace the buffer_memory_constraints here, as enforced by CodecImpl. Leaving all
// buffer_memory_constraints fields default is fine.
result.buffer_memory_constraints().emplace();
return result;
}
} // namespace
class CodecImplFailures : public gtest::RealLoopFixture {
public:
using StreamProcessorPtr = ::fuchsia::media::StreamProcessorPtr;
void TearDown() override { token_request_ = nullptr; }
void Create(fidl::InterfaceRequest<fuchsia::media::StreamProcessor> request) {
fidl::InterfaceHandle<fuchsia::sysmem2::Allocator> sysmem;
sysmem_request_ = sysmem.NewRequest();
codec_impl_ = std::make_unique<CodecImpl>(
fidl::ClientEnd<fuchsia_sysmem2::Allocator>(sysmem.TakeChannel()), nullptr, dispatcher(),
thrd_current(), CreateDecoderParams(), std::move(request));
auto codec_adapter = std::make_unique<FakeCodecAdapter>(codec_impl_->lock(), codec_impl_.get());
codec_adapter_ = codec_adapter.get();
codec_impl_->SetCoreCodecAdapter(std::move(codec_adapter));
codec_impl_->BindAsync([this]() {
error_handler_ran_ = true;
codec_impl_ = nullptr;
});
}
protected:
// Just cache this request so that we can have a valid sysmem handle
std::optional<fidl::InterfaceRequest<fuchsia::sysmem2::Allocator>> sysmem_request_;
std::optional<fidl::InterfaceRequest<fuchsia::sysmem::BufferCollectionToken>> token_request_;
bool error_handler_ran_ = false;
std::unique_ptr<CodecImpl> codec_impl_;
FakeCodecAdapter* codec_adapter_;
};
TEST_F(CodecImplFailures, InputBufferCollectionConstraintsCpuUsage) {
StreamProcessorPtr processor;
processor.events().OnInputConstraints = [this, &processor](auto input_constraints) {
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token;
token_request_ = token.NewRequest();
auto buffer_collection_constraints = CreateValidInputBufferCollectionConstraints();
// Setting write usage on input buffers is invalid and will result in codec
// failure
buffer_collection_constraints.usage()->cpu() =
fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften;
codec_adapter_->SetBufferCollectionConstraints(kInputPort,
std::move(buffer_collection_constraints));
processor->SetInputBufferPartialSettings(
CreateStreamBufferPartialSettings(1, input_constraints, std::move(token)));
};
Create(processor.NewRequest());
RunLoopUntil([this]() { return error_handler_ran_; });
ASSERT_TRUE(error_handler_ran_);
}
TEST_F(CodecImplFailures, InputBufferCollectionConstraintsMinBufferCount) {
StreamProcessorPtr processor;
processor.events().OnInputConstraints = [this, &processor](auto input_constraints) {
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token;
token_request_ = token.NewRequest();
auto buffer_collection_constraints = CreateValidInputBufferCollectionConstraints();
// No buffers required for camping would be less than the minimum for the
// server
buffer_collection_constraints.min_buffer_count_for_camping() = 0;
codec_adapter_->SetBufferCollectionConstraints(kInputPort,
std::move(buffer_collection_constraints));
processor->SetInputBufferPartialSettings(
CreateStreamBufferPartialSettings(1, input_constraints, std::move(token)));
};
Create(processor.NewRequest());
RunLoopUntil([this]() { return error_handler_ran_; });
ASSERT_TRUE(error_handler_ran_);
}
class TestBufferCollection : public fuchsia::sysmem2::testing::BufferCollection_TestBase {
public:
TestBufferCollection() : binding_(this) {}
void Bind(fidl::InterfaceRequest<fuchsia::sysmem2::BufferCollection> request) {
binding_.Bind(std::move(request));
}
void NotImplemented_(const std::string& name) override {}
void WaitForAllBuffersAllocated(WaitForAllBuffersAllocatedCallback callback) override {
wait_callback_ = std::move(callback);
}
void FailAllocation() {
WaitForAllBuffersAllocatedCallback callback;
callback.swap(wait_callback_);
fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result result;
result.set_err(fuchsia::sysmem2::Error::CONSTRAINTS_INTERSECTION_EMPTY);
callback(std::move(result));
}
bool is_waiting() { return !!wait_callback_; }
private:
fidl::Binding<fuchsia::sysmem2::BufferCollection> binding_;
WaitForAllBuffersAllocatedCallback wait_callback_;
};
class TestAllocator : public fuchsia::sysmem2::testing::Allocator_TestBase {
public:
TestAllocator() : binding_(this) {}
void Bind(fidl::InterfaceRequest<fuchsia::sysmem2::Allocator> request) {
binding_.Bind(std::move(request));
}
void BindSharedCollection(
::fuchsia::sysmem2::AllocatorBindSharedCollectionRequest request) override {
collection_.Bind(std::move(*request.mutable_buffer_collection_request()));
}
void SetDebugClientInfo(::fuchsia::sysmem2::AllocatorSetDebugClientInfoRequest request) override {
}
void NotImplemented_(const std::string& name) override {
// Unexpected.
ZX_PANIC("NotImplemented_(): %s", name.c_str());
}
TestBufferCollection& collection() { return collection_; }
private:
fidl::Binding<fuchsia::sysmem2::Allocator> binding_;
TestBufferCollection collection_;
};
TEST_F(CodecImplFailures, InputBufferCollectionSysmemFailure) {
StreamProcessorPtr processor;
processor.events().OnInputConstraints = [this, &processor](auto input_constraints) {
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token;
token_request_ = token.NewRequest();
codec_adapter_->SetBufferCollectionConstraints(kInputPort,
CreateValidInputBufferCollectionConstraints());
processor->SetInputBufferPartialSettings(
CreateStreamBufferPartialSettings(1, input_constraints, std::move(token)));
};
Create(processor.NewRequest());
TestAllocator allocator;
allocator.Bind(std::move(sysmem_request_.value()));
sysmem_request_ = nullptr;
RunLoopUntil([&allocator]() { return allocator.collection().is_waiting(); });
ASSERT_TRUE(error_handler_ran_ == 0);
ASSERT_TRUE(allocator.collection().is_waiting());
allocator.collection().FailAllocation();
RunLoopUntil([this]() { return error_handler_ran_; });
ASSERT_TRUE(error_handler_ran_);
}