Coverage Report

Created: 2026-07-20 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/box.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
#include "libheif/heif.h"
21
#include <cstddef>
22
#include <cstdint>
23
#include "box.h"
24
#include "security_limits.h"
25
#include "nclx.h"
26
#include "codecs/jpeg_boxes.h"
27
#include "codecs/jpeg2000_boxes.h"
28
#include "codecs/hevc_boxes.h"
29
#include "image-items/mask_image.h"
30
#include "codecs/vvc_boxes.h"
31
#include "codecs/avc_boxes.h"
32
#include "codecs/avif_boxes.h"
33
#include "image-items/tiled.h"
34
#include "sequences/seq_boxes.h"
35
36
#include <iomanip>
37
#include <utility>
38
#include <iostream>
39
#include <algorithm>
40
#include <cstring>
41
#include <set>
42
#include <cassert>
43
#include <array>
44
#include <mutex>
45
46
47
#if WITH_UNCOMPRESSED_CODEC
48
#include "codecs/uncompressed/unc_boxes.h"
49
#endif
50
51
#ifndef M_PI
52
#define M_PI 3.14159265358979323846
53
#endif
54
55
#if !defined(_WIN32)
56
#include <unistd.h>
57
#else
58
#include <fcntl.h>
59
#include <io.h>
60
#endif
61
62
63
Fraction::Fraction(int32_t num, int32_t den)
64
5.28k
{
65
  // Reduce resolution of fraction until we are in a safe range.
66
  // We need this as adding fractions may lead to very large denominators
67
  // (e.g. 0x10000 * 0x10000 > 0x100000000 -> overflow, leading to integer 0)
68
69
5.28k
  numerator = num;
70
5.28k
  denominator = den;
71
72
32.7k
  while (denominator > MAX_FRACTION_VALUE || denominator < -MAX_FRACTION_VALUE) {
73
27.4k
    numerator /= 2;
74
27.4k
    denominator /= 2;
75
27.4k
  }
76
77
21.3k
  while (denominator > 1 && (numerator > MAX_FRACTION_VALUE || numerator < -MAX_FRACTION_VALUE)) {
78
16.0k
    numerator /= 2;
79
16.0k
    denominator /= 2;
80
16.0k
  }
81
5.28k
}
82
83
Fraction::Fraction(uint32_t num, uint32_t den)
84
2.64k
{
85
2.64k
  assert(num <= (uint32_t) std::numeric_limits<int32_t>::max());
86
2.64k
  assert(den <= (uint32_t) std::numeric_limits<int32_t>::max());
87
88
2.64k
  *this = Fraction(int32_t(num), int32_t(den));
89
2.64k
}
90
91
Fraction::Fraction(int64_t num, int64_t den)
92
0
{
93
0
  while (num < std::numeric_limits<int32_t>::min() || num > std::numeric_limits<int32_t>::max() ||
94
0
         den < std::numeric_limits<int32_t>::min() || den > std::numeric_limits<int32_t>::max()) {
95
0
    num = (num + (num>=0 ? 1 : -1)) / 2;
96
0
    den = (den + (den>=0 ? 1 : -1)) / 2;
97
0
  }
98
99
0
  numerator = static_cast<int32_t>(num);
100
0
  denominator = static_cast<int32_t>(den);
101
0
}
102
103
Fraction Fraction::operator+(const Fraction& b) const
104
0
{
105
0
  if (denominator == b.denominator) {
106
0
    int64_t n = int64_t{numerator} + b.numerator;
107
0
    int64_t d = denominator;
108
0
    return Fraction{n,d};
109
0
  }
110
0
  else {
111
0
    int64_t n = int64_t{numerator} * b.denominator + int64_t{b.numerator} * denominator;
112
0
    int64_t d = int64_t{denominator} * b.denominator;
113
0
    return Fraction{n,d};
114
0
  }
115
0
}
116
117
Fraction Fraction::operator-(const Fraction& b) const
118
0
{
119
0
  if (denominator == b.denominator) {
120
0
    int64_t n = int64_t{numerator} - b.numerator;
121
0
    int64_t d = denominator;
122
0
    return Fraction{n,d};
123
0
  }
124
0
  else {
125
0
    int64_t n = int64_t{numerator} * b.denominator - int64_t{b.numerator} * denominator;
126
0
    int64_t d = int64_t{denominator} * b.denominator;
127
0
    return Fraction{n,d};
128
0
  }
129
0
}
130
131
Fraction Fraction::operator+(int v) const
132
0
{
133
0
  return Fraction{numerator + v * int64_t(denominator), int64_t(denominator)};
134
0
}
135
136
Fraction Fraction::operator-(int v) const
137
0
{
138
0
  return Fraction{numerator - v * int64_t(denominator), int64_t(denominator)};
139
0
}
140
141
Fraction Fraction::operator/(int v) const
142
0
{
143
0
  return Fraction{int64_t(numerator), int64_t(denominator) * v};
144
0
}
145
146
int32_t Fraction::round_down() const
147
0
{
148
0
  return numerator / denominator;
149
0
}
150
151
int32_t Fraction::round_up() const
152
0
{
153
0
  return int32_t((numerator + int64_t(denominator) - 1) / denominator);
154
0
}
155
156
int32_t Fraction::round() const
157
60
{
158
60
  return int32_t((numerator + int64_t(denominator) / 2) / denominator);
159
60
}
160
161
bool Fraction::is_valid() const
162
4.08k
{
163
4.08k
  return denominator != 0;
164
4.08k
}
165
166
167
583k
BoxHeader::BoxHeader() = default;
168
169
170
std::vector<uint8_t> BoxHeader::get_type() const
171
0
{
172
0
  if (m_type == fourcc("uuid")) {
173
0
    return m_uuid_type;
174
0
  }
175
0
  else {
176
0
    std::vector<uint8_t> type(4);
177
0
    type[0] = static_cast<uint8_t>((m_type >> 24) & 0xFF);
178
0
    type[1] = static_cast<uint8_t>((m_type >> 16) & 0xFF);
179
0
    type[2] = static_cast<uint8_t>((m_type >> 8) & 0xFF);
180
0
    type[3] = static_cast<uint8_t>((m_type >> 0) & 0xFF);
181
0
    return type;
182
0
  }
183
0
}
184
185
186
std::string BoxHeader::get_type_string() const
187
224k
{
188
224k
  if (m_type == fourcc("uuid")) {
189
    // 8-4-4-4-12
190
191
766
    std::ostringstream sstr;
192
766
    sstr << std::hex;
193
766
    sstr << std::setfill('0');
194
195
13.0k
    for (int i = 0; i < 16; i++) {
196
12.2k
      if (i == 4 || i == 6 || i == 8 || i == 10) {
197
3.06k
        sstr << '-';
198
3.06k
      }
199
200
12.2k
      sstr << std::setw(2);
201
12.2k
      sstr << ((int) m_uuid_type[i]);
202
12.2k
    }
203
204
766
    return sstr.str();
205
766
  }
206
224k
  else {
207
224k
    return fourcc_to_string(m_type);
208
224k
  }
209
224k
}
210
211
212
std::vector<uint8_t> BoxHeader::get_uuid_type() const
213
1.94k
{
214
1.94k
  if (m_type != fourcc("uuid")) {
215
0
    return {};
216
0
  }
217
218
1.94k
  return m_uuid_type;
219
1.94k
}
220
221
222
void BoxHeader::set_uuid_type(const std::vector<uint8_t>& type)
223
28
{
224
28
  m_type = fourcc("uuid");
225
28
  m_uuid_type = type;
226
28
}
227
228
229
Error BoxHeader::parse_header(BitstreamRange& range)
230
290k
{
231
290k
  StreamReader::grow_status status;
232
290k
  status = range.wait_for_available_bytes(8);
233
290k
  if (status != StreamReader::grow_status::size_reached) {
234
    // TODO: return recoverable error at timeout
235
36
    return Error(heif_error_Invalid_input,
236
36
                 heif_suberror_End_of_data);
237
36
  }
238
239
290k
  m_size = range.read32();
240
290k
  m_type = range.read32();
241
242
290k
  m_header_size = 8;
243
244
290k
  if (m_size == 1) {
245
1.13k
    status = range.wait_for_available_bytes(8);
246
1.13k
    if (status != StreamReader::grow_status::size_reached) {
247
      // TODO: return recoverable error at timeout
248
2
      return Error(heif_error_Invalid_input,
249
2
                   heif_suberror_End_of_data);
250
2
    }
251
252
1.12k
    uint64_t high = range.read32();
253
1.12k
    uint64_t low = range.read32();
254
255
1.12k
    m_size = (high << 32) | low;
256
1.12k
    m_header_size += 8;
257
258
1.12k
    std::stringstream sstr;
259
1.12k
    sstr << "Box size " << m_size << " exceeds security limit.";
260
261
1.12k
    if (m_size > MAX_LARGE_BOX_SIZE) {
262
16
      return Error(heif_error_Memory_allocation_error,
263
16
                   heif_suberror_Security_limit_exceeded,
264
16
                   sstr.str());
265
16
    }
266
1.12k
  }
267
268
290k
  if (m_type == fourcc("uuid")) {
269
816
    status = range.wait_for_available_bytes(16);
270
816
    if (status != StreamReader::grow_status::size_reached) {
271
      // TODO: return recoverable error at timeout
272
6
      return Error(heif_error_Invalid_input,
273
6
                   heif_suberror_End_of_data);
274
6
    }
275
276
810
    if (range.prepare_read(16)) {
277
807
      m_uuid_type.resize(16);
278
807
      bool success = range.get_istream()->read((char*) m_uuid_type.data(), 16);
279
807
      assert(success);
280
807
      (void) success;
281
807
    }
282
283
810
    m_header_size += 16;
284
810
  }
285
286
290k
  return range.get_error();
287
290k
}
288
289
290
int Box::calculate_header_size(bool data64bit) const
291
5.27k
{
292
5.27k
  int header_size = 8;  // does not include "FullBox" fields.
293
294
5.27k
  if (get_short_type() == fourcc("uuid")) {
295
0
    header_size += 16;
296
0
  }
297
298
5.27k
  if (data64bit) {
299
0
    header_size += 8;
300
0
  }
301
302
5.27k
  return header_size;
303
5.27k
}
304
305
306
size_t Box::reserve_box_header_space(StreamWriter& writer, bool data64bit) const
307
5.27k
{
308
5.27k
  size_t start_pos = writer.get_position();
309
310
5.27k
  int header_size = calculate_header_size(data64bit);
311
312
5.27k
  writer.skip(header_size);
313
314
5.27k
  return start_pos;
315
5.27k
}
316
317
318
size_t FullBox::reserve_box_header_space(StreamWriter& writer, bool data64bit) const
319
0
{
320
0
  size_t start_pos = Box::reserve_box_header_space(writer, data64bit);
321
322
0
  writer.skip(4);
323
324
0
  return start_pos;
325
0
}
326
327
328
Error FullBox::write_header(StreamWriter& writer, size_t total_size, bool data64bit) const
329
0
{
330
0
  auto err = Box::write_header(writer, total_size, data64bit);
331
0
  if (err) {
332
0
    return err;
333
0
  }
334
335
0
  assert((get_flags() & ~0x00FFFFFFU) == 0);
336
337
0
  writer.write32((get_version() << 24) | get_flags());
338
339
0
  return Error::Ok;
340
0
}
341
342
343
Error Box::prepend_header(StreamWriter& writer, size_t box_start, bool data64bit) const
344
5.27k
{
345
5.27k
  size_t total_size = writer.data_size() - box_start;
346
347
5.27k
  writer.set_position(box_start);
348
349
5.27k
  auto err = write_header(writer, total_size, data64bit);
350
351
5.27k
  writer.set_position_to_end();  // Note: should we move to the end of the box after writing the header?
352
353
5.27k
  return err;
354
5.27k
}
355
356
357
Error Box::write_header(StreamWriter& writer, size_t total_size, bool data64bit) const
358
5.27k
{
359
5.27k
  bool large_size = (total_size > 0xFFFFFFFF);
360
361
  // --- write header
362
363
5.27k
  if (large_size && !data64bit) {
364
    // Note: as an alternative, we could return an error here. If it fails, the user has to try again with 64 bit.
365
0
    writer.insert(8);
366
0
  }
367
368
5.27k
  if (large_size) {
369
0
    writer.write32(1);
370
0
  }
371
5.27k
  else {
372
5.27k
    assert(total_size <= 0xFFFFFFFF);
373
5.27k
    writer.write32((uint32_t) total_size);
374
5.27k
  }
375
376
5.27k
  writer.write32(get_short_type());
377
378
5.27k
  if (large_size) {
379
0
    writer.write64(total_size);
380
0
  }
381
382
5.27k
  if (get_short_type() == fourcc("uuid")) {
383
0
    assert(get_type().size() == 16);
384
0
    writer.write(get_type());
385
0
  }
386
387
5.27k
  return Error::Ok;
388
5.27k
}
389
390
391
std::string BoxHeader::dump(Indent& indent) const
392
0
{
393
0
  std::ostringstream sstr;
394
0
  sstr << indent << "Box: " << get_type_string();
395
0
  const char* debug_name = debug_box_name();
396
0
  if (debug_name) {
397
0
    sstr << " ----- (" << debug_name << ")\n";
398
0
  }
399
0
  else {
400
0
    sstr << " -----\n";
401
0
  }
402
403
0
  sstr << indent << "size: " << get_box_size() << "   (header size: " << get_header_size() << ")\n";
404
405
0
  return sstr.str();
406
0
}
407
408
409
Error Box::parse(BitstreamRange& range, const heif_security_limits* limits)
410
64
{
411
  // skip box
412
413
64
  if (get_box_size() == size_until_end_of_file) {
414
11
    range.skip_to_end_of_file();
415
11
  }
416
53
  else {
417
53
    uint64_t content_size = get_box_size() - get_header_size();
418
419
53
    assert(MAX_BOX_SIZE <= SIZE_MAX);
420
421
53
    if (content_size > MAX_BOX_SIZE) {
422
0
      return Error(heif_error_Invalid_input,
423
0
                   heif_suberror_Invalid_box_size);
424
0
    }
425
426
53
    if (range.prepare_read(static_cast<size_t>(content_size))) {
427
53
      range.get_istream()->seek_cur(get_box_size() - get_header_size());
428
53
    }
429
53
  }
430
431
  // Note: seekg() clears the eof flag and it will not be set again afterwards,
432
  // hence we have to test for the fail flag.
433
434
64
  return range.get_error();
435
64
}
436
437
438
Error FullBox::parse_full_box_header(BitstreamRange& range)
439
107k
{
440
107k
  uint32_t data = range.read32();
441
107k
  m_version = static_cast<uint8_t>(data >> 24);
442
107k
  m_flags = data & 0x00FFFFFF;
443
  //m_is_full_box = true;
444
445
107k
  m_header_size += 4;
446
447
107k
  return range.get_error();
448
107k
}
449
450
451
Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_security_limits* limits)
452
225k
{
453
225k
  BoxHeader hdr;
454
225k
  Error err = hdr.parse_header(range);
455
225k
  if (err) {
456
139
    return err;
457
139
  }
458
459
224k
  if (range.error()) {
460
0
    return range.get_error();
461
0
  }
462
463
224k
  result->reset();
464
465
224k
  std::shared_ptr<Box> box;
466
467
224k
  switch (hdr.get_short_type()) {
468
45.0k
    case fourcc("ftyp"):
469
45.0k
      box = std::make_shared<Box_ftyp>();
470
45.0k
      break;
471
472
2.18k
    case fourcc("free"):
473
2.19k
    case fourcc("skip"):
474
2.19k
      box = std::make_shared<Box_free>();
475
2.19k
      break;
476
477
15.7k
    case fourcc("meta"):
478
15.7k
      box = std::make_shared<Box_meta>();
479
15.7k
      break;
480
481
9.25k
    case fourcc("hdlr"):
482
9.25k
      box = std::make_shared<Box_hdlr>();
483
9.25k
      break;
484
485
9.48k
    case fourcc("pitm"):
486
9.48k
      box = std::make_shared<Box_pitm>();
487
9.48k
      break;
488
489
9.54k
    case fourcc("iloc"):
490
9.54k
      box = std::make_shared<Box_iloc>();
491
9.54k
      break;
492
493
9.81k
    case fourcc("iinf"):
494
9.81k
      box = std::make_shared<Box_iinf>();
495
9.81k
      break;
496
497
24.1k
    case fourcc("infe"):
498
24.1k
      box = std::make_shared<Box_infe>();
499
24.1k
      break;
500
501
9.43k
    case fourcc("iprp"):
502
9.43k
      box = std::make_shared<Box_iprp>();
503
9.43k
      break;
504
505
9.14k
    case fourcc("ipco"):
506
9.14k
      box = std::make_shared<Box_ipco>();
507
9.14k
      break;
508
509
8.76k
    case fourcc("ipma"):
510
8.76k
      box = std::make_shared<Box_ipma>();
511
8.76k
      break;
512
513
10.0k
    case fourcc("ispe"):
514
10.0k
      box = std::make_shared<Box_ispe>();
515
10.0k
      break;
516
517
572
    case fourcc("auxC"):
518
572
      box = std::make_shared<Box_auxC>();
519
572
      break;
520
521
342
    case fourcc("irot"):
522
342
      box = std::make_shared<Box_irot>();
523
342
      break;
524
525
223
    case fourcc("imir"):
526
223
      box = std::make_shared<Box_imir>();
527
223
      break;
528
529
1.94k
    case fourcc("clap"):
530
1.94k
      box = std::make_shared<Box_clap>();
531
1.94k
      break;
532
533
63
    case fourcc("iscl"):
534
63
      box = std::make_shared<Box_iscl>();
535
63
      break;
536
537
1.67k
    case fourcc("iref"):
538
1.67k
      box = std::make_shared<Box_iref>();
539
1.67k
      break;
540
541
48
    case fourcc("rref"):
542
48
      box = std::make_shared<Box_rref>();
543
48
      break;
544
545
7.32k
    case fourcc("hvcC"):
546
7.32k
      box = std::make_shared<Box_hvcC>();
547
7.32k
      break;
548
549
51
    case fourcc("hvc1"):
550
51
      box = std::make_shared<Box_hvc1>();
551
51
      break;
552
553
1.58k
    case fourcc("av1C"):
554
1.58k
      box = std::make_shared<Box_av1C>();
555
1.58k
      break;
556
557
51
    case fourcc("av01"):
558
51
      box = std::make_shared<Box_av01>();
559
51
      break;
560
561
193
    case fourcc("vvcC"):
562
193
      box = std::make_shared<Box_vvcC>();
563
193
      break;
564
565
7
    case fourcc("vvc1"):
566
7
      box = std::make_shared<Box_vvc1>();
567
7
      break;
568
569
501
    case fourcc("idat"):
570
501
      box = std::make_shared<Box_idat>();
571
501
      break;
572
573
301
    case fourcc("grpl"):
574
301
      box = std::make_shared<Box_grpl>();
575
301
      break;
576
577
556
    case fourcc("pymd"):
578
556
      box = std::make_shared<Box_pymd>();
579
556
      break;
580
581
108
    case fourcc("altr"):
582
108
      box = std::make_shared<Box_EntityToGroup>();
583
108
      break;
584
585
58
    case fourcc("ster"):
586
58
      box = std::make_shared<Box_ster>();
587
58
      break;
588
589
220
    case fourcc("dinf"):
590
220
      box = std::make_shared<Box_dinf>();
591
220
      break;
592
593
175
    case fourcc("dref"):
594
175
      box = std::make_shared<Box_dref>();
595
175
      break;
596
597
99
    case fourcc("url "):
598
99
      box = std::make_shared<Box_url>();
599
99
      break;
600
601
903
    case fourcc("colr"):
602
903
      box = std::make_shared<Box_colr>();
603
903
      break;
604
605
2.82k
    case fourcc("pixi"):
606
2.82k
      box = std::make_shared<Box_pixi>();
607
2.82k
      break;
608
609
65
    case fourcc("pasp"):
610
65
      box = std::make_shared<Box_pasp>();
611
65
      break;
612
613
129
    case fourcc("lsel"):
614
129
      box = std::make_shared<Box_lsel>();
615
129
      break;
616
617
23
    case fourcc("a1op"):
618
23
      box = std::make_shared<Box_a1op>();
619
23
      break;
620
621
40
    case fourcc("a1lx"):
622
40
      box = std::make_shared<Box_a1lx>();
623
40
      break;
624
625
4
    case fourcc("clli"):
626
4
      box = std::make_shared<Box_clli>();
627
4
      break;
628
629
27
    case fourcc("mdcv"):
630
27
      box = std::make_shared<Box_mdcv>();
631
27
      break;
632
633
28
    case fourcc("amve"):
634
28
      box = std::make_shared<Box_amve>();
635
28
      break;
636
637
12
    case fourcc("ndwt"):
638
12
      box = std::make_shared<Box_ndwt>();
639
12
      break;
640
641
59
    case fourcc("cmin"):
642
59
      box = std::make_shared<Box_cmin>();
643
59
      break;
644
645
358
    case fourcc("cmex"):
646
358
      box = std::make_shared<Box_cmex>();
647
358
      break;
648
649
121
    case fourcc("udes"):
650
121
      box = std::make_shared<Box_udes>();
651
121
      break;
652
653
597
    case fourcc("jpgC"):
654
597
      box = std::make_shared<Box_jpgC>();
655
597
      break;
656
657
2
    case fourcc("mjpg"):
658
2
      box = std::make_shared<Box_mjpg>();
659
2
      break;
660
661
27
    case fourcc("elng"):
662
27
      box = std::make_shared<Box_elng>();
663
27
      break;
664
665
666
#if WITH_UNCOMPRESSED_CODEC
667
    case fourcc("cmpd"):
668
      box = std::make_shared<Box_cmpd>();
669
      break;
670
671
    case fourcc("uncC"):
672
      box = std::make_shared<Box_uncC>();
673
      break;
674
675
    case fourcc("cmpC"):
676
      box = std::make_shared<Box_cmpC>();
677
      break;
678
679
    case fourcc("icef"):
680
      box = std::make_shared<Box_icef>();
681
      break;
682
683
    case fourcc("cpat"):
684
      box = std::make_shared<Box_cpat>();
685
      break;
686
687
    case fourcc("splz"):
688
      box = std::make_shared<Box_splz>();
689
      break;
690
691
    case fourcc("sbpm"):
692
      box = std::make_shared<Box_sbpm>();
693
      break;
694
695
    case fourcc("snuc"):
696
      box = std::make_shared<Box_snuc>();
697
      break;
698
699
    case fourcc("cloc"):
700
      box = std::make_shared<Box_cloc>();
701
      break;
702
703
    case fourcc("uncv"):
704
      box = std::make_shared<Box_uncv>();
705
      break;
706
#endif
707
708
    // --- JPEG 2000
709
710
121
    case fourcc("j2kH"):
711
121
      box = std::make_shared<Box_j2kH>();
712
121
      break;
713
714
60
    case fourcc("cdef"):
715
60
      box = std::make_shared<Box_cdef>();
716
60
      break;
717
718
70
    case fourcc("cmap"):
719
70
      box = std::make_shared<Box_cmap>();
720
70
      break;
721
722
272
    case fourcc("pclr"):
723
272
      box = std::make_shared<Box_pclr>();
724
272
      break;
725
726
54
    case fourcc("j2kL"):
727
54
      box = std::make_shared<Box_j2kL>();
728
54
      break;
729
730
7
    case fourcc("j2ki"):
731
7
      box = std::make_shared<Box_j2ki>();
732
7
      break;
733
734
735
    // --- mski
736
737
328
    case fourcc("mskC"):
738
328
      box = std::make_shared<Box_mskC>();
739
328
      break;
740
741
    // --- TAI timestamps
742
743
183
    case fourcc("itai"):
744
183
      box = std::make_shared<Box_itai>();
745
183
      break;
746
747
7
    case fourcc("taic"):
748
7
      box = std::make_shared<Box_taic>();
749
7
      break;
750
751
    // --- AVC (H.264)
752
753
393
    case fourcc("avcC"):
754
393
      box = std::make_shared<Box_avcC>();
755
393
      break;
756
757
16
    case fourcc("avc1"):
758
16
      box = std::make_shared<Box_avc1>();
759
16
      break;
760
761
#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
762
    case fourcc("tilC"):
763
      box = std::make_shared<Box_tilC>();
764
      break;
765
#endif
766
767
1.59k
    case fourcc("mini"):
768
1.59k
      box = std::make_shared<Box_mini>();
769
1.59k
      break;
770
771
66
    case fourcc("mdat"):
772
      // avoid generating a 'Box_other'
773
66
      box = std::make_shared<Box>();
774
66
      break;
775
776
766
    case fourcc("uuid"):
777
766
      if (hdr.get_uuid_type() == std::vector<uint8_t>{0x22, 0xcc, 0x04, 0xc7, 0xd6, 0xd9, 0x4e, 0x07, 0x9d, 0x90, 0x4e, 0xb6, 0xec, 0xba, 0xf3, 0xa3}) {
778
33
        box = std::make_shared<Box_cmin>();
779
33
      }
780
733
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x43, 0x63, 0xe9, 0x14, 0x5b, 0x7d, 0x4a, 0xab, 0x97, 0xae, 0xbe, 0xa6, 0x98, 0x03, 0xb4, 0x34}) {
781
288
        box = std::make_shared<Box_cmex>();
782
288
      }
783
445
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x26, 0x1e, 0xf3, 0x74, 0x1d, 0x97, 0x5b, 0xba, 0xac, 0xbd, 0x9d, 0x2c, 0x8e, 0xa7, 0x35, 0x22}) {
784
28
        box = std::make_shared<Box_gimi_content_id>();
785
28
      }
786
#if WITH_UNCOMPRESSED_CODEC
787
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x9d, 0xb9, 0xdd, 0x6e, 0x37, 0x3c, 0x5a, 0x4e, 0x81, 0x10, 0x21, 0xfc, 0x83, 0xa9, 0x11, 0xfd}) {
788
        box = std::make_shared<Box_gimi_component_content_ids>();
789
      }
790
#endif
791
417
      else {
792
417
        box = std::make_shared<Box_other>(hdr.get_short_type());
793
417
      }
794
766
      break;
795
796
    // --- sequences
797
798
2.63k
    case fourcc("moov"):
799
2.63k
      box = std::make_shared<Box_moov>();
800
2.63k
      break;
801
802
65
    case fourcc("mvhd"):
803
65
      box = std::make_shared<Box_mvhd>();
804
65
      break;
805
806
1.73k
    case fourcc("trak"):
807
1.73k
      box = std::make_shared<Box_trak>();
808
1.73k
      break;
809
810
32
    case fourcc("tkhd"):
811
32
      box = std::make_shared<Box_tkhd>();
812
32
      break;
813
814
1.62k
    case fourcc("mdia"):
815
1.62k
      box = std::make_shared<Box_mdia>();
816
1.62k
      break;
817
818
14
    case fourcc("mdhd"):
819
14
      box = std::make_shared<Box_mdhd>();
820
14
      break;
821
822
1.54k
    case fourcc("minf"):
823
1.54k
      box = std::make_shared<Box_minf>();
824
1.54k
      break;
825
826
7
    case fourcc("vmhd"):
827
7
      box = std::make_shared<Box_vmhd>();
828
7
      break;
829
830
1.48k
    case fourcc("stbl"):
831
1.48k
      box = std::make_shared<Box_stbl>();
832
1.48k
      break;
833
834
108
    case fourcc("stsd"):
835
108
      box = std::make_shared<Box_stsd>();
836
108
      break;
837
838
85
    case fourcc("stts"):
839
85
      box = std::make_shared<Box_stts>();
840
85
      break;
841
842
94
    case fourcc("ctts"):
843
94
      box = std::make_shared<Box_ctts>();
844
94
      break;
845
846
163
    case fourcc("stsc"):
847
163
      box = std::make_shared<Box_stsc>();
848
163
      break;
849
850
1.15k
    case fourcc("stco"):
851
1.15k
      box = std::make_shared<Box_stco>();
852
1.15k
      break;
853
854
93
    case fourcc("stsz"):
855
93
      box = std::make_shared<Box_stsz>();
856
93
      break;
857
858
71
    case fourcc("stss"):
859
71
      box = std::make_shared<Box_stss>();
860
71
      break;
861
862
24
    case fourcc("ccst"):
863
24
      box = std::make_shared<Box_ccst>();
864
24
      break;
865
866
28
    case fourcc("auxi"):
867
28
      box = std::make_shared<Box_auxi>();
868
28
      break;
869
870
11
    case fourcc("edts"):
871
11
      box = std::make_shared<Box_edts>();
872
11
      break;
873
874
67
    case fourcc("elst"):
875
67
      box = std::make_shared<Box_elst>();
876
67
      break;
877
878
47
    case fourcc("sbgp"):
879
47
      box = std::make_shared<Box_sbgp>();
880
47
      break;
881
882
192
    case fourcc("sgpd"):
883
192
      box = std::make_shared<Box_sgpd>();
884
192
      break;
885
886
28
    case fourcc("btrt"):
887
28
      box = std::make_shared<Box_btrt>();
888
28
      break;
889
890
62
    case fourcc("saiz"):
891
62
      box = std::make_shared<Box_saiz>();
892
62
      break;
893
894
78
    case fourcc("saio"):
895
78
      box = std::make_shared<Box_saio>();
896
78
      break;
897
898
112
    case fourcc("urim"):
899
112
      box = std::make_shared<Box_URIMetaSampleEntry>();
900
112
      break;
901
902
64
    case fourcc("uri "):
903
64
      box = std::make_shared<Box_uri>();
904
64
      break;
905
906
33
    case fourcc("nmhd"):
907
33
      box = std::make_shared<Box_nmhd>();
908
33
      break;
909
910
1.05k
    case fourcc("tref"):
911
1.05k
      box = std::make_shared<Box_tref>();
912
1.05k
      break;
913
914
115
    case fourcc("sdtp"):
915
115
      box = std::make_shared<Box_sdtp>();
916
115
      break;
917
918
    // OMAF
919
66
    case fourcc("prfr"):
920
66
      box = std::make_shared<Box_prfr>();
921
66
      break;
922
923
24.0k
    default:
924
24.0k
      box = std::make_shared<Box_other>(hdr.get_short_type());
925
24.0k
      break;
926
224k
  }
927
928
224k
  box->set_short_header(hdr);
929
930
224k
  box->m_debug_box_type = hdr.get_type_string(); // only for debugging
931
932
933
224k
  if (range.get_nesting_level() > MAX_BOX_NESTING_LEVEL) {
934
2
    return Error(heif_error_Memory_allocation_error,
935
2
                 heif_suberror_Security_limit_exceeded,
936
2
                 "Security limit for maximum nesting of boxes has been exceeded");
937
2
  }
938
939
224k
  if (hdr.has_fixed_box_size()) {
940
    // Sanity checks
941
210k
    if (hdr.get_box_size() < hdr.get_header_size()) {
942
11
      std::stringstream sstr;
943
11
      sstr << "Box size (" << hdr.get_box_size() << " bytes) smaller than header size ("
944
11
           << hdr.get_header_size() << " bytes)";
945
946
11
      return {heif_error_Invalid_input,
947
11
              heif_suberror_Invalid_box_size,
948
11
              sstr.str()};
949
11
    }
950
951
    // this is >= 0 because of above condition
952
210k
    auto nBytes = static_cast<uint64_t>(hdr.get_box_size() - hdr.get_header_size());
953
210k
    if (nBytes > SIZE_MAX) {
954
0
      return {heif_error_Memory_allocation_error,
955
0
              heif_suberror_Invalid_box_size,
956
0
              "Box size too large"};
957
0
    }
958
959
    // Security check: make sure that box size does not exceed int64 size.
960
961
210k
    if (hdr.get_box_size() > (uint64_t) std::numeric_limits<int64_t>::max()) {
962
0
      return {heif_error_Invalid_input,
963
0
              heif_suberror_Invalid_box_size};
964
0
    }
965
966
    // --- wait for data to arrive
967
968
210k
    auto status = range.wait_for_available_bytes(static_cast<size_t>(nBytes));
969
210k
    if (status != StreamReader::grow_status::size_reached) {
970
      // TODO: return recoverable error at timeout
971
2.14k
      return {heif_error_Invalid_input,
972
2.14k
              heif_suberror_End_of_data};
973
2.14k
    }
974
210k
  }
975
976
222k
  auto box_size = static_cast<int64_t>(hdr.get_box_size());
977
222k
  int64_t box_size_without_header = hdr.has_fixed_box_size() ? (box_size - hdr.get_header_size()) : (int64_t)range.get_remaining_bytes();
978
979
  // Box size may not be larger than remaining bytes in parent box.
980
981
222k
  if ((int64_t)range.get_remaining_bytes() < box_size_without_header) {
982
51
    return {heif_error_Invalid_input,
983
51
            heif_suberror_Invalid_box_size};
984
51
  }
985
986
987
  // Create child bitstream range and read box from that range.
988
989
222k
  BitstreamRange boxrange(range.get_istream(),
990
222k
                          box_size_without_header,
991
222k
                          &range);
992
993
222k
  err = box->parse(boxrange, limits);
994
222k
  boxrange.skip_to_end_of_box();
995
996
222k
  if (err == Error::Ok) {
997
210k
    *result = std::move(box);
998
210k
  }
999
11.8k
  else {
1000
11.8k
    parse_error_fatality fatality = box->get_parse_error_fatality();
1001
1002
11.8k
    box = std::make_shared<Box_Error>(box->get_short_type(), err, fatality);
1003
1004
    // We return a Box_Error that represents the parse error.
1005
11.8k
    *result = std::move(box);
1006
11.8k
  }
1007
1008
222k
  return err;
1009
222k
}
1010
1011
1012
std::string Box::dump(Indent& indent) const
1013
0
{
1014
0
  std::ostringstream sstr;
1015
1016
0
  sstr << BoxHeader::dump(indent);
1017
1018
0
  return sstr.str();
1019
0
}
1020
1021
1022
std::string FullBox::dump(Indent& indent) const
1023
0
{
1024
0
  std::ostringstream sstr;
1025
1026
0
  sstr << Box::dump(indent);
1027
1028
0
  sstr << indent << "version: " << ((int) m_version) << "\n"
1029
0
       << indent << "flags: " << std::hex << m_flags << "\n";
1030
1031
0
  return sstr.str();
1032
0
}
1033
1034
1035
Error Box::write(StreamWriter& writer) const
1036
0
{
1037
0
  size_t box_start = reserve_box_header_space(writer);
1038
1039
0
  Error err = write_children(writer);
1040
1041
0
  prepend_header(writer, box_start);
1042
1043
0
  return err;
1044
0
}
1045
1046
1047
bool Box::operator==(const Box& other) const
1048
5.04k
{
1049
5.04k
  if (this->get_short_type() != other.get_short_type()) {
1050
2.41k
    return false;
1051
2.41k
  }
1052
1053
2.63k
  StreamWriter writer1;
1054
2.63k
  StreamWriter writer2;
1055
1056
2.63k
  this->write(writer1);
1057
2.63k
  other.write(writer2);
1058
1059
2.63k
  return writer1.get_data() == writer2.get_data();
1060
5.04k
}
1061
1062
1063
bool Box::remove_child_box(const std::shared_ptr<const Box>& box)
1064
0
{
1065
0
  for (int i=0; i<(int)m_children.size(); i++) {
1066
0
    if (m_children[i].get() == box.get()) {
1067
0
      m_children.erase(m_children.begin() + i);
1068
0
      return true;
1069
0
    }
1070
0
  }
1071
1072
0
  return false;
1073
0
}
1074
1075
1076
bool Box::equal(const std::shared_ptr<Box>& box1, const std::shared_ptr<Box>& box2)
1077
5.81k
{
1078
5.81k
    if (!box1 || !box2) {
1079
0
        return false;
1080
0
    }
1081
1082
    // This was introduced because of j2kH having child boxes.
1083
    // TODO: we might also deduplicate them by comparing all child boxes.
1084
5.81k
    if (box1->has_child_boxes() || box2->has_child_boxes()) {
1085
62
      return false;
1086
62
    }
1087
1088
5.75k
    return *box1 == *box2;
1089
5.81k
}
1090
1091
1092
Error Box::read_children(BitstreamRange& range, uint32_t max_number, const heif_security_limits* limits)
1093
53.9k
{
1094
53.9k
  uint32_t count = 0;
1095
1096
203k
  while (!range.eof() && !range.error()) {
1097
160k
    std::shared_ptr<Box> box;
1098
160k
    Error error = Box::read(range, &box, limits);
1099
160k
    if (error != Error::Ok && (!box || box->get_parse_error_fatality() == parse_error_fatality::fatal)) {
1100
5.36k
      return error;
1101
5.36k
    }
1102
1103
155k
    if (max_number == READ_CHILDREN_ALL) {
1104
126k
      uint32_t max_children;
1105
126k
      if (get_short_type() == fourcc("iinf")) {
1106
10
        max_children = limits->max_items;
1107
10
      }
1108
126k
      else {
1109
126k
        max_children = limits->max_children_per_box;
1110
126k
      }
1111
1112
126k
      if (max_children && m_children.size() > max_children) {
1113
4
        std::stringstream sstr;
1114
4
        sstr << "Maximum number of child boxes (" << max_children << ") in '" << get_type_string() << "' box exceeded.";
1115
1116
        // Sanity check.
1117
4
        return Error(heif_error_Memory_allocation_error,
1118
4
                     heif_suberror_Security_limit_exceeded,
1119
4
                     sstr.str());
1120
4
      }
1121
126k
    }
1122
1123
155k
    m_children.push_back(std::move(box));
1124
1125
1126
    // count the new child and end reading new children when we reached the expected number
1127
1128
155k
    count++;
1129
1130
155k
    if (max_number != READ_CHILDREN_ALL &&
1131
28.9k
        count == max_number) {
1132
5.67k
      break;
1133
5.67k
    }
1134
155k
  }
1135
1136
48.5k
  return range.get_error();
1137
53.9k
}
1138
1139
1140
Error Box::write_children(StreamWriter& writer) const
1141
0
{
1142
0
  for (const auto& child : m_children) {
1143
0
    Error err = child->write(writer);
1144
0
    if (err) {
1145
0
      return err;
1146
0
    }
1147
0
  }
1148
1149
0
  return Error::Ok;
1150
0
}
1151
1152
1153
std::string Box::dump_children(Indent& indent, bool with_index) const
1154
0
{
1155
0
  std::ostringstream sstr;
1156
1157
0
  bool first = true;
1158
0
  int idx=1;
1159
1160
0
  indent++;
1161
0
  for (const auto& childBox : m_children) {
1162
0
    if (first) {
1163
0
      first = false;
1164
0
    }
1165
0
    else {
1166
0
      sstr << indent << "\n";
1167
0
    }
1168
1169
0
    if (with_index) {
1170
0
      sstr << indent << "index: " << idx << "\n";
1171
0
      idx++;
1172
0
    }
1173
1174
0
    sstr << childBox->dump(indent);
1175
0
  }
1176
0
  indent--;
1177
1178
0
  return sstr.str();
1179
0
}
1180
1181
1182
void Box::derive_box_version_recursive()
1183
0
{
1184
0
  derive_box_version();
1185
1186
0
  for (auto& child : m_children) {
1187
0
    child->derive_box_version_recursive();
1188
0
  }
1189
0
}
1190
1191
1192
void Box::patch_file_pointers_recursively(StreamWriter& writer, size_t offset)
1193
0
{
1194
0
  patch_file_pointers(writer, offset);
1195
1196
0
  for (auto& child : m_children) {
1197
0
    child->patch_file_pointers_recursively(writer, offset);
1198
0
  }
1199
0
}
1200
1201
1202
Error Box_other::parse(BitstreamRange& range, const heif_security_limits* limits)
1203
22.6k
{
1204
22.6k
  if (has_fixed_box_size()) {
1205
18.9k
    size_t len;
1206
18.9k
    if (get_box_size() >= get_header_size()) {
1207
18.9k
      auto len64 = get_box_size() - get_header_size();
1208
18.9k
      if (len64 > MAX_BOX_SIZE) {
1209
0
        return {heif_error_Invalid_input,
1210
0
                heif_suberror_Security_limit_exceeded,
1211
0
                "Box size too large"};
1212
0
      }
1213
1214
18.9k
      len = static_cast<size_t>(len64);
1215
1216
18.9k
      m_data.resize(len);
1217
18.9k
      range.read(m_data.data(), len);
1218
18.9k
    }
1219
0
    else {
1220
0
      return {heif_error_Invalid_input,
1221
0
              heif_suberror_Invalid_box_size};
1222
0
    }
1223
18.9k
  }
1224
3.63k
  else {
1225
    // TODO: boxes until end of file (we will probably never need this)
1226
3.63k
  }
1227
1228
22.6k
  return range.get_error();
1229
22.6k
}
1230
1231
1232
Error Box_other::write(StreamWriter& writer) const
1233
0
{
1234
0
  size_t box_start = reserve_box_header_space(writer);
1235
1236
0
  if (get_box_size() >= get_header_size()) {
1237
0
    writer.write(m_data);
1238
0
    prepend_header(writer, box_start);
1239
0
    return Error::Ok;
1240
0
  }
1241
0
  else {
1242
0
    return Error(heif_error_Invalid_input,
1243
0
                 heif_suberror_Invalid_box_size);
1244
0
  }
1245
0
}
1246
1247
1248
std::string Box_other::dump(Indent& indent) const
1249
0
{
1250
0
  std::ostringstream sstr;
1251
1252
0
  sstr << BoxHeader::dump(indent);
1253
1254
  // --- show raw box content
1255
1256
0
  size_t len = 0;
1257
0
  if (get_box_size() >= get_header_size()) {
1258
    // We can cast because if it does not fit, it would fail during parsing.
1259
0
    len = static_cast<size_t>(get_box_size() - get_header_size());
1260
0
  }
1261
0
  else {
1262
0
    sstr << indent << "invalid box size " << get_box_size() << " (smaller than header)\n";
1263
0
    return sstr.str();
1264
0
  }
1265
1266
0
  sstr << write_raw_data_as_hex(m_data.data(), len,
1267
0
                                indent.get_string() + "data: ",
1268
0
                                indent.get_string() + "      ");
1269
1270
0
  return sstr.str();
1271
0
}
1272
1273
1274
std::string Box_Error::dump(Indent& indent) const
1275
0
{
1276
0
  std::ostringstream sstr;
1277
0
  sstr << indent << '\'' << fourcc_to_string(m_box_type_with_parse_error) << "' parse error: " << m_error.message << "\n";
1278
0
  sstr << indent << "fatality: ";
1279
0
  switch (m_fatality) {
1280
0
    case parse_error_fatality::fatal: sstr << "fatal\n"; break;
1281
0
    case parse_error_fatality::ignorable: sstr << "ignorable\n"; break;
1282
0
    case parse_error_fatality::optional: sstr << "optional\n"; break;
1283
0
  }
1284
1285
0
  return sstr.str();
1286
0
}
1287
1288
parse_error_fatality Box_Error::get_parse_error_fatality() const
1289
9.21k
{
1290
9.21k
  return m_fatality;
1291
9.21k
}
1292
1293
1294
Error Box_ftyp::parse(BitstreamRange& range, const heif_security_limits* limits)
1295
44.9k
{
1296
44.9k
  m_major_brand = range.read32();
1297
44.9k
  m_minor_version = range.read32();
1298
1299
44.9k
  uint64_t box_size = get_box_size();
1300
44.9k
  if (box_size < 8 || box_size - 8 < get_header_size()) {
1301
    // Sanity check.
1302
6
    return Error(heif_error_Invalid_input,
1303
6
                 heif_suberror_Invalid_box_size,
1304
6
                 "ftyp box too small (less than 8 bytes)");
1305
6
  }
1306
1307
44.9k
  uint64_t n_minor_brands = (get_box_size() - get_header_size() - 8) / 4;
1308
1309
44.9k
  if (limits->max_number_of_file_brands && n_minor_brands > limits->max_number_of_file_brands) {
1310
1
    return {
1311
1
      heif_error_Memory_allocation_error,
1312
1
      heif_suberror_Security_limit_exceeded,
1313
1
      "Number of minor brands in file exceeds security limit"
1314
1
    };
1315
1
  }
1316
1317
160k
  for (uint64_t i = 0; i < n_minor_brands && !range.error(); i++) {
1318
115k
    m_compatible_brands.push_back(range.read32());
1319
115k
  }
1320
1321
44.9k
  return range.get_error();
1322
44.9k
}
1323
1324
1325
bool Box_ftyp::has_compatible_brand(heif_brand2 brand) const
1326
75.7k
{
1327
75.7k
  return std::find(m_compatible_brands.begin(),
1328
75.7k
                   m_compatible_brands.end(),
1329
75.7k
                   brand) !=
1330
75.7k
         m_compatible_brands.end();
1331
75.7k
}
1332
1333
1334
std::string Box_ftyp::dump(Indent& indent) const
1335
0
{
1336
0
  std::ostringstream sstr;
1337
1338
0
  sstr << BoxHeader::dump(indent);
1339
1340
0
  sstr << indent << "major brand: " << fourcc_to_string(m_major_brand) << "\n"
1341
0
       << indent << "minor version: ";
1342
0
  if (m_minor_version < ('A' << 24)) {
1343
    // This is probably a version number
1344
0
    sstr << m_minor_version;
1345
0
  } else {
1346
    // probably a 4CC, as used for mif3
1347
0
    sstr << fourcc_to_string(m_minor_version);
1348
0
  }
1349
0
  sstr << "\n" << indent << "compatible brands: ";
1350
1351
0
  bool first = true;
1352
0
  for (uint32_t brand : m_compatible_brands) {
1353
0
    if (first) { first = false; }
1354
0
    else { sstr << ','; }
1355
1356
0
    sstr << fourcc_to_string(brand);
1357
0
  }
1358
0
  sstr << "\n";
1359
1360
0
  return sstr.str();
1361
0
}
1362
1363
1364
void Box_ftyp::add_compatible_brand(heif_brand2 brand)
1365
0
{
1366
0
  if (!has_compatible_brand(brand)) {
1367
0
    m_compatible_brands.push_back(brand);
1368
0
  }
1369
0
}
1370
1371
1372
Error Box_ftyp::write(StreamWriter& writer) const
1373
0
{
1374
0
  size_t box_start = reserve_box_header_space(writer);
1375
1376
0
  writer.write32(m_major_brand);
1377
0
  writer.write32(m_minor_version);
1378
1379
0
  for (uint32_t b : m_compatible_brands) {
1380
0
    writer.write32(b);
1381
0
  }
1382
1383
0
  prepend_header(writer, box_start);
1384
1385
0
  return Error::Ok;
1386
0
}
1387
1388
1389
Error Box_free::parse(BitstreamRange& range, const heif_security_limits* limits)
1390
2.19k
{
1391
2.19k
  range.skip_to_end_of_box();
1392
2.19k
  return range.get_error();
1393
2.19k
}
1394
1395
1396
std::string Box_free::dump(Indent& indent) const
1397
0
{
1398
0
  std::ostringstream sstr;
1399
0
  sstr << BoxHeader::dump(indent);
1400
0
  return sstr.str();
1401
0
}
1402
1403
1404
Error Box_free::write(StreamWriter& writer) const
1405
0
{
1406
0
  size_t box_start = reserve_box_header_space(writer);
1407
0
  prepend_header(writer, box_start);
1408
0
  return Error::Ok;
1409
0
}
1410
1411
1412
Error Box_meta::parse(BitstreamRange& range, const heif_security_limits* limits)
1413
15.7k
{
1414
15.7k
  parse_full_box_header(range);
1415
1416
15.7k
  if (get_version() != 0) {
1417
25
    return unsupported_version_error("meta");
1418
25
  }
1419
1420
  /*
1421
  uint64_t boxSizeLimit;
1422
  if (get_box_size() == BoxHeader::size_until_end_of_file) {
1423
    boxSizeLimit = sizeLimit;
1424
  }
1425
  else {
1426
    boxSizeLimit = get_box_size() - get_header_size();
1427
  }
1428
  */
1429
1430
15.7k
  return read_children(range, READ_CHILDREN_ALL, limits);
1431
15.7k
}
1432
1433
1434
std::string Box_meta::dump(Indent& indent) const
1435
0
{
1436
0
  std::ostringstream sstr;
1437
0
  sstr << Box::dump(indent);
1438
0
  sstr << dump_children(indent);
1439
1440
0
  return sstr.str();
1441
0
}
1442
1443
1444
Error FullBox::unsupported_version_error(const char* box) const
1445
583
{
1446
583
  std::stringstream sstr;
1447
583
  sstr << box << " box data version " << ((int) m_version) << " is not implemented yet";
1448
1449
583
  return {heif_error_Unsupported_feature,
1450
583
          heif_suberror_Unsupported_data_version,
1451
583
          sstr.str()};
1452
583
}
1453
1454
1455
Error Box_hdlr::parse(BitstreamRange& range, const heif_security_limits* limits)
1456
9.25k
{
1457
9.25k
  parse_full_box_header(range);
1458
1459
9.25k
  if (get_version() != 0) {
1460
5
    return unsupported_version_error("hdlr");
1461
5
  }
1462
1463
9.25k
  m_pre_defined = range.read32();
1464
9.25k
  m_handler_type = range.read32();
1465
1466
37.0k
  for (int i = 0; i < 3; i++) {
1467
27.7k
    m_reserved[i] = range.read32();
1468
27.7k
  }
1469
1470
9.25k
  m_name = range.read_string();
1471
1472
9.25k
  return range.get_error();
1473
9.25k
}
1474
1475
1476
std::string Box_hdlr::dump(Indent& indent) const
1477
0
{
1478
0
  std::ostringstream sstr;
1479
0
  sstr << Box::dump(indent);
1480
0
  sstr << indent << "pre_defined: " << m_pre_defined << "\n"
1481
0
       << indent << "handler_type: " << fourcc_to_string(m_handler_type) << "\n"
1482
0
       << indent << "name: " << m_name << "\n";
1483
1484
0
  return sstr.str();
1485
0
}
1486
1487
1488
Error Box_hdlr::write(StreamWriter& writer) const
1489
0
{
1490
0
  size_t box_start = reserve_box_header_space(writer);
1491
1492
0
  writer.write32(m_pre_defined);
1493
0
  writer.write32(m_handler_type);
1494
1495
0
  for (int i = 0; i < 3; i++) {
1496
0
    writer.write32(m_reserved[i]);
1497
0
  }
1498
1499
0
  writer.write(m_name);
1500
1501
0
  prepend_header(writer, box_start);
1502
1503
0
  return Error::Ok;
1504
0
}
1505
1506
1507
Error Box_pitm::parse(BitstreamRange& range, const heif_security_limits* limits)
1508
9.48k
{
1509
9.48k
  parse_full_box_header(range);
1510
1511
9.48k
  if (get_version() > 1) {
1512
3
    return unsupported_version_error("pitm");
1513
3
  }
1514
1515
1516
9.47k
  if (get_version() == 0) {
1517
9.47k
    m_item_ID = range.read16();
1518
9.47k
  }
1519
8
  else {
1520
8
    m_item_ID = range.read32();
1521
8
  }
1522
1523
9.47k
  return range.get_error();
1524
9.48k
}
1525
1526
1527
std::string Box_pitm::dump(Indent& indent) const
1528
0
{
1529
0
  std::ostringstream sstr;
1530
0
  sstr << Box::dump(indent);
1531
0
  sstr << indent << "item_ID: " << m_item_ID << "\n";
1532
1533
0
  return sstr.str();
1534
0
}
1535
1536
1537
void Box_pitm::derive_box_version()
1538
0
{
1539
0
  if (m_item_ID <= 0xFFFF) {
1540
0
    set_version(0);
1541
0
  }
1542
0
  else {
1543
0
    set_version(1);
1544
0
  }
1545
0
}
1546
1547
1548
Error Box_pitm::write(StreamWriter& writer) const
1549
0
{
1550
0
  size_t box_start = reserve_box_header_space(writer);
1551
1552
0
  if (get_version() == 0) {
1553
0
    assert(m_item_ID <= 0xFFFF);
1554
0
    writer.write16((uint16_t) m_item_ID);
1555
0
  }
1556
0
  else {
1557
0
    writer.write32(m_item_ID);
1558
0
  }
1559
1560
0
  prepend_header(writer, box_start);
1561
1562
0
  return Error::Ok;
1563
0
}
1564
1565
1566
Error Box_iloc::parse(BitstreamRange& range, const heif_security_limits* limits)
1567
9.53k
{
1568
9.53k
  parse_full_box_header(range);
1569
1570
9.53k
  if (get_version() > 2) {
1571
4
    return unsupported_version_error("iloc");
1572
4
  }
1573
1574
9.53k
  const int version = get_version();
1575
1576
9.53k
  uint16_t values4 = range.read16();
1577
1578
9.53k
  int offset_size = (values4 >> 12) & 0xF;
1579
9.53k
  int length_size = (values4 >> 8) & 0xF;
1580
9.53k
  int base_offset_size = (values4 >> 4) & 0xF;
1581
9.53k
  int index_size = 0;
1582
1583
9.53k
  if (version == 1 || version == 2) {
1584
676
    index_size = (values4 & 0xF);
1585
676
  }
1586
1587
9.53k
  uint32_t item_count = 0;
1588
9.53k
  if (version < 2) {
1589
9.45k
    item_count = range.read16();
1590
9.45k
  }
1591
81
  else if (version == 2) {
1592
81
    item_count = range.read32();
1593
81
  }
1594
1595
  // Sanity check. (This might be obsolete now as we check for range.error() below).
1596
9.53k
  if (limits->max_items && item_count > limits->max_items) {
1597
45
    std::stringstream sstr;
1598
45
    sstr << "iloc box contains " << item_count << " items, which exceeds the security limit of "
1599
45
         << limits->max_items << " items.";
1600
1601
45
    return Error(heif_error_Memory_allocation_error,
1602
45
                 heif_suberror_Security_limit_exceeded,
1603
45
                 sstr.str());
1604
45
  }
1605
1606
38.0k
  for (uint32_t i = 0; i < item_count; i++) {
1607
28.7k
    Item item;
1608
1609
28.7k
    if (range.eof()) {
1610
21
      std::stringstream sstr;
1611
21
      sstr << "iloc box should contain " << item_count << " items, but we can only read " << i << " items.";
1612
1613
21
      return {heif_error_Invalid_input,
1614
21
              heif_suberror_End_of_data,
1615
21
              sstr.str()};
1616
21
    }
1617
1618
28.7k
    if (version < 2) {
1619
28.4k
      item.item_ID = range.read16();
1620
28.4k
    }
1621
312
    else if (version == 2) {
1622
312
      item.item_ID = range.read32();
1623
312
    }
1624
1625
28.7k
    if (version >= 1) {
1626
8.17k
      values4 = range.read16();
1627
8.17k
      item.construction_method = (values4 & 0xF);
1628
8.17k
    }
1629
1630
28.7k
    item.data_reference_index = range.read16();
1631
1632
28.7k
    item.base_offset = 0;
1633
28.7k
    if (base_offset_size == 4) {
1634
8.20k
      item.base_offset = range.read32();
1635
8.20k
    }
1636
20.5k
    else if (base_offset_size == 8) {
1637
805
      item.base_offset = ((uint64_t) range.read32()) << 32;
1638
805
      item.base_offset |= range.read32();
1639
805
    }
1640
1641
28.7k
    uint16_t extent_count = range.read16();
1642
1643
    // Sanity check.
1644
28.7k
    auto max_iloc_extents = limits->max_iloc_extents_per_item;
1645
28.7k
    if (max_iloc_extents && extent_count > max_iloc_extents) {
1646
82
      std::stringstream sstr;
1647
82
      sstr << "Number of extents in iloc box (" << extent_count << ") exceeds security limit ("
1648
82
           << max_iloc_extents << ")\n";
1649
1650
82
      return Error(heif_error_Memory_allocation_error,
1651
82
                   heif_suberror_Security_limit_exceeded,
1652
82
                   sstr.str());
1653
82
    }
1654
1655
59.5k
    for (int e = 0; e < extent_count; e++) {
1656
30.9k
      Extent extent;
1657
1658
30.9k
      if (range.eof()) {
1659
30
        std::stringstream sstr;
1660
30
        sstr << "iloc item should contain " << extent_count << " extents, but we can only read " << e << " extents.";
1661
1662
30
        return {heif_error_Invalid_input,
1663
30
                heif_suberror_End_of_data,
1664
30
                sstr.str()};
1665
30
      }
1666
1667
30.9k
      if ((version == 1 || version == 2) && index_size > 0) {
1668
2.11k
        if (index_size == 4) {
1669
648
          extent.index = range.read32();
1670
648
        }
1671
1.46k
        else if (index_size == 8) {
1672
391
          extent.index = ((uint64_t) range.read32()) << 32;
1673
391
          extent.index |= range.read32();
1674
391
        }
1675
2.11k
      }
1676
1677
30.9k
      extent.offset = 0;
1678
30.9k
      if (offset_size == 4) {
1679
23.4k
        extent.offset = range.read32();
1680
23.4k
      }
1681
7.43k
      else if (offset_size == 8) {
1682
328
        extent.offset = ((uint64_t) range.read32()) << 32;
1683
328
        extent.offset |= range.read32();
1684
328
      }
1685
1686
1687
30.9k
      extent.length = 0;
1688
30.9k
      if (length_size == 4) {
1689
23.3k
        extent.length = range.read32();
1690
23.3k
      }
1691
7.60k
      else if (length_size == 8) {
1692
554
        extent.length = ((uint64_t) range.read32()) << 32;
1693
554
        extent.length |= range.read32();
1694
554
      }
1695
1696
30.9k
      item.extents.push_back(extent);
1697
30.9k
    }
1698
1699
28.6k
    if (!range.error()) {
1700
28.5k
      m_items.push_back(item);
1701
28.5k
    }
1702
28.6k
  }
1703
1704
9.35k
  return range.get_error();
1705
9.48k
}
1706
1707
1708
Box_iloc::Box_iloc()
1709
9.72k
{
1710
9.72k
  set_short_type(fourcc("iloc"));
1711
1712
9.72k
  set_use_tmp_file(false);
1713
9.72k
}
1714
1715
1716
Box_iloc::~Box_iloc()
1717
9.72k
{
1718
9.72k
  if (m_use_tmpfile) {
1719
0
    unlink(m_tmp_filename);
1720
0
  }
1721
9.72k
}
1722
1723
1724
void Box_iloc::set_use_tmp_file(bool flag)
1725
9.72k
{
1726
9.72k
  m_use_tmpfile = flag;
1727
9.72k
  if (flag) {
1728
0
#if !defined(_WIN32)
1729
0
    strcpy(m_tmp_filename, "/tmp/libheif-XXXXXX");
1730
0
    m_tmpfile_fd = mkstemp(m_tmp_filename);
1731
#else
1732
    // TODO Currently unused code. Implement when needed.
1733
    assert(false);
1734
#  if 0
1735
    char tmpname[L_tmpnam_s];
1736
    // TODO: check return value (errno_t)
1737
    tmpnam_s(tmpname, L_tmpnam_s);
1738
    _sopen_s(&m_tmpfile_fd, tmpname, _O_CREAT | _O_TEMPORARY | _O_TRUNC | _O_RDWR, _SH_DENYRW, _S_IREAD | _S_IWRITE);
1739
#  endif
1740
#endif
1741
0
  }
1742
9.72k
}
1743
1744
1745
std::string Box_iloc::dump(Indent& indent) const
1746
0
{
1747
0
  std::ostringstream sstr;
1748
0
  sstr << Box::dump(indent);
1749
1750
0
  for (const Item& item : m_items) {
1751
0
    sstr << indent << "item ID: " << item.item_ID << "\n"
1752
0
         << indent << "  construction method: " << ((int) item.construction_method) << "\n"
1753
0
         << indent << "  data_reference_index: " << std::hex
1754
0
         << item.data_reference_index << std::dec << "\n"
1755
0
         << indent << "  base_offset: " << item.base_offset << "\n";
1756
1757
0
    sstr << indent << "  extents: ";
1758
0
    for (const Extent& extent : item.extents) {
1759
0
      sstr << extent.offset << "," << extent.length;
1760
0
      if (extent.index != 0) {
1761
0
        sstr << ";index=" << extent.index;
1762
0
      }
1763
0
      sstr << " ";
1764
0
    }
1765
0
    sstr << "\n";
1766
0
  }
1767
1768
0
  return sstr.str();
1769
0
}
1770
1771
1772
Error Box_iloc::read_data(heif_item_id item,
1773
                          const std::shared_ptr<StreamReader>& istr,
1774
                          const std::shared_ptr<Box_idat>& idat,
1775
                          std::vector<uint8_t>* dest,
1776
                          const heif_security_limits* limits) const
1777
5.58k
{
1778
5.58k
  return read_data(item, istr, idat, dest, 0, std::numeric_limits<uint64_t>::max(), limits);
1779
5.58k
}
1780
1781
1782
Error Box_iloc::read_data(heif_item_id item_id,
1783
                          const std::shared_ptr<StreamReader>& istr,
1784
                          const std::shared_ptr<Box_idat>& idat,
1785
                          std::vector<uint8_t>* dest,
1786
                          uint64_t offset, uint64_t size,
1787
                          const heif_security_limits* limits) const
1788
12.5k
{
1789
12.5k
  const Item* item = nullptr;
1790
78.0k
  for (auto& i : m_items) {
1791
78.0k
    if (i.item_ID == item_id) {
1792
10.7k
      item = &i;
1793
10.7k
      break;
1794
10.7k
    }
1795
78.0k
  }
1796
1797
12.5k
  if (!item) {
1798
1.85k
    std::stringstream sstr;
1799
1.85k
    sstr << "Item with ID " << item_id << " has no compressed data";
1800
1801
1.85k
    return Error(heif_error_Invalid_input,
1802
1.85k
                 heif_suberror_No_item_data,
1803
1.85k
                 sstr.str());
1804
1.85k
  }
1805
1806
10.7k
#if ENABLE_MULTITHREADING_SUPPORT
1807
10.7k
  static std::mutex read_mutex;
1808
1809
10.7k
  std::lock_guard<std::mutex> lock(read_mutex);
1810
10.7k
#endif
1811
1812
10.7k
  bool limited_size = (size != std::numeric_limits<uint64_t>::max());
1813
1814
1815
  // TODO: this function should always append the data to the output vector as this is used when
1816
  //       the image data is concatenated with data in a configuration box. However, it seems that
1817
  //       this function clears the array in some cases. This should be corrected.
1818
1819
10.7k
  for (const auto& extent : item->extents) {
1820
10.7k
    if (item->construction_method == 0) {
1821
1822
      // --- make sure that all data is available
1823
1824
9.81k
      if (extent.offset > MAX_FILE_POS ||
1825
9.78k
          item->base_offset > MAX_FILE_POS ||
1826
9.78k
          extent.length > MAX_FILE_POS) {
1827
67
        return {heif_error_Invalid_input,
1828
67
                heif_suberror_Security_limit_exceeded,
1829
67
                "iloc data pointers out of allowed range"};
1830
67
      }
1831
1832
9.74k
      StreamReader::grow_status status = istr->wait_for_file_size(extent.offset + item->base_offset + extent.length);
1833
9.74k
      if (status == StreamReader::grow_status::size_beyond_eof) {
1834
        // Out-of-bounds
1835
        // TODO: I think we should not clear this. Maybe we want to try reading again later and
1836
        // hence should not lose the data already read.
1837
1.62k
        dest->clear();
1838
1839
1.62k
        std::stringstream sstr;
1840
1.62k
        sstr << "Extent in iloc box references data outside of file bounds "
1841
1.62k
             << "(points to file position " << extent.offset + item->base_offset << ")\n";
1842
1843
1.62k
        return {heif_error_Invalid_input,
1844
1.62k
                heif_suberror_End_of_data,
1845
1.62k
                sstr.str()};
1846
1.62k
      }
1847
8.11k
      else if (status == StreamReader::grow_status::timeout) {
1848
        // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
1849
0
        return {heif_error_Invalid_input,
1850
0
                heif_suberror_End_of_data};
1851
0
      }
1852
1853
1854
      // skip to reading offset
1855
1856
8.11k
      uint64_t skip_len = std::min(offset, extent.length);
1857
8.11k
      offset -= skip_len;
1858
1859
8.11k
      uint64_t read_len = std::min(extent.length - skip_len, size);
1860
1861
8.11k
      if (offset > 0) {
1862
0
        continue;
1863
0
      }
1864
1865
8.11k
      if (read_len == 0) {
1866
702
        continue;
1867
702
      }
1868
1869
7.41k
      size_t old_size = dest->size();
1870
1871
      // --- security check that we do not allocate too much memory
1872
1873
7.41k
      auto max_memory_block_size = limits->max_memory_block_size;
1874
7.41k
      if (max_memory_block_size && max_memory_block_size - old_size < read_len) {
1875
0
        std::stringstream sstr;
1876
0
        sstr << "iloc box contained " << extent.length << " bytes, total memory size would be "
1877
0
             << (old_size + extent.length) << " bytes, exceeding the security limit of "
1878
0
             << max_memory_block_size << " bytes";
1879
1880
0
        return {heif_error_Memory_allocation_error,
1881
0
                heif_suberror_Security_limit_exceeded,
1882
0
                sstr.str()};
1883
0
      }
1884
1885
1886
      // --- request file range
1887
1888
7.41k
      uint64_t data_start_pos = extent.offset + item->base_offset + skip_len;
1889
7.41k
      uint64_t rangeRequestEndPos = istr->request_range(data_start_pos, data_start_pos + read_len);
1890
7.41k
      if (rangeRequestEndPos == 0) {
1891
0
        return istr->get_error();
1892
0
      }
1893
1894
      // --- move file pointer to start of data
1895
1896
7.41k
      bool success = istr->seek(data_start_pos);
1897
7.41k
      if (!success) {
1898
0
        return {heif_error_Invalid_input,
1899
0
                heif_suberror_Unspecified,
1900
0
                "Error setting input file position"};
1901
0
      }
1902
1903
1904
      // --- read data
1905
1906
7.41k
      dest->resize(static_cast<size_t>(old_size + read_len));
1907
7.41k
      success = istr->read((char*) dest->data() + old_size, static_cast<size_t>(read_len));
1908
7.41k
      if (!success) {
1909
0
        return {heif_error_Invalid_input,
1910
0
                heif_suberror_Unspecified,
1911
0
                "Error reading input file"};
1912
0
      }
1913
1914
7.41k
      size -= read_len;
1915
7.41k
    }
1916
903
    else if (item->construction_method == 1) {
1917
856
      if (!idat) {
1918
23
        return {heif_error_Invalid_input,
1919
23
                heif_suberror_No_idat_box,
1920
23
                "idat box referenced in iref box is not present in file"};
1921
23
      }
1922
1923
833
      Error err = idat->read_data(istr,
1924
833
                                  extent.offset + item->base_offset,
1925
833
                                  extent.length,
1926
833
                                  *dest, limits);
1927
833
      if (err) {
1928
390
        return err;
1929
390
      }
1930
1931
443
      size -= extent.length;
1932
443
    }
1933
47
    else {
1934
47
      std::stringstream sstr;
1935
47
      sstr << "Item construction method " << (int) item->construction_method << " not implemented";
1936
47
      return {heif_error_Unsupported_feature,
1937
47
              heif_suberror_Unsupported_item_construction_method,
1938
47
              sstr.str()};
1939
47
    }
1940
10.7k
  }
1941
1942
  // --- we could not read all data
1943
1944
8.54k
  if (limited_size && size > 0) {
1945
0
    return {heif_error_Invalid_input,
1946
0
            heif_suberror_End_of_data,
1947
0
            "Not enough data present in 'iloc' to satisfy request."};
1948
0
  }
1949
1950
8.54k
  return Error::Ok;
1951
8.54k
}
1952
1953
1954
Error Box_iloc::append_data(heif_item_id item_ID,
1955
                            const std::vector<uint8_t>& data,
1956
                            uint8_t construction_method)
1957
0
{
1958
  // check whether this item ID already exists
1959
1960
0
  size_t idx;
1961
0
  for (idx = 0; idx < m_items.size(); idx++) {
1962
0
    if (m_items[idx].item_ID == item_ID) {
1963
0
      break;
1964
0
    }
1965
0
  }
1966
1967
  // item does not exist -> add a new one to the end
1968
1969
0
  if (idx == m_items.size()) {
1970
0
    Item item;
1971
0
    item.item_ID = item_ID;
1972
0
    item.construction_method = construction_method;
1973
1974
0
    m_items.push_back(item);
1975
0
  }
1976
1977
0
  if (m_items[idx].construction_method != construction_method) {
1978
    // TODO: return error: construction methods do not match
1979
0
  }
1980
1981
0
  Extent extent;
1982
0
  extent.length = data.size();
1983
1984
0
  if (m_use_tmpfile && construction_method==0) {
1985
0
#if !defined(_WIN32)
1986
0
    ssize_t cnt = ::write(m_tmpfile_fd, data.data(), data.size());
1987
#else
1988
    // TODO Currently unused code. Implement when needed.
1989
    assert(false);
1990
#  if 0
1991
    int cnt = _write(m_tmpfile_fd, data.data(), data.size());
1992
#  else
1993
    int cnt = -1;
1994
#  endif
1995
#endif
1996
0
    if (cnt < 0) {
1997
0
      std::stringstream sstr;
1998
0
      sstr << "Could not write to tmp file: error " << errno;
1999
0
      return {heif_error_Encoding_error,
2000
0
              heif_suberror_Unspecified,
2001
0
              sstr.str()};
2002
0
    }
2003
0
    else if ((size_t)cnt != data.size()) {
2004
0
      return {heif_error_Encoding_error,
2005
0
              heif_suberror_Unspecified,
2006
0
              "Could not write to tmp file (storage full?)"};
2007
0
    }
2008
0
  }
2009
0
  else {
2010
0
    if (!m_items[idx].extents.empty()) {
2011
0
      Extent& e = m_items[idx].extents.back();
2012
0
      e.data.insert(e.data.end(), data.begin(), data.end());
2013
0
      e.length = e.data.size();
2014
0
      return Error::Ok;
2015
0
    }
2016
2017
0
    extent.data = data;
2018
0
  }
2019
2020
0
  if (construction_method == 1) {
2021
0
    extent.offset = m_idat_offset;
2022
0
    extent.length = data.size();
2023
2024
0
    m_idat_offset += (int) data.size();
2025
0
  }
2026
2027
0
  m_items[idx].extents.push_back(std::move(extent));
2028
2029
0
  return Error::Ok;
2030
0
}
2031
2032
2033
Error Box_iloc::replace_data(heif_item_id item_ID,
2034
                             uint64_t output_offset,
2035
                             const std::vector<uint8_t>& data,
2036
                             uint8_t construction_method)
2037
0
{
2038
0
  assert(construction_method == 0); // TODO
2039
2040
  // check whether this item ID already exists
2041
2042
0
  size_t idx;
2043
0
  for (idx = 0; idx < m_items.size(); idx++) {
2044
0
    if (m_items[idx].item_ID == item_ID) {
2045
0
      break;
2046
0
    }
2047
0
  }
2048
2049
0
  assert(idx != m_items.size());
2050
2051
0
  uint64_t data_start = 0;
2052
0
  for (auto& extent : m_items[idx].extents) {
2053
0
    if (output_offset >= extent.data.size()) {
2054
0
      output_offset -= extent.data.size();
2055
0
    }
2056
0
    else {
2057
0
      uint64_t write_n = std::min(extent.data.size() - output_offset,
2058
0
                                  data.size() - data_start);
2059
0
      assert(write_n > 0);
2060
2061
0
      memcpy(extent.data.data() + output_offset, data.data() + data_start, write_n);
2062
2063
0
      data_start += write_n;
2064
0
      output_offset = 0;
2065
0
    }
2066
2067
0
    if (data_start == data.size()) {
2068
0
      break;
2069
0
    }
2070
0
  }
2071
2072
0
  return Error::Ok;
2073
0
}
2074
2075
2076
void Box_iloc::derive_box_version()
2077
0
{
2078
0
  int min_version = m_user_defined_min_version;
2079
2080
0
  if (m_items.size() > 0xFFFF) {
2081
0
    min_version = std::max(min_version, 2);
2082
0
  }
2083
2084
0
  m_offset_size = 0;
2085
0
  m_length_size = 0;
2086
0
  m_base_offset_size = 0;
2087
0
  m_index_size = 0;
2088
2089
0
  uint64_t total_data_size = 0;
2090
2091
0
  for (const auto& item : m_items) {
2092
    // check item_ID size
2093
0
    if (item.item_ID > 0xFFFF) {
2094
0
      min_version = std::max(min_version, 2);
2095
0
    }
2096
2097
    // check construction method
2098
0
    if (item.construction_method != 0) {
2099
0
      min_version = std::max(min_version, 1);
2100
0
    }
2101
2102
0
    total_data_size += item.extents[0].length;
2103
2104
    /* cannot compute this here because values are not set yet
2105
    // base offset size
2106
    if (item.base_offset > 0xFFFFFFFF) {
2107
      m_base_offset_size = std::max(m_base_offset_size, (uint8_t)8);
2108
    }
2109
    else if (item.base_offset > 0) {
2110
      m_base_offset_size = std::max(m_base_offset_size, (uint8_t)4);
2111
    }
2112
*/
2113
2114
    /*
2115
    for (const auto& extent : item.extents) {
2116
      // extent index size
2117
2118
      if (extent.index != 0) {
2119
        min_version = std::max(min_version, 1);
2120
        m_index_size = 4;
2121
      }
2122
2123
      if (extent.index > 0xFFFFFFFF) {
2124
        m_index_size = 8;
2125
      }
2126
2127
      // extent offset size
2128
      if (extent.offset > 0xFFFFFFFF) {
2129
        m_offset_size = 8;
2130
      }
2131
      else {
2132
        m_offset_size = 4;
2133
      }
2134
2135
      // extent length size
2136
      if (extent.length > 0xFFFFFFFF) {
2137
        m_length_size = 8;
2138
      }
2139
      else {
2140
        m_length_size = 4;
2141
      }
2142
    }
2143
      */
2144
0
  }
2145
2146
0
  uint64_t maximum_meta_box_size_guess = 0x10000000; // 256 MB
2147
0
  if (total_data_size + maximum_meta_box_size_guess > 0xFFFFFFFF) {
2148
0
    m_base_offset_size = 8;
2149
0
  }
2150
0
  else {
2151
0
    m_base_offset_size = 4;
2152
0
  }
2153
2154
0
  m_offset_size = 4;
2155
0
  m_length_size = 4;
2156
0
  m_index_size = 0;
2157
2158
  // extent.length is already known; extent.offset within an item equals
2159
  // the cumulative length of all preceding extents (they are written sequentially).
2160
  // base_offset absorbs the absolute file position, so extent.offset is relative.
2161
0
  for (const auto& item : m_items) {
2162
0
    uint64_t cumulative_offset = 0;
2163
0
    for (const auto& extent : item.extents) {
2164
0
      if (cumulative_offset > 0xFFFFFFFF) {
2165
0
        m_offset_size = 8;
2166
0
      }
2167
0
      if (extent.length > 0xFFFFFFFF) {
2168
0
        m_length_size = 8;
2169
0
      }
2170
0
      cumulative_offset += extent.length;
2171
0
    }
2172
0
  }
2173
2174
0
  set_version((uint8_t) min_version);
2175
0
}
2176
2177
2178
Error Box_iloc::write(StreamWriter& writer) const
2179
0
{
2180
  // --- write idat
2181
2182
0
  size_t sum_idat_size = 0;
2183
2184
0
  for (const auto& item : m_items) {
2185
0
    if (item.construction_method == 1) {
2186
0
      for (const auto& extent : item.extents) {
2187
0
        sum_idat_size += extent.data.size();
2188
0
      }
2189
0
    }
2190
0
  }
2191
2192
0
  if (sum_idat_size > 0) {
2193
0
    writer.write32((uint32_t) (sum_idat_size + 8));
2194
0
    writer.write32(fourcc("idat"));
2195
2196
0
    for (auto& item : m_items) {
2197
0
      if (item.construction_method == 1) {
2198
0
        for (auto& extent : item.extents) {
2199
0
          writer.write(extent.data);
2200
0
        }
2201
0
      }
2202
0
    }
2203
0
  }
2204
2205
2206
  // --- write iloc box
2207
2208
0
  size_t box_start = reserve_box_header_space(writer);
2209
2210
0
  m_iloc_box_start = writer.get_position();
2211
2212
0
  int nSkip = 0;
2213
2214
0
  nSkip += 2;
2215
0
  nSkip += (get_version() < 2) ? 2 : 4; // item_count
2216
2217
0
  for (const auto& item : m_items) {
2218
0
    nSkip += (get_version() < 2) ? 2 : 4; // item_ID
2219
0
    nSkip += (get_version() >= 1) ? 2 : 0; // construction method
2220
0
    nSkip += 4 + m_base_offset_size;
2221
2222
0
    for (const auto& extent : item.extents) {
2223
0
      (void) extent;
2224
2225
0
      if (get_version() >= 1) {
2226
0
        nSkip += m_index_size;
2227
0
      }
2228
2229
0
      nSkip += m_offset_size + m_length_size;
2230
0
    }
2231
0
  }
2232
2233
0
  writer.skip(nSkip);
2234
0
  prepend_header(writer, box_start);
2235
2236
0
  patch_iloc_header(writer); // Write iloc box. If there is an mdat, it will later be overwritten.
2237
2238
0
  return Error::Ok;
2239
0
}
2240
2241
2242
Error Box_iloc::write_mdat_after_iloc(StreamWriter& writer)
2243
0
{
2244
  // --- compute sum of all mdat data
2245
2246
0
  size_t sum_mdat_size = 0;
2247
2248
0
  for (const auto& item : m_items) {
2249
0
    if (item.construction_method == 0) {
2250
0
      for (const auto& extent : item.extents) {
2251
0
        sum_mdat_size += extent.length;
2252
0
      }
2253
0
    }
2254
0
  }
2255
2256
  // --- write mdat box
2257
2258
0
  if (sum_mdat_size <= 0xFFFFFFFF) {
2259
0
    writer.write32((uint32_t) (sum_mdat_size + 8));
2260
0
    writer.write32(fourcc("mdat"));
2261
0
  }
2262
0
  else {
2263
    // box size > 4 GB
2264
2265
0
    writer.write32(1);
2266
0
    writer.write32(fourcc("mdat"));
2267
0
    writer.write64(sum_mdat_size+8+8);
2268
0
  }
2269
2270
0
  if (m_use_tmpfile) {
2271
0
    ::lseek(m_tmpfile_fd, 0, SEEK_SET);
2272
0
  }
2273
2274
0
  for (auto& item : m_items) {
2275
0
    if (item.construction_method == 0) {
2276
0
      item.base_offset = writer.get_position();
2277
2278
0
      for (auto& extent : item.extents) {
2279
0
        extent.offset = writer.get_position() - item.base_offset;
2280
        //extent.length = extent.data.size();
2281
2282
0
        if (m_use_tmpfile) {
2283
0
          std::vector<uint8_t> data(extent.length);
2284
0
#if !defined(_WIN32)
2285
0
          ssize_t cnt = ::read(m_tmpfile_fd, data.data(), extent.length);
2286
#else
2287
          // TODO Currently unused code. Implement when needed.
2288
          assert(false);
2289
# if 0
2290
          int cnt = _read(m_tmpfile_fd, data.data(), extent.length);
2291
# else
2292
          int cnt = -1;
2293
# endif
2294
#endif
2295
0
          if (cnt<0) {
2296
0
            std::stringstream sstr;
2297
0
            sstr << "Cannot read tmp data file, error " << errno;
2298
0
            return {heif_error_Encoding_error,
2299
0
                    heif_suberror_Unspecified,
2300
0
                    sstr.str()};
2301
0
          }
2302
0
          else if ((uint64_t)cnt != extent.length) {
2303
0
            return {heif_error_Encoding_error,
2304
0
                    heif_suberror_Unspecified,
2305
0
                    "Tmp data could not be read completely"};
2306
0
          }
2307
0
          writer.write(data);
2308
0
        }
2309
0
        else {
2310
0
          writer.write(extent.data);
2311
0
        }
2312
0
      }
2313
0
    }
2314
0
  }
2315
2316
2317
  // --- patch iloc box
2318
2319
0
  patch_iloc_header(writer);
2320
2321
0
  return Error::Ok;
2322
0
}
2323
2324
2325
void Box_iloc::patch_iloc_header(StreamWriter& writer) const
2326
0
{
2327
0
  size_t old_pos = writer.get_position();
2328
0
  writer.set_position(m_iloc_box_start);
2329
2330
0
  writer.write8((uint8_t) ((m_offset_size << 4) | (m_length_size)));
2331
0
  writer.write8((uint8_t) ((m_base_offset_size << 4) | (m_index_size)));
2332
2333
0
  if (get_version() < 2) {
2334
0
    writer.write16((uint16_t) m_items.size());
2335
0
  }
2336
0
  else {
2337
0
    writer.write32((uint32_t) m_items.size());
2338
0
  }
2339
2340
0
  for (const auto& item : m_items) {
2341
0
    if (get_version() < 2) {
2342
0
      writer.write16((uint16_t) item.item_ID);
2343
0
    }
2344
0
    else {
2345
0
      writer.write32((uint32_t) item.item_ID);
2346
0
    }
2347
2348
0
    if (get_version() >= 1) {
2349
0
      writer.write16(item.construction_method);
2350
0
    }
2351
2352
0
    writer.write16(item.data_reference_index);
2353
0
    if (m_base_offset_size > 0) {
2354
0
      writer.write(m_base_offset_size, item.base_offset);
2355
0
    }
2356
0
    else {
2357
0
      assert(item.base_offset == 0);
2358
0
    }
2359
0
    writer.write16((uint16_t) item.extents.size());
2360
2361
0
    for (const auto& extent : item.extents) {
2362
0
      if (get_version() >= 1 && m_index_size > 0) {
2363
0
        writer.write(m_index_size, extent.index);
2364
0
      }
2365
2366
0
      writer.write(m_offset_size, extent.offset);
2367
0
      writer.write(m_length_size, extent.length);
2368
0
    }
2369
0
  }
2370
2371
0
  writer.set_position(old_pos);
2372
0
}
2373
2374
2375
/*
2376
 *                     version <= 1    version 2   version > 2    mime     uri
2377
 * -----------------------------------------------------------------------------------------------
2378
 * item id               16               16           32          16/32   16/32
2379
 * protection index      16               16           16          16      16
2380
 * item type             -                yes          yes         yes     yes
2381
 * item name             yes              yes          yes         yes     yes
2382
 * content type          yes              -            -           yes     -
2383
 * content encoding      yes              -            -           yes     -
2384
 * hidden item           -                yes          yes         yes     yes
2385
 * item uri type         -                -            -           -       yes
2386
 *
2387
 * Note: HEIF does not allow version 0 and version 1 boxes ! (see 23008-12, 10.2.1)
2388
 */
2389
2390
Error Box_infe::parse(BitstreamRange& range, const heif_security_limits* limits)
2391
24.1k
{
2392
24.1k
  parse_full_box_header(range);
2393
2394
  // only versions 2,3 are required by HEIF
2395
24.1k
  if (get_version() > 3) {
2396
8
    return unsupported_version_error("infe");
2397
8
  }
2398
2399
24.1k
  if (get_version() <= 1) {
2400
402
    m_item_ID = range.read16();
2401
402
    m_item_protection_index = range.read16();
2402
2403
402
    m_item_name = range.read_string();
2404
402
    m_content_type = range.read_string();
2405
402
    m_content_encoding = range.read_string();
2406
402
  }
2407
2408
24.1k
  m_item_type_4cc = 0;
2409
2410
24.1k
  if (get_version() >= 2) {
2411
23.7k
    m_hidden_item = (get_flags() & 1);
2412
2413
23.7k
    if (get_version() == 2) {
2414
23.6k
      m_item_ID = range.read16();
2415
23.6k
    }
2416
94
    else {
2417
94
      m_item_ID = range.read32();
2418
94
    }
2419
2420
23.7k
    m_item_protection_index = range.read16();
2421
23.7k
    m_item_type_4cc = range.read32();
2422
2423
23.7k
    m_item_name = range.read_string();
2424
23.7k
    if (m_item_type_4cc == fourcc("mime")) {
2425
75
      m_content_type = range.read_string();
2426
75
      m_content_encoding = range.read_string();
2427
75
    }
2428
23.6k
    else if (m_item_type_4cc == fourcc("uri ")) {
2429
10
      m_item_uri_type = range.read_string();
2430
10
    }
2431
23.7k
  }
2432
2433
24.1k
  return range.get_error();
2434
24.1k
}
2435
2436
2437
void Box_infe::derive_box_version()
2438
0
{
2439
0
  int min_version = 0;
2440
2441
0
  if (m_hidden_item) {
2442
0
    min_version = std::max(min_version, 2);
2443
0
  }
2444
2445
0
  if (m_item_ID > 0xFFFF) {
2446
0
    min_version = std::max(min_version, 3);
2447
0
  }
2448
2449
2450
0
  if (m_item_type_4cc != 0) {
2451
0
    min_version = std::max(min_version, 2);
2452
0
  }
2453
2454
0
  set_version((uint8_t) min_version);
2455
0
}
2456
2457
2458
void Box_infe::set_hidden_item(bool hidden)
2459
0
{
2460
0
  m_hidden_item = hidden;
2461
2462
0
  if (m_hidden_item) {
2463
0
    set_flags(get_flags() | 1U);
2464
0
  }
2465
0
  else {
2466
0
    set_flags(get_flags() & ~1U);
2467
0
  }
2468
0
}
2469
2470
Error Box_infe::write(StreamWriter& writer) const
2471
0
{
2472
0
  size_t box_start = reserve_box_header_space(writer);
2473
2474
0
  if (get_version() <= 1) {
2475
0
    writer.write16((uint16_t) m_item_ID);
2476
0
    writer.write16(m_item_protection_index);
2477
2478
0
    writer.write(m_item_name);
2479
0
    writer.write(m_content_type);
2480
0
    writer.write(m_content_encoding);
2481
0
  }
2482
2483
0
  if (get_version() >= 2) {
2484
0
    if (get_version() == 2) {
2485
0
      writer.write16((uint16_t) m_item_ID);
2486
0
    }
2487
0
    else if (get_version() == 3) {
2488
0
      writer.write32(m_item_ID);
2489
0
    }
2490
2491
0
    writer.write16(m_item_protection_index);
2492
2493
0
    writer.write32(m_item_type_4cc);
2494
2495
0
    writer.write(m_item_name);
2496
0
    if (m_item_type_4cc == fourcc("mime")) {
2497
0
      writer.write(m_content_type);
2498
0
      writer.write(m_content_encoding);
2499
0
    }
2500
0
    else if (m_item_type_4cc == fourcc("uri ")) {
2501
0
      writer.write(m_item_uri_type);
2502
0
    }
2503
0
  }
2504
2505
0
  prepend_header(writer, box_start);
2506
2507
0
  return Error::Ok;
2508
0
}
2509
2510
2511
std::string Box_infe::dump(Indent& indent) const
2512
0
{
2513
0
  std::ostringstream sstr;
2514
0
  sstr << Box::dump(indent);
2515
2516
0
  sstr << indent << "item_ID: " << m_item_ID << "\n"
2517
0
       << indent << "item_protection_index: " << m_item_protection_index << "\n"
2518
0
       << indent << "item_type: " << fourcc_to_string(m_item_type_4cc) << "\n"
2519
0
       << indent << "item_name: " << m_item_name << "\n";
2520
2521
0
  if (m_item_type_4cc == fourcc("mime")) {
2522
0
    sstr << indent << "content_type: " << m_content_type << "\n"
2523
0
         << indent << "content_encoding: " << m_content_encoding << "\n";
2524
0
  }
2525
2526
0
  if (m_item_type_4cc == fourcc("uri ")) {
2527
0
    sstr << indent << "item uri type: " << m_item_uri_type << "\n";
2528
0
  }
2529
2530
0
  sstr << indent << "hidden item: " << std::boolalpha << m_hidden_item << "\n";
2531
2532
0
  return sstr.str();
2533
0
}
2534
2535
2536
Error Box_iinf::parse(BitstreamRange& range, const heif_security_limits* limits)
2537
9.80k
{
2538
9.80k
  parse_full_box_header(range);
2539
2540
  // TODO: there are several images in circulation that have an iinf version=2. We should not enforce this with a hard error.
2541
9.80k
  if (false && get_version() > 1) {
2542
0
    return unsupported_version_error("iinf");
2543
0
  }
2544
2545
9.80k
  int nEntries_size = (get_version() > 0) ? 4 : 2;
2546
2547
9.80k
  uint32_t item_count;
2548
9.80k
  if (nEntries_size == 2) {
2549
9.42k
    item_count = range.read16();
2550
9.42k
  }
2551
377
  else {
2552
377
    item_count = range.read32();
2553
377
  }
2554
2555
9.80k
  if (item_count == 0) {
2556
234
    return Error::Ok;
2557
234
  }
2558
2559
9.56k
  return read_children(range, item_count, limits);
2560
9.80k
}
2561
2562
2563
std::string Box_iinf::dump(Indent& indent) const
2564
0
{
2565
0
  std::ostringstream sstr;
2566
0
  sstr << Box::dump(indent);
2567
2568
0
  sstr << dump_children(indent);
2569
2570
0
  return sstr.str();
2571
0
}
2572
2573
2574
Error Box_iprp::parse(BitstreamRange& range, const heif_security_limits* limits)
2575
9.41k
{
2576
  //parse_full_box_header(range);
2577
2578
9.41k
  return read_children(range, READ_CHILDREN_ALL, limits);
2579
9.41k
}
2580
2581
2582
void Box_iinf::derive_box_version()
2583
0
{
2584
0
  if (m_children.size() > 0xFFFF) {
2585
0
    set_version(1);
2586
0
  }
2587
0
  else {
2588
0
    set_version(0);
2589
0
  }
2590
0
}
2591
2592
2593
Error Box_iinf::write(StreamWriter& writer) const
2594
0
{
2595
0
  size_t box_start = reserve_box_header_space(writer);
2596
2597
0
  int nEntries_size = (get_version() > 0) ? 4 : 2;
2598
2599
0
  writer.write(nEntries_size, m_children.size());
2600
2601
2602
0
  Error err = write_children(writer);
2603
2604
0
  prepend_header(writer, box_start);
2605
2606
0
  return err;
2607
0
}
2608
2609
2610
std::string Box_iprp::dump(Indent& indent) const
2611
0
{
2612
0
  std::ostringstream sstr;
2613
0
  sstr << Box::dump(indent);
2614
2615
0
  sstr << dump_children(indent);
2616
2617
0
  return sstr.str();
2618
0
}
2619
2620
2621
uint32_t Box_ipco::find_or_append_child_box(const std::shared_ptr<Box>& box)
2622
2.41k
{
2623
5.90k
  for (uint32_t i = 0; i < (uint32_t) m_children.size(); i++) {
2624
5.81k
    if (Box::equal(m_children[i], box)) {
2625
2.33k
      return i;
2626
2.33k
    }
2627
5.81k
  }
2628
82
  return append_child_box(box);
2629
2.41k
}
2630
2631
2632
Error Box_ipco::parse(BitstreamRange& range, const heif_security_limits* limits)
2633
9.14k
{
2634
  //parse_full_box_header(range);
2635
2636
9.14k
  return read_children(range, READ_CHILDREN_ALL, limits);
2637
9.14k
}
2638
2639
2640
std::string Box_ipco::dump(Indent& indent) const
2641
0
{
2642
0
  std::ostringstream sstr;
2643
0
  sstr << Box::dump(indent);
2644
2645
0
  sstr << dump_children(indent, true);
2646
2647
0
  return sstr.str();
2648
0
}
2649
2650
2651
Error Box_pixi::parse(BitstreamRange& range, const heif_security_limits* limits)
2652
2.82k
{
2653
2.82k
  parse_full_box_header(range);
2654
2655
2.82k
  if (get_version() != 0) {
2656
206
    return unsupported_version_error("pixi");
2657
206
  }
2658
2659
2.62k
  StreamReader::grow_status status;
2660
2.62k
  uint8_t num_channels = range.read8();
2661
2.62k
  status = range.wait_for_available_bytes(num_channels);
2662
2.62k
  if (status != StreamReader::grow_status::size_reached) {
2663
    // TODO: return recoverable error at timeout
2664
152
    return Error(heif_error_Invalid_input,
2665
152
                 heif_suberror_End_of_data);
2666
152
  }
2667
2668
2.46k
  m_bits_per_channel.resize(num_channels);
2669
154k
  for (int i = 0; i < num_channels; i++) {
2670
151k
    m_bits_per_channel[i] = range.read8();
2671
151k
  }
2672
2673
2.46k
  return range.get_error();
2674
2.62k
}
2675
2676
2677
std::string Box_pixi::dump(Indent& indent) const
2678
0
{
2679
0
  std::ostringstream sstr;
2680
0
  sstr << Box::dump(indent);
2681
2682
0
  sstr << indent << "bits_per_channel: ";
2683
2684
0
  for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
2685
0
    if (i > 0) sstr << ",";
2686
0
    sstr << ((int) m_bits_per_channel[i]);
2687
0
  }
2688
2689
0
  sstr << "\n";
2690
2691
0
  return sstr.str();
2692
0
}
2693
2694
2695
Error Box_pixi::write(StreamWriter& writer) const
2696
0
{
2697
0
  size_t box_start = reserve_box_header_space(writer);
2698
2699
0
  if (m_bits_per_channel.size() > 255 ||
2700
0
      m_bits_per_channel.empty()) {
2701
    // TODO: error
2702
0
    assert(false);
2703
0
  }
2704
2705
0
  writer.write8((uint8_t) (m_bits_per_channel.size()));
2706
0
  for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
2707
0
    writer.write8(m_bits_per_channel[i]);
2708
0
  }
2709
2710
0
  prepend_header(writer, box_start);
2711
2712
0
  return Error::Ok;
2713
0
}
2714
2715
2716
Error Box_pasp::parse(BitstreamRange& range, const heif_security_limits* limits)
2717
62
{
2718
  //parse_full_box_header(range);
2719
2720
62
  hSpacing = range.read32();
2721
62
  vSpacing = range.read32();
2722
2723
62
  return range.get_error();
2724
62
}
2725
2726
2727
std::string Box_pasp::dump(Indent& indent) const
2728
0
{
2729
0
  std::ostringstream sstr;
2730
0
  sstr << Box::dump(indent);
2731
2732
0
  sstr << indent << "hSpacing: " << hSpacing << "\n";
2733
0
  sstr << indent << "vSpacing: " << vSpacing << "\n";
2734
2735
0
  return sstr.str();
2736
0
}
2737
2738
2739
Error Box_pasp::write(StreamWriter& writer) const
2740
0
{
2741
0
  size_t box_start = reserve_box_header_space(writer);
2742
2743
0
  writer.write32(hSpacing);
2744
0
  writer.write32(vSpacing);
2745
2746
0
  prepend_header(writer, box_start);
2747
2748
0
  return Error::Ok;
2749
0
}
2750
2751
2752
Error Box_lsel::parse(BitstreamRange& range, const heif_security_limits* limits)
2753
128
{
2754
128
  layer_id = range.read16();
2755
2756
128
  return range.get_error();
2757
128
}
2758
2759
2760
std::string Box_lsel::dump(Indent& indent) const
2761
0
{
2762
0
  std::ostringstream sstr;
2763
0
  sstr << Box::dump(indent);
2764
2765
0
  sstr << indent << "layer_id: " << layer_id << "\n";
2766
2767
0
  return sstr.str();
2768
0
}
2769
2770
2771
Error Box_lsel::write(StreamWriter& writer) const
2772
0
{
2773
0
  size_t box_start = reserve_box_header_space(writer);
2774
2775
0
  writer.write16(layer_id);
2776
2777
0
  prepend_header(writer, box_start);
2778
2779
0
  return Error::Ok;
2780
0
}
2781
2782
2783
Error Box_clli::parse(BitstreamRange& range, const heif_security_limits* limits)
2784
3
{
2785
  //parse_full_box_header(range);
2786
2787
3
  clli.max_content_light_level = range.read16();
2788
3
  clli.max_pic_average_light_level = range.read16();
2789
2790
3
  return range.get_error();
2791
3
}
2792
2793
2794
std::string Box_clli::dump(Indent& indent) const
2795
0
{
2796
0
  std::ostringstream sstr;
2797
0
  sstr << Box::dump(indent);
2798
2799
0
  sstr << indent << "max_content_light_level: " << clli.max_content_light_level << "\n";
2800
0
  sstr << indent << "max_pic_average_light_level: " << clli.max_pic_average_light_level << "\n";
2801
2802
0
  return sstr.str();
2803
0
}
2804
2805
2806
Error Box_clli::write(StreamWriter& writer) const
2807
0
{
2808
0
  size_t box_start = reserve_box_header_space(writer);
2809
2810
0
  writer.write16(clli.max_content_light_level);
2811
0
  writer.write16(clli.max_pic_average_light_level);
2812
2813
0
  prepend_header(writer, box_start);
2814
2815
0
  return Error::Ok;
2816
0
}
2817
2818
2819
Box_mdcv::Box_mdcv()
2820
1.39k
{
2821
1.39k
  set_short_type(fourcc("mdcv"));
2822
2823
1.39k
  memset(&mdcv, 0, sizeof(heif_mastering_display_colour_volume));
2824
1.39k
}
2825
2826
2827
Error Box_mdcv::parse(BitstreamRange& range, const heif_security_limits* limits)
2828
26
{
2829
  //parse_full_box_header(range);
2830
2831
104
  for (int c = 0; c < 3; c++) {
2832
78
    mdcv.display_primaries_x[c] = range.read16();
2833
78
    mdcv.display_primaries_y[c] = range.read16();
2834
78
  }
2835
2836
26
  mdcv.white_point_x = range.read16();
2837
26
  mdcv.white_point_y = range.read16();
2838
26
  mdcv.max_display_mastering_luminance = range.read32();
2839
26
  mdcv.min_display_mastering_luminance = range.read32();
2840
2841
26
  return range.get_error();
2842
26
}
2843
2844
2845
std::string Box_mdcv::dump(Indent& indent) const
2846
0
{
2847
0
  std::ostringstream sstr;
2848
0
  sstr << Box::dump(indent);
2849
2850
0
  sstr << indent << "display_primaries (x,y): ";
2851
0
  sstr << "(" << mdcv.display_primaries_x[0] << ";" << mdcv.display_primaries_y[0] << "), ";
2852
0
  sstr << "(" << mdcv.display_primaries_x[1] << ";" << mdcv.display_primaries_y[1] << "), ";
2853
0
  sstr << "(" << mdcv.display_primaries_x[2] << ";" << mdcv.display_primaries_y[2] << ")\n";
2854
2855
0
  sstr << indent << "white point (x,y): (" << mdcv.white_point_x << ";" << mdcv.white_point_y << ")\n";
2856
0
  sstr << indent << "max display mastering luminance: " << mdcv.max_display_mastering_luminance << "\n";
2857
0
  sstr << indent << "min display mastering luminance: " << mdcv.min_display_mastering_luminance << "\n";
2858
2859
0
  return sstr.str();
2860
0
}
2861
2862
2863
Error Box_mdcv::write(StreamWriter& writer) const
2864
0
{
2865
0
  size_t box_start = reserve_box_header_space(writer);
2866
2867
0
  for (int c = 0; c < 3; c++) {
2868
0
    writer.write16(mdcv.display_primaries_x[c]);
2869
0
    writer.write16(mdcv.display_primaries_y[c]);
2870
0
  }
2871
2872
0
  writer.write16(mdcv.white_point_x);
2873
0
  writer.write16(mdcv.white_point_y);
2874
2875
0
  writer.write32(mdcv.max_display_mastering_luminance);
2876
0
  writer.write32(mdcv.min_display_mastering_luminance);
2877
2878
0
  prepend_header(writer, box_start);
2879
2880
0
  return Error::Ok;
2881
0
}
2882
2883
Box_amve::Box_amve()
2884
879
{
2885
879
  set_short_type(fourcc("amve"));
2886
2887
  // These values are not valid.
2888
879
  amve.ambient_illumination = 0;
2889
879
  amve.ambient_light_x = 0;
2890
879
  amve.ambient_light_y = 0;
2891
879
}
2892
2893
Error Box_amve::parse(BitstreamRange& range, const heif_security_limits* limits)
2894
26
{
2895
26
  amve.ambient_illumination = range.read32();
2896
26
  amve.ambient_light_x = range.read16();
2897
26
  amve.ambient_light_y = range.read16();
2898
2899
26
  return range.get_error();
2900
26
}
2901
2902
2903
std::string Box_amve::dump(Indent& indent) const
2904
0
{
2905
0
  std::ostringstream sstr;
2906
0
  sstr << Box::dump(indent);
2907
2908
0
  sstr << indent << "ambient_illumination: " << amve.ambient_illumination << "\n";
2909
0
  sstr << indent << "ambient_light_x: " << amve.ambient_light_x << "\n";
2910
0
  sstr << indent << "ambient_light_y: " << amve.ambient_light_y << "\n";
2911
2912
0
  return sstr.str();
2913
0
}
2914
2915
2916
Error Box_amve::write(StreamWriter& writer) const
2917
0
{
2918
0
  size_t box_start = reserve_box_header_space(writer);
2919
2920
0
  writer.write32(amve.ambient_illumination);
2921
0
  writer.write16(amve.ambient_light_x);
2922
0
  writer.write16(amve.ambient_light_y);
2923
2924
0
  prepend_header(writer, box_start);
2925
2926
0
  return Error::Ok;
2927
0
}
2928
2929
2930
Error Box_ndwt::parse(BitstreamRange& range, const heif_security_limits* limits)
2931
11
{
2932
11
  parse_full_box_header(range);
2933
2934
11
  if (get_version() != 0) {
2935
10
    return unsupported_version_error("ndwt");
2936
10
  }
2937
2938
1
  m_diffuse_white_luminance = range.read32();
2939
2940
1
  return range.get_error();
2941
11
}
2942
2943
2944
std::string Box_ndwt::dump(Indent& indent) const
2945
0
{
2946
0
  std::ostringstream sstr;
2947
0
  sstr << Box::dump(indent);
2948
2949
0
  sstr << indent << "diffuse_white_luminance: " << m_diffuse_white_luminance << "\n";
2950
2951
0
  return sstr.str();
2952
0
}
2953
2954
2955
Error Box_ndwt::write(StreamWriter& writer) const
2956
0
{
2957
0
  size_t box_start = reserve_box_header_space(writer);
2958
2959
0
  writer.write32(m_diffuse_white_luminance);
2960
2961
0
  prepend_header(writer, box_start);
2962
2963
0
  return Error::Ok;
2964
0
}
2965
2966
2967
Box_cclv::Box_cclv()
2968
882
{
2969
882
  set_short_type(fourcc("cclv"));
2970
2971
882
  m_ccv_primaries_valid = false;
2972
882
}
2973
2974
2975
void Box_cclv::set_primaries(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
2976
252
{
2977
252
  m_ccv_primaries_valid = true;
2978
252
  m_ccv_primaries_x[0] = x0;
2979
252
  m_ccv_primaries_y[0] = y0;
2980
252
  m_ccv_primaries_x[1] = x1;
2981
252
  m_ccv_primaries_y[1] = y1;
2982
252
  m_ccv_primaries_x[2] = x2;
2983
252
  m_ccv_primaries_y[2] = y2;
2984
252
}
2985
2986
2987
Error Box_cclv::parse(BitstreamRange& range, const heif_security_limits* limits)
2988
0
{
2989
0
  uint8_t flags = range.read8();
2990
2991
0
  m_ccv_primaries_valid = (flags & 0b00100000);
2992
0
  bool ccv_min_luminance_valid = (flags & 0b00010000);
2993
0
  bool ccv_max_luminance_valid = (flags & 0b00001000);
2994
0
  bool ccv_avg_luminance_valid = (flags & 0b00000100);
2995
2996
0
  if (m_ccv_primaries_valid) {
2997
0
    for (int c = 0; c < 3; c++) {
2998
0
      m_ccv_primaries_x[c] = range.read32s();
2999
0
      m_ccv_primaries_y[c] = range.read32s();
3000
0
    }
3001
0
  }
3002
3003
0
  if (ccv_min_luminance_valid) {
3004
0
    m_ccv_min_luminance_value = range.read32();
3005
0
  }
3006
3007
0
  if (ccv_max_luminance_valid) {
3008
0
    m_ccv_max_luminance_value = range.read32();
3009
0
  }
3010
3011
0
  if (ccv_avg_luminance_valid) {
3012
0
    m_ccv_avg_luminance_value = range.read32();
3013
0
  }
3014
3015
0
  return range.get_error();
3016
0
}
3017
3018
3019
template <typename T> std::ostream& operator<<(std::ostream& ostr, const std::optional<T>& value)
3020
0
{
3021
0
  if (value) {
3022
0
    ostr << *value;
3023
0
  }
3024
0
  else {
3025
0
    ostr << "-";
3026
0
  }
3027
3028
0
  return ostr;
3029
0
}
3030
3031
3032
std::string Box_cclv::dump(Indent& indent) const
3033
0
{
3034
0
  std::ostringstream sstr;
3035
0
  sstr << Box::dump(indent);
3036
3037
0
  sstr << indent << "ccv_primaries_present_flag: " << m_ccv_primaries_valid << "\n";
3038
0
  if (m_ccv_primaries_valid) {
3039
0
    sstr << indent << "ccv_primaries (x,y): ";
3040
0
    sstr << "(" << m_ccv_primaries_x[0] << ";" << m_ccv_primaries_y[0] << "), ";
3041
0
    sstr << "(" << m_ccv_primaries_x[1] << ";" << m_ccv_primaries_y[1] << "), ";
3042
0
    sstr << "(" << m_ccv_primaries_x[2] << ";" << m_ccv_primaries_y[2] << ")\n";
3043
0
  }
3044
3045
0
  sstr << indent << "ccv_min_luminance_value: " << m_ccv_min_luminance_value << "\n";
3046
0
  sstr << indent << "ccv_max_luminance_value: " << m_ccv_max_luminance_value << "\n";
3047
0
  sstr << indent << "ccv_avg_luminance_value: " << m_ccv_avg_luminance_value << "\n";
3048
3049
0
  return sstr.str();
3050
0
}
3051
3052
3053
Error Box_cclv::write(StreamWriter& writer) const
3054
0
{
3055
0
  size_t box_start = reserve_box_header_space(writer);
3056
3057
0
  uint8_t flags = 0;
3058
0
  flags |= m_ccv_primaries_valid     ? uint8_t{0b00100000} : uint8_t{0};
3059
0
  flags |= m_ccv_min_luminance_value ? uint8_t{0b00010000} : uint8_t{0};
3060
0
  flags |= m_ccv_max_luminance_value ? uint8_t{0b00001000} : uint8_t{0};
3061
0
  flags |= m_ccv_avg_luminance_value ? uint8_t{0b00000100} : uint8_t{0};
3062
0
  writer.write8(flags);
3063
3064
0
  if (m_ccv_primaries_valid) {
3065
0
    for (int c = 0; c < 3; c++) {
3066
0
      writer.write32s(m_ccv_primaries_x[c]);
3067
0
      writer.write32s(m_ccv_primaries_y[c]);
3068
0
    }
3069
0
  }
3070
3071
0
  if (m_ccv_min_luminance_value) {
3072
0
    writer.write32(*m_ccv_min_luminance_value);
3073
0
  }
3074
3075
0
  if (m_ccv_max_luminance_value) {
3076
0
    writer.write32(*m_ccv_max_luminance_value);
3077
0
  }
3078
3079
0
  if (m_ccv_avg_luminance_value) {
3080
0
    writer.write32(*m_ccv_avg_luminance_value);
3081
0
  }
3082
3083
0
  prepend_header(writer, box_start);
3084
3085
0
  return Error::Ok;
3086
0
}
3087
3088
3089
Error Box_ipco::get_properties_for_item_ID(uint32_t itemID,
3090
                                           const std::shared_ptr<class Box_ipma>& ipma,
3091
                                           std::vector<std::shared_ptr<Box>>& out_properties) const
3092
37.5k
{
3093
37.5k
  const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
3094
37.5k
  if (property_assoc == nullptr) {
3095
2.07k
    std::stringstream sstr;
3096
2.07k
    sstr << "Item (ID=" << itemID << ") has no properties assigned to it in ipma box";
3097
3098
2.07k
    return Error(heif_error_Invalid_input,
3099
2.07k
                 heif_suberror_No_properties_assigned_to_item,
3100
2.07k
                 sstr.str());
3101
2.07k
  }
3102
3103
35.4k
  const auto& allProperties = get_all_child_boxes();
3104
103k
  for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
3105
103k
    if (assoc.property_index > allProperties.size()) {
3106
774
      std::stringstream sstr;
3107
774
      sstr << "Nonexisting property (index=" << assoc.property_index << ") for item "
3108
774
           << " ID=" << itemID << " referenced in ipma box";
3109
3110
774
      return Error(heif_error_Invalid_input,
3111
774
                   heif_suberror_Ipma_box_references_nonexisting_property,
3112
774
                   sstr.str());
3113
774
    }
3114
3115
102k
    if (assoc.property_index > 0) {
3116
96.0k
      out_properties.push_back(allProperties[assoc.property_index - 1]);
3117
96.0k
    }
3118
102k
  }
3119
3120
34.6k
  return Error::Ok;
3121
35.4k
}
3122
3123
3124
std::shared_ptr<Box> Box_ipco::get_property_for_item_ID(heif_item_id itemID,
3125
                                                        const std::shared_ptr<class Box_ipma>& ipma,
3126
                                                        uint32_t box_type) const
3127
8.61k
{
3128
8.61k
  const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
3129
8.61k
  if (property_assoc == nullptr) {
3130
0
    return nullptr;
3131
0
  }
3132
3133
8.61k
  const auto& allProperties = get_all_child_boxes();
3134
12.0k
  for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
3135
12.0k
    if (assoc.property_index > allProperties.size() ||
3136
12.0k
        assoc.property_index == 0) {
3137
3
      return nullptr;
3138
3
    }
3139
3140
11.9k
    const auto& property = allProperties[assoc.property_index - 1];
3141
11.9k
    if (property->get_short_type() == box_type) {
3142
8.61k
      return property;
3143
8.61k
    }
3144
11.9k
  }
3145
3146
0
  return nullptr;
3147
8.61k
}
3148
3149
3150
bool Box_ipco::is_property_essential_for_item(heif_item_id itemId,
3151
                                              const std::shared_ptr<const class Box>& property,
3152
                                              const std::shared_ptr<class Box_ipma>& ipma) const
3153
1.79k
{
3154
  // find property index
3155
3156
7.22k
  for (int i = 0; i < (int) m_children.size(); i++) {
3157
7.22k
    if (m_children[i] == property) {
3158
1.79k
      return ipma->is_property_essential_for_item(itemId, i + 1);
3159
1.79k
    }
3160
7.22k
  }
3161
3162
1.79k
  assert(false); // non-existing property
3163
0
  return false;
3164
1.79k
}
3165
3166
3167
Error Box_ispe::parse(BitstreamRange& range, const heif_security_limits* limits)
3168
10.0k
{
3169
10.0k
  parse_full_box_header(range);
3170
3171
10.0k
  if (get_version() != 0) {
3172
12
    return unsupported_version_error("ispe");
3173
12
  }
3174
3175
9.99k
  m_image_width = range.read32();
3176
9.99k
  m_image_height = range.read32();
3177
3178
9.99k
  return range.get_error();
3179
10.0k
}
3180
3181
3182
std::string Box_ispe::dump(Indent& indent) const
3183
0
{
3184
0
  std::ostringstream sstr;
3185
0
  sstr << Box::dump(indent);
3186
3187
0
  sstr << indent << "image width: " << m_image_width << "\n"
3188
0
       << indent << "image height: " << m_image_height << "\n";
3189
3190
0
  return sstr.str();
3191
0
}
3192
3193
3194
Error Box_ispe::write(StreamWriter& writer) const
3195
0
{
3196
0
  size_t box_start = reserve_box_header_space(writer);
3197
3198
0
  writer.write32(m_image_width);
3199
0
  writer.write32(m_image_height);
3200
3201
0
  prepend_header(writer, box_start);
3202
3203
0
  return Error::Ok;
3204
0
}
3205
3206
3207
bool Box_ispe::operator==(const Box& other) const
3208
708
{
3209
708
  const auto* other_ispe = dynamic_cast<const Box_ispe*>(&other);
3210
708
  if (other_ispe == nullptr) {
3211
708
    return false;
3212
708
  }
3213
3214
0
  return (m_image_width == other_ispe->m_image_width &&
3215
0
          m_image_height == other_ispe->m_image_height);
3216
708
}
3217
3218
3219
Error Box_ipma::parse(BitstreamRange& range, const heif_security_limits* limits)
3220
8.76k
{
3221
8.76k
  parse_full_box_header(range);
3222
3223
  // TODO: is there any specification of allowed values for the ipma version in the HEIF standards?
3224
3225
8.76k
  if (get_version() > 1) {
3226
2
    return unsupported_version_error("ipma");
3227
2
  }
3228
3229
8.76k
  uint32_t entry_cnt = range.read32();
3230
3231
8.76k
  if (limits->max_items && entry_cnt > limits->max_items) {
3232
41
    std::stringstream sstr;
3233
41
    sstr << "ipma box wants to define properties for " << entry_cnt
3234
41
         << " items, but the security limit has been set to " << limits->max_items << " items";
3235
41
    return {heif_error_Invalid_input,
3236
41
            heif_suberror_Security_limit_exceeded,
3237
41
            sstr.str()};
3238
41
  }
3239
3240
35.1k
  for (uint32_t i = 0; i < entry_cnt && !range.error() && !range.eof(); i++) {
3241
26.4k
    Entry entry;
3242
26.4k
    if (get_version() < 1) {
3243
25.7k
      entry.item_ID = range.read16();
3244
25.7k
    }
3245
715
    else {
3246
715
      entry.item_ID = range.read32();
3247
715
    }
3248
3249
26.4k
    int assoc_cnt = range.read8();
3250
123k
    for (int k = 0; k < assoc_cnt; k++) {
3251
97.3k
      PropertyAssociation association{};
3252
3253
97.3k
      uint16_t index;
3254
97.3k
      if (get_flags() & 1) {
3255
1.81k
        index = range.read16();
3256
1.81k
        association.essential = !!(index & 0x8000);
3257
1.81k
        association.property_index = (index & 0x7fff);
3258
1.81k
      }
3259
95.5k
      else {
3260
95.5k
        index = range.read8();
3261
95.5k
        association.essential = !!(index & 0x80);
3262
95.5k
        association.property_index = (index & 0x7f);
3263
95.5k
      }
3264
3265
97.3k
      entry.associations.push_back(association);
3266
97.3k
    }
3267
3268
26.4k
    m_entries.push_back(entry);
3269
26.4k
  }
3270
3271
8.72k
  return range.get_error();
3272
8.76k
}
3273
3274
3275
const std::vector<Box_ipma::PropertyAssociation>* Box_ipma::get_properties_for_item_ID(uint32_t itemID) const
3276
46.1k
{
3277
308k
  for (const auto& entry : m_entries) {
3278
308k
    if (entry.item_ID == itemID) {
3279
44.0k
      return &entry.associations;
3280
44.0k
    }
3281
308k
  }
3282
3283
2.07k
  return nullptr;
3284
46.1k
}
3285
3286
3287
bool Box_ipma::is_property_essential_for_item(heif_item_id itemId, int propertyIndex) const
3288
1.79k
{
3289
14.0k
  for (const auto& entry : m_entries) {
3290
14.0k
    if (entry.item_ID == itemId) {
3291
4.59k
      for (const auto& assoc : entry.associations) {
3292
4.59k
        if (assoc.property_index == propertyIndex) {
3293
1.79k
          return assoc.essential;
3294
1.79k
        }
3295
4.59k
      }
3296
1.79k
    }
3297
14.0k
  }
3298
3299
1.79k
  assert(false);
3300
0
  return false;
3301
1.79k
}
3302
3303
3304
void Box_ipma::add_property_for_item_ID(heif_item_id itemID,
3305
                                        PropertyAssociation assoc)
3306
3.66k
{
3307
3.66k
  size_t idx;
3308
47.5k
  for (idx = 0; idx < m_entries.size(); idx++) {
3309
47.3k
    if (m_entries[idx].item_ID == itemID) {
3310
3.44k
      break;
3311
3.44k
    }
3312
47.3k
  }
3313
3314
  // if itemID does not exist, add a new entry
3315
3.66k
  if (idx == m_entries.size()) {
3316
222
    Entry entry;
3317
222
    entry.item_ID = itemID;
3318
222
    m_entries.push_back(entry);
3319
222
  }
3320
3321
  // If the property is already associated with the item, skip.
3322
9.10k
  for (auto const& a : m_entries[idx].associations) {
3323
9.10k
    if (a.property_index == assoc.property_index) {
3324
2.28k
      return;
3325
2.28k
    }
3326
3327
    // TODO: should we check that the essential flag matches and return an internal error if not?
3328
9.10k
  }
3329
3330
  // add the property association
3331
1.38k
  m_entries[idx].associations.push_back(assoc);
3332
1.38k
}
3333
3334
3335
std::string Box_ipma::dump(Indent& indent) const
3336
0
{
3337
0
  std::ostringstream sstr;
3338
0
  sstr << Box::dump(indent);
3339
3340
0
  for (const Entry& entry : m_entries) {
3341
0
    sstr << indent << "associations for item ID: " << entry.item_ID << "\n";
3342
0
    indent++;
3343
0
    for (const auto& assoc : entry.associations) {
3344
0
      sstr << indent << "property index: " << assoc.property_index
3345
0
           << " (essential: " << std::boolalpha << assoc.essential << ")\n";
3346
0
    }
3347
0
    indent--;
3348
0
  }
3349
3350
0
  return sstr.str();
3351
0
}
3352
3353
3354
void Box_ipma::derive_box_version()
3355
0
{
3356
0
  int version = 0;
3357
0
  bool large_property_indices = false;
3358
3359
0
  for (const Entry& entry : m_entries) {
3360
0
    if (entry.item_ID > 0xFFFF) {
3361
0
      version = 1;
3362
0
    }
3363
3364
0
    for (const auto& assoc : entry.associations) {
3365
0
      if (assoc.property_index > 0x7F) {
3366
0
        large_property_indices = true;
3367
0
      }
3368
0
    }
3369
0
  }
3370
3371
0
  set_version((uint8_t) version);
3372
0
  set_flags(large_property_indices ? 1 : 0);
3373
0
}
3374
3375
3376
Error Box_ipma::write(StreamWriter& writer) const
3377
0
{
3378
0
  size_t box_start = reserve_box_header_space(writer);
3379
3380
0
  size_t entry_cnt = m_entries.size();
3381
0
  writer.write32((uint32_t) entry_cnt);
3382
3383
0
  for (const Entry& entry : m_entries) {
3384
3385
0
    if (get_version() < 1) {
3386
0
      writer.write16((uint16_t) entry.item_ID);
3387
0
    }
3388
0
    else {
3389
0
      writer.write32(entry.item_ID);
3390
0
    }
3391
3392
0
    size_t assoc_cnt = entry.associations.size();
3393
0
    if (assoc_cnt > 0xFF) {
3394
      // TODO: error, too many associations
3395
0
    }
3396
3397
0
    writer.write8((uint8_t) assoc_cnt);
3398
3399
0
    for (const PropertyAssociation& association : entry.associations) {
3400
3401
0
      if (get_flags() & 1) {
3402
0
        writer.write16((uint16_t) ((association.essential ? 0x8000 : 0) |
3403
0
                                   (association.property_index & 0x7FFF)));
3404
0
      }
3405
0
      else {
3406
0
        writer.write8((uint8_t) ((association.essential ? 0x80 : 0) |
3407
0
                                 (association.property_index & 0x7F)));
3408
0
      }
3409
0
    }
3410
0
  }
3411
3412
0
  prepend_header(writer, box_start);
3413
3414
0
  return Error::Ok;
3415
0
}
3416
3417
3418
void Box_ipma::insert_entries_from_other_ipma_box(const Box_ipma& b)
3419
5
{
3420
5
  m_entries.insert(m_entries.end(),
3421
5
                   b.m_entries.begin(),
3422
5
                   b.m_entries.end());
3423
5
}
3424
3425
3426
void Box_ipma::sort_properties(const std::shared_ptr<Box_ipco>& ipco)
3427
0
{
3428
0
  auto properties = ipco->get_all_child_boxes();
3429
3430
  // Stable-partition: all descriptive properties before all transformative properties.
3431
  // The order within each group is preserved (spec requires it for transformative properties).
3432
3433
0
  for (auto& item : m_entries) {
3434
0
    std::stable_partition(item.associations.begin(), item.associations.end(),
3435
0
                          [&properties](const PropertyAssociation& assoc) {
3436
0
                            if (assoc.property_index == 0 || assoc.property_index > properties.size()) {
3437
0
                              return true; // keep invalid/unset entries in the descriptive group
3438
0
                            }
3439
0
                            return !properties[assoc.property_index - 1]->is_transformative_property();
3440
0
                          });
3441
0
  }
3442
0
}
3443
3444
3445
Error Box_auxC::parse(BitstreamRange& range, const heif_security_limits* limits)
3446
569
{
3447
569
  parse_full_box_header(range);
3448
3449
569
  if (get_version() != 0) {
3450
2
    return unsupported_version_error("auxC");
3451
2
  }
3452
3453
567
  m_aux_type = range.read_string();
3454
3455
6.61k
  while (!range.eof()) {
3456
6.04k
    m_aux_subtypes.push_back(range.read8());
3457
6.04k
  }
3458
3459
567
  return range.get_error();
3460
569
}
3461
3462
3463
Error Box_auxC::write(StreamWriter& writer) const
3464
0
{
3465
0
  size_t box_start = reserve_box_header_space(writer);
3466
3467
0
  writer.write(m_aux_type);
3468
3469
0
  for (uint8_t subtype : m_aux_subtypes) {
3470
0
    writer.write8(subtype);
3471
0
  }
3472
3473
0
  prepend_header(writer, box_start);
3474
3475
0
  return Error::Ok;
3476
0
}
3477
3478
3479
std::string Box_auxC::dump(Indent& indent) const
3480
0
{
3481
0
  std::ostringstream sstr;
3482
0
  sstr << Box::dump(indent);
3483
3484
0
  sstr << indent << "aux type: " << m_aux_type << "\n"
3485
0
       << indent << "aux subtypes: ";
3486
0
  for (uint8_t subtype : m_aux_subtypes) {
3487
0
    sstr << std::hex << std::setw(2) << std::setfill('0') << ((int) subtype) << " ";
3488
0
  }
3489
3490
0
  sstr << "\n";
3491
3492
0
  return sstr.str();
3493
0
}
3494
3495
3496
Error Box_irot::parse(BitstreamRange& range, const heif_security_limits* limits)
3497
340
{
3498
  //parse_full_box_header(range);
3499
3500
340
  uint16_t rotation = range.read8();
3501
340
  rotation &= 0x03;
3502
3503
340
  m_rotation = rotation * 90;
3504
3505
340
  return range.get_error();
3506
340
}
3507
3508
3509
Error Box_irot::write(StreamWriter& writer) const
3510
18
{
3511
18
  size_t box_start = reserve_box_header_space(writer);
3512
3513
18
  writer.write8((uint8_t) (m_rotation / 90));
3514
3515
18
  prepend_header(writer, box_start);
3516
3517
18
  return Error::Ok;
3518
18
}
3519
3520
3521
std::string Box_irot::dump(Indent& indent) const
3522
0
{
3523
0
  std::ostringstream sstr;
3524
0
  sstr << Box::dump(indent);
3525
3526
0
  sstr << indent << "rotation: " << m_rotation << " degrees (CCW)\n";
3527
3528
0
  return sstr.str();
3529
0
}
3530
3531
3532
Error Box_imir::parse(BitstreamRange& range, const heif_security_limits* limits)
3533
222
{
3534
  //parse_full_box_header(range);
3535
3536
222
  uint8_t axis = range.read8();
3537
222
  if (axis & 1) {
3538
67
    m_axis = heif_transform_mirror_direction_horizontal;
3539
67
  }
3540
155
  else {
3541
155
    m_axis = heif_transform_mirror_direction_vertical;
3542
155
  }
3543
3544
222
  return range.get_error();
3545
222
}
3546
3547
3548
Error Box_imir::write(StreamWriter& writer) const
3549
30
{
3550
30
  size_t box_start = reserve_box_header_space(writer);
3551
3552
30
  writer.write8(m_axis);
3553
3554
30
  prepend_header(writer, box_start);
3555
3556
30
  return Error::Ok;
3557
30
}
3558
3559
3560
std::string Box_imir::dump(Indent& indent) const
3561
0
{
3562
0
  std::ostringstream sstr;
3563
0
  sstr << Box::dump(indent);
3564
3565
0
  sstr << indent << "mirror direction: ";
3566
0
  switch (m_axis) {
3567
0
    case heif_transform_mirror_direction_vertical:
3568
0
      sstr << "vertical\n";
3569
0
      break;
3570
0
    case heif_transform_mirror_direction_horizontal:
3571
0
      sstr << "horizontal\n";
3572
0
      break;
3573
0
    case heif_transform_mirror_direction_invalid:
3574
0
      sstr << "invalid\n";
3575
0
      break;
3576
0
  }
3577
3578
0
  return sstr.str();
3579
0
}
3580
3581
3582
Error Box_iscl::parse(BitstreamRange& range, const heif_security_limits* limits)
3583
63
{
3584
63
  parse_full_box_header(range);
3585
3586
63
  if (get_version() != 0) {
3587
11
    return unsupported_version_error("iscl");
3588
11
  }
3589
3590
52
  m_target_width_numerator = range.read16();
3591
52
  m_target_width_denominator = range.read16();
3592
52
  m_target_height_numerator = range.read16();
3593
52
  m_target_height_denominator = range.read16();
3594
3595
52
  if (m_target_width_numerator == 0 || m_target_width_denominator == 0 ||
3596
34
      m_target_height_numerator == 0 || m_target_height_denominator == 0) {
3597
32
    return {heif_error_Invalid_input,
3598
32
            heif_suberror_Invalid_fractional_number,
3599
32
            "iscl property has zero numerator or denominator"};
3600
32
  }
3601
3602
20
  return range.get_error();
3603
52
}
3604
3605
3606
Error Box_iscl::write(StreamWriter& writer) const
3607
0
{
3608
0
  size_t box_start = reserve_box_header_space(writer);
3609
3610
0
  writer.write16(m_target_width_numerator);
3611
0
  writer.write16(m_target_width_denominator);
3612
0
  writer.write16(m_target_height_numerator);
3613
0
  writer.write16(m_target_height_denominator);
3614
3615
0
  prepend_header(writer, box_start);
3616
3617
0
  return Error::Ok;
3618
0
}
3619
3620
3621
std::string Box_iscl::dump(Indent& indent) const
3622
0
{
3623
0
  std::ostringstream sstr;
3624
0
  sstr << FullBox::dump(indent);
3625
3626
0
  sstr << indent << "horizontal scaling factor: " << m_target_width_numerator << " / " << m_target_width_denominator << "\n";
3627
0
  sstr << indent << "vertical scaling factor:   " << m_target_height_numerator << " / " << m_target_height_denominator << "\n";
3628
3629
0
  return sstr.str();
3630
0
}
3631
3632
3633
Error Box_clap::parse(BitstreamRange& range, const heif_security_limits* limits)
3634
1.94k
{
3635
  //parse_full_box_header(range);
3636
3637
1.94k
  uint32_t clean_aperture_width_num = range.read32();
3638
1.94k
  uint32_t clean_aperture_width_den = range.read32();
3639
1.94k
  uint32_t clean_aperture_height_num = range.read32();
3640
1.94k
  uint32_t clean_aperture_height_den = range.read32();
3641
3642
  // Note: in the standard document 14496-12(2015), it says that the offset values should also be unsigned integers,
3643
  // but this is obviously an error. Even the accompanying standard text says that offsets may be negative.
3644
1.94k
  int32_t horizontal_offset_num = (int32_t) range.read32();
3645
1.94k
  uint32_t horizontal_offset_den = (uint32_t) range.read32();
3646
1.94k
  int32_t vertical_offset_num = (int32_t) range.read32();
3647
1.94k
  uint32_t vertical_offset_den = (uint32_t) range.read32();
3648
3649
1.94k
  if (clean_aperture_width_num > (uint32_t) std::numeric_limits<int32_t>::max() ||
3650
1.88k
      clean_aperture_width_den > (uint32_t) std::numeric_limits<int32_t>::max() ||
3651
1.72k
      clean_aperture_height_num > (uint32_t) std::numeric_limits<int32_t>::max() ||
3652
1.60k
      clean_aperture_height_den > (uint32_t) std::numeric_limits<int32_t>::max() ||
3653
1.48k
      horizontal_offset_den > (uint32_t) std::numeric_limits<int32_t>::max() ||
3654
1.39k
      vertical_offset_den > (uint32_t) std::numeric_limits<int32_t>::max()) {
3655
620
    return Error(heif_error_Invalid_input,
3656
620
                 heif_suberror_Invalid_fractional_number,
3657
620
                 "Exceeded supported value range.");
3658
620
  }
3659
3660
1.32k
  m_clean_aperture_width = Fraction(clean_aperture_width_num,
3661
1.32k
                                    clean_aperture_width_den);
3662
1.32k
  m_clean_aperture_height = Fraction(clean_aperture_height_num,
3663
1.32k
                                     clean_aperture_height_den);
3664
1.32k
  m_horizontal_offset = Fraction(horizontal_offset_num, (int32_t) horizontal_offset_den);
3665
1.32k
  m_vertical_offset = Fraction(vertical_offset_num, (int32_t) vertical_offset_den);
3666
1.32k
  if (!m_clean_aperture_width.is_valid() || !m_clean_aperture_height.is_valid() ||
3667
945
      !m_horizontal_offset.is_valid() || !m_vertical_offset.is_valid()) {
3668
822
    return Error(heif_error_Invalid_input,
3669
822
                 heif_suberror_Invalid_fractional_number);
3670
822
  }
3671
3672
499
  return range.get_error();
3673
1.32k
}
3674
3675
3676
Error Box_clap::write(StreamWriter& writer) const
3677
0
{
3678
0
  size_t box_start = reserve_box_header_space(writer);
3679
3680
0
  writer.write32(m_clean_aperture_width.numerator);
3681
0
  writer.write32(m_clean_aperture_width.denominator);
3682
0
  writer.write32(m_clean_aperture_height.numerator);
3683
0
  writer.write32(m_clean_aperture_height.denominator);
3684
0
  writer.write32s(m_horizontal_offset.numerator);
3685
0
  writer.write32(m_horizontal_offset.denominator);
3686
0
  writer.write32s(m_vertical_offset.numerator);
3687
0
  writer.write32(m_vertical_offset.denominator);
3688
3689
0
  prepend_header(writer, box_start);
3690
3691
0
  return Error::Ok;
3692
0
}
3693
3694
3695
std::string Box_clap::dump(Indent& indent) const
3696
0
{
3697
0
  std::ostringstream sstr;
3698
0
  sstr << Box::dump(indent);
3699
3700
0
  sstr << indent << "clean_aperture: " << m_clean_aperture_width.numerator
3701
0
       << "/" << m_clean_aperture_width.denominator << " x "
3702
0
       << m_clean_aperture_height.numerator << "/"
3703
0
       << m_clean_aperture_height.denominator << "\n";
3704
0
  sstr << indent << "offset: " << m_horizontal_offset.numerator << "/"
3705
0
       << m_horizontal_offset.denominator << " ; "
3706
0
       << m_vertical_offset.numerator << "/"
3707
0
       << m_vertical_offset.denominator << "\n";
3708
3709
0
  return sstr.str();
3710
0
}
3711
3712
3713
double Box_clap::left(int image_width) const
3714
0
{
3715
0
  Fraction pcX = m_horizontal_offset + Fraction(image_width - 1, 2);
3716
0
  Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
3717
0
  return left.to_double();
3718
0
}
3719
3720
double Box_clap::top(int image_height) const
3721
0
{
3722
0
  Fraction pcY = m_vertical_offset + Fraction(image_height - 1, 2);
3723
0
  Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
3724
0
  return top.to_double();
3725
0
}
3726
3727
3728
int Box_clap::left_rounded(uint32_t image_width) const
3729
0
{
3730
  // pcX = horizOff + (width  - 1)/2
3731
  // pcX ± (cleanApertureWidth - 1)/2
3732
3733
  // left = horizOff + (width-1)/2 - (clapWidth-1)/2
3734
3735
  // Guard against image_width==0: `image_width - 1U` would underflow to
3736
  // UINT32_MAX and overflow the Fraction (GHSA-jc8f-p23p-5hjg).
3737
0
  if (image_width == 0) {
3738
0
    return 0;
3739
0
  }
3740
3741
0
  Fraction pcX = m_horizontal_offset + Fraction(image_width - 1U, 2U);
3742
0
  Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
3743
3744
0
  return left.round_down();
3745
0
}
3746
3747
int Box_clap::right_rounded(uint32_t image_width) const
3748
0
{
3749
0
  Fraction right = m_clean_aperture_width - 1 + left_rounded(image_width);
3750
3751
0
  return right.round();
3752
0
}
3753
3754
int Box_clap::top_rounded(uint32_t image_height) const
3755
0
{
3756
  // Guard against image_height==0 underflowing the Fraction (see left_rounded).
3757
0
  if (image_height == 0) {
3758
0
    return 0;
3759
0
  }
3760
3761
0
  Fraction pcY = m_vertical_offset + Fraction(image_height - 1U, 2U);
3762
0
  Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
3763
3764
0
  return top.round();
3765
0
}
3766
3767
int Box_clap::bottom_rounded(uint32_t image_height) const
3768
0
{
3769
0
  Fraction bottom = m_clean_aperture_height - 1 + top_rounded(image_height);
3770
3771
0
  return bottom.round();
3772
0
}
3773
3774
int Box_clap::get_width_rounded() const
3775
30
{
3776
30
  return m_clean_aperture_width.round();
3777
30
}
3778
3779
int Box_clap::get_height_rounded() const
3780
30
{
3781
30
  return m_clean_aperture_height.round();
3782
30
}
3783
3784
void Box_clap::set(uint32_t clap_width, uint32_t clap_height,
3785
                   uint32_t image_width, uint32_t image_height)
3786
0
{
3787
0
  assert(image_width >= clap_width);
3788
0
  assert(image_height >= clap_height);
3789
3790
0
  m_clean_aperture_width = Fraction(clap_width, 1U);
3791
0
  m_clean_aperture_height = Fraction(clap_height, 1U);
3792
3793
0
  m_horizontal_offset = Fraction(-(int32_t) (image_width - clap_width), 2);
3794
0
  m_vertical_offset = Fraction(-(int32_t) (image_height - clap_height), 2);
3795
0
}
3796
3797
3798
Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
3799
1.66k
{
3800
1.66k
  parse_full_box_header(range);
3801
3802
1.66k
  if (get_version() > 1) {
3803
2
    return unsupported_version_error("iref");
3804
2
  }
3805
3806
5.45k
  while (!range.eof()) {
3807
3.84k
    Reference ref;
3808
3809
3.84k
    Error err = ref.header.parse_header(range);
3810
3.84k
    if (err != Error::Ok) {
3811
6
      return err;
3812
6
    }
3813
3814
3.83k
    int read_len = (get_version() == 0) ? 16 : 32;
3815
3816
3.83k
    ref.from_item_ID = static_cast<uint32_t>(range.read_uint(read_len));
3817
3.83k
    uint16_t nRefs = range.read16();
3818
3819
3.83k
    if (nRefs==0) {
3820
8
      return {heif_error_Invalid_input,
3821
8
              heif_suberror_Unspecified,
3822
8
              "Input file has an 'iref' box with no references."};
3823
8
    }
3824
3825
3.82k
    if (limits->max_items && nRefs > limits->max_items) {
3826
20
      std::stringstream sstr;
3827
20
      sstr << "Number of references in iref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references.";
3828
3829
20
      return {heif_error_Invalid_input,
3830
20
              heif_suberror_Security_limit_exceeded,
3831
20
              sstr.str()};
3832
20
    }
3833
3834
26.5k
    for (int i = 0; i < nRefs; i++) {
3835
22.7k
      if (range.eof()) {
3836
18
        std::stringstream sstr;
3837
18
        sstr << "iref box should contain " << nRefs << " references, but we can only read " << i << " references.";
3838
3839
18
        return {heif_error_Invalid_input,
3840
18
                heif_suberror_End_of_data,
3841
18
                sstr.str()};
3842
18
      }
3843
3844
22.7k
      ref.to_item_ID.push_back(static_cast<uint32_t>(range.read_uint(read_len)));
3845
22.7k
    }
3846
3847
3.79k
    m_references.push_back(ref);
3848
3.79k
  }
3849
3850
3851
  // --- check for duplicate references
3852
3853
1.61k
  if (auto error = check_for_double_references()) {
3854
78
    return error;
3855
78
  }
3856
3857
3858
#if 0
3859
  // Note: This input sanity check first did not work as expected.
3860
  // Its idea was to prevent infinite recursions while decoding when the input file
3861
  // contains cyclic references. However, apparently there are cases where cyclic
3862
  // references are actually allowed, like with images that have premultiplied alpha channels:
3863
  // | Box: iref -----
3864
  // | size: 40   (header size: 12)
3865
  // | reference with type 'auxl' from ID: 2 to IDs: 1
3866
  // | reference with type 'prem' from ID: 1 to IDs: 2
3867
  //
3868
  // We now test for cyclic references during the image decoding.
3869
  // We pass down the item IDs that have already been seen during the decoding process.
3870
  // If we try to decode an image IDs that has already been seen previously, we throw an error.
3871
  // The advantage is that the error only occurs when we are trying to decode the faulty image.
3872
3873
  // --- check for cyclic references
3874
3875
  for (const auto& ref : m_references) {
3876
    if (ref.header.get_short_type() != fourcc("dimg") &&
3877
        ref.header.get_short_type() != fourcc("auxl")) {
3878
      continue;
3879
    }
3880
3881
    std::set<heif_item_id> reached_ids; // IDs that we have already reached in the DAG
3882
    std::set<heif_item_id> todo;    // IDs that still need to be followed
3883
3884
    bool reverse = (ref.header.get_short_type() != fourcc("auxl"));
3885
3886
    if (!reverse) {
3887
      todo.insert(ref.from_item_ID);  // start at base item
3888
    }
3889
    else {
3890
      if (ref.to_item_ID.empty()) {
3891
        continue;
3892
      }
3893
3894
      // TODO: what if aux image is assigned to multiple images?
3895
      todo.insert(ref.to_item_ID[0]);  // start at base item
3896
    }
3897
3898
    while (!todo.empty()) {
3899
      // transfer ID into set of reached IDs
3900
      auto id = *todo.begin();
3901
      todo.erase(id);
3902
      reached_ids.insert(id);
3903
3904
      // if this ID refers to another 'iref', follow it
3905
3906
      for (const auto& succ_ref : m_references) {
3907
        if (succ_ref.header.get_short_type() != fourcc("dimg") &&
3908
            succ_ref.header.get_short_type() != fourcc("auxl")) {
3909
          continue;
3910
        }
3911
3912
        heif_item_id from;
3913
        std::vector<heif_item_id> to;
3914
3915
        if (succ_ref.header.get_short_type() == fourcc("auxl")) {
3916
          if (succ_ref.to_item_ID.empty()) {
3917
            continue;
3918
          }
3919
3920
          from = succ_ref.to_item_ID[0];
3921
          to = {succ_ref.from_item_ID};
3922
        }
3923
        else {
3924
          from = succ_ref.from_item_ID;
3925
          to = succ_ref.to_item_ID;
3926
        }
3927
3928
        if (from == id) {
3929
3930
          // Check whether any successor IDs has been visited yet, which would be an error.
3931
          // Otherwise, put that ID into the 'todo' set.
3932
3933
          for (const auto& succ_ref_id : to) {
3934
            if (reached_ids.find(succ_ref_id) != reached_ids.end()) {
3935
              return Error(heif_error_Invalid_input,
3936
                           heif_suberror_Unspecified,
3937
                           "'iref' has cyclic references");
3938
            }
3939
3940
            todo.insert(succ_ref_id);
3941
          }
3942
        }
3943
      }
3944
    }
3945
  }
3946
#endif
3947
3948
1.53k
  return range.get_error();
3949
1.61k
}
3950
3951
3952
Error Box_iref::check_for_double_references() const
3953
1.61k
{
3954
3.73k
  for (const auto& ref : m_references) {
3955
3.73k
    std::set<heif_item_id> to_ids;
3956
20.4k
    for (const auto to_id : ref.to_item_ID) {
3957
20.4k
      if (to_ids.find(to_id) == to_ids.end()) {
3958
20.4k
        to_ids.insert(to_id);
3959
20.4k
      }
3960
78
      else {
3961
78
        return {heif_error_Invalid_input,
3962
78
                heif_suberror_Unspecified,
3963
78
                "'iref' has double references"};
3964
78
      }
3965
20.4k
    }
3966
3.73k
  }
3967
3968
1.53k
  return Error::Ok;
3969
1.61k
}
3970
3971
3972
void Box_iref::derive_box_version()
3973
0
{
3974
0
  uint8_t version = 0;
3975
3976
0
  for (const auto& ref : m_references) {
3977
0
    if (ref.from_item_ID > 0xFFFF) {
3978
0
      version = 1;
3979
0
      break;
3980
0
    }
3981
3982
0
    for (uint32_t r : ref.to_item_ID) {
3983
0
      if (r > 0xFFFF) {
3984
0
        version = 1;
3985
0
        break;
3986
0
      }
3987
0
    }
3988
0
  }
3989
3990
0
  set_version(version);
3991
0
}
3992
3993
3994
Error Box_iref::write(StreamWriter& writer) const
3995
0
{
3996
0
  if (auto error = check_for_double_references()) {
3997
0
    return error;
3998
0
  }
3999
4000
0
  size_t box_start = reserve_box_header_space(writer);
4001
4002
0
  int id_size = ((get_version() == 0) ? 2 : 4);
4003
4004
0
  for (const auto& ref : m_references) {
4005
0
    uint32_t box_size = uint32_t(4 + 4 + 2 + id_size * (1 + ref.to_item_ID.size()));
4006
4007
    // we write the BoxHeader ourselves since it is very simple
4008
0
    writer.write32(box_size);
4009
0
    writer.write32(ref.header.get_short_type());
4010
4011
0
    writer.write(id_size, ref.from_item_ID);
4012
0
    writer.write16((uint16_t) ref.to_item_ID.size());
4013
4014
0
    for (uint32_t r : ref.to_item_ID) {
4015
0
      writer.write(id_size, r);
4016
0
    }
4017
0
  }
4018
4019
0
  prepend_header(writer, box_start);
4020
4021
0
  return Error::Ok;
4022
0
}
4023
4024
4025
std::string Box_iref::dump(Indent& indent) const
4026
0
{
4027
0
  std::ostringstream sstr;
4028
0
  sstr << Box::dump(indent);
4029
4030
0
  for (const auto& ref : m_references) {
4031
0
    sstr << indent << "reference with type '" << ref.header.get_type_string() << "'"
4032
0
         << " from ID: " << ref.from_item_ID
4033
0
         << " to IDs: ";
4034
0
    for (uint32_t id : ref.to_item_ID) {
4035
0
      sstr << id << " ";
4036
0
    }
4037
0
    sstr << "\n";
4038
0
  }
4039
4040
0
  return sstr.str();
4041
0
}
4042
4043
4044
bool Box_iref::has_references(uint32_t itemID) const
4045
0
{
4046
0
  for (const Reference& ref : m_references) {
4047
0
    if (ref.from_item_ID == itemID) {
4048
0
      return true;
4049
0
    }
4050
0
  }
4051
4052
0
  return false;
4053
0
}
4054
4055
4056
std::vector<Box_iref::Reference> Box_iref::get_references_from(heif_item_id itemID) const
4057
7.55k
{
4058
7.55k
  std::vector<Reference> references;
4059
4060
21.7k
  for (const Reference& ref : m_references) {
4061
21.7k
    if (ref.from_item_ID == itemID) {
4062
1.59k
      references.push_back(ref);
4063
1.59k
    }
4064
21.7k
  }
4065
4066
7.55k
  return references;
4067
7.55k
}
4068
4069
4070
std::vector<uint32_t> Box_iref::get_references(uint32_t itemID, uint32_t ref_type) const
4071
20.4k
{
4072
54.3k
  for (const Reference& ref : m_references) {
4073
54.3k
    if (ref.from_item_ID == itemID &&
4074
6.16k
        ref.header.get_short_type() == ref_type) {
4075
3.83k
      return ref.to_item_ID;
4076
3.83k
    }
4077
54.3k
  }
4078
4079
16.5k
  return std::vector<uint32_t>();
4080
20.4k
}
4081
4082
4083
void Box_iref::add_references(heif_item_id from_id, uint32_t type, const std::vector<heif_item_id>& to_ids)
4084
261
{
4085
261
  Reference ref;
4086
261
  ref.header.set_short_type(type);
4087
261
  ref.from_item_ID = from_id;
4088
261
  ref.to_item_ID = to_ids;
4089
4090
261
  assert(to_ids.size() <= 0xFFFF);
4091
4092
261
  m_references.push_back(ref);
4093
261
}
4094
4095
4096
void Box_iref::overwrite_reference(heif_item_id from_id, uint32_t type, uint32_t reference_idx, heif_item_id to_item)
4097
0
{
4098
0
  for (auto& ref : m_references) {
4099
0
    if (ref.from_item_ID == from_id && ref.header.get_short_type() == type) {
4100
0
      assert(reference_idx < ref.to_item_ID.size());
4101
4102
0
      ref.to_item_ID[reference_idx] = to_item;
4103
0
      return;
4104
0
    }
4105
0
  }
4106
4107
0
  assert(false); // reference was not found
4108
0
}
4109
4110
4111
Error Box_rref::parse(BitstreamRange& range, const heif_security_limits* limits)
4112
47
{
4113
47
  parse_full_box_header(range);
4114
4115
47
  if (get_version() > 1) {
4116
2
    return unsupported_version_error("rref");
4117
2
  }
4118
4119
45
  size_t remainingBytes = range.get_remaining_bytes();
4120
4121
  // The number of reference types should be stored in uint(8), but the
4122
  // common test images C043, C044 store them as uint(32).
4123
  // Let us detect this case by the number of data bytes in this box.
4124
45
  bool cnt_as_uint32 = (remainingBytes > 0 && remainingBytes % 4 == 0);
4125
4126
45
  uint8_t nRefTypes;
4127
45
  if (cnt_as_uint32) {
4128
    // fix broken C043, C044 files
4129
    // TODO: remove me when the files have been corrected as the above check can misfire when there is extra padding
4130
32
    nRefTypes = (uint8_t)range.read32();
4131
32
  }
4132
13
  else {
4133
13
    nRefTypes = range.read8();
4134
13
  }
4135
4136
426
  for (uint8_t i = 0; i < nRefTypes; i++) {
4137
393
    uint32_t refType = range.read32();
4138
4139
393
    if (range.error()) {
4140
12
      std::stringstream sstr;
4141
12
      sstr << "rref box should contain " << ((int)nRefTypes) << " reference types, but we can only read " << m_reference_types.size() << " reference types.";
4142
4143
12
      return {
4144
12
        heif_error_Invalid_input,
4145
12
        heif_suberror_End_of_data,
4146
12
        sstr.str()
4147
12
      };
4148
12
    }
4149
4150
381
    m_reference_types.push_back(refType);
4151
381
  }
4152
4153
33
  return range.get_error();
4154
45
}
4155
4156
4157
static constexpr std::array supported_reference_types{
4158
  fourcc("thmb"),
4159
  fourcc("base"),
4160
  fourcc("cdsc"),
4161
  fourcc("dimg"),
4162
  fourcc("auxl"),
4163
  fourcc("prem")
4164
};
4165
4166
4167
bool Box_rref::all_reference_types_supported() const
4168
0
{
4169
  // TODO: replace with std::ranges variant once it is widely supported (e.g. on older clang/libc++):
4170
  //   return std::ranges::all_of(m_reference_types, [](uint32_t t) {
4171
  //     return std::ranges::find(supported_reference_types, t) != supported_reference_types.end();
4172
  //   });
4173
0
  return std::all_of(m_reference_types.begin(), m_reference_types.end(), [](uint32_t t) {
4174
0
    return std::find(supported_reference_types.begin(), supported_reference_types.end(), t)
4175
0
           != supported_reference_types.end();
4176
0
  });
4177
0
}
4178
4179
4180
Error Box_rref::reference_types_supported_error() const
4181
0
{
4182
0
  std::vector<uint32_t> unsupported_types;
4183
4184
0
  for (uint32_t refType : m_reference_types) {
4185
    // TODO: replace with std::ranges variant once it is widely supported (e.g. on older clang/libc++):
4186
    //   if (std::ranges::find(supported_reference_types, refType) == supported_reference_types.end()) {
4187
0
    if (std::find(supported_reference_types.begin(), supported_reference_types.end(), refType) == supported_reference_types.end()) {
4188
0
      unsupported_types.push_back(refType);
4189
0
    }
4190
0
  }
4191
4192
0
  if (unsupported_types.empty()) {
4193
0
    return Error::Ok;
4194
0
  }
4195
4196
0
  std::stringstream sstr;
4197
4198
0
  sstr << "Unsupported reference types: ";
4199
0
  for (size_t i = 0; i < unsupported_types.size(); i++) {
4200
0
    if (i > 0) {
4201
0
      sstr << ", ";
4202
0
    }
4203
0
    sstr << fourcc_to_string(unsupported_types[i]);
4204
0
  }
4205
4206
0
  return {
4207
0
    heif_error_Unsupported_feature,
4208
0
    heif_suberror_Unspecified,
4209
0
    sstr.str()
4210
0
  };
4211
0
}
4212
4213
4214
Error Box_rref::write(StreamWriter& writer) const
4215
0
{
4216
0
  size_t box_start = reserve_box_header_space(writer);
4217
4218
0
  if (m_reference_types.size() > std::numeric_limits<uint8_t>::max()) {
4219
0
    return {
4220
0
      heif_error_Usage_error,
4221
0
      heif_suberror_Unspecified,
4222
0
      "Too many rref reference types."
4223
0
    };
4224
0
  }
4225
4226
0
  writer.write8(static_cast<uint8_t>(m_reference_types.size()));
4227
4228
0
  for (uint32_t refType : m_reference_types) {
4229
0
    writer.write32(refType);
4230
0
  }
4231
4232
0
  prepend_header(writer, box_start);
4233
4234
0
  return Error::Ok;
4235
0
}
4236
4237
4238
std::string Box_rref::dump(Indent& indent) const
4239
0
{
4240
0
  std::ostringstream sstr;
4241
0
  sstr << Box::dump(indent);
4242
4243
0
  sstr << indent << "reference types: ";
4244
0
  for (size_t i = 0; i < m_reference_types.size(); i++) {
4245
0
    if (i > 0) sstr << ", ";
4246
0
    sstr << fourcc_to_string(m_reference_types[i]);
4247
0
  }
4248
0
  sstr << "\n";
4249
4250
0
  return sstr.str();
4251
0
}
4252
4253
4254
Error Box_idat::parse(BitstreamRange& range, const heif_security_limits* limits)
4255
498
{
4256
  //parse_full_box_header(range);
4257
4258
498
  m_data_start_pos = range.get_istream()->get_position();
4259
4260
498
  return range.get_error();
4261
498
}
4262
4263
4264
Error Box_idat::write(StreamWriter& writer) const
4265
0
{
4266
0
  size_t box_start = reserve_box_header_space(writer);
4267
4268
0
  writer.write(m_data_for_writing);
4269
4270
0
  prepend_header(writer, box_start);
4271
4272
0
  return Error::Ok;
4273
0
}
4274
4275
4276
std::string Box_idat::dump(Indent& indent) const
4277
0
{
4278
0
  std::ostringstream sstr;
4279
0
  sstr << Box::dump(indent);
4280
4281
0
  if (get_box_size() >= get_header_size()) {
4282
0
    sstr << indent << "number of data bytes: " << get_box_size() - get_header_size() << "\n";
4283
0
  } else {
4284
0
     sstr << indent << "number of data bytes is invalid\n";
4285
0
  }
4286
4287
0
  return sstr.str();
4288
0
}
4289
4290
4291
Error Box_idat::read_data(const std::shared_ptr<StreamReader>& istr,
4292
                          uint64_t start, uint64_t length,
4293
                          std::vector<uint8_t>& out_data,
4294
                          const heif_security_limits* limits) const
4295
833
{
4296
  // --- security check that we do not allocate too much data
4297
4298
833
  auto curr_size = out_data.size();
4299
4300
833
  auto max_memory_block_size = limits->max_memory_block_size;
4301
833
  if (max_memory_block_size && max_memory_block_size - curr_size < length) {
4302
69
    std::stringstream sstr;
4303
69
    sstr << "idat box contained " << length << " bytes, total memory size would be "
4304
69
         << (curr_size + length) << " bytes, exceeding the security limit of "
4305
69
         << max_memory_block_size << " bytes";
4306
4307
69
    return Error(heif_error_Memory_allocation_error,
4308
69
                 heif_suberror_Security_limit_exceeded,
4309
69
                 sstr.str());
4310
69
  }
4311
4312
4313
  // move to start of data
4314
764
  if (start > (uint64_t) m_data_start_pos + get_box_size()) {
4315
286
    return Error(heif_error_Invalid_input,
4316
286
                 heif_suberror_End_of_data);
4317
286
  }
4318
478
  else if (length > get_box_size() || start + length > get_box_size()) {
4319
30
    return Error(heif_error_Invalid_input,
4320
30
                 heif_suberror_End_of_data);
4321
30
  }
4322
4323
448
  StreamReader::grow_status status = istr->wait_for_file_size((int64_t) m_data_start_pos + start + length);
4324
448
  if (status == StreamReader::grow_status::size_beyond_eof ||
4325
443
      status == StreamReader::grow_status::timeout) {
4326
    // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
4327
5
    return Error(heif_error_Invalid_input,
4328
5
                 heif_suberror_End_of_data);
4329
5
  }
4330
4331
443
  bool success;
4332
443
  success = istr->seek(m_data_start_pos + (std::streampos) start);
4333
443
  assert(success);
4334
443
  (void) success;
4335
4336
443
  if (length > 0) {
4337
    // reserve space for the data in the output array
4338
395
    out_data.resize(static_cast<size_t>(curr_size + length));
4339
395
    uint8_t* data = &out_data[curr_size];
4340
4341
395
    success = istr->read((char*) data, static_cast<size_t>(length));
4342
395
    assert(success);
4343
395
    (void) success;
4344
395
  }
4345
4346
443
  return Error::Ok;
4347
448
}
4348
4349
4350
Error Box_grpl::parse(BitstreamRange& range, const heif_security_limits* limits)
4351
299
{
4352
  //parse_full_box_header(range);
4353
4354
299
  return read_children(range, READ_CHILDREN_ALL, limits); // should we pass the parsing context 'grpl' or are the box types unique?
4355
299
}
4356
4357
4358
std::string Box_grpl::dump(Indent& indent) const
4359
0
{
4360
0
  std::ostringstream sstr;
4361
0
  sstr << Box::dump(indent);
4362
0
  sstr << dump_children(indent);
4363
0
  return sstr.str();
4364
0
}
4365
4366
4367
Error Box_EntityToGroup::parse(BitstreamRange& range, const heif_security_limits* limits)
4368
716
{
4369
716
  Error err = parse_full_box_header(range);
4370
716
  if (err != Error::Ok) {
4371
443
    return err;
4372
443
  }
4373
4374
273
  group_id = range.read32();
4375
273
  uint32_t nEntities = range.read32();
4376
4377
273
  if (nEntities > range.get_remaining_bytes() / 4) {
4378
53
    std::stringstream sstr;
4379
53
    size_t maxEntries = range.get_remaining_bytes() / 4;
4380
53
    sstr << "entity group box should contain " << nEntities << " entities, but we can only read " << maxEntries << " entities.";
4381
4382
53
    return {heif_error_Invalid_input,
4383
53
            heif_suberror_End_of_data,
4384
53
            sstr.str()};
4385
53
  }
4386
4387
220
  if (limits->max_size_entity_group && nEntities > limits->max_size_entity_group) {
4388
11
    std::stringstream sstr;
4389
11
    sstr << "entity group box contains " << nEntities << " entities, but the security limit is set to " << limits->max_size_entity_group << " entities.";
4390
4391
11
    return {heif_error_Invalid_input,
4392
11
            heif_suberror_Security_limit_exceeded,
4393
11
            sstr.str()};
4394
11
  }
4395
4396
209
  entity_ids.resize(nEntities);
4397
772
  for (uint32_t i = 0; i < nEntities; i++) {
4398
563
    entity_ids[i] = range.read32();
4399
563
  }
4400
4401
209
  return Error::Ok;
4402
220
}
4403
4404
4405
Error Box_EntityToGroup::write(StreamWriter& writer) const
4406
0
{
4407
0
  size_t box_start = reserve_box_header_space(writer);
4408
4409
0
  write_entity_group_ids(writer);
4410
4411
0
  prepend_header(writer, box_start);
4412
4413
0
  return Error::Ok;
4414
0
}
4415
4416
4417
void Box_EntityToGroup::write_entity_group_ids(StreamWriter& writer) const
4418
0
{
4419
0
  assert(entity_ids.size() <= 0xFFFFFFFF);
4420
4421
0
  writer.write32(group_id);
4422
0
  writer.write32(static_cast<uint32_t>(entity_ids.size()));
4423
4424
0
  for (uint32_t id : entity_ids) {
4425
0
    writer.write32(id);
4426
0
  }
4427
0
}
4428
4429
4430
std::string Box_EntityToGroup::dump(Indent& indent) const
4431
0
{
4432
0
  std::ostringstream sstr;
4433
0
  sstr << Box::dump(indent);
4434
4435
0
  sstr << indent << "group id: " << group_id << "\n"
4436
0
       << indent << "entity IDs: ";
4437
4438
0
  bool first = true;
4439
0
  for (uint32_t id : entity_ids) {
4440
0
    if (first) {
4441
0
      first = false;
4442
0
    }
4443
0
    else {
4444
0
      sstr << ' ';
4445
0
    }
4446
4447
0
    sstr << id;
4448
0
  }
4449
4450
0
  sstr << "\n";
4451
4452
0
  return sstr.str();
4453
0
}
4454
4455
4456
Error Box_ster::parse(BitstreamRange& range, const heif_security_limits* limits)
4457
56
{
4458
56
  Error err = Box_EntityToGroup::parse(range, limits);
4459
56
  if (err) {
4460
14
    return err;
4461
14
  }
4462
4463
42
  if (entity_ids.size() != 2) {
4464
4
    return {heif_error_Invalid_input,
4465
4
            heif_suberror_Invalid_box_size,
4466
4
            "'ster' entity group does not exists of exactly two images"};
4467
4
  }
4468
4469
38
  return Error::Ok;
4470
42
}
4471
4472
4473
std::string Box_ster::dump(Indent& indent) const
4474
0
{
4475
0
  std::ostringstream sstr;
4476
0
  sstr << Box::dump(indent);
4477
4478
0
  sstr << indent << "group id: " << group_id << "\n"
4479
0
       << indent << "left image ID: " << entity_ids[0] << "\n"
4480
0
       << indent << "right image ID: " << entity_ids[1] << "\n";
4481
4482
0
  return sstr.str();
4483
0
}
4484
4485
4486
4487
Error Box_pymd::parse(BitstreamRange& range, const heif_security_limits* limits)
4488
552
{
4489
552
  Error err = Box_EntityToGroup::parse(range, limits);
4490
552
  if (err) {
4491
488
    return err;
4492
488
  }
4493
4494
64
  tile_size_x = range.read16();
4495
64
  tile_size_y = range.read16();
4496
4497
315
  for (size_t i = 0; i < entity_ids.size(); i++) {
4498
251
    LayerInfo layer{};
4499
251
    layer.layer_binning = range.read16();
4500
251
    layer.tiles_in_layer_row_minus1 = range.read16();
4501
251
    layer.tiles_in_layer_column_minus1 = range.read16();
4502
4503
251
    m_layer_infos.push_back(layer);
4504
251
  }
4505
4506
64
  return Error::Ok;
4507
552
}
4508
4509
4510
Error Box_pymd::write(StreamWriter& writer) const
4511
0
{
4512
0
  size_t box_start = reserve_box_header_space(writer);
4513
4514
0
  Box_EntityToGroup::write_entity_group_ids(writer);
4515
4516
0
  writer.write16(tile_size_x);
4517
0
  writer.write16(tile_size_y);
4518
4519
0
  for (size_t i = 0; i < entity_ids.size(); i++) {
4520
0
    const LayerInfo& layer = m_layer_infos[i];
4521
4522
0
    writer.write16(layer.layer_binning);
4523
0
    writer.write16(layer.tiles_in_layer_row_minus1);
4524
0
    writer.write16(layer.tiles_in_layer_column_minus1);
4525
0
  }
4526
4527
0
  prepend_header(writer, box_start);
4528
4529
0
  return Error::Ok;
4530
0
}
4531
4532
4533
std::string Box_pymd::dump(Indent& indent) const
4534
0
{
4535
0
  std::ostringstream sstr;
4536
0
  sstr << Box_EntityToGroup::dump(indent);
4537
4538
0
  sstr << indent << "tile size: " << tile_size_x << "x" << tile_size_y << "\n";
4539
4540
0
  int layerNr = 0;
4541
0
  for (const auto& layer : m_layer_infos) {
4542
0
    sstr << indent << "layer " << layerNr << ":\n"
4543
0
         << indent << "| binning: " << layer.layer_binning << "\n"
4544
0
         << indent << "| tiles: " << (layer.tiles_in_layer_row_minus1 + 1) << "x" << (layer.tiles_in_layer_column_minus1 + 1) << "\n";
4545
4546
0
    layerNr++;
4547
0
  }
4548
4549
0
  return sstr.str();
4550
0
}
4551
4552
4553
Error Box_dinf::parse(BitstreamRange& range, const heif_security_limits* limits)
4554
215
{
4555
  //parse_full_box_header(range);
4556
4557
215
  return read_children(range, READ_CHILDREN_ALL, limits);
4558
215
}
4559
4560
4561
std::string Box_dinf::dump(Indent& indent) const
4562
0
{
4563
0
  std::ostringstream sstr;
4564
0
  sstr << Box::dump(indent);
4565
0
  sstr << dump_children(indent);
4566
4567
0
  return sstr.str();
4568
0
}
4569
4570
4571
Error Box_dref::parse(BitstreamRange& range, const heif_security_limits* limits)
4572
174
{
4573
174
  parse_full_box_header(range);
4574
4575
174
  if (get_version() != 0) {
4576
2
    return unsupported_version_error("dref");
4577
2
  }
4578
4579
172
  uint32_t nEntities = range.read32();
4580
4581
  /*
4582
  for (int i=0;i<nEntities;i++) {
4583
    if (range.eof()) {
4584
      break;
4585
    }
4586
  }
4587
  */
4588
4589
172
  if (nEntities > (uint32_t)std::numeric_limits<int>::max()) {
4590
7
    return Error(heif_error_Memory_allocation_error,
4591
7
                 heif_suberror_Security_limit_exceeded,
4592
7
                 "Too many entities in dref box.");
4593
7
  }
4594
4595
165
  Error err = read_children(range, (int)nEntities, limits);
4596
165
  if (err) {
4597
28
    return err;
4598
28
  }
4599
4600
137
  if (m_children.size() != nEntities) {
4601
    // TODO return Error(
4602
38
  }
4603
4604
137
  return err;
4605
165
}
4606
4607
4608
Error Box_dref::write(StreamWriter& writer) const
4609
0
{
4610
0
  size_t box_start = reserve_box_header_space(writer);
4611
4612
0
  if (m_children.size() > 0xFFFF) {
4613
0
    return {
4614
0
      heif_error_Usage_error,
4615
0
      heif_suberror_Unspecified,
4616
0
      "Too many dref children boxes."
4617
0
    };
4618
0
  }
4619
4620
0
  writer.write32(static_cast<uint32_t>(m_children.size()));
4621
0
  write_children(writer);
4622
4623
0
  prepend_header(writer, box_start);
4624
0
  return Error::Ok;
4625
0
}
4626
4627
4628
std::string Box_dref::dump(Indent& indent) const
4629
0
{
4630
0
  std::ostringstream sstr;
4631
0
  sstr << Box::dump(indent);
4632
0
  sstr << dump_children(indent);
4633
4634
0
  return sstr.str();
4635
0
}
4636
4637
4638
Error Box_url::parse(BitstreamRange& range, const heif_security_limits* limits)
4639
96
{
4640
96
  parse_full_box_header(range);
4641
4642
96
  if (get_version() > 0) {
4643
3
    return unsupported_version_error("url");
4644
3
  }
4645
4646
93
  if (get_flags() & 1) {
4647
    // data in same file
4648
62
    m_location.clear();
4649
62
  }
4650
31
  else {
4651
31
    m_location = range.read_string();
4652
31
  }
4653
4654
93
  return range.get_error();
4655
96
}
4656
4657
4658
Error Box_url::write(StreamWriter& writer) const
4659
0
{
4660
0
  size_t box_start = reserve_box_header_space(writer);
4661
4662
0
  if (m_location.empty()) {
4663
0
    assert(get_flags() == 1);
4664
0
  }
4665
0
  else {
4666
0
    assert(get_flags() == 0);
4667
0
    writer.write(m_location);
4668
0
  }
4669
4670
0
  prepend_header(writer, box_start);
4671
0
  return Error::Ok;
4672
0
}
4673
4674
4675
std::string Box_url::dump(Indent& indent) const
4676
0
{
4677
0
  std::ostringstream sstr;
4678
0
  sstr << Box::dump(indent);
4679
  //sstr << dump_children(indent);
4680
4681
0
  sstr << indent << "location: " << m_location << "\n";
4682
4683
0
  return sstr.str();
4684
0
}
4685
4686
4687
Error Box_udes::parse(BitstreamRange& range, const heif_security_limits* limits)
4688
119
{
4689
119
  parse_full_box_header(range);
4690
4691
119
  if (get_version() > 0) {
4692
30
    return unsupported_version_error("udes");
4693
30
  }
4694
4695
89
  m_lang = range.read_string();
4696
89
  m_name = range.read_string();
4697
89
  m_description = range.read_string();
4698
89
  m_tags = range.read_string();
4699
89
  return range.get_error();
4700
119
}
4701
4702
std::string Box_udes::dump(Indent& indent) const
4703
0
{
4704
0
  std::ostringstream sstr;
4705
0
  sstr << Box::dump(indent);
4706
0
  sstr << indent << "lang: " << m_lang << "\n";
4707
0
  sstr << indent << "name: " << m_name << "\n";
4708
0
  sstr << indent << "description: " << m_description << "\n";
4709
0
  sstr << indent << "tags: " << m_lang << "\n";
4710
0
  return sstr.str();
4711
0
}
4712
4713
Error Box_udes::write(StreamWriter& writer) const
4714
0
{
4715
0
  size_t box_start = reserve_box_header_space(writer);
4716
0
  writer.write(m_lang);
4717
0
  writer.write(m_name);
4718
0
  writer.write(m_description);
4719
0
  writer.write(m_tags);
4720
0
  prepend_header(writer, box_start);
4721
0
  return Error::Ok;
4722
0
}
4723
4724
4725
void Box_cmin::RelativeIntrinsicMatrix::compute_focal_length(int image_width, int image_height,
4726
                                                             double& out_focal_length_x, double& out_focal_length_y) const
4727
0
{
4728
0
  out_focal_length_x = focal_length_x * image_width;
4729
4730
0
  if (is_anisotropic) {
4731
0
    out_focal_length_y = focal_length_y * image_height;
4732
0
  }
4733
0
  else {
4734
0
    out_focal_length_y = out_focal_length_x;
4735
0
  }
4736
0
}
4737
4738
4739
void Box_cmin::RelativeIntrinsicMatrix::compute_principal_point(int image_width, int image_height,
4740
                                                                double& out_principal_point_x, double& out_principal_point_y) const
4741
0
{
4742
0
  out_principal_point_x = principal_point_x * image_width;
4743
0
  out_principal_point_y = principal_point_y * image_height;
4744
0
}
4745
4746
4747
Box_cmin::AbsoluteIntrinsicMatrix Box_cmin::RelativeIntrinsicMatrix::to_absolute(int image_width, int image_height) const
4748
0
{
4749
0
  AbsoluteIntrinsicMatrix m{};
4750
0
  compute_focal_length(image_width, image_height, m.focal_length_x, m.focal_length_y);
4751
0
  compute_principal_point(image_width, image_height, m.principal_point_x, m.principal_point_y);
4752
0
  m.skew = skew;
4753
4754
0
  return m;
4755
0
}
4756
4757
4758
std::string Box_cmin::dump(Indent& indent) const
4759
0
{
4760
0
  std::ostringstream sstr;
4761
0
  sstr << Box::dump(indent);
4762
0
  sstr << indent << "principal-point: " << m_matrix.principal_point_x << ", " << m_matrix.principal_point_y << "\n";
4763
0
  if (m_matrix.is_anisotropic) {
4764
0
    sstr << indent << "focal-length: " << m_matrix.focal_length_x << ", " << m_matrix.focal_length_y << "\n";
4765
0
    sstr << indent << "skew: " << m_matrix.skew << "\n";
4766
0
  }
4767
0
  else {
4768
0
    sstr << indent << "focal-length: " << m_matrix.focal_length_x << "\n";
4769
0
    sstr << indent << "no skew\n";
4770
0
  }
4771
4772
0
  return sstr.str();
4773
0
}
4774
4775
4776
Error Box_cmin::parse(BitstreamRange& range, const heif_security_limits* limits)
4777
92
{
4778
92
  parse_full_box_header(range);
4779
4780
92
  if (get_version() > 0) {
4781
43
    return unsupported_version_error("cmin");
4782
43
  }
4783
4784
49
  m_denominatorShift = (get_flags() & 0x1F00) >> 8;
4785
49
  uint32_t denominator = (1U << m_denominatorShift);
4786
4787
49
  m_matrix.focal_length_x = range.read32s() / (double)denominator;
4788
49
  m_matrix.principal_point_x = range.read32s() / (double)denominator;
4789
49
  m_matrix.principal_point_y = range.read32s() / (double)denominator;
4790
4791
49
  if (get_flags() & 1) {
4792
23
    m_skewDenominatorShift = ((get_flags()) & 0x1F0000) >> 16;
4793
23
    uint32_t skewDenominator = (1U << m_skewDenominatorShift);
4794
4795
23
    m_matrix.focal_length_y = range.read32s() / (double)denominator;
4796
23
    m_matrix.skew = range.read32s() / (double)skewDenominator;
4797
4798
23
    m_matrix.is_anisotropic = true;
4799
23
  }
4800
26
  else {
4801
26
    m_matrix.is_anisotropic = false;
4802
26
    m_matrix.focal_length_y = 0;
4803
26
    m_matrix.skew = 0;
4804
26
  }
4805
49
  return range.get_error();
4806
92
}
4807
4808
4809
static uint32_t get_signed_fixed_point_shift(double v)
4810
0
{
4811
0
  if (v==0) {
4812
0
    return 31;
4813
0
  }
4814
4815
0
  v = std::abs(v);
4816
4817
0
  uint32_t shift = 0;
4818
0
  while (v < (1<<30)) {
4819
0
    v *= 2;
4820
0
    shift++;
4821
4822
0
    if (shift==31) {
4823
0
      return shift;
4824
0
    }
4825
0
  }
4826
4827
0
  return shift;
4828
0
}
4829
4830
4831
void Box_cmin::set_intrinsic_matrix(RelativeIntrinsicMatrix matrix)
4832
0
{
4833
0
  m_matrix = matrix;
4834
4835
0
  uint32_t flags = 0;
4836
0
  flags |= matrix.is_anisotropic ? 1 : 0;
4837
4838
0
  uint32_t shift_fx = get_signed_fixed_point_shift(matrix.focal_length_x);
4839
0
  uint32_t shift_px = get_signed_fixed_point_shift(matrix.principal_point_x);
4840
0
  uint32_t shift_py = get_signed_fixed_point_shift(matrix.principal_point_y);
4841
0
  m_denominatorShift = std::min(std::min(shift_fx, shift_px), shift_py);
4842
4843
0
  if (matrix.is_anisotropic) {
4844
0
    uint32_t shift_fy = get_signed_fixed_point_shift(matrix.focal_length_y);
4845
0
    m_denominatorShift = std::min(m_denominatorShift, shift_fy);
4846
4847
0
    m_skewDenominatorShift = get_signed_fixed_point_shift(matrix.skew);
4848
0
  }
4849
0
  else {
4850
0
    m_skewDenominatorShift = 0;
4851
0
  }
4852
4853
0
  flags |= (m_denominatorShift << 8);
4854
0
  flags |= (m_skewDenominatorShift << 16);
4855
4856
0
  set_flags(flags);
4857
0
}
4858
4859
4860
Error Box_cmin::write(StreamWriter& writer) const
4861
0
{
4862
0
  size_t box_start = reserve_box_header_space(writer);
4863
4864
0
  uint32_t denominator = (1U << m_denominatorShift);
4865
4866
0
  writer.write32s(static_cast<int32_t>(m_matrix.focal_length_x * denominator));
4867
0
  writer.write32s(static_cast<int32_t>(m_matrix.principal_point_x * denominator));
4868
0
  writer.write32s(static_cast<int32_t>(m_matrix.principal_point_y * denominator));
4869
4870
0
  if (get_flags() & 1) {
4871
0
    writer.write32s(static_cast<int32_t>(m_matrix.focal_length_y * denominator));
4872
4873
0
    uint32_t skewDenominator = (1U << m_skewDenominatorShift);
4874
0
    writer.write32s(static_cast<int32_t>(m_matrix.skew * skewDenominator));
4875
0
  }
4876
4877
0
  prepend_header(writer, box_start);
4878
4879
0
  return Error::Ok;
4880
0
}
4881
4882
4883
static std::array<double,9> mul(const std::array<double,9>& a, const std::array<double,9>& b)
4884
0
{
4885
0
  std::array<double, 9> m{};
4886
4887
0
  m[0] = a[0]*b[0] + a[1]*b[3] + a[2]*b[6];
4888
0
  m[1] = a[0]*b[1] + a[1]*b[4] + a[2]*b[7];
4889
0
  m[2] = a[0]*b[2] + a[1]*b[5] + a[2]*b[8];
4890
4891
0
  m[3] = a[3]*b[0] + a[4]*b[3] + a[5]*b[6];
4892
0
  m[4] = a[3]*b[1] + a[4]*b[4] + a[5]*b[7];
4893
0
  m[5] = a[3]*b[2] + a[4]*b[5] + a[5]*b[8];
4894
4895
0
  m[6] = a[6]*b[0] + a[7]*b[3] + a[8]*b[6];
4896
0
  m[7] = a[6]*b[1] + a[7]*b[4] + a[8]*b[7];
4897
0
  m[8] = a[6]*b[2] + a[7]*b[5] + a[8]*b[8];
4898
4899
0
  return m;
4900
0
}
4901
4902
4903
std::array<double,9> Box_cmex::ExtrinsicMatrix::calculate_rotation_matrix() const
4904
0
{
4905
0
  std::array<double,9> m{};
4906
4907
0
  if (rotation_as_quaternions) {
4908
0
    double qx = quaternion_x;
4909
0
    double qy = quaternion_y;
4910
0
    double qz = quaternion_z;
4911
0
    double qw = quaternion_w;
4912
4913
0
    m[0] = 1-2*(qy*qy+qz*qz);
4914
0
    m[1] = 2*(qx*qy-qz*qw);
4915
0
    m[2] = 2*(qx*qz+qy*qw);
4916
0
    m[3] = 2*(qx*qy+qz*qw);
4917
0
    m[4] = 1-2*(qx*qx+qz*qz);
4918
0
    m[5] = 2*(qy*qz-qx*qw);
4919
0
    m[6] = 2*(qx*qz-qy*qw);
4920
0
    m[7] = 2*(qy*qz+qx*qw);
4921
0
    m[8] = 1-2*(qx*qx+qy*qy);
4922
0
  }
4923
0
  else {
4924
    // This rotation order fits the conformance data
4925
    // https://github.com/MPEGGroup/FileFormatConformance
4926
    // branch m62054_extrinsics : FileFormatConformance/data/file_features/under_consideration/ex_in_trinsics/extrinsic_rotation
4927
4928
0
    std::array<double,9> m_yaw{};    // Z
4929
0
    std::array<double,9> m_pitch{};  // Y
4930
0
    std::array<double,9> m_roll{};   // X
4931
4932
0
    const double d2r = M_PI/180;
4933
4934
0
    double x = d2r * rotation_roll;
4935
0
    double y = d2r * rotation_pitch;
4936
0
    double z = d2r * rotation_yaw;
4937
4938
    // X
4939
0
    m_roll[0] = 1;
4940
0
    m_roll[4] = m_roll[8] = cos(x);
4941
0
    m_roll[5] = -sin(x);
4942
0
    m_roll[7] = sin(x);
4943
4944
    // Y
4945
0
    m_pitch[4] = 1;
4946
0
    m_pitch[0] = m_pitch[8] = cos(y);
4947
0
    m_pitch[6] = -sin(y);
4948
0
    m_pitch[2] = sin(y);
4949
4950
    // Z
4951
0
    m_yaw[8] = 1;
4952
0
    m_yaw[0] = m_yaw[4] = cos(z);
4953
0
    m_yaw[1] = -sin(z);
4954
0
    m_yaw[3] = sin(z);
4955
4956
0
    m = mul(m_yaw, mul(m_pitch, m_roll));
4957
0
  }
4958
4959
0
  return m;
4960
0
}
4961
4962
4963
Error Box_cmex::parse(BitstreamRange& range, const heif_security_limits* limits)
4964
643
{
4965
643
  parse_full_box_header(range);
4966
4967
643
  if (get_version() > 0) {
4968
151
    return unsupported_version_error("cmex");
4969
151
  }
4970
4971
492
  m_matrix = ExtrinsicMatrix{};
4972
4973
492
  if (get_flags() & pos_x_present) {
4974
288
    m_has_pos_x = true;
4975
288
    m_matrix.pos_x = range.read32s();
4976
288
  }
4977
4978
492
  if (get_flags() & pos_y_present) {
4979
369
    m_has_pos_y = true;
4980
369
    m_matrix.pos_y = range.read32s();
4981
369
  }
4982
4983
492
  if (get_flags() & pos_z_present) {
4984
110
    m_has_pos_z = true;
4985
110
    m_matrix.pos_z = range.read32s();
4986
110
  }
4987
4988
492
  if (get_flags() & orientation_present) {
4989
367
    m_has_orientation = true;
4990
4991
367
    if (get_version() == 0) {
4992
367
      bool use32bit = (get_flags() & rot_large_field_size);
4993
367
      int32_t quat_x = use32bit ? range.read32s() : range.read16s();
4994
367
      int32_t quat_y = use32bit ? range.read32s() : range.read16s();
4995
367
      int32_t quat_z = use32bit ? range.read32s() : range.read16s();
4996
4997
367
      uint32_t div = 1U << (14 + (use32bit ? 16 : 0));
4998
4999
367
      m_matrix.rotation_as_quaternions = true;
5000
367
      m_matrix.quaternion_x = quat_x / (double)div;
5001
367
      m_matrix.quaternion_y = quat_y / (double)div;
5002
367
      m_matrix.quaternion_z = quat_z / (double)div;
5003
5004
367
      double q_sum = (m_matrix.quaternion_x * m_matrix.quaternion_x +
5005
367
                      m_matrix.quaternion_y * m_matrix.quaternion_y +
5006
367
                      m_matrix.quaternion_z * m_matrix.quaternion_z);
5007
5008
367
      if (q_sum > 1.0) {
5009
59
        return Error(heif_error_Invalid_input,
5010
59
                     heif_suberror_Unspecified,
5011
59
                     "Invalid quaternion in extrinsic rotation matrix");
5012
59
      }
5013
5014
308
      m_matrix.quaternion_w = sqrt(1 - q_sum);
5015
5016
308
    } else if (get_version() == 1) {
5017
0
      uint32_t div = 1<<16;
5018
0
      m_matrix.rotation_yaw = range.read32s() / (double)div;
5019
0
      m_matrix.rotation_pitch = range.read32s() / (double)div;
5020
0
      m_matrix.rotation_roll = range.read32s() / (double)div;
5021
0
    }
5022
367
  }
5023
5024
433
  if (get_flags() & id_present) {
5025
77
    m_has_world_coordinate_system_id = true;
5026
77
    m_matrix.world_coordinate_system_id = range.read32();
5027
77
  }
5028
5029
433
  return range.get_error();
5030
492
}
5031
5032
5033
std::string Box_cmex::dump(Indent& indent) const
5034
0
{
5035
0
  std::ostringstream sstr;
5036
0
  sstr << Box::dump(indent);
5037
0
  sstr << indent << "camera position (um): ";
5038
0
  sstr << m_matrix.pos_x << " ; ";
5039
0
  sstr << m_matrix.pos_y << " ; ";
5040
0
  sstr << m_matrix.pos_z << "\n";
5041
5042
0
  sstr << indent << "orientation ";
5043
0
  if (m_matrix.rotation_as_quaternions) {
5044
0
    sstr << "(quaterion)\n";
5045
0
    sstr << indent << "  q = ["
5046
0
         << m_matrix.quaternion_x << ";"
5047
0
         << m_matrix.quaternion_y << ";"
5048
0
         << m_matrix.quaternion_z << ";"
5049
0
         << m_matrix.quaternion_w << "]\n";
5050
0
  }
5051
0
  else {
5052
0
    sstr << "(angles)\n";
5053
0
    sstr << indent << "  yaw:   " << m_matrix.rotation_yaw << "\n";
5054
0
    sstr << indent << "  pitch: " << m_matrix.rotation_pitch << "\n";
5055
0
    sstr << indent << "  roll:  " << m_matrix.rotation_roll << "\n";
5056
0
  }
5057
5058
0
  sstr << indent << "world coordinate system id: " << m_matrix.world_coordinate_system_id << "\n";
5059
5060
0
  return sstr.str();
5061
0
}
5062
5063
5064
5065
5066
Error Box_cmex::set_extrinsic_matrix(ExtrinsicMatrix matrix)
5067
0
{
5068
0
  m_matrix = matrix;
5069
5070
0
  uint32_t flags = 0;
5071
5072
0
  m_has_pos_x = (matrix.pos_x != 0);
5073
0
  m_has_pos_y = (matrix.pos_y != 0);
5074
0
  m_has_pos_z = (matrix.pos_z != 0);
5075
5076
0
  if (m_has_pos_x) {
5077
0
    flags |= pos_x_present;
5078
0
  }
5079
5080
0
  if (m_has_pos_y) {
5081
0
    flags |= pos_y_present;
5082
0
  }
5083
5084
0
  if (m_has_pos_z) {
5085
0
    flags |= pos_z_present;
5086
0
  }
5087
5088
0
  if (matrix.rotation_as_quaternions) {
5089
0
    if (matrix.quaternion_x != 0 ||
5090
0
        matrix.quaternion_y != 0 ||
5091
0
        matrix.quaternion_z != 0) {
5092
0
      flags |= orientation_present;
5093
5094
0
      double q_sum = (m_matrix.quaternion_x * m_matrix.quaternion_x +
5095
0
                      m_matrix.quaternion_y * m_matrix.quaternion_y +
5096
0
                      m_matrix.quaternion_z * m_matrix.quaternion_z);
5097
5098
0
      if (q_sum > 1.0) {
5099
0
        return Error(heif_error_Invalid_input,
5100
0
                     heif_suberror_Unspecified,
5101
0
                     "Invalid quaternion in extrinsic rotation matrix");
5102
0
      }
5103
5104
0
      if (matrix.quaternion_w < 0) {
5105
0
        matrix.quaternion_x = -matrix.quaternion_x;
5106
0
        matrix.quaternion_y = -matrix.quaternion_y;
5107
0
        matrix.quaternion_z = -matrix.quaternion_z;
5108
0
        matrix.quaternion_w = -matrix.quaternion_w;
5109
0
      }
5110
0
    }
5111
0
  }
5112
0
  else {
5113
0
    if (matrix.rotation_yaw != 0 ||
5114
0
        matrix.rotation_pitch != 0 ||
5115
0
        matrix.rotation_roll != 0) {
5116
0
      flags |= orientation_present;
5117
5118
0
      if (matrix.rotation_yaw < -180.0 || matrix.rotation_yaw >= 180.0) {
5119
0
        return Error(heif_error_Invalid_input,
5120
0
                     heif_suberror_Unspecified,
5121
0
                     "Invalid yaw angle");
5122
0
      }
5123
5124
0
      if (matrix.rotation_pitch < -90.0 || matrix.rotation_pitch > 90.0) {
5125
0
        return Error(heif_error_Invalid_input,
5126
0
                     heif_suberror_Unspecified,
5127
0
                     "Invalid pitch angle");
5128
0
      }
5129
5130
0
      if (matrix.rotation_roll < -180.0 || matrix.rotation_roll >= 180.0) {
5131
0
        return Error(heif_error_Invalid_input,
5132
0
                     heif_suberror_Unspecified,
5133
0
                     "Invalid roll angle");
5134
0
      }
5135
0
    }
5136
0
  }
5137
5138
0
  if (matrix.orientation_is_32bit) {
5139
0
    flags |= rot_large_field_size;
5140
0
  }
5141
5142
0
  if (matrix.world_coordinate_system_id != 0) {
5143
0
    flags |= id_present;
5144
0
  }
5145
5146
0
  set_flags(flags);
5147
0
  set_version(m_matrix.rotation_as_quaternions ? 0 : 1);
5148
5149
0
  return Error::Ok;
5150
0
}
5151
5152
5153
Error Box_cmex::write(StreamWriter& writer) const
5154
0
{
5155
0
  size_t box_start = reserve_box_header_space(writer);
5156
5157
0
  if (m_has_pos_x) {
5158
0
    writer.write32s(m_matrix.pos_x);
5159
0
  }
5160
5161
0
  if (m_has_pos_y) {
5162
0
    writer.write32s(m_matrix.pos_y);
5163
0
  }
5164
5165
0
  if (m_has_pos_z) {
5166
0
    writer.write32s(m_matrix.pos_z);
5167
0
  }
5168
5169
0
  if (m_has_orientation) {
5170
0
    if (m_matrix.rotation_as_quaternions) {
5171
0
      if (m_matrix.orientation_is_32bit) {
5172
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_x * (1<<30)));
5173
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_y * (1<<30)));
5174
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_z * (1<<30)));
5175
0
      }
5176
0
      else {
5177
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_x * (1<<14)));
5178
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_y * (1<<14)));
5179
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_z * (1<<14)));
5180
0
      }
5181
0
    }
5182
0
    else {
5183
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_yaw * (1<<16)));
5184
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_pitch * (1<<16)));
5185
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_roll * (1<<16)));
5186
0
    }
5187
0
  }
5188
5189
0
  if (m_has_world_coordinate_system_id) {
5190
0
    writer.write32(m_matrix.world_coordinate_system_id);
5191
0
  }
5192
5193
5194
0
  prepend_header(writer, box_start);
5195
5196
0
  return Error::Ok;
5197
0
}
5198
5199
5200
std::string Box_taic::dump(const heif_tai_clock_info& info, Indent& indent)
5201
0
{
5202
0
  std::ostringstream sstr;
5203
0
  sstr << indent << "time_uncertainty: ";
5204
0
  if (info.time_uncertainty == heif_tai_clock_info_time_uncertainty_unknown) {
5205
0
    sstr << "unknown\n";
5206
0
  }
5207
0
  else {
5208
0
    sstr << info.time_uncertainty << "\n";
5209
0
  }
5210
0
  sstr << indent << "clock_resolution: " << info.clock_resolution << "\n";
5211
0
  sstr << indent << "clock_drift_rate: ";
5212
0
  if (info.clock_drift_rate == heif_tai_clock_info_clock_drift_rate_unknown) {
5213
0
    sstr << "undefined\n";
5214
0
  }
5215
0
  else {
5216
0
    sstr << info.clock_drift_rate << "\n";
5217
0
  }
5218
5219
0
  sstr << indent << "clock_type: " << int(info.clock_type) << " ";
5220
0
  switch (info.clock_type) {
5221
0
    case heif_tai_clock_info_clock_type_unknown: sstr << "(unknown)\n"; break;
5222
0
    case heif_tai_clock_info_clock_type_synchronized_to_atomic_source: sstr << "(synchronized to atomic source)\n"; break;
5223
0
    case heif_tai_clock_info_clock_type_not_synchronized_to_atomic_source: sstr << "(not synchronized to atomic source)\n"; break;
5224
0
    default: sstr << "(illegal value)\n"; break;;
5225
0
  }
5226
0
  return sstr.str();
5227
0
}
5228
5229
5230
0
std::string Box_taic::dump(Indent& indent) const {
5231
0
  std::ostringstream sstr;
5232
0
  sstr << Box::dump(indent);
5233
0
  sstr << dump(m_info, indent);
5234
5235
0
  return sstr.str();
5236
0
}
5237
5238
0
Error Box_taic::write(StreamWriter& writer) const {
5239
0
  size_t box_start = reserve_box_header_space(writer);
5240
0
  writer.write64(m_info.time_uncertainty);
5241
0
  writer.write32(m_info.clock_resolution);
5242
0
  writer.write32(m_info.clock_drift_rate);
5243
0
  writer.write8(static_cast<uint8_t>(m_info.clock_type << 6));
5244
5245
0
  prepend_header(writer, box_start);
5246
5247
0
  return Error::Ok;
5248
0
}
5249
5250
6
Error Box_taic::parse(BitstreamRange& range, const heif_security_limits*) {
5251
6
  parse_full_box_header(range);
5252
5253
6
  m_info.time_uncertainty = range.read64();
5254
6
  m_info.clock_resolution = range.read32();
5255
5256
6
  m_info.clock_drift_rate = range.read32s();
5257
6
  m_info.clock_type = range.read8() >> 6;
5258
6
  return range.get_error();
5259
6
}
5260
5261
5262
bool operator==(const heif_tai_clock_info& a,
5263
                const heif_tai_clock_info& b)
5264
0
{
5265
0
  return a.version == b.version &&
5266
0
         a.time_uncertainty == b.time_uncertainty &&
5267
0
         a.clock_resolution == b.clock_resolution &&
5268
0
         a.clock_drift_rate == b.clock_drift_rate &&
5269
0
         a.clock_type == b.clock_type;
5270
0
}
5271
5272
bool Box_taic::operator==(const Box& other) const
5273
0
{
5274
0
  const auto* other_taic = dynamic_cast<const Box_taic*>(&other);
5275
0
  if (other_taic == nullptr) {
5276
0
    return false;
5277
0
  }
5278
5279
0
  return m_info == other_taic->m_info;
5280
0
}
5281
5282
5283
0
std::string Box_itai::dump(Indent& indent) const {
5284
0
  std::ostringstream sstr;
5285
0
  sstr << Box::dump(indent);
5286
0
  sstr << indent << "tai_timestamp: " << m_timestamp.tai_timestamp << "\n";
5287
0
  sstr << indent << "synchronization_state: " << int(m_timestamp.synchronization_state) << "\n";
5288
0
  sstr << indent << "timestamp_generation_failure: " << int(m_timestamp.timestamp_generation_failure) << "\n";
5289
0
  sstr << indent << "timestamp_is_modified: " << int(m_timestamp.timestamp_is_modified) << "\n";
5290
0
  return sstr.str();
5291
0
}
5292
5293
5294
std::vector<uint8_t> Box_itai::encode_tai_to_bitstream(const heif_tai_timestamp_packet* tai)
5295
0
{
5296
0
  StreamWriter writer;
5297
0
  writer.write64(tai->tai_timestamp);
5298
5299
0
  uint8_t status_bits = 0;
5300
0
  status_bits |= tai->synchronization_state ? (1 << 7) : 0;
5301
0
  status_bits |= tai->timestamp_generation_failure ? (1 << 6) : 0;
5302
0
  status_bits |= tai->timestamp_is_modified ? (1 << 5) : 0;
5303
5304
0
  writer.write8(status_bits);
5305
5306
0
  return writer.get_data();
5307
0
}
5308
5309
bool operator==(const heif_tai_timestamp_packet& a,
5310
                const heif_tai_timestamp_packet& b)
5311
0
{
5312
0
  return a.version == b.version &&
5313
0
         a.tai_timestamp == b.tai_timestamp &&
5314
0
         a.synchronization_state == b.synchronization_state &&
5315
0
         a.timestamp_generation_failure == b.timestamp_generation_failure &&
5316
0
         a.timestamp_is_modified == b.timestamp_is_modified;
5317
0
}
5318
5319
bool Box_itai::operator==(const Box& other) const
5320
0
{
5321
0
  const auto* other_itai = dynamic_cast<const Box_itai*>(&other);
5322
0
  if (other_itai == nullptr) {
5323
0
    return false;
5324
0
  }
5325
5326
0
  return m_timestamp == other_itai->m_timestamp;
5327
0
}
5328
5329
5330
5331
uint64_t uint8_vector_to_uint64_BE(const uint8_t* data)
5332
0
{
5333
0
  uint64_t value = ((static_cast<uint64_t>(data[0]) << 56) |
5334
0
                    (static_cast<uint64_t>(data[1]) << 48) |
5335
0
                    (static_cast<uint64_t>(data[2]) << 40) |
5336
0
                    (static_cast<uint64_t>(data[3]) << 32) |
5337
0
                    (static_cast<uint64_t>(data[4]) << 24) |
5338
0
                    (static_cast<uint64_t>(data[5]) << 16) |
5339
0
                    (static_cast<uint64_t>(data[6]) << 8) |
5340
0
                    (static_cast<uint64_t>(data[7]) << 0));
5341
5342
0
  return value;
5343
0
}
5344
5345
5346
Result<heif_tai_timestamp_packet> Box_itai::decode_tai_from_vector(const std::vector<uint8_t>& data)
5347
0
{
5348
0
  if (data.size() != 9) {
5349
0
    return Error{heif_error_Invalid_input,
5350
0
                 heif_suberror_Unspecified,
5351
0
                 "Wrong size of TAI timestamp data"};
5352
0
  }
5353
5354
0
  uint8_t status_bits = data[8];
5355
5356
0
  heif_tai_timestamp_packet tai;
5357
0
  tai.version = 1;
5358
0
  tai.tai_timestamp = uint8_vector_to_uint64_BE(data.data());
5359
0
  tai.synchronization_state = !!(status_bits & 0x80);
5360
0
  tai.timestamp_generation_failure = !!(status_bits & 0x40);
5361
0
  tai.timestamp_is_modified = !!(status_bits & 0x20);
5362
5363
0
  return tai;
5364
0
}
5365
5366
5367
0
Error Box_itai::write(StreamWriter& writer) const {
5368
0
  size_t box_start = reserve_box_header_space(writer);
5369
5370
0
  std::vector<uint8_t> tai_data = encode_tai_to_bitstream(&m_timestamp);
5371
5372
0
  writer.write(tai_data);
5373
5374
0
  prepend_header(writer, box_start);
5375
0
  return Error::Ok;
5376
0
}
5377
5378
182
Error Box_itai::parse(BitstreamRange& range, const heif_security_limits*) {
5379
182
  parse_full_box_header(range);
5380
5381
182
  m_timestamp.version = 1;
5382
182
  m_timestamp.tai_timestamp = range.read64();
5383
5384
182
  uint8_t status_bits = range.read8();
5385
5386
182
  m_timestamp.synchronization_state = !!(status_bits & 0x80);
5387
182
  m_timestamp.timestamp_generation_failure = !!(status_bits & 0x40);
5388
182
  m_timestamp.timestamp_is_modified = !!(status_bits & 0x20);
5389
5390
182
  return range.get_error();
5391
182
}
5392
5393
Error Box_elng::parse(BitstreamRange& range, const heif_security_limits* limits)
5394
24
{
5395
24
  parse_full_box_header(range);
5396
5397
24
  if (get_version() > 0) {
5398
11
    return unsupported_version_error("elng");
5399
11
  }
5400
5401
13
  m_lang = range.read_string();
5402
13
  return range.get_error();
5403
24
}
5404
5405
std::string Box_elng::dump(Indent& indent) const
5406
0
{
5407
0
  std::ostringstream sstr;
5408
0
  sstr << Box::dump(indent);
5409
0
  sstr << indent << "extended_language: " << m_lang << "\n";
5410
0
  return sstr.str();
5411
0
}
5412
5413
Error Box_elng::write(StreamWriter& writer) const
5414
0
{
5415
0
  size_t box_start = reserve_box_header_space(writer);
5416
0
  writer.write(m_lang);
5417
0
  prepend_header(writer, box_start);
5418
0
  return Error::Ok;
5419
0
}
5420
5421
5422
Error Box_gimi_content_id::parse(BitstreamRange& range, const heif_security_limits* limits)
5423
28
{
5424
28
  m_content_id = range.read_string_until_eof();
5425
5426
28
  return range.get_error();
5427
28
}
5428
5429
5430
Error Box_gimi_content_id::write(StreamWriter& writer) const
5431
0
{
5432
0
  size_t box_start = reserve_box_header_space(writer);
5433
5434
0
  writer.write(m_content_id, false);
5435
5436
0
  prepend_header(writer, box_start);
5437
5438
0
  return Error::Ok;
5439
0
}
5440
5441
5442
std::string Box_gimi_content_id::dump(Indent& indent) const
5443
0
{
5444
0
  std::ostringstream sstr;
5445
0
  sstr << Box::dump(indent);
5446
5447
0
  sstr << indent << "content ID: " << m_content_id << "\n";
5448
5449
0
  return sstr.str();
5450
0
}