blob: db7d39ba2feec4defe32aabd7f48c5675749203b [file] [log] [blame]
// Copyright 2019 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/test/image.h"
#include <memory>
#include "base/files/file_util.h"
#include "base/hash/md5.h"
#include "base/json/json_reader.h"
#include "base/values.h"
#include "media/base/test_data_util.h"
#include "media/gpu/macros.h"
namespace media {
namespace test {
namespace {
// Resolve the specified test file path to an absolute path. The path can be
// either an absolute path, a path relative to the current directory, or a path
// relative to the test data path.
absl::optional<base::FilePath> ResolveFilePath(
const base::FilePath& file_path) {
base::FilePath resolved_path = file_path;
// Try to resolve the path into an absolute path. If the path doesn't exist,
// it might be relative to the test data dir.
if (!resolved_path.IsAbsolute()) {
resolved_path = base::MakeAbsoluteFilePath(
PathExists(resolved_path)
? resolved_path
: media::GetTestDataPath().Append(resolved_path));
}
return PathExists(resolved_path)
? absl::optional<base::FilePath>(resolved_path)
: absl::nullopt;
}
// Converts the |pixel_format| string into a VideoPixelFormat.
VideoPixelFormat ConvertStringtoPixelFormat(const std::string& pixel_format) {
if (pixel_format == "BGRA") {
return PIXEL_FORMAT_ARGB;
} else if (pixel_format == "I420") {
return PIXEL_FORMAT_I420;
} else if (pixel_format == "NV12") {
return PIXEL_FORMAT_NV12;
} else if (pixel_format == "YV12") {
return PIXEL_FORMAT_YV12;
} else if (pixel_format == "RGBA") {
return PIXEL_FORMAT_ABGR;
} else if (pixel_format == "I422") {
return PIXEL_FORMAT_I422;
} else if (pixel_format == "YUYV") {
return PIXEL_FORMAT_YUY2;
} else {
VLOG(2) << pixel_format << " is not supported.";
return PIXEL_FORMAT_UNKNOWN;
}
}
} // namespace
// Suffix to append to the image file path to get the metadata file path.
constexpr const base::FilePath::CharType* kMetadataSuffix =
FILE_PATH_LITERAL(".json");
Image::Image(const base::FilePath& file_path) : file_path_(file_path) {}
Image::~Image() {}
bool Image::Load() {
DCHECK(!file_path_.empty());
DCHECK(!IsLoaded());
absl::optional<base::FilePath> resolved_path = ResolveFilePath(file_path_);
if (!resolved_path) {
LOG(ERROR) << "Image file not found: " << file_path_;
return false;
}
file_path_ = resolved_path.value();
DVLOGF(2) << "File path: " << file_path_;
if (!mapped_file_.Initialize(file_path_)) {
LOG(ERROR) << "Failed to read file: " << file_path_;
return false;
}
if (!LoadMetadata()) {
LOG(ERROR) << "Failed to load metadata";
return false;
}
// Verify that the image's checksum matches the checksum in the metadata.
base::MD5Digest digest;
base::MD5Sum(mapped_file_.data(), mapped_file_.length(), &digest);
if (base::MD5DigestToBase16(digest) != checksum_) {
LOG(ERROR) << "Image checksum not matching metadata";
return false;
}
return true;
}
bool Image::IsLoaded() const {
return mapped_file_.IsValid();
}
bool Image::LoadMetadata() {
if (IsMetadataLoaded()) {
return true;
}
base::FilePath json_path = file_path_.AddExtension(kMetadataSuffix);
absl::optional<base::FilePath> resolved_path = ResolveFilePath(json_path);
if (!resolved_path) {
LOG(ERROR) << "Image metadata file not found: " << json_path;
return false;
}
json_path = resolved_path.value();
if (!base::PathExists(json_path)) {
VLOGF(1) << "Image metadata file not found: " << json_path.BaseName();
return false;
}
std::string json_data;
if (!base::ReadFileToString(json_path, &json_data)) {
VLOGF(1) << "Failed to read image metadata file: " << json_path;
return false;
}
auto metadata_result =
base::JSONReader::ReadAndReturnValueWithError(json_data);
if (!metadata_result.value) {
VLOGF(1) << "Failed to parse image metadata: " << json_path << ": "
<< metadata_result.error_message;
return false;
}
absl::optional<base::Value> metadata = std::move(metadata_result.value);
// Get the pixel format from the json data.
const base::Value* pixel_format =
metadata->FindKeyOfType("pixel_format", base::Value::Type::STRING);
if (!pixel_format) {
VLOGF(1) << "Key \"pixel_format\" is not found in " << json_path;
return false;
}
pixel_format_ = ConvertStringtoPixelFormat(pixel_format->GetString());
if (pixel_format_ == PIXEL_FORMAT_UNKNOWN) {
VLOGF(1) << pixel_format->GetString() << " is not supported";
return false;
}
// Get the image dimensions from the json data.
const base::Value* width =
metadata->FindKeyOfType("width", base::Value::Type::INTEGER);
if (!width) {
VLOGF(1) << "Key \"width\" is not found in " << json_path;
return false;
}
const base::Value* height =
metadata->FindKeyOfType("height", base::Value::Type::INTEGER);
if (!height) {
VLOGF(1) << "Key \"height\" is not found in " << json_path;
return false;
}
size_ = gfx::Size(width->GetInt(), height->GetInt());
// Try to get the visible rectangle of the image from the json data.
// These values are not in json data if all the image data is in the visible
// area.
visible_rect_ = gfx::Rect(size_);
const base::Value* visible_rect_info =
metadata->FindKeyOfType("visible_rect", base::Value::Type::LIST);
if (visible_rect_info) {
base::Value::ConstListView values = visible_rect_info->GetList();
if (values.size() != 4) {
VLOGF(1) << "unexpected json format for visible rectangle";
return false;
}
int origin_x = values[0].GetInt();
int origin_y = values[1].GetInt();
int visible_width = values[2].GetInt();
int visible_height = values[3].GetInt();
visible_rect_ =
gfx::Rect(origin_x, origin_y, visible_width, visible_height);
}
// Get the image rotation info from the json data.
const base::Value* rotation =
metadata->FindKeyOfType("rotation", base::Value::Type::INTEGER);
if (!rotation) {
// Default rotation value is VIDEO_ROTATION_0
rotation_ = VIDEO_ROTATION_0;
} else {
switch (rotation->GetInt()) {
case 0:
rotation_ = VIDEO_ROTATION_0;
break;
case 90:
rotation_ = VIDEO_ROTATION_90;
break;
case 180:
rotation_ = VIDEO_ROTATION_180;
break;
case 270:
rotation_ = VIDEO_ROTATION_270;
break;
default:
VLOGF(1) << "Invalid rotation value: " << rotation->GetInt();
return false;
};
}
// Get the image checksum from the json data.
const base::Value* checksum =
metadata->FindKeyOfType("checksum", base::Value::Type::STRING);
if (!checksum) {
VLOGF(1) << "Key \"checksum\" is not found in " << json_path;
return false;
}
checksum_ = checksum->GetString();
return true;
}
bool Image::IsMetadataLoaded() const {
return pixel_format_ != PIXEL_FORMAT_UNKNOWN;
}
uint8_t* Image::Data() const {
return mapped_file_.data();
}
size_t Image::DataSize() const {
return mapped_file_.length();
}
VideoPixelFormat Image::PixelFormat() const {
return pixel_format_;
}
const gfx::Size& Image::Size() const {
return size_;
}
const gfx::Rect& Image::VisibleRect() const {
return visible_rect_;
}
VideoRotation Image::Rotation() const {
return rotation_;
}
const char* Image::Checksum() const {
return checksum_.data();
}
} // namespace test
} // namespace media