• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
cleanup_line(std::string & 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.
get_indent(const std::string & line,int * indent,bool * has_list_prefix)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 
SetIntegerValue(int64_t v)76   void SetIntegerValue(int64_t v) {
77     type_ = Type::kInteger;
78     int_value_ = v;
79   }
80 
SetFloatingPointValue(double v)81   void SetFloatingPointValue(double v) {
82     type_ = Type::kFloatingPoint;
83     double_value_ = v;
84   }
85 
Clear()86   void Clear() { type_ = Type::kNone; }
87 
ValueAsFloatingPoint(int line_idx,double * v)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>
IntegerValueInRange(int64_t min,int64_t max,int line_idx,T * v)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  */
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)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>
parse_integer_list(std::ifstream & file,int min_indent,int * line_idx,std::vector<T> * result)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>
value_present(const T & v)260 std::pair<T, bool> value_present(const T &v) {
261   return std::make_pair(v, true);
262 }
263 
parse_color_properties(std::ifstream & file,int min_indent,int * line_idx,ColorProperties * color)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 
parse_multilayer_layer_alpha(std::ifstream & file,int min_indent,int * line_idx,AlphaInformation * alpha_info)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_bit_depth") {
314       RETURN_IF_FALSE(value.IntegerValueInRange(
315           /*min=*/8, /*max=*/15, *line_idx, &alpha_info->alpha_bit_depth));
316     } else if (field_name == "alpha_clip_idc") {
317       RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
318                                                 &alpha_info->alpha_clip_idc));
319     } else if (field_name == "alpha_incr_flag") {
320       RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
321                                                 &alpha_info->alpha_incr_flag));
322     } else if (field_name == "alpha_transparent_value") {
323       // At this point we may not have parsed 'alpha_bit_depth' yet, so the
324       // exact range is checked later.
325       RETURN_IF_FALSE(value.IntegerValueInRange(
326           std::numeric_limits<uint16_t>::min(),
327           std::numeric_limits<uint16_t>::max(), *line_idx,
328           &alpha_info->alpha_transparent_value));
329     } else if (field_name == "alpha_opaque_value") {
330       // At this point we may not have parsed 'alpha_bit_depth' yet, so the
331       // exact range is checked later.
332       RETURN_IF_FALSE(value.IntegerValueInRange(
333           std::numeric_limits<uint16_t>::min(),
334           std::numeric_limits<uint16_t>::max(), *line_idx,
335           &alpha_info->alpha_opaque_value));
336     } else if (field_name == "alpha_color_description") {
337       ColorProperties color;
338       RETURN_IF_FALSE(parse_color_properties(file, indent, line_idx, &color));
339       alpha_info->alpha_color_description = value_present(color);
340     } else if (field_name == "label_type_id") {
341       RETURN_IF_FALSE(
342           parse_integer_list<uint16_t>(file, /*min_indent=*/indent + 1,
343                                        line_idx, &alpha_info->label_type_id));
344     } else {
345       fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
346               field_name.c_str(), *line_idx);
347       return false;
348     }
349   }
350   if (syntax_error) return false;
351 
352   // Validation.
353   if (alpha_info->alpha_bit_depth == 0) {
354     fprintf(stderr,
355             "Error: alpha_bit_depth must be specified (in range [8, 15]) for "
356             "alpha info\n");
357     return false;
358   }
359   const int alpha_max = (1 << (alpha_info->alpha_bit_depth + 1)) - 1;
360   if (alpha_info->alpha_transparent_value > alpha_max) {
361     fprintf(stderr, "Error: alpha_transparent_value %d out of range [0, %d]\n",
362             alpha_info->alpha_transparent_value, alpha_max);
363     return false;
364   }
365   if (alpha_info->alpha_opaque_value > alpha_max) {
366     fprintf(stderr, "Error: alpha_opaque_value %d out of range [0, %d]\n",
367             alpha_info->alpha_opaque_value, alpha_max);
368     return false;
369   }
370   if ((!alpha_info->label_type_id.empty()) &&
371       (alpha_info->alpha_use_idc != ALPHA_SEGMENTATION)) {
372     fprintf(stderr,
373             "Error: label_type_id can only be set if alpha_use_idc is %d\n",
374             ALPHA_SEGMENTATION);
375     return false;
376   }
377   const int alpha_range = (std::abs(alpha_info->alpha_opaque_value -
378                                     alpha_info->alpha_transparent_value) +
379                            1);
380   if (!alpha_info->label_type_id.empty() &&
381       static_cast<int>(alpha_info->label_type_id.size()) != alpha_range) {
382     fprintf(stderr,
383             "Error: if present, label_type_id size must be "
384             "equal to the range of alpha values between "
385             "alpha_transparent_value and alpha_opaque_value (expected "
386             "%d values, found %d values)\n",
387             alpha_range, static_cast<int>(alpha_info->label_type_id.size()));
388     return false;
389   }
390   if (alpha_info->alpha_color_description.second &&
391       (alpha_info->alpha_use_idc != ALPHA_STRAIGHT)) {
392     fprintf(stderr,
393             "Error: alpha_color_description can only be set if alpha_use_idc "
394             "is %d\n",
395             ALPHA_STRAIGHT);
396     return false;
397   }
398   return true;
399 }
400 
parse_multilayer_layer_depth(std::ifstream & file,int min_indent,int * line_idx,DepthInformation * depth_info)401 bool parse_multilayer_layer_depth(std::ifstream &file, int min_indent,
402                                   int *line_idx, DepthInformation *depth_info) {
403   bool has_list_prefix;
404   int indent = -1;
405   std::string field_name;
406   ParsedValue value;
407   bool syntax_error;
408   *depth_info = {};
409   while (parse_line(file, min_indent, /*is_list=*/false, &indent,
410                     &has_list_prefix, line_idx, &field_name, &value,
411                     &syntax_error)) {
412     if (field_name == "z_near") {
413       double tmp;
414       RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
415       DepthRepresentationElement el;
416       RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
417       depth_info->z_near = value_present(el);
418     } else if (field_name == "z_far") {
419       double tmp;
420       RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
421       DepthRepresentationElement el;
422       RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
423       depth_info->z_far = value_present(el);
424     } else if (field_name == "d_min") {
425       double tmp;
426       RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
427       DepthRepresentationElement el;
428       RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
429       depth_info->d_min = value_present(el);
430     } else if (field_name == "d_max") {
431       double tmp;
432       RETURN_IF_FALSE(value.ValueAsFloatingPoint(*line_idx, &tmp));
433       DepthRepresentationElement el;
434       RETURN_IF_FALSE(double_to_depth_representation_element(tmp, &el));
435       depth_info->d_max = value_present(el);
436     } else if (field_name == "depth_representation_type") {
437       RETURN_IF_FALSE(
438           value.IntegerValueInRange(/*min=*/0, /*max=*/15, *line_idx,
439                                     &depth_info->depth_representation_type));
440     } else if (field_name == "disparity_ref_view_id") {
441       RETURN_IF_FALSE(value.IntegerValueInRange(
442           /*min=*/0, /*max=*/3, *line_idx, &depth_info->disparity_ref_view_id));
443     } else if (field_name == "depth_nonlinear_precision") {
444       RETURN_IF_FALSE(
445           value.IntegerValueInRange(/*min=*/8, /*max=*/23, *line_idx,
446                                     &depth_info->depth_nonlinear_precision));
447     } else if (field_name == "depth_nonlinear_representation_model") {
448       RETURN_IF_FALSE(parse_integer_list<uint32_t>(
449           file,
450           /*min_indent=*/indent + 1, line_idx,
451           &depth_info->depth_nonlinear_representation_model));
452     } else {
453       fprintf(stderr, "Error: Unknown field '%s' at line %d\n",
454               field_name.c_str(), *line_idx);
455       return false;
456     }
457   }
458   if (syntax_error) return false;
459 
460   // Validation.
461   if (depth_info->depth_representation_type == 3 &&
462       depth_info->depth_nonlinear_precision == 0) {
463     fprintf(stderr,
464             "Error: depth_nonlinear_precision must be specified (in range [8, "
465             "23]) when "
466             "depth_representation_type is 3\n");
467     return false;
468   }
469   if ((depth_info->depth_representation_type == 3) !=
470       (!depth_info->depth_nonlinear_representation_model.empty())) {
471     fprintf(stderr,
472             "Error: depth_nonlinear_representation_model must be set if and "
473             "only if depth_representation_type is 3\n");
474     return false;
475   }
476   const uint32_t depth_max = (1 << depth_info->depth_nonlinear_precision) - 1;
477   for (uint32_t v : depth_info->depth_nonlinear_representation_model) {
478     if (v > depth_max) {
479       fprintf(stderr,
480               "Error: depth_nonlinear_representation_model value %d out of "
481               "range [0, %d]\n",
482               v, depth_max);
483       return false;
484     }
485   }
486 
487   return true;
488 }
489 
validate_layer(const LayerMetadata & layer,bool layer_has_alpha,bool layer_has_depth)490 bool validate_layer(const LayerMetadata &layer, bool layer_has_alpha,
491                     bool layer_has_depth) {
492   if (layer_has_alpha != (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA &&
493                           layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
494     fprintf(stderr,
495             "Error: alpha info must be set if and only if layer_type is "
496             "%d and layer_metadata_scpoe is >= %d\n",
497             MULTILAYER_LAYER_TYPE_ALPHA, SCOPE_GLOBAL);
498     return false;
499   }
500   if (layer_has_depth != (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH &&
501                           layer.layer_metadata_scope >= SCOPE_GLOBAL)) {
502     fprintf(stderr,
503             "Error: depth info must be set if and only if layer_type is "
504             "%d and layer_metadata_scpoe is >= %d\n",
505             MULTILAYER_LAYER_TYPE_DEPTH, SCOPE_GLOBAL);
506     return false;
507   }
508   return true;
509 }
510 
parse_multilayer_layer_metadata(std::ifstream & file,int min_indent,int * line_idx,std::vector<LayerMetadata> & layers)511 bool parse_multilayer_layer_metadata(std::ifstream &file, int min_indent,
512                                      int *line_idx,
513                                      std::vector<LayerMetadata> &layers) {
514   bool has_list_prefix;
515   int indent = -1;
516   std::string field_name;
517   ParsedValue value;
518   bool syntax_error;
519   bool layer_has_alpha = false;
520   bool layer_has_depth = false;
521   while (parse_line(file, min_indent, /*is_list=*/true, &indent,
522                     &has_list_prefix, line_idx, &field_name, &value,
523                     &syntax_error)) {
524     if (has_list_prefix) {
525       // Start of a new layer.
526       if (layers.size() >= kMaxNumSpatialLayers) {
527         fprintf(stderr,
528                 "Error: Too many layers at line %d, the maximum is %d\n",
529                 *line_idx, kMaxNumSpatialLayers);
530         return false;
531       }
532 
533       // Validate the previous layer.
534       if (!layers.empty()) {
535         validate_layer(layers.back(), layer_has_alpha, layer_has_depth);
536       }
537       if (layers.size() == 1 && layers.back().layer_color_description.second) {
538         fprintf(stderr,
539                 "Error: layer_color_description cannot be specified for the "
540                 "first layer\n");
541         return false;
542       }
543 
544       layers.push_back({});
545       layer_has_alpha = false;
546       layer_has_depth = false;
547     }
548     if (layers.empty()) {
549       fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx);
550       return false;
551     }
552 
553     LayerMetadata *layer = &layers.back();
554     // Check if string starts with field name.
555     if ((field_name == "layer_type")) {
556       RETURN_IF_FALSE(value.IntegerValueInRange(
557           /*min=*/0, /*max=*/31, *line_idx, &layer->layer_type));
558     } else if ((field_name == "luma_plane_only_flag")) {
559       RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/1, *line_idx,
560                                                 &layer->luma_plane_only_flag));
561     } else if ((field_name == "layer_view_type")) {
562       RETURN_IF_FALSE(value.IntegerValueInRange(
563           /*min=*/0, /*max=*/7, *line_idx, &layer->layer_view_type));
564     } else if ((field_name == "group_id")) {
565       RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/3, *line_idx,
566                                                 &layer->group_id));
567     } else if ((field_name == "layer_dependency_idc")) {
568       RETURN_IF_FALSE(value.IntegerValueInRange(/*min=*/0, /*max=*/7, *line_idx,
569                                                 &layer->layer_dependency_idc));
570     } else if ((field_name == "layer_metadata_scope")) {
571       RETURN_IF_FALSE(value.IntegerValueInRange(
572           /*min=*/0, /*max=*/3, *line_idx, &layer->layer_metadata_scope));
573     } else if ((field_name == "layer_color_description")) {
574       ColorProperties color_properties;
575       RETURN_IF_FALSE(
576           parse_color_properties(file, indent, line_idx, &color_properties));
577       layer->layer_color_description = value_present(color_properties);
578     } else if ((field_name == "alpha")) {
579       layer_has_alpha = true;
580       RETURN_IF_FALSE(parse_multilayer_layer_alpha(
581           file,
582           /*min_indent=*/indent + 1, line_idx, &layer->global_alpha_info));
583     } else if (field_name == "depth") {
584       layer_has_depth = true;
585       RETURN_IF_FALSE(parse_multilayer_layer_depth(
586           file,
587           /*min_indent=*/indent + 1, line_idx, &layer->global_depth_info));
588       if ((layer->global_depth_info.d_min.second ||
589            layer->global_depth_info.d_max.second) &&
590           layer->global_depth_info.disparity_ref_view_id ==
591               (layers.size() - 1)) {
592         fprintf(stderr,
593                 "disparity_ref_view_id must be different from the layer's id "
594                 "for layer %d (zero-based index)\n",
595                 static_cast<int>(layers.size()) - 1);
596         return false;
597       }
598     } else {
599       fprintf(stderr, "Error: Unknown field %s at line %d\n",
600               field_name.c_str(), *line_idx);
601       return false;
602     }
603   }
604   if (syntax_error) return false;
605   validate_layer(layers.back(), layer_has_alpha, layer_has_depth);
606   return true;
607 }
608 
parse_multilayer_metadata(std::ifstream & file,MultilayerMetadata * multilayer)609 bool parse_multilayer_metadata(std::ifstream &file,
610                                MultilayerMetadata *multilayer) {
611   int line_idx = 0;
612   bool has_list_prefix;
613   int indent = -1;
614   std::string field_name;
615   ParsedValue value;
616   bool syntax_error;
617   *multilayer = {};
618   while (parse_line(file, /*min_indent=*/0, /*is_list=*/false, &indent,
619                     &has_list_prefix, &line_idx, &field_name, &value,
620                     &syntax_error)) {
621     // Check if string starts with field name.
622     if ((field_name == "use_case")) {
623       RETURN_IF_FALSE(value.IntegerValueInRange(
624           /*min=*/0, /*max=*/63, line_idx, &multilayer->use_case));
625     } else if ((field_name == "layers")) {
626       RETURN_IF_FALSE(parse_multilayer_layer_metadata(
627           file,
628           /*min_indent=*/indent + 1, &line_idx, multilayer->layers));
629     } else {
630       fprintf(stderr, "Error: Unknown field %s at line %d\n",
631               field_name.c_str(), line_idx);
632       return false;
633     }
634   }
635   if (syntax_error) return false;
636   return true;
637 }
638 
format_depth_representation_element(const std::pair<DepthRepresentationElement,bool> & element)639 std::string format_depth_representation_element(
640     const std::pair<DepthRepresentationElement, bool> &element) {
641   if (!element.second) {
642     return "absent";
643   } else {
644     return std::to_string(
645                depth_representation_element_to_double(element.first)) +
646            " (sign " + std::to_string(element.first.sign_flag) + " exponent " +
647            std::to_string(element.first.exponent) + " mantissa " +
648            std::to_string(element.first.mantissa) + " mantissa_len " +
649            std::to_string(element.first.mantissa_len) + ")";
650   }
651 }
652 
format_color_properties(const std::pair<ColorProperties,bool> & color_properties)653 std::string format_color_properties(
654     const std::pair<ColorProperties, bool> &color_properties) {
655   if (!color_properties.second) {
656     return "absent";
657   } else {
658     return std::to_string(color_properties.first.color_primaries) + "/" +
659            std::to_string(color_properties.first.transfer_characteristics) +
660            "/" + std::to_string(color_properties.first.matrix_coefficients) +
661            (color_properties.first.color_range ? "F" : "L");
662   }
663 }
664 
validate_multilayer_metadata(const MultilayerMetadata & multilayer)665 bool validate_multilayer_metadata(const MultilayerMetadata &multilayer) {
666   if (multilayer.layers.empty()) {
667     fprintf(stderr, "Error: No layers found, there must be at least one\n");
668     return false;
669   }
670   if (multilayer.layers.size() > 4) {
671     fprintf(stderr, "Error: Too many layers, found %d, max 4\n",
672             static_cast<int>(multilayer.layers.size()));
673     return false;
674   }
675 
676   bool same_view_type = true;
677   MultilayerViewType view_type = multilayer.layers[0].layer_view_type;
678   for (const LayerMetadata &layer : multilayer.layers) {
679     if (layer.layer_view_type != view_type) {
680       same_view_type = false;
681       break;
682     }
683   }
684 
685   for (int i = 0; i < static_cast<int>(multilayer.layers.size()); ++i) {
686     const LayerMetadata &layer = multilayer.layers[i];
687     switch (multilayer.use_case) {
688       case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
689       case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
690       case MULTILAYER_USE_CASE_STEREO:
691       case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
692       case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
693       case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
694       case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
695         if (layer.layer_metadata_scope != SCOPE_GLOBAL) {
696           fprintf(
697               stderr,
698               "Error: for use_case %d, all layers must have scope %d, found %d "
699               "instead for layer %d (zero-based index)\n",
700               multilayer.use_case, SCOPE_GLOBAL, layer.layer_metadata_scope, i);
701           return false;
702         }
703         break;
704       default: break;
705     }
706     switch (multilayer.use_case) {
707       case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
708       case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
709       case MULTILAYER_USE_CASE_ALPHA:
710       case MULTILAYER_USE_CASE_DEPTH:
711       case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
712       case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
713       case MULTILAYER_USE_CASE_444:
714       case MULTILAYER_USE_CASE_420_444:
715         if (!same_view_type) {
716           fprintf(stderr,
717                   "Error: for use_case %d, all layers must have the same view "
718                   "type, found different view_type for layer %d (zero-based "
719                   "index)\n",
720                   multilayer.use_case, i);
721           return false;
722         }
723       default: break;
724     }
725     if (layer.layer_type != MULTILAYER_LAYER_TYPE_UNSPECIFIED)
726       switch (multilayer.use_case) {
727         case MULTILAYER_USE_CASE_GLOBAL_ALPHA:
728         case MULTILAYER_USE_CASE_ALPHA:
729         case MULTILAYER_USE_CASE_STEREO_GLOBAL_ALPHA:
730         case MULTILAYER_USE_CASE_STEREO_ALPHA:
731           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
732               layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
733             fprintf(stderr,
734                     "Error: for use_case %d, all layers must be of type %d or "
735                     "%d, found %d for layer %d (zero-based index)\n",
736                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
737                     MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
738             return false;
739           }
740           break;
741         case MULTILAYER_USE_CASE_GLOBAL_DEPTH:
742         case MULTILAYER_USE_CASE_DEPTH:
743         case MULTILAYER_USE_CASE_STEREO_GLOBAL_DEPTH:
744         case MULTILAYER_USE_CASE_STEREO_DEPTH:
745           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
746               layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
747             fprintf(stderr,
748                     "Error: for use_case %d, all layers must be of type %d or "
749                     "%d, found %d for layer %d (zero-based index)\n",
750                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
751                     MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
752             return false;
753           }
754           break;
755         case MULTILAYER_USE_CASE_STEREO:
756           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE) {
757             fprintf(stderr,
758                     "Error: for use_case %d, all layers must be of type %d, "
759                     "found %d for layer %d (zero-based index)\n",
760                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
761                     layer.layer_type, i);
762             return false;
763           }
764           break;
765         case MULTILAYER_USE_CASE_444_GLOBAL_ALPHA:
766           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
767               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
768               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
769               layer.layer_type != MULTILAYER_LAYER_TYPE_ALPHA) {
770             fprintf(stderr,
771                     "Error: for use_case %d, all layers must be of type %d, "
772                     "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
773                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
774                     MULTILAYER_LAYER_TYPE_TEXTURE_2,
775                     MULTILAYER_LAYER_TYPE_TEXTURE_3,
776                     MULTILAYER_LAYER_TYPE_ALPHA, layer.layer_type, i);
777             return false;
778           }
779           break;
780         case MULTILAYER_USE_CASE_444_GLOBAL_DEPTH:
781           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
782               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
783               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3 &&
784               layer.layer_type != MULTILAYER_LAYER_TYPE_DEPTH) {
785             fprintf(stderr,
786                     "Error: for use_case %d, all layers must be of type %d, "
787                     "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
788                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
789                     MULTILAYER_LAYER_TYPE_TEXTURE_2,
790                     MULTILAYER_LAYER_TYPE_TEXTURE_3,
791                     MULTILAYER_LAYER_TYPE_DEPTH, layer.layer_type, i);
792             return false;
793           }
794           break;
795         case MULTILAYER_USE_CASE_444:
796           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
797               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
798               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
799             fprintf(
800                 stderr,
801                 "Error: for use_case %d, all layers must be of type %d, %d, or "
802                 "%d, found %d for layer %d (zero-based index)\n",
803                 multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE_1,
804                 MULTILAYER_LAYER_TYPE_TEXTURE_2,
805                 MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
806             return false;
807           }
808           break;
809         case MULTILAYER_USE_CASE_420_444:
810           if (layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE &&
811               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_1 &&
812               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_2 &&
813               layer.layer_type != MULTILAYER_LAYER_TYPE_TEXTURE_3) {
814             fprintf(stderr,
815                     "Error: for use_case %d, all layers must be of type %d, "
816                     "%d, %d, or %d, found %d for layer %d (zero-based index)\n",
817                     multilayer.use_case, MULTILAYER_LAYER_TYPE_TEXTURE,
818                     MULTILAYER_LAYER_TYPE_TEXTURE_1,
819                     MULTILAYER_LAYER_TYPE_TEXTURE_2,
820                     MULTILAYER_LAYER_TYPE_TEXTURE_3, layer.layer_type, i);
821             return false;
822           }
823           break;
824         default: break;
825       }
826     if (layer.layer_dependency_idc >= (1 << i)) {
827       fprintf(stderr,
828               "Error: layer_dependency_idc of layer %d (zero-based index) must "
829               "be in [0, %d], found %d for layer %d (zero-based index)\n",
830               i, (1 << i) - 1, layer.layer_dependency_idc, i);
831       return false;
832     }
833     if ((layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA ||
834          layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) &&
835         layer.layer_color_description.second) {
836       fprintf(stderr,
837               "Error: alpha or depth layers cannot have "
838               "layer_color_description for layer %d (zero-based index)\n",
839               i);
840       return false;
841     }
842   }
843   return true;
844 }
845 
846 }  // namespace
847 
depth_representation_element_to_double(const DepthRepresentationElement & e)848 double depth_representation_element_to_double(
849     const DepthRepresentationElement &e) {
850   // Let x be a variable that is computed using four variables s, e, m, and n,
851   // as follows: If e is greater than 0 and less than 127, x is set equal to
852   // (−1)^s*2^(e−31) * (1+m÷2^n).
853   // Otherwise (e is equal to 0), x is set equal to (−1)^s*2^−(30+n)*m.
854   if (e.exponent > 0) {
855     return (e.sign_flag ? -1 : 1) * std::pow(2.0, e.exponent - 31) *
856            (1 + static_cast<double>(e.mantissa) /
857                     (static_cast<int64_t>(1) << e.mantissa_len));
858   } else {
859     return (e.sign_flag ? -1 : 1) * e.mantissa *
860            std::pow(2.0, -30 + e.mantissa_len);
861   }
862 }
863 
double_to_depth_representation_element(double v,DepthRepresentationElement * element)864 bool double_to_depth_representation_element(
865     double v, DepthRepresentationElement *element) {
866   const double orig = v;
867   if (v == 0.0) {
868     *element = { 0, 0, 0, 1 };
869     return true;
870   }
871   const bool sign = v < 0.0;
872   if (sign) {
873     v = -v;
874   }
875   int exp = 0;
876   if (v >= 1.0) {
877     while (v >= 2.0) {
878       ++exp;
879       v /= 2;
880     }
881   } else {
882     while (v < 1.0) {
883       ++exp;
884       v *= 2.0;
885     }
886     exp = -exp;
887   }
888   if ((exp + 31) <= 0 || (exp + 31) > 126) {
889     fprintf(stderr,
890             "Error: Floating point value %f out of range (too large or too "
891             "small)\n",
892             orig);
893     return false;
894   }
895   assert(v >= 1.0 && v < 2.0);
896   v -= 1.0;
897   uint32_t mantissa = 0;
898   uint8_t mantissa_len = 0;
899   constexpr uint8_t kMaxMantissaLen = 32;
900   do {
901     const int bit = (v >= 0.5);
902     mantissa = (mantissa << 1) + bit;
903     v -= bit * 0.5;
904     ++mantissa_len;
905     v *= 2.0;
906   } while (mantissa_len < kMaxMantissaLen && v > 0.0);
907   *element = { sign, static_cast<uint8_t>(exp + 31), mantissa_len, mantissa };
908   return true;
909 }
910 
parse_multilayer_file(const char * metadata_path,MultilayerMetadata * multilayer)911 bool parse_multilayer_file(const char *metadata_path,
912                            MultilayerMetadata *multilayer) {
913   std::ifstream file(metadata_path);
914   if (!file.is_open()) {
915     fprintf(stderr, "Error: Failed to open %s\n", metadata_path);
916     return false;
917   }
918 
919   if (!parse_multilayer_metadata(file, multilayer) ||
920       !validate_multilayer_metadata(*multilayer)) {
921     return false;
922   }
923   return multilayer;
924 }
925 
print_multilayer_metadata(const MultilayerMetadata & multilayer)926 void print_multilayer_metadata(const MultilayerMetadata &multilayer) {
927   printf("=== Multilayer metadata ===\n");
928   printf("use_case: %d\n", multilayer.use_case);
929   for (size_t i = 0; i < multilayer.layers.size(); ++i) {
930     const LayerMetadata &layer = multilayer.layers[i];
931     printf("layer %zu\n", i);
932     printf("  layer_type: %d\n", layer.layer_type);
933     printf("  luma_plane_only_flag: %d\n", layer.luma_plane_only_flag);
934     printf("  layer_view_type: %d\n", layer.layer_view_type);
935     printf("  group_id: %d\n", layer.group_id);
936     printf("  layer_dependency_idc: %d\n", layer.layer_dependency_idc);
937     printf("  layer_metadata_scope: %d\n", layer.layer_metadata_scope);
938     printf("  layer_color_description: %s\n",
939            format_color_properties(layer.layer_color_description).c_str());
940     if (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA) {
941       printf("  alpha:\n");
942       printf("    alpha_use_idc: %d\n", layer.global_alpha_info.alpha_use_idc);
943       printf("    alpha_bit_depth: %d\n",
944              layer.global_alpha_info.alpha_bit_depth);
945       printf("    alpha_clip_idc: %d\n",
946              layer.global_alpha_info.alpha_clip_idc);
947       printf("    alpha_incr_flag: %d\n",
948              layer.global_alpha_info.alpha_incr_flag);
949       printf("    alpha_transparent_value: %hu\n",
950              layer.global_alpha_info.alpha_transparent_value);
951       printf("    alpha_opaque_value: %hu\n",
952              layer.global_alpha_info.alpha_opaque_value);
953       printf("    alpha_color_description: %s\n",
954              format_color_properties(
955                  layer.global_alpha_info.alpha_color_description)
956                  .c_str());
957       printf("    label_type_id:");
958       for (uint16_t label_type_id : layer.global_alpha_info.label_type_id) {
959         printf(" %d", label_type_id);
960       }
961       printf("\n");
962     } else if (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) {
963       printf("  depth:\n");
964       printf("    z_near: %s\n",
965              format_depth_representation_element(layer.global_depth_info.z_near)
966                  .c_str());
967       printf("    z_far: %s\n",
968              format_depth_representation_element(layer.global_depth_info.z_far)
969                  .c_str());
970       printf("    d_min: %s\n",
971              format_depth_representation_element(layer.global_depth_info.d_min)
972                  .c_str());
973       printf("    d_max: %s\n",
974              format_depth_representation_element(layer.global_depth_info.d_max)
975                  .c_str());
976       printf("    depth_representation_type: %d\n",
977              layer.global_depth_info.depth_representation_type);
978       printf("    disparity_ref_view_id: %d\n",
979              layer.global_depth_info.disparity_ref_view_id);
980       printf("    depth_nonlinear_precision: %d\n",
981              layer.global_depth_info.depth_nonlinear_precision);
982       printf("    depth_nonlinear_representation_model:");
983       for (uint32_t depth_nonlinear_representation_model :
984            layer.global_depth_info.depth_nonlinear_representation_model) {
985         printf(" %d", depth_nonlinear_representation_model);
986       }
987       printf("\n");
988     }
989   }
990   printf("\n");
991 }
992 
993 }  // namespace libaom_examples
994