blob: c81821c5e7e0ebcf91cb2839bb5e81a568a66803 [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/mediaplayer/test/fakes/fake_image_pipe.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <iomanip>
#include <iostream>
#include "lib/fxl/logging.h"
namespace media_player {
namespace test {
FakeImagePipe::FakeImagePipe()
: dispatcher_(async_get_default_dispatcher()),
binding_(this),
weak_factory_(this) {}
FakeImagePipe::~FakeImagePipe() {
while (!image_presentation_queue_.empty()) {
FXL_DCHECK(image_presentation_queue_.front().release_fences_);
for (auto& release_fence :
*image_presentation_queue_.front().release_fences_) {
release_fence.signal(0, ZX_EVENT_SIGNALED);
}
image_presentation_queue_.pop_front();
}
}
void FakeImagePipe::Bind(
fidl::InterfaceRequest<fuchsia::images::ImagePipe> request) {
binding_.Bind(std::move(request));
}
void FakeImagePipe::OnPresentScene(zx::time presentation_time,
zx::time next_presentation_time,
zx::duration presentation_interval) {
next_presentation_time_ = next_presentation_time;
presentation_interval_ = presentation_interval;
while (image_presentation_queue_.size() > 1 &&
image_presentation_queue_.front().presentation_time_ <
static_cast<uint64_t>(presentation_time.get())) {
FXL_DCHECK(image_presentation_queue_.front().release_fences_);
for (auto& release_fence :
*(image_presentation_queue_.front().release_fences_)) {
release_fence.signal(0, ZX_EVENT_SIGNALED);
}
image_presentation_queue_.pop_front();
}
}
void FakeImagePipe::AddImage(uint32_t image_id,
fuchsia::images::ImageInfo image_info,
zx::vmo memory, uint64_t offset_bytes,
uint64_t size_bytes,
fuchsia::images::MemoryType memory_type) {
if (image_id == expected_black_image_id_) {
if (expected_black_image_info_) {
ExpectImageInfo(*expected_black_image_info_, image_info);
}
} else {
if (dump_expectations_) {
FXL_DCHECK(image_info.pixel_format == fuchsia::images::PixelFormat::YV12);
std::cerr << "{.width = " << image_info.width << ",\n"
<< ".height = " << image_info.height << ",\n"
<< ".stride = " << image_info.stride << ",\n"
<< ".pixel_format = fuchsia::images::PixelFormat::YV12,\n"
<< "};\n";
}
if (expected_image_info_) {
ExpectImageInfo(*expected_image_info_, image_info);
}
}
auto [iter, inserted] = images_by_id_.emplace(
image_id, Image(std::move(image_info), std::move(memory), offset_bytes,
size_bytes));
if (!inserted) {
FXL_LOG(ERROR) << "AddImage image_id: (" << image_id
<< ") refers to existing image, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
auto& image = iter->second;
if (image.offset_bytes_ + image.size_bytes_ > image.vmo_mapper_.size()) {
FXL_LOG(ERROR) << "AddImage image_id: (" << image_id << ") offset_bytes ("
<< image.offset_bytes_ << ") plus size_bytes ("
<< image.size_bytes_ << ") exceeds vmo size ("
<< image.vmo_mapper_.size() << "), closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
}
void FakeImagePipe::RemoveImage(uint32_t image_id) {
auto removed = images_by_id_.erase(image_id);
if (removed != 1) {
FXL_LOG(ERROR) << "RemoveImage: image_id (" << image_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
for (auto& image_presentation : image_presentation_queue_) {
if (image_presentation.image_id_ == image_id) {
FXL_DCHECK(image_presentation.release_fences_);
for (auto& release_fence : *image_presentation.release_fences_) {
release_fence.signal(0, ZX_EVENT_SIGNALED);
}
}
}
}
void FakeImagePipe::PresentImage(uint32_t image_id, uint64_t presentation_time,
std::vector<zx::event> acquire_fences,
std::vector<zx::event> release_fences,
PresentImageCallback callback) {
FXL_DCHECK(callback);
// The video renderer doesn't use the acquire fences, so we don't support
// them in the fake.
FXL_CHECK(acquire_fences.empty())
<< "PresentImage: acquire_fences not supported.";
if (prev_presentation_time_ > presentation_time) {
FXL_LOG(ERROR) << "PresentImage: presentation_time (" << presentation_time
<< ") less than previous (" << prev_presentation_time_
<< "), closing connection.";
expected_ = false;
binding_.Unbind();
}
prev_presentation_time_ = presentation_time;
if (initial_presentation_time_ == 0 && presentation_time != 0) {
initial_presentation_time_ = presentation_time;
}
auto iter = images_by_id_.find(image_id);
if (iter == images_by_id_.end()) {
FXL_LOG(ERROR) << "PresentImage: image_id (" << image_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
Image& image = iter->second;
uint64_t size = image.image_info_.stride * image.image_info_.height;
if (image.offset_bytes_ + size > image.vmo_mapper_.size()) {
FXL_LOG(ERROR) << "PresentImage: image exceeds vmo limits";
FXL_LOG(ERROR) << " vmo size " << image.vmo_mapper_.size();
FXL_LOG(ERROR) << " image offset " << image.offset_bytes_;
FXL_LOG(ERROR) << " image stride " << image.image_info_.stride;
FXL_LOG(ERROR) << " image height " << image.image_info_.height;
expected_ = false;
return;
}
void* image_payload = reinterpret_cast<uint8_t*>(image.vmo_mapper_.start()) +
image.offset_bytes_;
if (dump_expectations_) {
// Here's we're dumping the packet to the console to generate some C++ code
// that can easily be pasted into a test as a 'golden'. We use |std::cerr|,
// because we don't want/need the usual log line header we get with FXL_LOG.
// Also, this ends up on the console, not in the logs.
std::cerr << "{ " << presentation_time - initial_presentation_time_ << ", "
<< size << ", 0x" << std::hex << std::setw(16)
<< std::setfill('0')
<< PacketHash(image_payload, image.image_info_) << std::dec
<< " },\n";
}
if (!expected_packets_info_.empty()) {
if (expected_packets_info_iter_ == expected_packets_info_.end()) {
FXL_LOG(ERROR) << "PresentImage: frame supplied after expected packets";
expected_ = false;
}
if (expected_packets_info_iter_->size() != size ||
expected_packets_info_iter_->hash() !=
PacketHash(image_payload, image.image_info_)) {
FXL_LOG(ERROR)
<< "PresentImage: supplied frame doesn't match expected packet info";
FXL_LOG(ERROR) << "actual: "
<< presentation_time - initial_presentation_time_ << ", "
<< size << ", 0x" << std::hex << std::setw(16)
<< std::setfill('0')
<< PacketHash(image_payload, image.image_info_)
<< std::dec;
FXL_LOG(ERROR) << "expected: " << expected_packets_info_iter_->pts()
<< ", " << expected_packets_info_iter_->size() << ", 0x"
<< std::hex << std::setw(16) << std::setfill('0')
<< expected_packets_info_iter_->hash() << std::dec;
expected_ = false;
}
++expected_packets_info_iter_;
}
image_presentation_queue_.emplace_back(image_id, presentation_time,
std::move(release_fences));
async::PostTask(dispatcher_, [this, callback = std::move(callback),
weak_this = GetWeakThis()]() {
if (!weak_this) {
callback(fuchsia::images::PresentationInfo{.presentation_time = 0,
.presentation_interval = 0});
return;
}
callback(fuchsia::images::PresentationInfo{
.presentation_time =
static_cast<uint64_t>(next_presentation_time_.get()),
.presentation_interval =
static_cast<uint64_t>(presentation_interval_.get())});
});
}
void FakeImagePipe::ExpectImageInfo(const fuchsia::images::ImageInfo& expected,
const fuchsia::images::ImageInfo& actual) {
if (actual.transform != expected.transform) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.transform value "
<< fidl::ToUnderlying(actual.transform);
expected_ = false;
}
if (actual.width != expected.width) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.width value "
<< actual.width;
expected_ = false;
}
if (actual.height != expected.height) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.height value "
<< actual.height;
expected_ = false;
}
if (actual.stride != expected.stride) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.stride value "
<< actual.stride;
expected_ = false;
}
if (actual.pixel_format != expected.pixel_format) {
FXL_LOG(ERROR)
<< "ExpectImageInfo: unexpected ImageInfo.pixel_format value "
<< fidl::ToUnderlying(actual.pixel_format);
expected_ = false;
}
if (actual.color_space != expected.color_space) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.color_space value "
<< fidl::ToUnderlying(actual.color_space);
expected_ = false;
}
if (actual.tiling != expected.tiling) {
FXL_LOG(ERROR) << "ExpectImageInfo: unexpected ImageInfo.tiling value "
<< fidl::ToUnderlying(actual.tiling);
expected_ = false;
}
if (actual.alpha_format != expected.alpha_format) {
FXL_LOG(ERROR)
<< "ExpectImageInfo: unexpected ImageInfo.alpha_format value "
<< fidl::ToUnderlying(actual.alpha_format);
expected_ = false;
}
}
uint64_t FakeImagePipe::PacketHash(
const void* data, const fuchsia::images::ImageInfo& image_info) {
FXL_DCHECK(data);
FXL_DCHECK(expected_display_height_ <= image_info.height);
FXL_DCHECK(image_info.pixel_format == fuchsia::images::PixelFormat::YV12);
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
uint64_t hash = 0;
// Hash the Y plane.
for (uint32_t line = 0; line < expected_display_height_; ++line) {
hash = PacketInfo::Hash(bytes, image_info.width, hash);
bytes += image_info.stride;
}
bytes += image_info.stride * (image_info.height - expected_display_height_);
// Hash the V plane.
for (uint32_t line = 0; line < expected_display_height_ / 2; ++line) {
hash = PacketInfo::Hash(bytes, image_info.width / 2, hash);
bytes += image_info.stride / 2;
}
bytes +=
(image_info.stride * (image_info.height - expected_display_height_)) / 4;
// Hash the U plane.
for (uint32_t line = 0; line < expected_display_height_ / 2; ++line) {
hash = PacketInfo::Hash(bytes, image_info.width / 2, hash);
bytes += image_info.stride / 2;
}
return hash;
}
////////////////////////////////////////////////////////////////////////////////
// FakeImagePipe::Image implementation.
FakeImagePipe::Image::Image(fuchsia::images::ImageInfo image_info,
zx::vmo memory, uint64_t offset_bytes,
uint64_t size_bytes)
: image_info_(std::move(image_info)),
offset_bytes_(offset_bytes),
size_bytes_(size_bytes) {
uint64_t size;
zx_status_t status = memory.get_size(&size);
FXL_CHECK(status == ZX_OK);
status = vmo_mapper_.Map(memory, 0, size, ZX_VM_PERM_READ, nullptr);
FXL_CHECK(status == ZX_OK);
}
} // namespace test
} // namespace media_player