1
0
Fork 0
firefox/third_party/aom/examples/multilayer_metadata.cc
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

928 lines
35 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2024, Alliance for Open Media. All rights reserved.
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include "examples/multilayer_metadata.h"
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#include "aom/aom_integer.h"
#include "examples/multilayer_metadata.h"
namespace libaom_examples {
namespace {
#define RETURN_IF_FALSE(A) \
do { \
if (!(A)) { \
return false; \
} \
} while (0)
constexpr int kMaxNumSpatialLayers = 4;
// Removes comments and trailing spaces from the line.
void cleanup_line(std::string &line) {
// Remove everything after the first '#'.
std::size_t comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line.resize(comment_pos);
}
// Remove spaces at the end of the line.
while (!line.empty() && line.back() == ' ') {
line.resize(line.length() - 1);
}
}
// Finds the indentation level of the line, and sets 'has_list_prefix' to true
// if the line has a '-' indicating a new item in a list.
void get_indent(const std::string &line, int *indent, bool *has_list_prefix) {
*indent = 0;
*has_list_prefix = false;
while (
*indent < static_cast<int>(line.length()) &&
(line[*indent] == ' ' || line[*indent] == '\t' || line[*indent] == '-')) {
if (line[*indent] == '-') {
*has_list_prefix = true;
}
++(*indent);
}
}
class ParsedValue {
public:
enum class Type { kNone, kInteger, kFloatingPoint };
void SetIntegerValue(int64_t v) {
type_ = Type::kInteger;
int_value_ = v;
}
void SetFloatingPointValue(double v) {
type_ = Type::kFloatingPoint;
double_value_ = v;
}
void Clear() { type_ = Type::kNone; }
bool ValueAsFloatingPoint(int line_idx, double *v) {
if (type_ == Type::kNone) {
fprintf(
stderr,
"No value found where floating point value was expected at line %d\n",
line_idx);
return false;
}
*v = (type_ == Type::kFloatingPoint) ? double_value_
: static_cast<double>(int_value_);
return true;
}
template <typename T>
bool IntegerValueInRange(int64_t min, int64_t max, int line_idx, T *v) {
switch (type_) {
case Type::kInteger:
if (int_value_ < min || int_value_ > max) {
fprintf(stderr,
"Integer value %" PRId64 " out of range [%" PRId64
", %" PRId64 "] at line %d\n",
int_value_, min, max, line_idx);
return false;
}
*v = static_cast<T>(int_value_);
return true;
case Type::kFloatingPoint:
fprintf(stderr,
"Floating point value found where integer was expected at line "
"%d\n",
line_idx);
return false;
case Type::kNone:
default:
fprintf(stderr,
"No value found where integer was expected at line %d\n",
line_idx);
return false;
}
}
private:
Type type_ = Type::kNone;
int64_t int_value_ = 0;
double double_value_ = 0.0f;
};
/*
* Parses the next line from the file, skipping empty lines.
* Returns false if the end of the file was reached, or if the line was indented
* less than 'min_indent', meaning that parsing should go back to the previous
* function in the stack.
*
* 'min_indent' is the minimum indentation expected for the next line.
* 'is_list' must be true if the line is allowed to contain list items ('-').
* 'indent' MUST be initialized to -1 before the first call, and is then set to
* the indentation of the line.
* 'has_list_prefix' is set to true if the line starts a new list item with '-'.
* 'line_idx' is set to the index of the last line read.
* 'field_name' is set to the field name if the line contains a colon, or to an
* empty string otherwise.
* 'value' is set to the value on the line if present.
* In case of syntax error, 'syntax_error' is set to true and the function
* returns false.
*/
bool parse_line(std::ifstream &file, int min_indent, bool is_list, int *indent,
bool *has_list_prefix, int *line_idx, std::string *field_name,
ParsedValue *value, bool *syntax_error) {
*field_name = "";
*syntax_error = false;
value->Clear();
std::string line;
std::ifstream::pos_type prev_file_position;
const int prev_indent = *indent;
while (prev_file_position = file.tellg(), std::getline(file, line)) {
cleanup_line(line);
get_indent(line, indent, has_list_prefix);
line = line.substr(*indent); // skip indentation
// If the line is indented less than 'min_indent', it belongs to the outer
// object, and parsing should go back to the previous function in the stack.
if (!line.empty() &&
(*indent < min_indent || (prev_indent > 0 && *indent < prev_indent))) {
// Undo reading the last line.
if (!file.seekg(prev_file_position, std::ios::beg)) {
fprintf(stderr, "Failed to seek to previous file position\n");
*syntax_error = true;
return false;
}
return false;
}
++(*line_idx);
if (line.empty()) continue;
if (prev_indent >= 0 && prev_indent != *indent) {
fprintf(stderr, "Error: Bad indentation at line %d\n", *line_idx);
*syntax_error = true;
return false;
}
if (*has_list_prefix && !is_list) {
fprintf(stderr, "Error: Unexpected list item at line %d\n", *line_idx);
*syntax_error = true;
return false;
}
std::string value_str = line;
size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
*field_name = line.substr(0, colon_pos);
value_str = line.substr(colon_pos + 1);
}
if (!value_str.empty()) {
char *endptr;
if (line.find('.') != std::string::npos) {
value->SetFloatingPointValue(strtod(value_str.c_str(), &endptr));
if (*endptr != '\0') {
fprintf(stderr,
"Error: Failed to parse floating point value from '%s' at "
"line %d\n",
value_str.c_str(), *line_idx);
*syntax_error = true;
return false;
}
} else {
value->SetIntegerValue(strtol(value_str.c_str(), &endptr, 10));
if (*endptr != '\0') {
fprintf(stderr,
"Error: Failed to parse integer from '%s' at line %d\n",
value_str.c_str(), *line_idx);
*syntax_error = true;
return false;
}
}
}
return true;
}
return false; // Reached the end of the file.
}
template <typename T>
bool parse_integer_list(std::ifstream &file, int min_indent, int *line_idx,
std::vector<T> *result) {
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
while (parse_line(file, min_indent, /*is_list=*/true, &indent,
&has_list_prefix, line_idx, &field_name, &value,
&syntax_error)) {
if (!field_name.empty()) {
fprintf(
stderr,
"Error: Unexpected field name '%s' at line %d, expected a number\n",
field_name.c_str(), *line_idx);
return false;
} else if (!has_list_prefix) {
fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
return false;
} else {
T v;
RETURN_IF_FALSE(value.IntegerValueInRange(
static_cast<int64_t>(std::numeric_limits<T>::min()),
static_cast<int64_t>(std::numeric_limits<T>::max()), *line_idx, &v));
result->push_back(v);
}
}
if (syntax_error) return false;
return true;
}
template <typename T>
std::pair<T, bool> value_present(const T &v) {
return std::make_pair(v, true);
}
bool parse_color_properties(std::ifstream &file, int min_indent, int *line_idx,
ColorProperties *color) {
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
*color = {};
while (parse_line(file, min_indent, /*is_list=*/false, &indent,
&has_list_prefix, line_idx, &field_name, &value,
&syntax_error)) {
if (field_name == "color_range") {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
&color->color_range));
} else if (field_name == "color_primaries") {
if (!value.IntegerValueInRange(/*min=*/0, /*max=*/255, *line_idx,
&color->color_primaries)) {
return false;
}
} else if (field_name == "transfer_characteristics") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/255, *line_idx, &color->transfer_characteristics));
} else if (field_name == "matrix_coefficients") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/255, *line_idx, &color->matrix_coefficients));
} else {
fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
field_name.c_str(), *line_idx);
return false;
}
}
if (syntax_error) return false;
return true;
}
bool parse_multilayer_layer_alpha(std::ifstream &file, int min_indent,
int *line_idx, AlphaInformation *alpha_info) {
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
*alpha_info = {};
while (parse_line(file, min_indent, /*is_list=*/false, &indent,
&has_list_prefix, line_idx, &field_name, &value,
&syntax_error)) {
if (field_name == "alpha_use_idc") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/7, *line_idx, &alpha_info->alpha_use_idc));
} else if (field_name == "alpha_simple_flag") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/1, *line_idx, &alpha_info->alpha_simple_flag));
} else if (field_name == "alpha_bit_depth") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/8, /*max=*/15, *line_idx, &alpha_info->alpha_bit_depth));
} else if (field_name == "alpha_clip_idc") {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
&alpha_info->alpha_clip_idc));
} else if (field_name == "alpha_incr_flag") {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
&alpha_info->alpha_incr_flag));
} else if (field_name == "alpha_transparent_value") {
// At this point we may not have parsed 'alpha_bit_depth' yet, so the
// exact range is checked later.
RETURN_IF_FALSE(value.IntegerValueInRange(
std::numeric_limits<uint16_t>::min(),
std::numeric_limits<uint16_t>::max(), *line_idx,
&alpha_info->alpha_transparent_value));
} else if (field_name == "alpha_opaque_value") {
// At this point we may not have parsed 'alpha_bit_depth' yet, so the
// exact range is checked later.
RETURN_IF_FALSE(value.IntegerValueInRange(
std::numeric_limits<uint16_t>::min(),
std::numeric_limits<uint16_t>::max(), *line_idx,
&alpha_info->alpha_opaque_value));
} else if (field_name == "alpha_color_description") {
ColorProperties color;
RETURN_IF_FALSE(parse_color_properties(file, indent, line_idx, &color));
alpha_info->alpha_color_description = value_present(color);
} else {
fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
field_name.c_str(), *line_idx);
return false;
}
}
if (syntax_error) return false;
// Validation.
if (alpha_info->alpha_bit_depth == 0) {
fprintf(stderr,
"Error: alpha_bit_depth must be specified (in range [8, 15]) for "
"alpha info\n");
return false;
}
const int alpha_max = (1 << (alpha_info->alpha_bit_depth + 1)) - 1;
if (alpha_info->alpha_transparent_value > alpha_max) {
fprintf(stderr, "Error: alpha_transparent_value %d out of range [0, %d]\n",
alpha_info->alpha_transparent_value, alpha_max);
return false;
}
if (alpha_info->alpha_opaque_value > alpha_max) {
fprintf(stderr, "Error: alpha_opaque_value %d out of range [0, %d]\n",
alpha_info->alpha_opaque_value, alpha_max);
return false;
}
if (alpha_info->alpha_color_description.second &&
(alpha_info->alpha_use_idc != ALPHA_STRAIGHT)) {
fprintf(stderr,
"Error: alpha_color_description can only be set if alpha_use_idc "
"is %d\n",
ALPHA_STRAIGHT);
return false;
}
return true;
}
bool parse_multilayer_layer_depth(std::ifstream &file, int min_indent,
int *line_idx, DepthInformation *depth_info) {
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
*depth_info = {};
while (parse_line(file, min_indent, /*is_list=*/false, &indent,
&has_list_prefix, line_idx, &field_name, &value,
&syntax_error)) {
if (field_name == "z_near") {
double tmp;
RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
DepthRepresentationElement el;
RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
depth_info->z_near = value_present(el);
} else if (field_name == "z_far") {
double tmp;
RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
DepthRepresentationElement el;
RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
depth_info->z_far = value_present(el);
} else if (field_name == "d_min") {
double tmp;
RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
DepthRepresentationElement el;
RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
depth_info->d_min = value_present(el);
} else if (field_name == "d_max") {
double tmp;
RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
DepthRepresentationElement el;
RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
depth_info->d_max = value_present(el);
} else if (field_name == "depth_representation_type") {
RETURN_IF_FALSE(
value.IntegerValueInRange(/*min=*/0, /*max=*/15, *line_idx,
&depth_info->depth_representation_type));
} else if (field_name == "disparity_ref_view_id") {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/3, *line_idx, &depth_info->disparity_ref_view_id));
} else {
fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
field_name.c_str(), *line_idx);
return false;
}
}
if (syntax_error) return false;
return true;
}
bool validate_layer(const LayerMetadata &layer, bool layer_has_alpha,
bool layer_has_depth) {
if (layer_has_alpha != (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA &&
layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
fprintf(stderr,
"Error: alpha info must be set if and only if layer_type is "
"%d and layer_metadata_scpoe is >= %d\n",
MULTILAYER_LAYER_TYPE_ALPHA, SCOPE_GLOBAL);
return false;
}
if (layer_has_depth != (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH &&
layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
fprintf(stderr,
"Error: depth info must be set if and only if layer_type is "
"%d and layer_metadata_scpoe is >= %d\n",
MULTILAYER_LAYER_TYPE_DEPTH, SCOPE_GLOBAL);
return false;
}
return true;
}
bool parse_multilayer_layer_metadata(std::ifstream &file, int min_indent,
int *line_idx,
std::vector<LayerMetadata> &layers) {
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
bool layer_has_alpha = false;
bool layer_has_depth = false;
while (parse_line(file, min_indent, /*is_list=*/true, &indent,
&has_list_prefix, line_idx, &field_name, &value,
&syntax_error)) {
if (has_list_prefix) {
// Start of a new layer.
if (layers.size() >= kMaxNumSpatialLayers) {
fprintf(stderr,
"Error: Too many layers at line %d, the maximum is %d\n",
*line_idx, kMaxNumSpatialLayers);
return false;
}
// Validate the previous layer.
if (!layers.empty()) {
validate_layer(layers.back(), layer_has_alpha, layer_has_depth);
}
if (layers.size() == 1 && layers.back().layer_color_description.second) {
fprintf(stderr,
"Error: layer_color_description cannot be specified for the "
"first layer\n");
return false;
}
layers.push_back({});
layer_has_alpha = false;
layer_has_depth = false;
}
if (layers.empty()) {
fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
return false;
}
LayerMetadata *layer = &layers.back();
// Check if string starts with field name.
if ((field_name == "layer_type")) {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/31, *line_idx, &layer->layer_type));
} else if ((field_name == "luma_plane_only_flag")) {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
&layer->luma_plane_only_flag));
} else if ((field_name == "layer_view_type")) {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/7, *line_idx, &layer->layer_view_type));
} else if ((field_name == "group_id")) {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
&layer->group_id));
} else if ((field_name == "layer_dependency_idc")) {
RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/7, *line_idx,
&layer->layer_dependency_idc));
} else if ((field_name == "layer_metadata_scope")) {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/3, *line_idx, &layer->layer_metadata_scope));
} else if ((field_name == "layer_color_description")) {
ColorProperties color_properties;
RETURN_IF_FALSE(
parse_color_properties(file, indent, line_idx, &color_properties));
layer->layer_color_description = value_present(color_properties);
} else if ((field_name == "alpha")) {
layer_has_alpha = true;
RETURN_IF_FALSE(parse_multilayer_layer_alpha(
file,
/*min_indent=*/indent + 1, line_idx, &layer->global_alpha_info));
} else if (field_name == "depth") {
layer_has_depth = true;
RETURN_IF_FALSE(parse_multilayer_layer_depth(
file,
/*min_indent=*/indent + 1, line_idx, &layer->global_depth_info));
if ((layer->global_depth_info.d_min.second ||
layer->global_depth_info.d_max.second) &&
layer->global_depth_info.disparity_ref_view_id ==
(layers.size() - 1)) {
fprintf(stderr,
"disparity_ref_view_id must be different from the layer's id "
"for layer %d (zero-based index)\n",
static_cast<int>(layers.size()) - 1);
return false;
}
} else {
fprintf(stderr, "Error: Unknown field %s at line %d\n",
field_name.c_str(), *line_idx);
return false;
}
}
if (syntax_error) return false;
validate_layer(layers.back(), layer_has_alpha, layer_has_depth);
return true;
}
bool parse_multilayer_metadata(std::ifstream &file,
MultilayerMetadata *multilayer) {
int line_idx = 0;
bool has_list_prefix;
int indent = -1;
std::string field_name;
ParsedValue value;
bool syntax_error;
*multilayer = {};
while (parse_line(file, /*min_indent=*/0, /*is_list=*/false, &indent,
&has_list_prefix, &line_idx, &field_name, &value,
&syntax_error)) {
// Check if string starts with field name.
if ((field_name == "use_case")) {
RETURN_IF_FALSE(value.IntegerValueInRange(
/*min=*/0, /*max=*/63, line_idx, &multilayer->use_case));
} else if ((field_name == "layers")) {
RETURN_IF_FALSE(parse_multilayer_layer_metadata(
file,
/*min_indent=*/indent + 1, &line_idx, multilayer->layers));
} else {
fprintf(stderr, "Error: Unknown field %s at line %d\n",
field_name.c_str(), line_idx);
return false;
}
}
if (syntax_error) return false;
return true;
}
std::string format_depth_representation_element(
const std::pair<DepthRepresentationElement, bool> &element) {
if (!element.second) {
return "absent";
} else {
return std::to_string(
depth_representation_element_to_double(element.first)) +
" (sign " + std::to_string(element.first.sign_flag) + " exponent " +
std::to_string(element.first.exponent) + " mantissa " +
std::to_string(element.first.mantissa) + " mantissa_len " +
std::to_string(element.first.mantissa_len) + ")";
}
}
std::string format_color_properties(
const std::pair<ColorProperties, bool> &color_properties) {
if (!color_properties.second) {
return "absent";
} else {
return std::to_string(color_properties.first.color_primaries) + "/" +
std::to_string(color_properties.first.transfer_characteristics) +
"/" + std::to_string(color_properties.first.matrix_coefficients) +
(color_properties.first.color_range ? "F" : "L");
}
}
bool validate_multilayer_metadata(const MultilayerMetadata &multilayer) {
if (multilayer.layers.empty()) {
fprintf(stderr, "Error: No layers found, there must be at least one\n");
return false;
}
if (multilayer.layers.size() > 4) {
fprintf(stderr, "Error: Too many layers, found %d, max 4\n",
static_cast<int>(multilayer.layers.size()));
return false;
}
bool same_view_type = true;
MultilayerViewType view_type = multilayer.layers[0].layer_view_type;
for (const LayerMetadata &layer : multilayer.layers) {
if (layer.layer_view_type != view_type) {
same_view_type = false;
break;
}
}
for (int i = 0; i < static_cast<int>(multilayer.layers.size()); ++i) {
const LayerMetadata &layer = multilayer.layers[i];
switch (multilayer.use_case) {
case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_STEREO:
case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
if (layer.layer_metadata_scope != SCOPE_GLOBAL) {
fprintf(
stderr,
"Error: for use_case %d, all layers must have scope %d, found %d "
"instead for layer %d (zero-based index)\n",
multilayer.use_case, SCOPE_GLOBAL, layer.layer_metadata_scope, i);
return false;
}
break;
default: break;
}
switch (multilayer.use_case) {
case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_ALPHA:
case MULTILAYER_USE_CASE_DEPTH:
case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_444:
case MULTILAYER_USE_CASE_420_444:
if (!same_view_type) {
fprintf(stderr,
"Error: for use_case %d, all layers must have the same view "
"type, found different view_type for layer %d (zero-based "
"index)\n",
multilayer.use_case, i);
return false;
}
default: break;
}
if (layer.layer_type != MULTILAYER_LAYER_TYPE_UNSPECIFIED)
switch (multilayer.use_case) {
case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_ALPHA:
case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
case MULTILAYER_USE_CASE_STEREO_ALPHA:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d or "
"%d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_DEPTH:
case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
case MULTILAYER_USE_CASE_STEREO_DEPTH:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d or "
"%d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_STEREO:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d, "
"found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d, "
"%d, %d, or %d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
MULTILAYER_LAYER_TYPE_TEXTURE_2,
MULTILAYER_LAYER_TYPE_TEXTURE_3,
MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d, "
"%d, %d, or %d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
MULTILAYER_LAYER_TYPE_TEXTURE_2,
MULTILAYER_LAYER_TYPE_TEXTURE_3,
MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_444:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
fprintf(
stderr,
"Error: for use_case %d, all layers must be of type %d, %d, or "
"%d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
MULTILAYER_LAYER_TYPE_TEXTURE_2,
MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
return false;
}
break;
case MULTILAYER_USE_CASE_420_444:
if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
fprintf(stderr,
"Error: for use_case %d, all layers must be of type %d, "
"%d, %d, or %d, found %d for layer %d (zero-based index)\n",
multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
MULTILAYER_LAYER_TYPE_TEXTURE_1,
MULTILAYER_LAYER_TYPE_TEXTURE_2,
MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
return false;
}
break;
default: break;
}
if (layer.layer_dependency_idc >= (1 << i)) {
fprintf(stderr,
"Error: layer_dependency_idc of layer %d (zero-based index) must "
"be in [0, %d], found %d for layer %d (zero-based index)\n",
i, (1 << i) - 1, layer.layer_dependency_idc, i);
return false;
}
if ((layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA ||
layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) &&
layer.layer_color_description.second) {
fprintf(stderr,
"Error: alpha or depth layers cannot have "
"layer_color_description for layer %d (zero-based index)\n",
i);
return false;
}
}
return true;
}
} // namespace
double depth_representation_element_to_double(
const DepthRepresentationElement &e) {
// Let x be a variable that is computed using four variables s, e, m, and n,
// as follows: If e is greater than 0 and less than 127, x is set equal to
// (1)^s*2^(e31) * (1+m÷2^n).
// Otherwise (e is equal to 0), x is set equal to (1)^s*2^(30+n)*m.
if (e.exponent > 0) {
return (e.sign_flag ? -1 : 1) * std::pow(2.0, e.exponent - 31) *
(1 + static_cast<double>(e.mantissa) /
(static_cast<int64_t>(1) << e.mantissa_len));
} else {
return (e.sign_flag ? -1 : 1) * e.mantissa *
std::pow(2.0, -30 + e.mantissa_len);
}
}
bool double_to_depth_representation_element(
double v, DepthRepresentationElement *element) {
const double orig = v;
if (v == 0.0) {
*element = { 0, 0, 0, 1 };
return true;
}
const bool sign = v < 0.0;
if (sign) {
v = -v;
}
int exp = 0;
if (v >= 1.0) {
while (v >= 2.0) {
++exp;
v /= 2;
}
} else {
while (v < 1.0) {
++exp;
v *= 2.0;
}
exp = -exp;
}
if ((exp + 31) <= 0 || (exp + 31) > 126) {
fprintf(stderr,
"Error: Floating point value %f out of range (too large or too "
"small)\n",
orig);
return false;
}
assert(v >= 1.0 && v < 2.0);
v -= 1.0;
uint32_t mantissa = 0;
uint8_t mantissa_len = 0;
constexpr uint8_t kMaxMantissaLen = 32;
do {
const int bit = (v >= 0.5);
mantissa = (mantissa << 1) + bit;
v -= bit * 0.5;
++mantissa_len;
v *= 2.0;
} while (mantissa_len < kMaxMantissaLen && v > 0.0);
*element = { sign, static_cast<uint8_t>(exp + 31), mantissa_len, mantissa };
return true;
}
bool parse_multilayer_file(const char *metadata_path,
MultilayerMetadata *multilayer) {
std::ifstream file(metadata_path);
if (!file.is_open()) {
fprintf(stderr, "Error: Failed to open %s\n", metadata_path);
return false;
}
if (!parse_multilayer_metadata(file, multilayer) ||
!validate_multilayer_metadata(*multilayer)) {
return false;
}
return multilayer;
}
void print_multilayer_metadata(const MultilayerMetadata &multilayer) {
printf("=== Multilayer metadata ===\n");
printf("use_case: %d\n", multilayer.use_case);
for (size_t i = 0; i < multilayer.layers.size(); ++i) {
const LayerMetadata &layer = multilayer.layers[i];
printf("layer %zu\n", i);
printf(" layer_type: %d\n", layer.layer_type);
printf(" luma_plane_only_flag: %d\n", layer.luma_plane_only_flag);
printf(" layer_view_type: %d\n", layer.layer_view_type);
printf(" group_id: %d\n", layer.group_id);
printf(" layer_dependency_idc: %d\n", layer.layer_dependency_idc);
printf(" layer_metadata_scope: %d\n", layer.layer_metadata_scope);
printf(" layer_color_description: %s\n",
format_color_properties(layer.layer_color_description).c_str());
if (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA) {
printf(" alpha:\n");
printf(" alpha_use_idc: %d\n", layer.global_alpha_info.alpha_use_idc);
printf(" alpha_simple_flag: %d\n",
layer.global_alpha_info.alpha_simple_flag);
if (!layer.global_alpha_info.alpha_simple_flag) {
printf(" alpha_bit_depth: %d\n",
layer.global_alpha_info.alpha_bit_depth);
printf(" alpha_clip_idc: %d\n",
layer.global_alpha_info.alpha_clip_idc);
printf(" alpha_incr_flag: %d\n",
layer.global_alpha_info.alpha_incr_flag);
printf(" alpha_transparent_value: %hu\n",
layer.global_alpha_info.alpha_transparent_value);
printf(" alpha_opaque_value: %hu\n",
layer.global_alpha_info.alpha_opaque_value);
printf(" alpha_color_description: %s\n",
format_color_properties(
layer.global_alpha_info.alpha_color_description)
.c_str());
}
} else if (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) {
printf(" depth:\n");
printf(" z_near: %s\n",
format_depth_representation_element(layer.global_depth_info.z_near)
.c_str());
printf(" z_far: %s\n",
format_depth_representation_element(layer.global_depth_info.z_far)
.c_str());
printf(" d_min: %s\n",
format_depth_representation_element(layer.global_depth_info.d_min)
.c_str());
printf(" d_max: %s\n",
format_depth_representation_element(layer.global_depth_info.d_max)
.c_str());
printf(" depth_representation_type: %d\n",
layer.global_depth_info.depth_representation_type);
printf(" disparity_ref_view_id: %d\n",
layer.global_depth_info.disparity_ref_view_id);
printf("\n");
}
}
printf("\n");
}
} // namespace libaom_examples