tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

multilayer_metadata.cc (37497B)


      1 /*
      2 * Copyright (c) 2024, Alliance for Open Media. All rights reserved.
      3 *
      4 * This source code is subject to the terms of the BSD 2 Clause License and
      5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
      6 * was not distributed with this source code in the LICENSE file, you can
      7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
      8 * Media Patent License 1.0 was not distributed with this source code in the
      9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
     10 */
     11 
     12 #include "examples/multilayer_metadata.h"
     13 
     14 #include <assert.h>
     15 #include <inttypes.h>
     16 #include <limits.h>
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 
     21 #include <cmath>
     22 #include <fstream>
     23 #include <iostream>
     24 #include <limits>
     25 #include <string>
     26 #include <vector>
     27 
     28 #include "aom/aom_integer.h"
     29 #include "examples/multilayer_metadata.h"
     30 
     31 namespace libaom_examples {
     32 
     33 namespace {
     34 
     35 #define RETURN_IF_FALSE(A) \
     36  do {                     \
     37    if (!(A)) {            \
     38      return false;        \
     39    }                      \
     40  } while (0)
     41 
     42 constexpr int kMaxNumSpatialLayers = 4;
     43 
     44 // Removes comments and trailing spaces from the line.
     45 void cleanup_line(std::string &line) {
     46  // Remove everything after the first '#'.
     47  std::size_t comment_pos = line.find('#');
     48  if (comment_pos != std::string::npos) {
     49    line.resize(comment_pos);
     50  }
     51  // Remove spaces at the end of the line.
     52  while (!line.empty() && line.back() == ' ') {
     53    line.resize(line.length() - 1);
     54  }
     55 }
     56 
     57 // Finds the indentation level of the line, and sets 'has_list_prefix' to true
     58 // if the line has a '-' indicating a new item in a list.
     59 void get_indent(const std::string &line, int *indent, bool *has_list_prefix) {
     60  *indent = 0;
     61  *has_list_prefix = false;
     62  while (
     63      *indent < static_cast<int>(line.length()) &&
     64      (line[*indent] == ' ' || line[*indent] == '\t' || line[*indent] == '-')) {
     65    if (line[*indent] == '-') {
     66      *has_list_prefix = true;
     67    }
     68    ++(*indent);
     69  }
     70 }
     71 
     72 class ParsedValue {
     73 public:
     74  enum class Type { kNone, kInteger, kFloatingPoint };
     75 
     76  void SetIntegerValue(int64_t v) {
     77    type_ = Type::kInteger;
     78    int_value_ = v;
     79  }
     80 
     81  void SetFloatingPointValue(double v) {
     82    type_ = Type::kFloatingPoint;
     83    double_value_ = v;
     84  }
     85 
     86  void Clear() { type_ = Type::kNone; }
     87 
     88  bool ValueAsFloatingPoint(int line_idx, double *v) {
     89    if (type_ == Type::kNone) {
     90      fprintf(
     91          stderr,
     92          "No value found where floating point value was expected at line %d\n",
     93          line_idx);
     94      return false;
     95    }
     96    *v = (type_ == Type::kFloatingPoint) ? double_value_
     97                                         : static_cast<double>(int_value_);
     98    return true;
     99  }
    100 
    101  template <typename T>
    102  bool IntegerValueInRange(int64_t min, int64_t max, int line_idx, T *v) {
    103    switch (type_) {
    104      case Type::kInteger:
    105        if (int_value_ < min || int_value_ > max) {
    106          fprintf(stderr,
    107                  "Integer value %" PRId64 " out of range [%" PRId64
    108                  ", %" PRId64 "] at line %d\n",
    109                  int_value_, min, max, line_idx);
    110          return false;
    111        }
    112        *v = static_cast<T>(int_value_);
    113        return true;
    114      case Type::kFloatingPoint:
    115        fprintf(stderr,
    116                "Floating point value found where integer was expected at line "
    117                "%d\n",
    118                line_idx);
    119        return false;
    120      case Type::kNone:
    121      default:
    122        fprintf(stderr,
    123                "No value found where integer was expected at line %d\n",
    124                line_idx);
    125        return false;
    126    }
    127  }
    128 
    129 private:
    130  Type type_ = Type::kNone;
    131  int64_t int_value_ = 0;
    132  double double_value_ = 0.0f;
    133 };
    134 
    135 /*
    136 * Parses the next line from the file, skipping empty lines.
    137 * Returns false if the end of the file was reached, or if the line was indented
    138 * less than 'min_indent', meaning that parsing should go back to the previous
    139 * function in the stack.
    140 *
    141 * 'min_indent' is the minimum indentation expected for the next line.
    142 * 'is_list' must be true if the line is allowed to contain list items ('-').
    143 * 'indent' MUST be initialized to -1 before the first call, and is then set to
    144 * the indentation of the line.
    145 * 'has_list_prefix' is set to true if the line starts a new list item with '-'.
    146 * 'line_idx' is set to the index of the last line read.
    147 * 'field_name' is set to the field name if the line contains a colon, or to an
    148 * empty string otherwise.
    149 * 'value' is set to the value on the line if present.
    150 * In case of syntax error, 'syntax_error' is set to true and the function
    151 * returns false.
    152 */
    153 bool parse_line(std::ifstream &file, int min_indent, bool is_list, int *indent,
    154                bool *has_list_prefix, int *line_idx, std::string *field_name,
    155                ParsedValue *value, bool *syntax_error) {
    156  *field_name = "";
    157  *syntax_error = false;
    158  value->Clear();
    159  std::string line;
    160  std::ifstream::pos_type prev_file_position;
    161  const int prev_indent = *indent;
    162  while (prev_file_position = file.tellg(), std::getline(file, line)) {
    163    cleanup_line(line);
    164    get_indent(line, indent, has_list_prefix);
    165    line = line.substr(*indent);  // skip indentation
    166    // If the line is indented less than 'min_indent', it belongs to the outer
    167    // object, and parsing should go back to the previous function in the stack.
    168    if (!line.empty() &&
    169        (*indent < min_indent || (prev_indent > 0 && *indent < prev_indent))) {
    170      // Undo reading the last line.
    171      if (!file.seekg(prev_file_position, std::ios::beg)) {
    172        fprintf(stderr, "Failed to seek to previous file position\n");
    173        *syntax_error = true;
    174        return false;
    175      }
    176      return false;
    177    }
    178 
    179    ++(*line_idx);
    180    if (line.empty()) continue;
    181 
    182    if (prev_indent >= 0 && prev_indent != *indent) {
    183      fprintf(stderr, "Error: Bad indentation at line %d\n", *line_idx);
    184      *syntax_error = true;
    185      return false;
    186    }
    187    if (*has_list_prefix && !is_list) {
    188      fprintf(stderr, "Error: Unexpected list item at line %d\n", *line_idx);
    189      *syntax_error = true;
    190      return false;
    191    }
    192 
    193    std::string value_str = line;
    194    size_t colon_pos = line.find(':');
    195    if (colon_pos != std::string::npos) {
    196      *field_name = line.substr(0, colon_pos);
    197      value_str = line.substr(colon_pos + 1);
    198    }
    199    if (!value_str.empty()) {
    200      char *endptr;
    201      if (line.find('.') != std::string::npos) {
    202        value->SetFloatingPointValue(strtod(value_str.c_str(), &endptr));
    203        if (*endptr != '\0') {
    204          fprintf(stderr,
    205                  "Error: Failed to parse floating point value from '%s' at "
    206                  "line %d\n",
    207                  value_str.c_str(), *line_idx);
    208          *syntax_error = true;
    209          return false;
    210        }
    211      } else {
    212        value->SetIntegerValue(strtol(value_str.c_str(), &endptr, 10));
    213        if (*endptr != '\0') {
    214          fprintf(stderr,
    215                  "Error: Failed to parse integer from '%s' at line %d\n",
    216                  value_str.c_str(), *line_idx);
    217          *syntax_error = true;
    218          return false;
    219        }
    220      }
    221    }
    222    return true;
    223  }
    224  return false;  // Reached the end of the file.
    225 }
    226 
    227 template <typename T>
    228 bool parse_integer_list(std::ifstream &file, int min_indent, int *line_idx,
    229                        std::vector<T> *result) {
    230  bool has_list_prefix;
    231  int indent = -1;
    232  std::string field_name;
    233  ParsedValue value;
    234  bool syntax_error;
    235  while (parse_line(file, min_indent, /*is_list=*/true, &indent,
    236                    &has_list_prefix, line_idx, &field_name, &value,
    237                    &syntax_error)) {
    238    if (!field_name.empty()) {
    239      fprintf(
    240          stderr,
    241          "Error: Unexpected field name '%s' at line %d, expected a number\n",
    242          field_name.c_str(), *line_idx);
    243      return false;
    244    } else if (!has_list_prefix) {
    245      fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
    246      return false;
    247    } else {
    248      T v;
    249      RETURN_IF_FALSE(value.IntegerValueInRange(
    250          static_cast<int64_t>(std::numeric_limits<T>::min()),
    251          static_cast<int64_t>(std::numeric_limits<T>::max()), *line_idx, &v));
    252      result->push_back(v);
    253    }
    254  }
    255  if (syntax_error) return false;
    256  return true;
    257 }
    258 
    259 template <typename T>
    260 std::pair<T, bool> value_present(const T &v) {
    261  return std::make_pair(v, true);
    262 }
    263 
    264 bool parse_color_properties(std::ifstream &file, int min_indent, int *line_idx,
    265                            ColorProperties *color) {
    266  bool has_list_prefix;
    267  int indent = -1;
    268  std::string field_name;
    269  ParsedValue value;
    270  bool syntax_error;
    271  *color = {};
    272  while (parse_line(file, min_indent, /*is_list=*/false, &indent,
    273                    &has_list_prefix, line_idx, &field_name, &value,
    274                    &syntax_error)) {
    275    if (field_name == "color_range") {
    276      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
    277                                                &color->color_range));
    278    } else if (field_name == "color_primaries") {
    279      if (!value.IntegerValueInRange(/*min=*/0, /*max=*/255, *line_idx,
    280                                     &color->color_primaries)) {
    281        return false;
    282      }
    283    } else if (field_name == "transfer_characteristics") {
    284      RETURN_IF_FALSE(value.IntegerValueInRange(
    285          /*min=*/0, /*max=*/255, *line_idx, &color->transfer_characteristics));
    286    } else if (field_name == "matrix_coefficients") {
    287      RETURN_IF_FALSE(value.IntegerValueInRange(
    288          /*min=*/0, /*max=*/255, *line_idx, &color->matrix_coefficients));
    289    } else {
    290      fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
    291              field_name.c_str(), *line_idx);
    292      return false;
    293    }
    294  }
    295  if (syntax_error) return false;
    296  return true;
    297 }
    298 
    299 bool parse_multilayer_layer_alpha(std::ifstream &file, int min_indent,
    300                                  int *line_idx, AlphaInformation *alpha_info) {
    301  bool has_list_prefix;
    302  int indent = -1;
    303  std::string field_name;
    304  ParsedValue value;
    305  bool syntax_error;
    306  *alpha_info = {};
    307  while (parse_line(file, min_indent, /*is_list=*/false, &indent,
    308                    &has_list_prefix, line_idx, &field_name, &value,
    309                    &syntax_error)) {
    310    if (field_name == "alpha_use_idc") {
    311      RETURN_IF_FALSE(value.IntegerValueInRange(
    312          /*min=*/0, /*max=*/7, *line_idx, &alpha_info->alpha_use_idc));
    313    } else if (field_name == "alpha_simple_flag") {
    314      RETURN_IF_FALSE(value.IntegerValueInRange(
    315          /*min=*/0, /*max=*/1, *line_idx, &alpha_info->alpha_simple_flag));
    316    } else if (field_name == "alpha_bit_depth") {
    317      RETURN_IF_FALSE(value.IntegerValueInRange(
    318          /*min=*/8, /*max=*/15, *line_idx, &alpha_info->alpha_bit_depth));
    319    } else if (field_name == "alpha_clip_idc") {
    320      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
    321                                                &alpha_info->alpha_clip_idc));
    322    } else if (field_name == "alpha_incr_flag") {
    323      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
    324                                                &alpha_info->alpha_incr_flag));
    325    } else if (field_name == "alpha_transparent_value") {
    326      // At this point we may not have parsed 'alpha_bit_depth' yet, so the
    327      // exact range is checked later.
    328      RETURN_IF_FALSE(value.IntegerValueInRange(
    329          std::numeric_limits<uint16_t>::min(),
    330          std::numeric_limits<uint16_t>::max(), *line_idx,
    331          &alpha_info->alpha_transparent_value));
    332    } else if (field_name == "alpha_opaque_value") {
    333      // At this point we may not have parsed 'alpha_bit_depth' yet, so the
    334      // exact range is checked later.
    335      RETURN_IF_FALSE(value.IntegerValueInRange(
    336          std::numeric_limits<uint16_t>::min(),
    337          std::numeric_limits<uint16_t>::max(), *line_idx,
    338          &alpha_info->alpha_opaque_value));
    339    } else if (field_name == "alpha_color_description") {
    340      ColorProperties color;
    341      RETURN_IF_FALSE(parse_color_properties(file, indent, line_idx, &color));
    342      alpha_info->alpha_color_description = value_present(color);
    343    } else {
    344      fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
    345              field_name.c_str(), *line_idx);
    346      return false;
    347    }
    348  }
    349  if (syntax_error) return false;
    350 
    351  // Validation.
    352  if (alpha_info->alpha_bit_depth == 0) {
    353    fprintf(stderr,
    354            "Error: alpha_bit_depth must be specified (in range [8, 15]) for "
    355            "alpha info\n");
    356    return false;
    357  }
    358  const int alpha_max = (1 << (alpha_info->alpha_bit_depth + 1)) - 1;
    359  if (alpha_info->alpha_transparent_value > alpha_max) {
    360    fprintf(stderr, "Error: alpha_transparent_value %d out of range [0, %d]\n",
    361            alpha_info->alpha_transparent_value, alpha_max);
    362    return false;
    363  }
    364  if (alpha_info->alpha_opaque_value > alpha_max) {
    365    fprintf(stderr, "Error: alpha_opaque_value %d out of range [0, %d]\n",
    366            alpha_info->alpha_opaque_value, alpha_max);
    367    return false;
    368  }
    369  if (alpha_info->alpha_color_description.second &&
    370      (alpha_info->alpha_use_idc != ALPHA_STRAIGHT)) {
    371    fprintf(stderr,
    372            "Error: alpha_color_description can only be set if alpha_use_idc "
    373            "is %d\n",
    374            ALPHA_STRAIGHT);
    375    return false;
    376  }
    377  return true;
    378 }
    379 
    380 bool parse_multilayer_layer_depth(std::ifstream &file, int min_indent,
    381                                  int *line_idx, DepthInformation *depth_info) {
    382  bool has_list_prefix;
    383  int indent = -1;
    384  std::string field_name;
    385  ParsedValue value;
    386  bool syntax_error;
    387  *depth_info = {};
    388  while (parse_line(file, min_indent, /*is_list=*/false, &indent,
    389                    &has_list_prefix, line_idx, &field_name, &value,
    390                    &syntax_error)) {
    391    if (field_name == "z_near") {
    392      double tmp;
    393      RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
    394      DepthRepresentationElement el;
    395      RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
    396      depth_info->z_near = value_present(el);
    397    } else if (field_name == "z_far") {
    398      double tmp;
    399      RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
    400      DepthRepresentationElement el;
    401      RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
    402      depth_info->z_far = value_present(el);
    403    } else if (field_name == "d_min") {
    404      double tmp;
    405      RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
    406      DepthRepresentationElement el;
    407      RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
    408      depth_info->d_min = value_present(el);
    409    } else if (field_name == "d_max") {
    410      double tmp;
    411      RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
    412      DepthRepresentationElement el;
    413      RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
    414      depth_info->d_max = value_present(el);
    415    } else if (field_name == "depth_representation_type") {
    416      RETURN_IF_FALSE(
    417          value.IntegerValueInRange(/*min=*/0, /*max=*/15, *line_idx,
    418                                    &depth_info->depth_representation_type));
    419    } else if (field_name == "disparity_ref_view_id") {
    420      RETURN_IF_FALSE(value.IntegerValueInRange(
    421          /*min=*/0, /*max=*/3, *line_idx, &depth_info->disparity_ref_view_id));
    422    } else {
    423      fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
    424              field_name.c_str(), *line_idx);
    425      return false;
    426    }
    427  }
    428  if (syntax_error) return false;
    429 
    430  return true;
    431 }
    432 
    433 bool parse_multilayer_layer_local_metadata(
    434    std::ifstream &file, int min_indent, int *line_idx,
    435    std::vector<FrameLocalMetadata> &frames) {
    436  bool has_list_prefix;
    437  int indent = -1;
    438  std::string field_name;
    439  ParsedValue value;
    440  bool syntax_error;
    441  while (parse_line(file, min_indent, /*is_list=*/true, &indent,
    442                    &has_list_prefix, line_idx, &field_name, &value,
    443                    &syntax_error)) {
    444    if (has_list_prefix) {
    445      frames.push_back({});
    446    }
    447    if (frames.empty()) {
    448      fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
    449      return false;
    450    }
    451 
    452    if (field_name == "frame_idx") {
    453      RETURN_IF_FALSE(
    454          value.IntegerValueInRange(0, std::numeric_limits<long>::max(),
    455                                    *line_idx, &frames.back().frame_idx));
    456    } else if (field_name == "alpha") {
    457      RETURN_IF_FALSE(parse_multilayer_layer_alpha(
    458          file,
    459          /*min_indent=*/indent + 1, line_idx, &frames.back().alpha));
    460 
    461    } else if (field_name == "depth") {
    462      RETURN_IF_FALSE(parse_multilayer_layer_depth(
    463          file,
    464          /*min_indent=*/indent + 1, line_idx, &frames.back().depth));
    465 
    466    } else {
    467      fprintf(stderr, "Error: Unknown field %s at line %d\n",
    468              field_name.c_str(), *line_idx);
    469      return false;
    470    }
    471  }
    472  if (syntax_error) return false;
    473  return true;
    474 }
    475 
    476 bool validate_layer(const LayerMetadata &layer, bool layer_has_alpha,
    477                    bool layer_has_depth) {
    478  if (layer_has_alpha != (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA &&
    479                          layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
    480    fprintf(stderr,
    481            "Error: alpha info must be set if and only if layer_type is "
    482            "%d and layer_metadata_scope is >= %d\n",
    483            MULTILAYER_LAYER_TYPE_ALPHA, SCOPE_GLOBAL);
    484    return false;
    485  }
    486  if (layer_has_depth != (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH &&
    487                          layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
    488    fprintf(stderr,
    489            "Error: depth info must be set if and only if layer_type is "
    490            "%d and layer_metadata_scope is >= %d\n",
    491            MULTILAYER_LAYER_TYPE_DEPTH, SCOPE_GLOBAL);
    492    return false;
    493  }
    494  return true;
    495 }
    496 
    497 bool parse_multilayer_layer_metadata(std::ifstream &file, int min_indent,
    498                                     int *line_idx,
    499                                     std::vector<LayerMetadata> &layers) {
    500  bool has_list_prefix;
    501  int indent = -1;
    502  std::string field_name;
    503  ParsedValue value;
    504  bool syntax_error;
    505  bool layer_has_alpha = false;
    506  bool layer_has_depth = false;
    507  while (parse_line(file, min_indent, /*is_list=*/true, &indent,
    508                    &has_list_prefix, line_idx, &field_name, &value,
    509                    &syntax_error)) {
    510    if (has_list_prefix) {
    511      // Start of a new layer.
    512      if (layers.size() >= kMaxNumSpatialLayers) {
    513        fprintf(stderr,
    514                "Error: Too many layers at line %d, the maximum is %d\n",
    515                *line_idx, kMaxNumSpatialLayers);
    516        return false;
    517      }
    518 
    519      // Validate the previous layer.
    520      if (!layers.empty()) {
    521        RETURN_IF_FALSE(
    522            validate_layer(layers.back(), layer_has_alpha, layer_has_depth));
    523      }
    524      if (layers.size() == 1 && layers.back().layer_color_description.second) {
    525        fprintf(stderr,
    526                "Error: layer_color_description cannot be specified for the "
    527                "first layer\n");
    528        return false;
    529      }
    530 
    531      layers.push_back({});
    532      layer_has_alpha = false;
    533      layer_has_depth = false;
    534    }
    535    if (layers.empty()) {
    536      fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
    537      return false;
    538    }
    539 
    540    LayerMetadata *layer = &layers.back();
    541    // Check if string starts with field name.
    542    if ((field_name == "layer_type")) {
    543      RETURN_IF_FALSE(value.IntegerValueInRange(
    544          /*min=*/0, /*max=*/31, *line_idx, &layer->layer_type));
    545    } else if ((field_name == "luma_plane_only_flag")) {
    546      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
    547                                                &layer->luma_plane_only_flag));
    548    } else if ((field_name == "layer_view_type")) {
    549      RETURN_IF_FALSE(value.IntegerValueInRange(
    550          /*min=*/0, /*max=*/7, *line_idx, &layer->layer_view_type));
    551    } else if ((field_name == "group_id")) {
    552      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
    553                                                &layer->group_id));
    554    } else if ((field_name == "layer_dependency_idc")) {
    555      RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/7, *line_idx,
    556                                                &layer->layer_dependency_idc));
    557    } else if ((field_name == "layer_metadata_scope")) {
    558      RETURN_IF_FALSE(value.IntegerValueInRange(
    559          /*min=*/0, /*max=*/3, *line_idx, &layer->layer_metadata_scope));
    560    } else if ((field_name == "layer_color_description")) {
    561      ColorProperties color_properties;
    562      RETURN_IF_FALSE(
    563          parse_color_properties(file, indent, line_idx, &color_properties));
    564      layer->layer_color_description = value_present(color_properties);
    565    } else if ((field_name == "alpha")) {
    566      layer_has_alpha = true;
    567      RETURN_IF_FALSE(parse_multilayer_layer_alpha(file,
    568                                                   /*min_indent=*/indent + 1,
    569                                                   line_idx, &layer->alpha));
    570    } else if (field_name == "depth") {
    571      layer_has_depth = true;
    572      RETURN_IF_FALSE(parse_multilayer_layer_depth(file,
    573                                                   /*min_indent=*/indent + 1,
    574                                                   line_idx, &layer->depth));
    575      if ((layer->depth.d_min.second || layer->depth.d_max.second) &&
    576          layer->depth.disparity_ref_view_id == (layers.size() - 1)) {
    577        fprintf(stderr,
    578                "disparity_ref_view_id must be different from the layer's id "
    579                "for layer %d (zero-based index)\n",
    580                static_cast<int>(layers.size()) - 1);
    581        return false;
    582      }
    583    } else if (field_name == "local_metadata") {
    584      RETURN_IF_FALSE(parse_multilayer_layer_local_metadata(
    585          file, /*min_indent=*/indent + 1, line_idx, layer->local_metadata));
    586    } else {
    587      fprintf(stderr, "Error: Unknown field %s at line %d\n",
    588              field_name.c_str(), *line_idx);
    589      return false;
    590    }
    591  }
    592  if (syntax_error) return false;
    593  RETURN_IF_FALSE(
    594      validate_layer(layers.back(), layer_has_alpha, layer_has_depth));
    595  return true;
    596 }
    597 
    598 bool parse_multilayer_metadata(std::ifstream &file,
    599                               MultilayerMetadata *multilayer) {
    600  int line_idx = 0;
    601  bool has_list_prefix;
    602  int indent = -1;
    603  std::string field_name;
    604  ParsedValue value;
    605  bool syntax_error;
    606  *multilayer = {};
    607  while (parse_line(file, /*min_indent=*/0, /*is_list=*/false, &indent,
    608                    &has_list_prefix, &line_idx, &field_name, &value,
    609                    &syntax_error)) {
    610    // Check if string starts with field name.
    611    if ((field_name == "use_case")) {
    612      RETURN_IF_FALSE(value.IntegerValueInRange(
    613          /*min=*/0, /*max=*/63, line_idx, &multilayer->use_case));
    614    } else if ((field_name == "layers")) {
    615      RETURN_IF_FALSE(parse_multilayer_layer_metadata(
    616          file,
    617          /*min_indent=*/indent + 1, &line_idx, multilayer->layers));
    618    } else {
    619      fprintf(stderr, "Error: Unknown field %s at line %d\n",
    620              field_name.c_str(), line_idx);
    621      return false;
    622    }
    623  }
    624  if (syntax_error) return false;
    625  return true;
    626 }
    627 
    628 std::string format_depth_representation_element(
    629    const std::pair<DepthRepresentationElement, bool> &element) {
    630  if (!element.second) {
    631    return "absent";
    632  } else {
    633    return std::to_string(
    634               depth_representation_element_to_double(element.first)) +
    635           " (sign " + std::to_string(element.first.sign_flag) + " exponent " +
    636           std::to_string(element.first.exponent) + " mantissa " +
    637           std::to_string(element.first.mantissa) + " mantissa_len " +
    638           std::to_string(element.first.mantissa_len) + ")";
    639  }
    640 }
    641 
    642 std::string format_color_properties(
    643    const std::pair<ColorProperties, bool> &color_properties) {
    644  if (!color_properties.second) {
    645    return "absent";
    646  } else {
    647    return std::to_string(color_properties.first.color_primaries) + "/" +
    648           std::to_string(color_properties.first.transfer_characteristics) +
    649           "/" + std::to_string(color_properties.first.matrix_coefficients) +
    650           (color_properties.first.color_range ? "F" : "L");
    651  }
    652 }
    653 
    654 bool validate_multilayer_metadata(const MultilayerMetadata &multilayer) {
    655  if (multilayer.layers.empty()) {
    656    fprintf(stderr, "Error: No layers found, there must be at least one\n");
    657    return false;
    658  }
    659  if (multilayer.layers.size() > 4) {
    660    fprintf(stderr, "Error: Too many layers, found %d, max 4\n",
    661            static_cast<int>(multilayer.layers.size()));
    662    return false;
    663  }
    664 
    665  bool same_view_type = true;
    666  MultilayerViewType view_type = multilayer.layers[0].layer_view_type;
    667  for (const LayerMetadata &layer : multilayer.layers) {
    668    if (layer.layer_view_type != view_type) {
    669      same_view_type = false;
    670      break;
    671    }
    672  }
    673 
    674  for (int i = 0; i < static_cast<int>(multilayer.layers.size()); ++i) {
    675    const LayerMetadata &layer = multilayer.layers[i];
    676    switch (multilayer.use_case) {
    677      case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
    678      case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
    679      case MULTILAYER_USE_CASE_STEREO:
    680      case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
    681      case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
    682      case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
    683      case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
    684        if (layer.layer_metadata_scope != SCOPE_GLOBAL) {
    685          fprintf(
    686              stderr,
    687              "Error: for use_case %d, all layers must have scope %d, found %d "
    688              "instead for layer %d (zero-based index)\n",
    689              multilayer.use_case, SCOPE_GLOBAL, layer.layer_metadata_scope, i);
    690          return false;
    691        }
    692        break;
    693      default: break;
    694    }
    695    switch (multilayer.use_case) {
    696      case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
    697      case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
    698      case MULTILAYER_USE_CASE_ALPHA:
    699      case MULTILAYER_USE_CASE_DEPTH:
    700      case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
    701      case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
    702      case MULTILAYER_USE_CASE_444:
    703      case MULTILAYER_USE_CASE_420_444:
    704        if (!same_view_type) {
    705          fprintf(stderr,
    706                  "Error: for use_case %d, all layers must have the same view "
    707                  "type, found different view_type for layer %d (zero-based "
    708                  "index)\n",
    709                  multilayer.use_case, i);
    710          return false;
    711        }
    712      default: break;
    713    }
    714    if (layer.layer_type != MULTILAYER_LAYER_TYPE_UNSPECIFIED)
    715      switch (multilayer.use_case) {
    716        case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
    717        case MULTILAYER_USE_CASE_ALPHA:
    718        case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
    719        case MULTILAYER_USE_CASE_STEREO_ALPHA:
    720          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
    721              layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
    722            fprintf(stderr,
    723                    "Error: for use_case %d, all layers must be of type %d or "
    724                    "%d, found %d for layer %d (zero-based index)\n",
    725                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
    726                    MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
    727            return false;
    728          }
    729          break;
    730        case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
    731        case MULTILAYER_USE_CASE_DEPTH:
    732        case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
    733        case MULTILAYER_USE_CASE_STEREO_DEPTH:
    734          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
    735              layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
    736            fprintf(stderr,
    737                    "Error: for use_case %d, all layers must be of type %d or "
    738                    "%d, found %d for layer %d (zero-based index)\n",
    739                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
    740                    MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
    741            return false;
    742          }
    743          break;
    744        case MULTILAYER_USE_CASE_STEREO:
    745          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE) {
    746            fprintf(stderr,
    747                    "Error: for use_case %d, all layers must be of type %d, "
    748                    "found %d for layer %d (zero-based index)\n",
    749                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
    750                    layer.layer_type, i);
    751            return false;
    752          }
    753          break;
    754        case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
    755          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
    756              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
    757              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
    758              layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
    759            fprintf(stderr,
    760                    "Error: for use_case %d, all layers must be of type %d, "
    761                    "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
    762                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
    763                    MULTILAYER_LAYER_TYPE_TEXTURE_2,
    764                    MULTILAYER_LAYER_TYPE_TEXTURE_3,
    765                    MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
    766            return false;
    767          }
    768          break;
    769        case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
    770          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
    771              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
    772              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
    773              layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
    774            fprintf(stderr,
    775                    "Error: for use_case %d, all layers must be of type %d, "
    776                    "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
    777                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
    778                    MULTILAYER_LAYER_TYPE_TEXTURE_2,
    779                    MULTILAYER_LAYER_TYPE_TEXTURE_3,
    780                    MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
    781            return false;
    782          }
    783          break;
    784        case MULTILAYER_USE_CASE_444:
    785          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
    786              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
    787              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
    788            fprintf(
    789                stderr,
    790                "Error: for use_case %d, all layers must be of type %d, %d, or "
    791                "%d, found %d for layer %d (zero-based index)\n",
    792                multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
    793                MULTILAYER_LAYER_TYPE_TEXTURE_2,
    794                MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
    795            return false;
    796          }
    797          break;
    798        case MULTILAYER_USE_CASE_420_444:
    799          if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
    800              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
    801              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
    802              layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
    803            fprintf(stderr,
    804                    "Error: for use_case %d, all layers must be of type %d, "
    805                    "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
    806                    multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
    807                    MULTILAYER_LAYER_TYPE_TEXTURE_1,
    808                    MULTILAYER_LAYER_TYPE_TEXTURE_2,
    809                    MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
    810            return false;
    811          }
    812          break;
    813        default: break;
    814      }
    815    if (layer.layer_dependency_idc >= (1 << i)) {
    816      fprintf(stderr,
    817              "Error: layer_dependency_idc of layer %d (zero-based index) must "
    818              "be in [0, %d], found %d for layer %d (zero-based index)\n",
    819              i, (1 << i) - 1, layer.layer_dependency_idc, i);
    820      return false;
    821    }
    822    if ((layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA ||
    823         layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) &&
    824        layer.layer_color_description.second) {
    825      fprintf(stderr,
    826              "Error: alpha or depth layers cannot have "
    827              "layer_color_description for layer %d (zero-based index)\n",
    828              i);
    829      return false;
    830    }
    831  }
    832  return true;
    833 }
    834 
    835 void print_alpha_information(const AlphaInformation &alpha) {
    836  printf("    alpha_simple_flag: %d\n", alpha.alpha_simple_flag);
    837  if (!alpha.alpha_simple_flag) {
    838    printf("    alpha_bit_depth: %d\n", alpha.alpha_bit_depth);
    839    printf("    alpha_clip_idc: %d\n", alpha.alpha_clip_idc);
    840    printf("    alpha_incr_flag: %d\n", alpha.alpha_incr_flag);
    841    printf("    alpha_transparent_value: %hu\n", alpha.alpha_transparent_value);
    842    printf("    alpha_opaque_value: %hu\n", alpha.alpha_opaque_value);
    843    printf("    alpha_color_description: %s\n",
    844           format_color_properties(alpha.alpha_color_description).c_str());
    845  }
    846 }
    847 
    848 void print_depth_information(const DepthInformation &depth) {
    849  printf("    z_near: %s\n",
    850         format_depth_representation_element(depth.z_near).c_str());
    851  printf("    z_far: %s\n",
    852         format_depth_representation_element(depth.z_far).c_str());
    853  printf("    d_min: %s\n",
    854         format_depth_representation_element(depth.d_min).c_str());
    855  printf("    d_max: %s\n",
    856         format_depth_representation_element(depth.d_max).c_str());
    857  printf("    depth_representation_type: %d\n",
    858         depth.depth_representation_type);
    859  printf("    disparity_ref_view_id: %d\n", depth.disparity_ref_view_id);
    860 }
    861 
    862 }  // namespace
    863 
    864 double depth_representation_element_to_double(
    865    const DepthRepresentationElement &e) {
    866  // Let x be a variable that is computed using four variables s, e, m, and n,
    867  // as follows: If e is greater than 0 and less than 127, x is set equal to
    868  // (−1)^s*2^(e−31) * (1+m÷2^n).
    869  // Otherwise (e is equal to 0), x is set equal to (−1)^s*2^−(30+n)*m.
    870  if (e.exponent > 0) {
    871    return (e.sign_flag ? -1 : 1) * std::pow(2.0, e.exponent - 31) *
    872           (1 + static_cast<double>(e.mantissa) /
    873                    (static_cast<int64_t>(1) << e.mantissa_len));
    874  } else {
    875    return (e.sign_flag ? -1 : 1) * e.mantissa *
    876           std::pow(2.0, -30 + e.mantissa_len);
    877  }
    878 }
    879 
    880 bool double_to_depth_representation_element(
    881    double v, DepthRepresentationElement *element) {
    882  const double orig = v;
    883  if (v == 0.0) {
    884    *element = { 0, 0, 0, 1 };
    885    return true;
    886  }
    887  const bool sign = v < 0.0;
    888  if (sign) {
    889    v = -v;
    890  }
    891  int exp = 0;
    892  if (v >= 1.0) {
    893    while (v >= 2.0) {
    894      ++exp;
    895      v /= 2;
    896    }
    897  } else {
    898    while (v < 1.0) {
    899      ++exp;
    900      v *= 2.0;
    901    }
    902    exp = -exp;
    903  }
    904  if ((exp + 31) <= 0 || (exp + 31) > 126) {
    905    fprintf(stderr,
    906            "Error: Floating point value %f out of range (too large or too "
    907            "small)\n",
    908            orig);
    909    return false;
    910  }
    911  assert(v >= 1.0 && v < 2.0);
    912  v -= 1.0;
    913  uint32_t mantissa = 0;
    914  uint8_t mantissa_len = 0;
    915  constexpr uint8_t kMaxMantissaLen = 32;
    916  do {
    917    const int bit = (v >= 0.5);
    918    mantissa = (mantissa << 1) + bit;
    919    v -= bit * 0.5;
    920    ++mantissa_len;
    921    v *= 2.0;
    922  } while (mantissa_len < kMaxMantissaLen && v > 0.0);
    923  *element = { sign, static_cast<uint8_t>(exp + 31), mantissa_len, mantissa };
    924  return true;
    925 }
    926 
    927 bool parse_multilayer_file(const char *metadata_path,
    928                           MultilayerMetadata *multilayer) {
    929  std::ifstream file(metadata_path);
    930  if (!file.is_open()) {
    931    fprintf(stderr, "Error: Failed to open %s\n", metadata_path);
    932    return false;
    933  }
    934 
    935  if (!parse_multilayer_metadata(file, multilayer) ||
    936      !validate_multilayer_metadata(*multilayer)) {
    937    return false;
    938  }
    939  return multilayer;
    940 }
    941 
    942 void print_multilayer_metadata(const MultilayerMetadata &multilayer) {
    943  printf("=== Multilayer metadata ===\n");
    944  printf("use_case: %d\n", multilayer.use_case);
    945  for (size_t i = 0; i < multilayer.layers.size(); ++i) {
    946    const LayerMetadata &layer = multilayer.layers[i];
    947    printf("layer %zu\n", i);
    948    printf("  layer_type: %d\n", layer.layer_type);
    949    printf("  luma_plane_only_flag: %d\n", layer.luma_plane_only_flag);
    950    printf("  layer_view_type: %d\n", layer.layer_view_type);
    951    printf("  group_id: %d\n", layer.group_id);
    952    printf("  layer_dependency_idc: %d\n", layer.layer_dependency_idc);
    953    printf("  layer_metadata_scope: %d\n", layer.layer_metadata_scope);
    954    printf("  layer_color_description: %s\n",
    955           format_color_properties(layer.layer_color_description).c_str());
    956    if (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA) {
    957      if (layer.layer_metadata_scope >= SCOPE_GLOBAL) {
    958        printf("  global alpha:\n");
    959        print_alpha_information(layer.alpha);
    960      }
    961      for (const FrameLocalMetadata &local_metadata : layer.local_metadata) {
    962        printf("  local alpha for frame %ld:\n", local_metadata.frame_idx);
    963        print_alpha_information(local_metadata.alpha);
    964      }
    965    } else if (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) {
    966      printf("  global depth:\n");
    967      print_depth_information(layer.depth);
    968      for (const FrameLocalMetadata &local_metadata : layer.local_metadata) {
    969        printf("  local depth for frame %ld:\n", local_metadata.frame_idx);
    970        print_depth_information(local_metadata.depth);
    971      }
    972    }
    973  }
    974  printf("\n");
    975 }
    976 
    977 }  // namespace libaom_examples