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