Coverage Report

Created: 2025-07-23 08:18

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