blob: 83850e09ce2fc77397f91a6fe7834e673fef7538 [file] [log] [blame] [edit]
// 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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/gtest/real_loop_fixture.h>
#include <gtest/gtest.h>
#include "lib/media/codec_impl/codec_impl.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_single_buffer_mode(constraints.default_settings().single_buffer_mode())
.set_packet_count_for_server(constraints.default_settings().packet_count_for_server())
.set_packet_count_for_client(constraints.default_settings().packet_count_for_client())
.set_sysmem_token(std::move(token));
return settings;
}
auto CreateValidInputBufferCollectionConstraints() {
fuchsia::sysmem::BufferCollectionConstraints result;
result.usage.cpu = fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageReadOften;
result.min_buffer_count_for_camping = kInputMinBufferCountForCamping;
// Must specify true here, as enforced by CodecImpl. Leaving all
// buffer_memory_constraints fields default is fine.
result.has_buffer_memory_constraints = true;
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::sysmem::Allocator> sysmem;
sysmem_request_ = sysmem.NewRequest();
codec_impl_ =
std::make_unique<CodecImpl>(std::move(sysmem), 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::sysmem::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::sysmem::testing::BufferCollection_TestBase {
public:
TestBufferCollection() : binding_(this) {}
void Bind(fidl::InterfaceRequest<fuchsia::sysmem::BufferCollection> request) {
binding_.Bind(std::move(request));
}
void NotImplemented_(const std::string& name) override {}
void WaitForBuffersAllocated(WaitForBuffersAllocatedCallback callback) override {
wait_callback_ = std::move(callback);
}
void FailAllocation() {
WaitForBuffersAllocatedCallback callback;
callback.swap(wait_callback_);
callback(ZX_ERR_NOT_SUPPORTED, fuchsia::sysmem::BufferCollectionInfo_2());
}
bool is_waiting() { return !!wait_callback_; }
private:
fidl::Binding<fuchsia::sysmem::BufferCollection> binding_;
WaitForBuffersAllocatedCallback wait_callback_;
};
class TestAllocator : public fuchsia::sysmem::testing::Allocator_TestBase {
public:
TestAllocator() : binding_(this) {}
void Bind(fidl::InterfaceRequest<fuchsia::sysmem::Allocator> request) {
binding_.Bind(std::move(request));
}
void BindSharedCollection(fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token,
fidl::InterfaceRequest<fuchsia::sysmem::BufferCollection>
buffer_collection_request) override {
collection_.Bind(std::move(buffer_collection_request));
}
void NotImplemented_(const std::string& name) override {
// Unexpected.
ZX_PANIC("NotImplemented_(): %s\n", name.c_str());
}
TestBufferCollection& collection() { return collection_; }
private:
fidl::Binding<fuchsia::sysmem::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_);
}