Coverage Report

Created: 2026-04-01 07:24

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