Coverage Report

Created: 2026-10-27 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/sequences/seq_boxes.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2024 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
21
#include "sequences/seq_boxes.h"
22
#include <iomanip>
23
#include <set>
24
#include <limits>
25
#include <utility>
26
#include <algorithm>
27
28
29
Error Box_container::parse(BitstreamRange& range, const heif_security_limits* limits)
30
984
{
31
984
  return read_children(range, READ_CHILDREN_ALL, limits);
32
984
}
33
34
35
void Box_container::dump(std::ostream& sstr, Indent& indent, bool full_log) const
36
0
{
37
0
  Box::dump(sstr, indent, full_log);
38
0
  dump_children(sstr, indent, full_log);
39
40
0
  return;
41
0
}
42
43
44
double Box_mvhd::get_matrix_element(int idx) const
45
0
{
46
0
  if (idx == 8) {
47
0
    return 1.0;
48
0
  }
49
50
0
  return m_matrix[idx] / double(0x10000);
51
0
}
52
53
54
Error Box_mvhd::parse(BitstreamRange& range, const heif_security_limits* limits)
55
48
{
56
48
  parse_full_box_header(range);
57
58
48
  if (get_version() > 1) {
59
1
    return unsupported_version_error("mvhd");
60
1
  }
61
62
47
  if (get_version() == 1) {
63
3
    m_creation_time = range.read64();
64
3
    m_modification_time = range.read64();
65
3
    m_timescale = range.read32();
66
3
    m_duration = range.read64();
67
3
  }
68
44
  else {
69
    // version==0
70
44
    m_creation_time = range.read32();
71
44
    m_modification_time = range.read32();
72
44
    m_timescale = range.read32();
73
44
    m_duration = range.read32();
74
44
  }
75
76
47
  m_rate = range.read32();
77
47
  m_volume = range.read16();
78
47
  range.skip(2);
79
47
  range.skip(8);
80
423
  for (uint32_t& m : m_matrix) {
81
423
    m = range.read32();
82
423
  }
83
329
  for (int i = 0; i < 6; i++) {
84
282
    range.skip(4);
85
282
  }
86
87
47
  m_next_track_ID = range.read32();
88
89
47
  return range.get_error();
90
48
}
91
92
93
void Box_mvhd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
94
0
{
95
0
  FullBox::dump(sstr, indent, full_log);
96
0
  sstr << indent << "creation time:     " << m_creation_time << "\n"
97
0
      << indent << "modification time: " << m_modification_time << "\n"
98
0
      << indent << "timescale: " << m_timescale << "\n"
99
0
      << indent << "duration: " << m_duration << "\n";
100
0
  sstr << indent << "rate: " << get_rate() << "\n"
101
0
      << indent << "volume: " << get_volume() << "\n"
102
0
      << indent << "matrix:\n";
103
0
  for (int y = 0; y < 3; y++) {
104
0
    sstr << indent << "  ";
105
0
    for (int i = 0; i < 3; i++) {
106
0
      sstr << get_matrix_element(i + 3 * y) << " ";
107
0
    }
108
0
    sstr << "\n";
109
0
  }
110
0
  sstr << indent << "next_track_ID: " << m_next_track_ID << "\n";
111
112
0
  return;
113
0
}
114
115
116
void Box_mvhd::derive_box_version()
117
0
{
118
0
  if (m_creation_time > 0xFFFFFFFF ||
119
0
      m_modification_time > 0xFFFFFFFF ||
120
0
      m_timescale > 0xFFFFFFFF ||
121
0
      m_duration > 0xFFFFFFFF) {
122
0
    set_version(1);
123
0
  }
124
0
  else {
125
0
    set_version(0);
126
0
  }
127
0
}
128
129
130
Error Box_mvhd::write(StreamWriter& writer) const
131
0
{
132
0
  size_t box_start = reserve_box_header_space(writer);
133
134
0
  if (get_version() == 1) {
135
0
    writer.write64(m_creation_time);
136
0
    writer.write64(m_modification_time);
137
0
    writer.write32(m_timescale);
138
0
    writer.write64(m_duration);
139
0
  }
140
0
  else {
141
    // version==0
142
0
    writer.write32(static_cast<uint32_t>(m_creation_time));
143
0
    writer.write32(static_cast<uint32_t>(m_modification_time));
144
0
    writer.write32(static_cast<uint32_t>(m_timescale));
145
0
    writer.write32(static_cast<uint32_t>(m_duration));
146
0
  }
147
148
0
  writer.write32(m_rate);
149
0
  writer.write16(m_volume);
150
0
  writer.write16(0);
151
0
  writer.write64(0);
152
0
  for (uint32_t m : m_matrix) {
153
0
    writer.write32(m);
154
0
  }
155
0
  for (int i = 0; i < 6; i++) {
156
0
    writer.write32(0);
157
0
  }
158
159
0
  writer.write32(m_next_track_ID);
160
161
0
  prepend_header(writer, box_start);
162
163
0
  return Error::Ok;
164
0
}
165
166
167
double Box_tkhd::get_matrix_element(int idx) const
168
0
{
169
0
  if (idx == 8) {
170
0
    return 1.0;
171
0
  }
172
173
0
  return m_matrix[idx] / double(0x10000);
174
0
}
175
176
177
Error Box_tkhd::parse(BitstreamRange& range, const heif_security_limits* limits)
178
19
{
179
19
  parse_full_box_header(range);
180
181
19
  if (get_version() > 1) {
182
1
    return unsupported_version_error("tkhd");
183
1
  }
184
185
18
  if (get_version() == 1) {
186
2
    m_creation_time = range.read64();
187
2
    m_modification_time = range.read64();
188
2
    m_track_id = range.read32();
189
2
    range.skip(4);
190
2
    m_duration = range.read64();
191
2
  }
192
16
  else {
193
    // version==0
194
16
    m_creation_time = range.read32();
195
16
    m_modification_time = range.read32();
196
16
    m_track_id = range.read32();
197
16
    range.skip(4);
198
16
    m_duration = range.read32();
199
16
  }
200
201
18
  range.skip(8);
202
18
  m_layer = range.read16();
203
18
  m_alternate_group = range.read16();
204
18
  m_volume = range.read16();
205
18
  range.skip(2);
206
162
  for (uint32_t& m : m_matrix) {
207
162
    m = range.read32();
208
162
  }
209
210
18
  m_width = range.read32();
211
18
  m_height = range.read32();
212
213
18
  return range.get_error();
214
19
}
215
216
217
void Box_tkhd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
218
0
{
219
0
  FullBox::dump(sstr, indent, full_log);
220
0
  sstr << indent << "track enabled: " << ((get_flags() & Track_enabled) ? "yes" : "no") << "\n"
221
0
       << indent << "track in movie: " << ((get_flags() & Track_in_movie) ? "yes" : "no") << "\n"
222
0
       << indent << "track in preview: " << ((get_flags() & Track_in_preview) ? "yes" : "no") << "\n"
223
0
       << indent << "track size is aspect ratio: " << ((get_flags() & Track_size_is_aspect_ratio) ? "yes" : "no") << "\n";
224
0
  sstr << indent << "creation time:     " << m_creation_time << "\n"
225
0
      << indent << "modification time: " << m_modification_time << "\n"
226
0
      << indent << "track ID: " << m_track_id << "\n"
227
0
      << indent << "duration: " << m_duration << "\n";
228
0
  sstr << indent << "layer: " << m_layer << "\n"
229
0
      << indent << "alternate_group: " << m_alternate_group << "\n"
230
0
      << indent << "volume: " << get_volume() << "\n"
231
0
      << indent << "matrix:\n";
232
0
  for (int y = 0; y < 3; y++) {
233
0
    sstr << indent << "  ";
234
0
    for (int i = 0; i < 3; i++) {
235
0
      sstr << get_matrix_element(i + 3 * y) << " ";
236
0
    }
237
0
    sstr << "\n";
238
0
  }
239
240
0
  sstr << indent << "width: " << get_width() << "\n"
241
0
      << indent << "height: " << get_height() << "\n";
242
243
0
  return;
244
0
}
245
246
247
void Box_tkhd::derive_box_version()
248
0
{
249
0
  if (m_creation_time > 0xFFFFFFFF ||
250
0
      m_modification_time > 0xFFFFFFFF ||
251
0
      m_duration > 0xFFFFFFFF) {
252
0
    set_version(1);
253
0
  }
254
0
  else {
255
0
    set_version(0);
256
0
  }
257
0
}
258
259
260
Error Box_tkhd::write(StreamWriter& writer) const
261
0
{
262
0
  size_t box_start = reserve_box_header_space(writer);
263
264
0
  if (get_version() == 1) {
265
0
    writer.write64(m_creation_time);
266
0
    writer.write64(m_modification_time);
267
0
    writer.write32(m_track_id);
268
0
    writer.write32(0);
269
0
    writer.write64(m_duration);
270
0
  }
271
0
  else {
272
    // version==0
273
0
    writer.write32(static_cast<uint32_t>(m_creation_time));
274
0
    writer.write32(static_cast<uint32_t>(m_modification_time));
275
0
    writer.write32(m_track_id);
276
0
    writer.write32(0);
277
0
    writer.write32(static_cast<uint32_t>(m_duration));
278
0
  }
279
280
0
  writer.write64(0);
281
0
  writer.write16(m_layer);
282
0
  writer.write16(m_alternate_group);
283
0
  writer.write16(m_volume);
284
0
  writer.write16(0);
285
0
  for (uint32_t m : m_matrix) {
286
0
    writer.write32(m);
287
0
  }
288
289
0
  writer.write32(m_width);
290
0
  writer.write32(m_height);
291
292
0
  prepend_header(writer, box_start);
293
294
0
  return Error::Ok;
295
0
}
296
297
298
Error Box_mdhd::parse(BitstreamRange& range, const heif_security_limits* limits)
299
11
{
300
11
  parse_full_box_header(range);
301
302
11
  if (get_version() > 1) {
303
1
    return unsupported_version_error("mdhd");
304
1
  }
305
306
10
  if (get_version() == 1) {
307
8
    m_creation_time = range.read64();
308
8
    m_modification_time = range.read64();
309
8
    m_timescale = range.read32();
310
8
    m_duration = range.read64();
311
8
  }
312
2
  else {
313
    // version==0
314
2
    m_creation_time = range.read32();
315
2
    m_modification_time = range.read32();
316
2
    m_timescale = range.read32();
317
2
    m_duration = range.read32();
318
2
  }
319
320
10
  uint16_t language_packed = range.read16();
321
10
  m_language[0] = ((language_packed >> 10) & 0x1F) + 0x60;
322
10
  m_language[1] = ((language_packed >> 5) & 0x1F) + 0x60;
323
10
  m_language[2] = ((language_packed >> 0) & 0x1F) + 0x60;
324
10
  m_language[3] = 0;
325
326
10
  range.skip(2);
327
328
10
  return range.get_error();
329
11
}
330
331
332
void Box_mdhd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
333
0
{
334
0
  FullBox::dump(sstr, indent, full_log);
335
0
  sstr << indent << "creation time:     " << m_creation_time << "\n"
336
0
      << indent << "modification time: " << m_modification_time << "\n"
337
0
      << indent << "timescale: " << m_timescale << "\n"
338
0
      << indent << "duration: " << m_duration << "\n";
339
0
  sstr << indent << "language: " << m_language << "\n";
340
341
0
  return;
342
0
}
343
344
345
void Box_mdhd::derive_box_version()
346
0
{
347
0
  if (m_creation_time > 0xFFFFFFFF ||
348
0
      m_modification_time > 0xFFFFFFFF ||
349
0
      m_duration > 0xFFFFFFFF) {
350
0
    set_version(1);
351
0
  }
352
0
  else {
353
0
    set_version(0);
354
0
  }
355
0
}
356
357
358
Error Box_mdhd::write(StreamWriter& writer) const
359
0
{
360
0
  size_t box_start = reserve_box_header_space(writer);
361
362
0
  if (get_version() == 1) {
363
0
    writer.write64(m_creation_time);
364
0
    writer.write64(m_modification_time);
365
0
    writer.write32(m_timescale);
366
0
    writer.write64(m_duration);
367
0
  }
368
0
  else {
369
    // version==0
370
0
    writer.write32(static_cast<uint32_t>(m_creation_time));
371
0
    writer.write32(static_cast<uint32_t>(m_modification_time));
372
0
    writer.write32(m_timescale);
373
0
    writer.write32(static_cast<uint32_t>(m_duration));
374
0
  }
375
376
0
  auto language_packed = static_cast<uint16_t>((((m_language[0] - 0x60) & 0x1F) << 10) |
377
0
                                               (((m_language[1] - 0x60) & 0x1F) << 5) |
378
0
                                               (((m_language[2] - 0x60) & 0x1F) << 0));
379
0
  writer.write16(language_packed);
380
0
  writer.write16(0);
381
382
0
  prepend_header(writer, box_start);
383
384
0
  return Error::Ok;
385
0
}
386
387
388
389
Error Box_vmhd::parse(BitstreamRange& range, const heif_security_limits* limits)
390
152
{
391
152
  parse_full_box_header(range);
392
393
152
  if (get_version() > 0) {
394
1
    return unsupported_version_error("vmhd");
395
1
  }
396
397
151
  m_graphics_mode = range.read16();
398
453
  for (uint16_t& c : m_op_color) {
399
453
    c = range.read16();
400
453
  }
401
402
151
  return range.get_error();
403
152
}
404
405
406
void Box_vmhd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
407
0
{
408
0
  FullBox::dump(sstr, indent, full_log);
409
0
  sstr << indent << "graphics mode: " << m_graphics_mode;
410
0
  if (m_graphics_mode == 0) {
411
0
    sstr << " (copy)";
412
0
  }
413
0
  sstr << "\n"
414
0
       << indent << "op color: " << m_op_color[0] << "; " << m_op_color[1] << "; " << m_op_color[2] << "\n";
415
416
0
  return;
417
0
}
418
419
420
Error Box_vmhd::write(StreamWriter& writer) const
421
0
{
422
0
  size_t box_start = reserve_box_header_space(writer);
423
424
0
  writer.write16(m_graphics_mode);
425
0
  for (uint16_t c : m_op_color) {
426
0
    writer.write16(c);
427
0
  }
428
429
0
  prepend_header(writer, box_start);
430
431
0
  return Error::Ok;
432
0
}
433
434
435
Error Box_nmhd::parse(BitstreamRange& range, const heif_security_limits* limits)
436
11
{
437
11
  parse_full_box_header(range);
438
439
11
  if (get_version() > 0) {
440
1
    return unsupported_version_error("nmhd");
441
1
  }
442
443
10
  return range.get_error();
444
11
}
445
446
447
void Box_nmhd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
448
0
{
449
0
  FullBox::dump(sstr, indent, full_log);
450
451
0
  return;
452
0
}
453
454
455
Error Box_nmhd::write(StreamWriter& writer) const
456
0
{
457
0
  size_t box_start = reserve_box_header_space(writer);
458
459
0
  prepend_header(writer, box_start);
460
461
0
  return Error::Ok;
462
0
}
463
464
465
Error Box_stsd::parse(BitstreamRange& range, const heif_security_limits* limits)
466
31
{
467
31
  parse_full_box_header(range);
468
469
31
  if (get_version() > 0) {
470
1
    return unsupported_version_error("stsd");
471
1
  }
472
473
30
  uint32_t entry_count = range.read32();
474
475
30
  if (limits->max_sample_description_box_entries &&
476
30
      entry_count > limits->max_sample_description_box_entries) {
477
6
    std::stringstream sstr;
478
6
    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample description items exceeds the security limit of "
479
6
         << limits->max_sample_description_box_entries << " items";
480
481
6
    return {heif_error_Memory_allocation_error,
482
6
            heif_suberror_Security_limit_exceeded,
483
6
            sstr.str()};
484
485
6
  }
486
487
42
  for (uint32_t i = 0; i < entry_count; i++) {
488
26
    std::shared_ptr<Box> entrybox;
489
26
    Error err = Box::read(range, &entrybox, limits);
490
26
    if (err) {
491
8
      return err;
492
8
    }
493
494
#if 0
495
    auto visualSampleEntry_box = std::dynamic_pointer_cast<Box_VisualSampleEntry>(entrybox);
496
    if (!visualSampleEntry_box) {
497
      return Error{heif_error_Invalid_input,
498
                   heif_suberror_Unspecified,
499
                   "Invalid or unknown VisualSampleEntry in stsd box."};
500
    }
501
#endif
502
503
18
    m_sample_entries.push_back(entrybox);
504
18
  }
505
506
16
  return range.get_error();
507
24
}
508
509
510
void Box_stsd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
511
0
{
512
0
  FullBox::dump(sstr, indent, full_log);
513
0
  for (size_t i = 0; i < m_sample_entries.size(); i++) {
514
0
    sstr << indent << "[" << i << "]\n";
515
0
    indent++;
516
0
    m_sample_entries[i]->dump(sstr, indent, full_log);
517
0
    indent--;
518
0
  }
519
520
0
  return;
521
0
}
522
523
524
Error Box_stsd::write(StreamWriter& writer) const
525
0
{
526
0
  size_t box_start = reserve_box_header_space(writer);
527
528
0
  writer.write32(static_cast<uint32_t>(m_sample_entries.size()));
529
0
  for (const auto& sample : m_sample_entries) {
530
0
    sample->write(writer);
531
0
  }
532
533
0
  prepend_header(writer, box_start);
534
535
0
  return Error::Ok;
536
0
}
537
538
539
Error Box_stts::parse(BitstreamRange& range, const heif_security_limits* limits)
540
40
{
541
40
  parse_full_box_header(range);
542
543
40
  if (get_version() > 0) {
544
1
    return unsupported_version_error("stts");
545
1
  }
546
547
39
  uint32_t entry_count = range.read32();
548
549
39
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
550
6
    return {
551
6
      heif_error_Memory_allocation_error,
552
6
      heif_suberror_Security_limit_exceeded,
553
6
      "Security limit for maximum number of sequence frames exceeded"
554
6
    };
555
6
  }
556
557
33
  if (auto err = m_memory_handle.alloc(entry_count, sizeof(TimeToSample),
558
33
                                       limits, "the 'stts' table")) {
559
0
    return err;
560
0
  }
561
562
33
  m_entries.resize(entry_count);
563
564
385
  for (uint32_t i = 0; i < entry_count; i++) {
565
364
    if (range.eof()) {
566
12
      std::stringstream sstr;
567
12
      sstr << "stts box should contain " << entry_count << " entries, but box only contained "
568
12
          << i << " entries";
569
570
12
      return {
571
12
        heif_error_Invalid_input,
572
12
        heif_suberror_End_of_data,
573
12
        sstr.str()
574
12
      };
575
12
    }
576
577
352
    TimeToSample entry{};
578
352
    entry.sample_count = range.read32();
579
352
    entry.sample_delta = range.read32();
580
352
    m_entries[i] = entry;
581
352
  }
582
583
  // The run-lengths describe the samples of the track, so their sum is the track's
584
  // sample count: a 32-bit quantity that 'stsz' caps at max_sequence_frames. Nothing
585
  // above bounds the sum, so an oversized table would wrap get_number_of_samples()
586
  // and could thereby slip past the consistency check against 'stsz'.
587
21
  {
588
21
    uint64_t total_samples = 0;
589
229
    for (const auto& entry : m_entries) {
590
229
      total_samples += entry.sample_count;
591
229
    }
592
593
21
    uint64_t max_samples = (limits->max_sequence_frames > 0
594
21
                            ? limits->max_sequence_frames
595
21
                            : uint64_t{0xFFFFFFFF});
596
597
21
    if (total_samples > max_samples) {
598
13
      return {
599
13
        heif_error_Memory_allocation_error,
600
13
        heif_suberror_Security_limit_exceeded,
601
13
        "Security limit for maximum number of sequence frames exceeded"
602
13
      };
603
13
    }
604
21
  }
605
606
  // Precompute the prefix sum of sample_count for O(log entries) lookups in
607
  // get_sample_duration(). Safe as uint32 because the total was just checked to
608
  // be <= max_samples <= 0xFFFFFFFF.
609
8
  {
610
8
    uint32_t running = 0;
611
11
    for (auto& entry : m_entries) {
612
11
      running += entry.sample_count;
613
11
      entry.cumulative_sample_count = running;
614
11
    }
615
8
  }
616
617
8
  return range.get_error();
618
21
}
619
620
621
void Box_stts::dump(std::ostream& sstr, Indent& indent, bool full_log) const
622
0
{
623
0
  FullBox::dump(sstr, indent, full_log);
624
0
  for (size_t i = 0; i < m_entries.size(); i++) {
625
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_entries.size())) break;
626
0
    sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", delta=" << m_entries[i].sample_delta << "\n";
627
0
  }
628
629
0
  return;
630
0
}
631
632
633
Error Box_stts::write(StreamWriter& writer) const
634
0
{
635
0
  size_t box_start = reserve_box_header_space(writer);
636
637
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
638
0
  for (const auto& sample : m_entries) {
639
0
    writer.write32(sample.sample_count);
640
0
    writer.write32(sample.sample_delta);
641
0
  }
642
643
0
  prepend_header(writer, box_start);
644
645
0
  return Error::Ok;
646
0
}
647
648
649
uint32_t Box_stts::get_sample_duration(uint32_t sample_idx)
650
0
{
651
  // The entries are contiguous and cumulative_sample_count is the non-decreasing
652
  // index one past the last sample of each entry, so the entry covering sample_idx
653
  // is the first one whose cumulative_sample_count is strictly greater than
654
  // sample_idx. Binary search keeps this O(log entries); a linear scan would make
655
  // the decode/raw output paths O(entries) per sample (GHSA-xw34-mjcp-jqh8, V1).
656
0
  auto it = std::upper_bound(m_entries.begin(), m_entries.end(), sample_idx,
657
0
                             [](uint32_t idx, const TimeToSample& entry) {
658
0
                               return idx < entry.cumulative_sample_count;
659
0
                             });
660
661
0
  if (it == m_entries.end()) {
662
    // sample_idx is not covered by any entry.
663
0
    return 0;
664
0
  }
665
666
0
  return it->sample_delta;
667
0
}
668
669
670
void Box_stts::append_sample_duration(uint32_t duration)
671
0
{
672
0
  if (m_entries.empty() || m_entries.back().sample_delta != duration) {
673
0
    TimeToSample entry{};
674
0
    entry.sample_delta = duration;
675
0
    entry.sample_count = 1;
676
0
    entry.cumulative_sample_count = (m_entries.empty() ? 0 : m_entries.back().cumulative_sample_count) + 1;
677
0
    m_entries.push_back(entry);
678
0
    return;
679
0
  }
680
681
0
  m_entries.back().sample_count++;
682
0
  m_entries.back().cumulative_sample_count++;
683
0
}
684
685
686
uint64_t Box_stts::get_total_duration(bool include_last_frame_duration)
687
0
{
688
0
  uint64_t total = 0;
689
690
0
  for (const auto& entry : m_entries) {
691
0
    total += entry.sample_count * uint64_t(entry.sample_delta);
692
0
  }
693
694
0
  if (!include_last_frame_duration && !m_entries.empty()) {
695
0
    total -= m_entries.back().sample_delta;
696
0
  }
697
698
0
  return total;
699
0
}
700
701
702
uint32_t Box_stts::get_number_of_samples() const
703
0
{
704
  // The number of samples in a track is a 32-bit quantity (ISO/IEC 14496-12
705
  // 8.7.3: the 'stsz' sample_count "gives the number of samples in the track"),
706
  // and parse() rejects tables whose run-lengths sum beyond that. Accumulate in
707
  // 64 bit anyway so that a box built by the encoder cannot wrap silently.
708
0
  uint64_t total = 0;
709
0
  for (const auto& entry : m_entries) {
710
0
    total += entry.sample_count;
711
0
  }
712
713
0
  assert(total <= 0xFFFFFFFF);
714
0
  return static_cast<uint32_t>(total);
715
0
}
716
717
718
Error Box_ctts::parse(BitstreamRange& range, const heif_security_limits* limits)
719
45
{
720
45
  parse_full_box_header(range);
721
722
45
  uint8_t version = get_version();
723
724
45
  if (version > 1) {
725
1
    return unsupported_version_error("ctts");
726
1
  }
727
728
44
  uint32_t entry_count = range.read32();
729
730
44
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
731
5
    return {
732
5
      heif_error_Memory_allocation_error,
733
5
      heif_suberror_Security_limit_exceeded,
734
5
      "Security limit for maximum number of sequence frames exceeded"
735
5
    };
736
5
  }
737
738
39
  if (auto err = m_memory_handle.alloc(entry_count, sizeof(OffsetToSample),
739
39
                                       limits, "the 'ctts' table")) {
740
0
    return err;
741
0
  }
742
743
39
  m_entries.resize(entry_count);
744
745
649
  for (uint32_t i = 0; i < entry_count; i++) {
746
628
    if (range.eof()) {
747
18
      std::stringstream sstr;
748
18
      sstr << "ctts box should contain " << entry_count << " entries, but box only contained "
749
18
          << i << " entries";
750
751
18
      return {
752
18
        heif_error_Invalid_input,
753
18
        heif_suberror_End_of_data,
754
18
        sstr.str()
755
18
      };
756
18
    }
757
758
610
    OffsetToSample entry{};
759
610
    entry.sample_count = range.read32();
760
610
    if (version == 0) {
761
520
      uint32_t offset = range.read32();
762
#if 0
763
      // TODO: I disabled this because I found several files that seem to
764
      //       have wrong data. Since we are not using the 'ctts' data anyway,
765
      //       let's not care about it now.
766
767
      if (offset > INT32_MAX) {
768
        return {
769
          heif_error_Unsupported_feature,
770
          heif_suberror_Unsupported_parameter,
771
          "We don't support offsets > 0x7fff in 'ctts' box."
772
        };
773
      }
774
#endif
775
776
520
      entry.sample_offset = static_cast<int32_t>(offset);
777
520
    }
778
90
    else if (version == 1) {
779
90
      entry.sample_offset = range.read32s();
780
90
    }
781
0
    else {
782
0
      assert(false);
783
0
    }
784
785
610
    m_entries[i] = entry;
786
610
  }
787
788
  // The run-lengths describe the samples of the track, so their sum is the track's
789
  // sample count: a 32-bit quantity that 'stsz' caps at max_sequence_frames. Nothing
790
  // above bounds the sum, so an oversized table would wrap get_number_of_samples()
791
  // and could thereby slip past the consistency check against 'stsz'.
792
21
  {
793
21
    uint64_t total_samples = 0;
794
156
    for (const auto& entry : m_entries) {
795
156
      total_samples += entry.sample_count;
796
156
    }
797
798
21
    uint64_t max_samples = (limits->max_sequence_frames > 0
799
21
                            ? limits->max_sequence_frames
800
21
                            : uint64_t{0xFFFFFFFF});
801
802
21
    if (total_samples > max_samples) {
803
12
      return {
804
12
        heif_error_Memory_allocation_error,
805
12
        heif_suberror_Security_limit_exceeded,
806
12
        "Security limit for maximum number of sequence frames exceeded"
807
12
      };
808
12
    }
809
21
  }
810
811
9
  return range.get_error();
812
21
}
813
814
815
void Box_ctts::dump(std::ostream& sstr, Indent& indent, bool full_log) const
816
0
{
817
0
  FullBox::dump(sstr, indent, full_log);
818
0
  for (size_t i = 0; i < m_entries.size(); i++) {
819
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_entries.size())) break;
820
0
    sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", offset=" << m_entries[i].sample_offset << "\n";
821
0
  }
822
823
0
  return;
824
0
}
825
826
827
int32_t Box_ctts::compute_min_offset() const
828
0
{
829
0
  int32_t min_offset = INT32_MAX;
830
0
  for (const auto& entry : m_entries) {
831
0
    min_offset = std::min(min_offset, entry.sample_offset);
832
0
  }
833
834
0
  return min_offset;
835
0
}
836
837
838
uint32_t Box_ctts::get_number_of_samples() const
839
0
{
840
  // The number of samples in a track is a 32-bit quantity (ISO/IEC 14496-12
841
  // 8.7.3: the 'stsz' sample_count "gives the number of samples in the track"),
842
  // and parse() rejects tables whose run-lengths sum beyond that. Accumulate in
843
  // 64 bit anyway so that a box built by the encoder cannot wrap silently.
844
0
  uint64_t total = 0;
845
0
  for (const auto& entry : m_entries) {
846
0
    total += entry.sample_count;
847
0
  }
848
849
0
  assert(total <= 0xFFFFFFFF);
850
0
  return static_cast<uint32_t>(total);
851
0
}
852
853
854
Error Box_ctts::write(StreamWriter& writer) const
855
0
{
856
0
  size_t box_start = reserve_box_header_space(writer);
857
858
0
  int32_t min_offset;
859
860
0
  if (get_version() == 0) {
861
    // shift such that all offsets are >= 0
862
0
    min_offset = compute_min_offset();
863
0
  }
864
0
  else {
865
    // do not modify offsets
866
0
    min_offset = 0;
867
0
  }
868
869
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
870
0
  for (const auto& sample : m_entries) {
871
0
    writer.write32(sample.sample_count);
872
0
    writer.write32s(sample.sample_offset - min_offset);
873
0
  }
874
875
0
  prepend_header(writer, box_start);
876
877
0
  return Error::Ok;
878
0
}
879
880
881
int32_t Box_ctts::get_sample_offset(uint32_t sample_idx)
882
0
{
883
0
  for (const auto& entry : m_entries) {
884
0
    if (sample_idx < entry.sample_count) {
885
0
      return entry.sample_offset;
886
0
    }
887
0
    sample_idx -= entry.sample_count;
888
0
  }
889
890
0
  return 0;
891
0
}
892
893
894
void Box_ctts::append_sample_offset(int32_t offset)
895
0
{
896
0
  if (m_entries.empty() || m_entries.back().sample_offset != offset) {
897
0
    OffsetToSample entry{};
898
0
    entry.sample_offset = offset;
899
0
    entry.sample_count = 1;
900
0
    m_entries.push_back(entry);
901
0
    return;
902
0
  }
903
904
0
  m_entries.back().sample_count++;
905
0
}
906
907
908
bool Box_ctts::is_constant_offset() const
909
0
{
910
0
  return m_entries.empty() || m_entries.size() == 1;
911
0
}
912
913
void Box_ctts::derive_box_version()
914
0
{
915
0
  set_version(1);
916
0
}
917
918
919
Error Box_stsc::parse(BitstreamRange& range, const heif_security_limits* limits)
920
48
{
921
48
  parse_full_box_header(range);
922
923
48
  if (get_version() > 0) {
924
2
    return unsupported_version_error("stsc");
925
2
  }
926
927
46
  uint32_t entry_count = range.read32();
928
46
  if (entry_count == 0) {
929
1
    return {
930
1
      heif_error_Invalid_input,
931
1
      heif_suberror_Unspecified,
932
1
      "'stsc' box with zero entries."};
933
1
  }
934
935
  // Note: test against maximum number of frames (upper limit) since we have no limit on maximum number of chunks
936
45
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
937
8
    return {
938
8
      heif_error_Invalid_input,
939
8
      heif_suberror_Unspecified,
940
8
      "Number of chunks in `stsc` box exceeds security limits of maximum number of frames."};
941
8
  }
942
943
944
37
  if (auto err = m_memory_handle.alloc(entry_count, sizeof(SampleToChunk),
945
37
                                       limits, "the 'stsc' table")) {
946
0
    return err;
947
0
  }
948
949
37
  m_entries.resize(entry_count);
950
951
59
  for (uint32_t i = 0; i < entry_count; i++) {
952
56
    SampleToChunk entry{};
953
56
    entry.first_chunk = range.read32();
954
56
    entry.samples_per_chunk = range.read32();
955
56
    entry.sample_description_index = range.read32();
956
957
56
    if (entry.samples_per_chunk == 0) {
958
4
      return {
959
4
        heif_error_Invalid_input,
960
4
        heif_suberror_Unspecified,
961
4
        "'stsc' box with zero samples per chunk entry."};
962
4
    }
963
964
52
    if (entry.sample_description_index == 0) {
965
1
      return {
966
1
      heif_error_Invalid_input,
967
1
      heif_suberror_Unspecified,
968
1
      "'sample_description_index' in 'stsc' must not be 0."};
969
1
    }
970
971
51
    if (limits->max_sequence_frames > 0 && entry.samples_per_chunk > limits->max_sequence_frames) {
972
20
      return {
973
20
        heif_error_Invalid_input,
974
20
        heif_suberror_Unspecified,
975
20
        "Number of chunk samples in `stsc` box exceeds security limits of maximum number of frames."};
976
20
    }
977
978
    // Each entry starts a run of chunks, so decreasing values have no meaning (ISO/IEC 14496-12, 8.7.4).
979
    // get_chunk() relies on this order for its binary search. Equal values (empty runs) are tolerated.
980
31
    if (i > 0 && entry.first_chunk < m_entries[i - 1].first_chunk) {
981
9
      return {
982
9
        heif_error_Invalid_input,
983
9
        heif_suberror_Unspecified,
984
9
        "'first_chunk' values in 'stsc' box must not decrease."};
985
9
    }
986
987
22
    m_entries[i] = entry;
988
22
  }
989
990
3
  return range.get_error();
991
37
}
992
993
994
void Box_stsc::dump(std::ostream& sstr, Indent& indent, bool full_log) const
995
0
{
996
0
  FullBox::dump(sstr, indent, full_log);
997
0
  for (size_t i = 0; i < m_entries.size(); i++) {
998
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_entries.size())) break;
999
0
    sstr << indent << "[" << i << "]\n"
1000
0
        << indent << "  first chunk: " << m_entries[i].first_chunk << "\n"
1001
0
        << indent << "  samples per chunk: " << m_entries[i].samples_per_chunk << "\n"
1002
0
        << indent << "  sample description index: " << m_entries[i].sample_description_index << "\n";
1003
0
  }
1004
1005
0
  return;
1006
0
}
1007
1008
1009
Error Box_stsc::write(StreamWriter& writer) const
1010
0
{
1011
0
  size_t box_start = reserve_box_header_space(writer);
1012
1013
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
1014
0
  for (const auto& sample : m_entries) {
1015
0
    writer.write32(sample.first_chunk);
1016
0
    writer.write32(sample.samples_per_chunk);
1017
0
    writer.write32(sample.sample_description_index);
1018
0
  }
1019
1020
0
  prepend_header(writer, box_start);
1021
1022
0
  return Error::Ok;
1023
0
}
1024
1025
1026
const Box_stsc::SampleToChunk* Box_stsc::get_chunk(uint32_t idx) const
1027
0
{
1028
0
  assert(idx>=1);
1029
1030
  // The entries are ordered by first_chunk (checked in parse()). The entry covering chunk 'idx'
1031
  // is the last one that does not start after it. A linear scan would make Track::load(), which
1032
  // looks up every chunk, quadratic in the file size.
1033
0
  auto it = std::upper_bound(m_entries.begin(), m_entries.end(), idx,
1034
0
                             [](uint32_t chunk_idx, const SampleToChunk& entry) {
1035
0
                               return chunk_idx < entry.first_chunk;
1036
0
                             });
1037
1038
0
  if (it == m_entries.begin()) {
1039
0
    return nullptr;
1040
0
  }
1041
1042
0
  return &*(it - 1);
1043
0
}
1044
1045
1046
void Box_stsc::add_chunk(uint32_t description_index)
1047
0
{
1048
0
  SampleToChunk entry{};
1049
0
  entry.first_chunk = static_cast<uint32_t>(m_entries.size() + 1); // each entry describes one chunk
1050
0
  entry.samples_per_chunk = 0;
1051
0
  entry.sample_description_index = description_index;
1052
0
  m_entries.push_back(entry);
1053
0
}
1054
1055
1056
void Box_stsc::increase_samples_in_chunk(uint32_t nFrames)
1057
0
{
1058
0
  assert(!m_entries.empty());
1059
1060
0
  m_entries.back().samples_per_chunk += nFrames;
1061
0
}
1062
1063
1064
Error Box_stco::parse(BitstreamRange& range, const heif_security_limits* limits)
1065
44
{
1066
44
  parse_full_box_header(range);
1067
1068
44
  if (get_version() > 0) {
1069
1
    return unsupported_version_error("stco");
1070
1
  }
1071
1072
43
  uint32_t entry_count = range.read32();
1073
1074
  // Note: test against maximum number of frames (upper limit) since we have no limit on maximum number of chunks
1075
43
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
1076
3
    return {
1077
3
      heif_error_Invalid_input,
1078
3
      heif_suberror_Unspecified,
1079
3
      "Number of chunks in 'stco' box exceeds security limits of maximum number of frames."
1080
3
    };
1081
3
  }
1082
1083
  // check required memory
1084
1085
40
  uint64_t mem_size = static_cast<uint64_t>(entry_count) * sizeof(uint32_t);
1086
40
  if (auto err = m_memory_handle.alloc(mem_size,
1087
40
                                       limits, "the 'stco' table")) {
1088
0
    return err;
1089
0
  }
1090
1091
591
  for (uint32_t i = 0; i < entry_count; i++) {
1092
566
    m_offsets.push_back(range.read32());
1093
1094
566
    if (range.error()) {
1095
15
      return range.get_error();
1096
15
    }
1097
566
  }
1098
1099
25
  return range.get_error();
1100
40
}
1101
1102
1103
void Box_stco::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1104
0
{
1105
0
  FullBox::dump(sstr, indent, full_log);
1106
0
  for (size_t i = 0; i < m_offsets.size(); i++) {
1107
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_offsets.size())) break;
1108
0
    sstr << indent << "[" << i << "] : 0x" << std::hex << m_offsets[i] << std::dec << "\n";
1109
0
  }
1110
1111
0
  return;
1112
0
}
1113
1114
1115
Error Box_stco::write(StreamWriter& writer) const
1116
0
{
1117
0
  size_t box_start = reserve_box_header_space(writer);
1118
1119
0
  writer.write32(static_cast<uint32_t>(m_offsets.size()));
1120
1121
0
  m_offset_start_pos = writer.get_position();
1122
1123
0
  for (uint32_t offset : m_offsets) {
1124
0
    writer.write32(offset);
1125
0
  }
1126
1127
0
  prepend_header(writer, box_start);
1128
1129
0
  return Error::Ok;
1130
0
}
1131
1132
1133
void Box_stco::patch_file_pointers(StreamWriter& writer, size_t offset)
1134
0
{
1135
0
  size_t oldPosition = writer.get_position();
1136
1137
0
  writer.set_position(m_offset_start_pos);
1138
1139
0
  for (uint32_t chunk_offset : m_offsets) {
1140
0
    if (chunk_offset + offset > std::numeric_limits<uint32_t>::max()) {
1141
0
      writer.write32(0); // TODO: error
1142
0
    }
1143
0
    else {
1144
0
      writer.write32(static_cast<uint32_t>(chunk_offset + offset));
1145
0
    }
1146
0
  }
1147
1148
0
  writer.set_position(oldPosition);
1149
0
}
1150
1151
1152
1153
Error Box_stsz::parse(BitstreamRange& range, const heif_security_limits* limits)
1154
26
{
1155
26
  parse_full_box_header(range);
1156
1157
26
  if (get_version() > 0) {
1158
1
    return unsupported_version_error("stsz");
1159
1
  }
1160
1161
25
  m_fixed_sample_size = range.read32();
1162
25
  m_sample_count = range.read32();
1163
1164
25
  if (limits->max_sequence_frames > 0 && m_sample_count > limits->max_sequence_frames) {
1165
1
    return {
1166
1
      heif_error_Memory_allocation_error,
1167
1
      heif_suberror_Security_limit_exceeded,
1168
1
      "Security limit for maximum number of sequence frames exceeded"
1169
1
    };
1170
1
  }
1171
1172
24
  if (m_fixed_sample_size == 0) {
1173
    // check required memory
1174
1175
15
    if (auto err = m_memory_handle.alloc(m_sample_count, sizeof(uint32_t),
1176
15
                                         limits, "the 'stsz' table")) {
1177
0
      return err;
1178
0
    }
1179
1180
419
    for (uint32_t i = 0; i < m_sample_count; i++) {
1181
414
      if (range.eof()) {
1182
4
        std::stringstream sstr;
1183
4
        sstr << "stsz box should contain " << m_sample_count << " entries, but box only contained "
1184
4
            << i << " entries";
1185
1186
4
        return {
1187
4
          heif_error_Invalid_input,
1188
4
          heif_suberror_End_of_data,
1189
4
          sstr.str()
1190
4
        };
1191
4
      }
1192
1193
410
      m_sample_sizes.push_back(range.read32());
1194
1195
410
      if (range.error()) {
1196
6
        return range.get_error();
1197
6
      }
1198
410
    }
1199
15
  }
1200
1201
14
  return range.get_error();
1202
24
}
1203
1204
1205
void Box_stsz::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1206
0
{
1207
0
  FullBox::dump(sstr, indent, full_log);
1208
0
  sstr << indent << "sample count: " << m_sample_count << "\n";
1209
0
  if (m_fixed_sample_size == 0) {
1210
0
    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
1211
0
      if (dump_reached_entry_limit(sstr, indent, full_log, i, m_sample_sizes.size())) break;
1212
0
      sstr << indent << "[" << i << "] : " << m_sample_sizes[i] << "\n";
1213
0
    }
1214
0
  }
1215
0
  else {
1216
0
    sstr << indent << "fixed sample size: " << m_fixed_sample_size << "\n";
1217
0
  }
1218
1219
0
  return;
1220
0
}
1221
1222
1223
Error Box_stsz::write(StreamWriter& writer) const
1224
0
{
1225
0
  size_t box_start = reserve_box_header_space(writer);
1226
1227
0
  writer.write32(m_fixed_sample_size);
1228
0
  writer.write32(m_sample_count);
1229
0
  if (m_fixed_sample_size == 0) {
1230
0
    assert(m_sample_count == m_sample_sizes.size());
1231
1232
0
    for (uint32_t size : m_sample_sizes) {
1233
0
      writer.write32(size);
1234
0
    }
1235
0
  }
1236
1237
0
  prepend_header(writer, box_start);
1238
1239
0
  return Error::Ok;
1240
0
}
1241
1242
1243
void Box_stsz::append_sample_size(uint32_t size)
1244
0
{
1245
0
  if (m_sample_count == 0 && size != 0) {
1246
0
    m_fixed_sample_size = size;
1247
0
    m_sample_count = 1;
1248
0
    return;
1249
0
  }
1250
1251
0
  if (m_fixed_sample_size == size && size != 0) {
1252
0
    m_sample_count++;
1253
0
    return;
1254
0
  }
1255
1256
0
  if (m_fixed_sample_size != 0) {
1257
0
    for (uint32_t i = 0; i < m_sample_count; i++) {
1258
0
      m_sample_sizes.push_back(m_fixed_sample_size);
1259
0
    }
1260
1261
0
    m_fixed_sample_size = 0;
1262
0
  }
1263
1264
0
  m_sample_sizes.push_back(size);
1265
0
  m_sample_count++;
1266
1267
0
  assert(m_sample_count == m_sample_sizes.size());
1268
0
}
1269
1270
1271
Error Box_stss::parse(BitstreamRange& range, const heif_security_limits* limits)
1272
24
{
1273
24
  parse_full_box_header(range);
1274
1275
24
  if (get_version() > 0) {
1276
1
    return unsupported_version_error("stss");
1277
1
  }
1278
1279
23
  uint32_t sample_count = range.read32();
1280
1281
  // check required memory
1282
1283
23
  if (auto err = m_memory_handle.alloc(sample_count, sizeof(uint32_t),
1284
23
                                       limits, "the 'stss' table")) {
1285
1
    return err;
1286
1
  }
1287
1288
321
  for (uint32_t i = 0; i < sample_count; i++) {
1289
306
    m_sync_samples.push_back(range.read32());
1290
1291
306
    if (range.error()) {
1292
7
      return range.get_error();
1293
7
    }
1294
306
  }
1295
1296
15
  return range.get_error();
1297
22
}
1298
1299
1300
void Box_stss::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1301
0
{
1302
0
  FullBox::dump(sstr, indent, full_log);
1303
0
  for (size_t i = 0; i < m_sync_samples.size(); i++) {
1304
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_sync_samples.size())) break;
1305
0
    sstr << indent << "[" << i << "] : " << m_sync_samples[i] << "\n";
1306
0
  }
1307
1308
0
  return;
1309
0
}
1310
1311
1312
void Box_stss::set_total_number_of_samples(uint32_t num_samples)
1313
0
{
1314
0
  m_all_samples_are_sync_samples = (m_sync_samples.size() == num_samples);
1315
0
}
1316
1317
1318
Error Box_stss::write(StreamWriter& writer) const
1319
0
{
1320
  // If we don't need this box, skip it.
1321
0
  if (m_all_samples_are_sync_samples) {
1322
0
    return Error::Ok;
1323
0
  }
1324
1325
0
  size_t box_start = reserve_box_header_space(writer);
1326
1327
0
  writer.write32(static_cast<uint32_t>(m_sync_samples.size()));
1328
0
  for (uint32_t sample : m_sync_samples) {
1329
0
    writer.write32(sample);
1330
0
  }
1331
1332
0
  prepend_header(writer, box_start);
1333
1334
0
  return Error::Ok;
1335
0
}
1336
1337
1338
Error VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1339
84
{
1340
84
  (void)limits;
1341
1342
84
  range.skip(6);
1343
84
  data_reference_index = range.read16();
1344
1345
84
  pre_defined = range.read16();
1346
84
  range.skip(2);
1347
252
  for (uint32_t& p : pre_defined2) {
1348
252
    p = range.read32();
1349
252
  }
1350
84
  width = range.read16();
1351
84
  height = range.read16();
1352
84
  horizresolution = range.read32();
1353
84
  vertresolution = range.read32();
1354
84
  range.skip(4);
1355
84
  frame_count = range.read16();
1356
84
  compressorname = range.read_fixed_string(32);
1357
84
  depth = range.read16();
1358
84
  pre_defined3 = range.read16s();
1359
1360
  // other boxes from derived specifications
1361
  //std::shared_ptr<Box_clap> clap; // optional // TODO
1362
  //std::shared_ptr<Box_pixi> pixi; // optional // TODO
1363
1364
84
  return Error::Ok;
1365
84
}
1366
1367
1368
Error VisualSampleEntry::write(StreamWriter& writer) const
1369
0
{
1370
0
  writer.write32(0);
1371
0
  writer.write16(0);
1372
0
  writer.write16(data_reference_index);
1373
1374
0
  writer.write16(pre_defined);
1375
0
  writer.write16(0);
1376
0
  for (uint32_t p : pre_defined2) {
1377
0
    writer.write32(p);
1378
0
  }
1379
1380
0
  writer.write16(width);
1381
0
  writer.write16(height);
1382
0
  writer.write32(horizresolution);
1383
0
  writer.write32(vertresolution);
1384
0
  writer.write32(0);
1385
0
  writer.write16(frame_count);
1386
0
  writer.write_fixed_string(compressorname, 32);
1387
0
  writer.write16(depth);
1388
0
  writer.write16(pre_defined3);
1389
1390
0
  return Error::Ok;
1391
0
}
1392
1393
1394
void VisualSampleEntry::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1395
0
{
1396
0
  sstr << indent << "data reference index: " << data_reference_index << "\n"
1397
0
       << indent << "width: " << width << "\n"
1398
0
       << indent << "height: " << height << "\n"
1399
0
       << indent << "horiz. resolution: " << get_horizontal_resolution() << "\n"
1400
0
       << indent << "vert. resolution: " << get_vertical_resolution() << "\n"
1401
0
       << indent << "frame count: " << frame_count << "\n"
1402
0
       << indent << "compressorname: " << compressorname << "\n"
1403
0
       << indent << "depth: " << depth << "\n";
1404
1405
0
  return;
1406
0
}
1407
1408
1409
Error Box_URIMetaSampleEntry::write(StreamWriter& writer) const
1410
0
{
1411
0
  size_t box_start = reserve_box_header_space(writer);
1412
1413
0
  writer.write32(0);
1414
0
  writer.write16(0);
1415
0
  writer.write16(data_reference_index);
1416
1417
0
  write_children(writer);
1418
1419
0
  prepend_header(writer, box_start);
1420
1421
0
  return Error::Ok;
1422
0
}
1423
1424
1425
void Box_URIMetaSampleEntry::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1426
0
{
1427
0
  Box::dump(sstr, indent, full_log);
1428
0
  sstr << indent << "data reference index: " << data_reference_index << "\n";
1429
0
  dump_children(sstr, indent, full_log);
1430
0
  return;
1431
0
}
1432
1433
1434
Error Box_URIMetaSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1435
6
{
1436
6
  range.skip(6);
1437
6
  data_reference_index = range.read16();
1438
1439
6
  Error err = read_children(range, READ_CHILDREN_ALL, limits);
1440
6
  if (err) {
1441
3
    return err;
1442
3
  }
1443
1444
3
  return Error::Ok;
1445
6
}
1446
1447
1448
Error Box_uri::parse(BitstreamRange& range, const heif_security_limits* limits)
1449
20
{
1450
20
  parse_full_box_header(range);
1451
1452
20
  if (get_version() > 0) {
1453
1
    return unsupported_version_error("uri ");
1454
1
  }
1455
1456
19
  m_uri = range.read_string();
1457
1458
19
  return range.get_error();
1459
20
}
1460
1461
1462
void Box_uri::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1463
0
{
1464
0
  FullBox::dump(sstr, indent, full_log);
1465
0
  sstr << indent << "uri: " << m_uri << "\n";
1466
1467
0
  return;
1468
0
}
1469
1470
1471
Error Box_uri::write(StreamWriter& writer) const
1472
0
{
1473
0
  size_t box_start = reserve_box_header_space(writer);
1474
1475
0
  writer.write(m_uri);
1476
1477
0
  prepend_header(writer, box_start);
1478
1479
0
  return Error::Ok;
1480
0
}
1481
1482
1483
1484
Error Box_ccst::parse(BitstreamRange& range, const heif_security_limits* limits)
1485
11
{
1486
11
  parse_full_box_header(range);
1487
1488
11
  if (get_version() > 0) {
1489
1
    return unsupported_version_error("ccst");
1490
1
  }
1491
1492
10
  uint32_t bits = range.read32();
1493
1494
10
  auto& constraints = m_codingConstraints;
1495
1496
10
  constraints.all_ref_pics_intra = (bits & 0x80000000) != 0;
1497
10
  constraints.intra_pred_used = (bits & 0x40000000) != 0;
1498
10
  constraints.max_ref_per_pic = (bits >> 26) & 0x0F;
1499
1500
10
  return range.get_error();
1501
11
}
1502
1503
1504
void Box_ccst::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1505
0
{
1506
0
  const auto& constraints = m_codingConstraints;
1507
1508
0
  FullBox::dump(sstr, indent, full_log);
1509
0
  sstr << indent << "all ref pics intra: " << std::boolalpha <<constraints.all_ref_pics_intra << "\n"
1510
0
       << indent << "intra pred used: " << constraints.intra_pred_used << "\n"
1511
0
       << indent << "max ref per pic: " << ((int) constraints.max_ref_per_pic) << "\n";
1512
1513
0
  return;
1514
0
}
1515
1516
1517
Error Box_ccst::write(StreamWriter& writer) const
1518
0
{
1519
0
  const auto& constraints = m_codingConstraints;
1520
1521
0
  size_t box_start = reserve_box_header_space(writer);
1522
1523
0
  uint32_t bits = 0;
1524
1525
0
  if (constraints.all_ref_pics_intra) {
1526
0
    bits |= 0x80000000;
1527
0
  }
1528
1529
0
  if (constraints.intra_pred_used) {
1530
0
    bits |= 0x40000000;
1531
0
  }
1532
1533
0
  bits |= constraints.max_ref_per_pic << 26;
1534
1535
0
  writer.write32(bits);
1536
1537
0
  prepend_header(writer, box_start);
1538
1539
0
  return Error::Ok;
1540
0
}
1541
1542
1543
Error Box_auxi::parse(BitstreamRange& range, const heif_security_limits* limits)
1544
20
{
1545
20
  parse_full_box_header(range);
1546
1547
20
  if (get_version() > 0) {
1548
1
    return unsupported_version_error("auxi");
1549
1
  }
1550
1551
19
  m_aux_track_type = range.read_string();
1552
1553
19
  return range.get_error();
1554
20
}
1555
1556
1557
void Box_auxi::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1558
0
{
1559
0
  FullBox::dump(sstr, indent, full_log);
1560
0
  sstr << indent << "aux track info type: " << m_aux_track_type << "\n";
1561
1562
0
  return;
1563
0
}
1564
1565
1566
Error Box_auxi::write(StreamWriter& writer) const
1567
0
{
1568
0
  size_t box_start = reserve_box_header_space(writer);
1569
1570
0
  writer.write(m_aux_track_type);
1571
1572
0
  prepend_header(writer, box_start);
1573
1574
0
  return Error::Ok;
1575
0
}
1576
1577
1578
Error Box_VisualSampleEntry::write(StreamWriter& writer) const
1579
0
{
1580
0
  size_t box_start = reserve_box_header_space(writer);
1581
1582
0
  Error err = get_VisualSampleEntry_const().write(writer);
1583
0
  if (err) {
1584
0
    return err;
1585
0
  }
1586
1587
0
  write_children(writer);
1588
1589
0
  prepend_header(writer, box_start);
1590
1591
0
  return Error::Ok;
1592
0
}
1593
1594
1595
void Box_VisualSampleEntry::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1596
0
{
1597
0
  Box::dump(sstr, indent, full_log);
1598
0
  m_visualSampleEntry.dump(sstr, indent, full_log);
1599
0
  dump_children(sstr, indent, full_log);
1600
0
  return;
1601
0
}
1602
1603
1604
Error Box_VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1605
84
{
1606
84
  auto err = m_visualSampleEntry.parse(range, limits);
1607
84
  if (err) {
1608
0
    return err;
1609
0
  }
1610
1611
84
  err = read_children(range, READ_CHILDREN_ALL, limits);
1612
84
  if (err) {
1613
56
    return err;
1614
56
  }
1615
1616
28
  return Error::Ok;
1617
84
}
1618
1619
1620
void Box_sbgp::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1621
0
{
1622
0
  FullBox::dump(sstr, indent, full_log);
1623
0
  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
1624
1625
0
  if (m_grouping_type_parameter) {
1626
0
    sstr << indent << "grouping_type_parameter: " << *m_grouping_type_parameter << "\n";
1627
0
  }
1628
1629
0
  uint32_t total_samples = 0;
1630
0
  for (size_t i = 0; i < m_entries.size(); i++) {
1631
0
    if (full_log || i < MAX_DUMP_ENTRIES) {
1632
0
      sstr << indent << "[" << std::setw(2) << (i + 1) << "] : " << std::setw(3) << m_entries[i].sample_count << "x " << m_entries[i].group_description_index << "\n";
1633
0
    }
1634
0
    total_samples += m_entries[i].sample_count;
1635
0
  }
1636
0
  if (!full_log && m_entries.size() > MAX_DUMP_ENTRIES) {
1637
0
    sstr << indent << "... (" << (m_entries.size() - MAX_DUMP_ENTRIES) << " more)\n";
1638
0
  }
1639
0
  sstr << indent << "total samples: " << total_samples << "\n";
1640
1641
0
  return;
1642
0
}
1643
1644
1645
void Box_sbgp::derive_box_version()
1646
0
{
1647
0
  if (m_grouping_type_parameter) {
1648
0
    set_version(1);
1649
0
  }
1650
0
  else {
1651
0
    set_version(0);
1652
0
  }
1653
0
}
1654
1655
1656
Error Box_sbgp::write(StreamWriter& writer) const
1657
0
{
1658
0
  size_t box_start = reserve_box_header_space(writer);
1659
1660
0
  writer.write32(m_grouping_type);
1661
0
  if (m_grouping_type_parameter) {
1662
0
    writer.write32(*m_grouping_type_parameter);
1663
0
  }
1664
1665
0
  if (m_entries.size() > 0xFFFFFFFF) {
1666
0
    return {heif_error_Usage_error,
1667
0
            heif_suberror_Invalid_parameter_value,
1668
0
            "Too many sbgp entries."};
1669
0
  }
1670
1671
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
1672
0
  for (const auto& entry : m_entries) {
1673
0
    writer.write32(entry.sample_count);
1674
0
    writer.write32(entry.group_description_index);
1675
0
  }
1676
1677
0
  prepend_header(writer, box_start);
1678
1679
0
  return Error::Ok;
1680
0
}
1681
1682
1683
Error Box_sbgp::parse(BitstreamRange& range, const heif_security_limits* limits)
1684
55
{
1685
55
  parse_full_box_header(range);
1686
1687
55
  if (get_version() > 1) {
1688
1
    return unsupported_version_error("sbgp");
1689
1
  }
1690
1691
54
  m_grouping_type = range.read32();
1692
1693
54
  if (get_version() == 1) {
1694
5
    m_grouping_type_parameter = range.read32();
1695
5
  }
1696
1697
54
  uint32_t count = range.read32();
1698
54
  if (auto err = m_memory_handle.alloc(count, sizeof(Entry),
1699
54
                                       limits, "the 'sample to group' table")) {
1700
7
    return err;
1701
7
  }
1702
1703
546
  for (uint32_t i = 0; i < count; i++) {
1704
510
    Entry e{};
1705
510
    e.sample_count = range.read32();
1706
510
    e.group_description_index = range.read32();
1707
510
    m_entries.push_back(e);
1708
510
    if (range.error()) {
1709
11
      return range.get_error();
1710
11
    }
1711
510
  }
1712
1713
36
  return range.get_error();
1714
47
}
1715
1716
1717
std::string SampleGroupEntry_refs::dump() const
1718
0
{
1719
0
  std::stringstream sstr;
1720
0
  if (m_sample_id==0) {
1721
0
    sstr << "0 (non-ref) refs =";
1722
0
  }
1723
0
  else {
1724
0
    sstr << m_sample_id << " refs =";
1725
0
  }
1726
0
  for (uint32_t ref : m_direct_reference_sample_id) {
1727
0
    sstr << ' ' << ref;
1728
0
  }
1729
1730
0
  return sstr.str();
1731
0
}
1732
1733
Error SampleGroupEntry_refs::write(StreamWriter& writer) const
1734
0
{
1735
0
  return {};
1736
0
}
1737
1738
Error SampleGroupEntry_refs::parse(BitstreamRange& range, const heif_security_limits*)
1739
0
{
1740
0
  m_sample_id = range.read32();
1741
0
  uint8_t cnt = range.read8();
1742
0
  for (uint8_t i = 0; i < cnt; i++) {
1743
0
    m_direct_reference_sample_id.push_back(range.read32());
1744
0
  }
1745
1746
0
  return Error::Ok;
1747
0
}
1748
1749
1750
void Box_sgpd::derive_box_version()
1751
0
{
1752
0
  if (m_default_length) {
1753
0
    set_version(1);
1754
0
    assert(!m_default_sample_description_index);
1755
0
    return;
1756
0
  }
1757
1758
0
  if (m_default_sample_description_index) {
1759
0
    set_version(2);
1760
0
    return;
1761
0
  }
1762
1763
0
  set_version(0);
1764
0
}
1765
1766
1767
void Box_sgpd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1768
0
{
1769
0
  FullBox::dump(sstr, indent, full_log);
1770
1771
0
  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
1772
0
  if (m_default_length) {
1773
0
    sstr << indent << "default_length: " << *m_default_length << "\n";
1774
0
  }
1775
0
  if (m_default_sample_description_index) {
1776
0
    sstr << indent << "default_sample_description_index: " << *m_default_sample_description_index << "\n";
1777
0
  }
1778
1779
0
  for (size_t i=0; i<m_entries.size(); i++) {
1780
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_entries.size())) break;
1781
0
    sstr << indent << "[" << (i+1) << "] : ";
1782
0
    if (m_entries[i].sample_group_entry) {
1783
0
      sstr << m_entries[i].sample_group_entry->dump() << "\n";
1784
0
    }
1785
0
    else {
1786
0
      sstr << "empty (description_length=" << m_entries[i].description_length << ")\n";
1787
0
    }
1788
0
  }
1789
1790
0
  return;
1791
0
}
1792
1793
1794
Error Box_sgpd::write(StreamWriter& writer) const
1795
0
{
1796
0
return {};
1797
0
}
1798
1799
1800
Error Box_sgpd::parse(BitstreamRange& range, const heif_security_limits* limits)
1801
2
{
1802
2
  parse_full_box_header(range);
1803
1804
2
  m_grouping_type = range.read32();
1805
1806
  // Readers are expected to ignore sgpd boxes with grouping_types they don't
1807
  // understand. Skip parsing of unknown types to avoid allocating Entry objects
1808
  // for entries whose payload we wouldn't read anyway (and which, with
1809
  // version==1 + default_length!=0 or version>=2, would consume zero bytes per
1810
  // iteration and allow unbounded allocation from a tiny box).
1811
2
  if (m_grouping_type != fourcc("refs")) {
1812
2
    return Error::Ok;
1813
2
  }
1814
1815
0
  if (get_version() == 1) {
1816
0
    m_default_length = range.read32();
1817
0
  }
1818
1819
0
  if (get_version() >= 2) {
1820
0
    m_default_sample_description_index = range.read32();
1821
0
  }
1822
1823
0
  uint32_t entry_count = range.read32();
1824
1825
0
  if (limits->max_sample_group_description_box_entries &&
1826
0
      entry_count > limits->max_sample_group_description_box_entries) {
1827
0
    std::stringstream sstr;
1828
0
    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample group description items exceeds the security limit of "
1829
0
         << limits->max_sample_group_description_box_entries << " items";
1830
1831
0
    return {heif_error_Memory_allocation_error,
1832
0
            heif_suberror_Security_limit_exceeded,
1833
0
            sstr.str()};
1834
1835
0
  }
1836
1837
0
  if (auto err = m_memory_handle.alloc(entry_count, sizeof(Entry),
1838
0
                                       limits, "the 'sgpd' table")) {
1839
0
    return err;
1840
0
  }
1841
1842
0
  for (uint32_t i = 0; i < entry_count; i++) {
1843
0
    Entry entry;
1844
1845
0
    if (get_version() == 1) {
1846
0
      if (*m_default_length == 0) {
1847
0
        entry.description_length = range.read32();
1848
0
      }
1849
0
    }
1850
1851
0
    switch (m_grouping_type) {
1852
0
      case fourcc("refs"): {
1853
0
        entry.sample_group_entry = std::make_shared<SampleGroupEntry_refs>();
1854
0
        Error err = entry.sample_group_entry->parse(range, limits);
1855
0
        if (err) {
1856
0
          return err;
1857
0
        }
1858
1859
0
        break;
1860
0
      }
1861
1862
0
      default:
1863
0
        break;
1864
0
    }
1865
1866
0
    m_entries.emplace_back(std::move(entry));
1867
0
  }
1868
1869
0
  return Error::Ok;
1870
0
}
1871
1872
1873
void Box_btrt::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1874
0
{
1875
0
  FullBox::dump(sstr, indent, full_log);
1876
1877
0
  sstr << indent << "bufferSizeDB: " << m_bufferSizeDB << " bytes\n";
1878
0
  sstr << indent << "max bitrate: " << m_maxBitrate << " bits/sec\n";
1879
0
  sstr << indent << "avg bitrate: " << m_avgBitrate << " bits/sec\n";
1880
1881
0
  return;
1882
0
}
1883
1884
1885
Error Box_btrt::write(StreamWriter& writer) const
1886
0
{
1887
0
  size_t box_start = reserve_box_header_space(writer);
1888
1889
0
  writer.write32(m_bufferSizeDB);
1890
0
  writer.write32(m_maxBitrate);
1891
0
  writer.write32(m_avgBitrate);
1892
1893
0
  prepend_header(writer, box_start);
1894
1895
0
  return Error::Ok;
1896
0
}
1897
1898
1899
Error Box_btrt::parse(BitstreamRange& range, const heif_security_limits*)
1900
20
{
1901
20
  m_bufferSizeDB = range.read32();
1902
20
  m_maxBitrate = range.read32();
1903
20
  m_avgBitrate = range.read32();
1904
1905
20
  return Error::Ok;
1906
20
}
1907
1908
1909
1910
void Box_saiz::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
1911
0
{
1912
0
  m_aux_info_type = aux_info_type;
1913
0
  m_aux_info_type_parameter = aux_info_type_parameter;
1914
1915
0
  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
1916
0
  set_flags(nonnull ? 1 : 0);
1917
0
}
1918
1919
1920
void Box_saiz::add_sample_size(uint8_t s)
1921
0
{
1922
  // --- it is the first sample -> put into default size (except if it is a zero size = no sample aux info)
1923
1924
0
  if (s != 0 && m_num_samples == 0) {
1925
0
    m_default_sample_info_size = s;
1926
0
    m_num_samples = 1;
1927
0
    return;
1928
0
  }
1929
1930
  // --- if it's the default size, just add more to the number of default sizes
1931
1932
0
  if (s != 0 && s == m_default_sample_info_size) {
1933
0
    m_num_samples++;
1934
0
    return;
1935
0
  }
1936
1937
  // --- it is different from the default size -> add the list
1938
1939
  // first copy samples with the default size into the list
1940
1941
0
  if (m_default_sample_info_size != 0) {
1942
0
    for (uint32_t i = 0; i < m_num_samples; i++) {
1943
0
      m_sample_sizes.push_back(m_default_sample_info_size);
1944
0
    }
1945
1946
0
    m_default_sample_info_size = 0;
1947
0
  }
1948
1949
  // add the new sample size
1950
1951
0
  m_num_samples++;
1952
0
  m_sample_sizes.push_back(s);
1953
0
}
1954
1955
1956
uint8_t Box_saiz::get_sample_size(uint32_t idx)
1957
0
{
1958
0
  if (m_default_sample_info_size != 0) {
1959
0
    return m_default_sample_info_size;
1960
0
  }
1961
1962
0
  if (idx >= m_sample_sizes.size()) {
1963
0
    return 0;
1964
0
  }
1965
1966
0
  return m_sample_sizes[idx];
1967
0
}
1968
1969
1970
void Box_saiz::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1971
0
{
1972
0
  FullBox::dump(sstr, indent, full_log);
1973
1974
0
  sstr << indent << "aux_info_type: ";
1975
0
  if (m_aux_info_type == 0) {
1976
0
    sstr << "0\n";
1977
0
  }
1978
0
  else {
1979
0
    sstr << fourcc_to_string(m_aux_info_type) << "\n";
1980
0
  }
1981
1982
0
  sstr << indent << "aux_info_type_parameter: ";
1983
0
  if (m_aux_info_type_parameter == 0) {
1984
0
    sstr << "0\n";
1985
0
  }
1986
0
  else {
1987
0
    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
1988
0
  }
1989
1990
0
  sstr << indent << "default sample size: ";
1991
0
  if (m_default_sample_info_size == 0) {
1992
0
    sstr << "0 (variable)\n";
1993
0
  }
1994
0
  else {
1995
0
    sstr << ((int)m_default_sample_info_size) << "\n";
1996
0
  }
1997
1998
0
  if (m_default_sample_info_size == 0) {
1999
0
    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
2000
0
      if (dump_reached_entry_limit(sstr, indent, full_log, i, m_sample_sizes.size())) break;
2001
0
      sstr << indent << "[" << i << "] : " << ((int) m_sample_sizes[i]) << "\n";
2002
0
    }
2003
0
  }
2004
2005
0
  return;
2006
0
}
2007
2008
2009
Error Box_saiz::write(StreamWriter& writer) const
2010
0
{
2011
0
  size_t box_start = reserve_box_header_space(writer);
2012
2013
0
  if (get_flags() & 1) {
2014
0
    writer.write32(m_aux_info_type);
2015
0
    writer.write32(m_aux_info_type_parameter);
2016
0
  }
2017
2018
0
  writer.write8(m_default_sample_info_size);
2019
2020
0
  if (m_default_sample_info_size == 0) {
2021
0
    assert(m_num_samples == m_sample_sizes.size());
2022
2023
0
    uint32_t num_nonnull_samples = static_cast<uint32_t>(m_sample_sizes.size());
2024
0
    while (num_nonnull_samples > 0 && m_sample_sizes[num_nonnull_samples-1] == 0) {
2025
0
      num_nonnull_samples--;
2026
0
    }
2027
2028
0
    writer.write32(num_nonnull_samples);
2029
2030
0
    for (size_t i = 0; i < num_nonnull_samples; i++) {
2031
0
      writer.write8(m_sample_sizes[i]);
2032
0
    }
2033
0
  }
2034
0
  else {
2035
0
    writer.write32(m_num_samples);
2036
0
  }
2037
2038
0
  prepend_header(writer, box_start);
2039
2040
0
  return Error::Ok;
2041
0
}
2042
2043
2044
Error Box_saiz::parse(BitstreamRange& range, const heif_security_limits* limits)
2045
33
{
2046
33
  parse_full_box_header(range);
2047
2048
33
  if (get_flags() & 1) {
2049
28
    m_aux_info_type = range.read32();
2050
28
    m_aux_info_type_parameter = range.read32();
2051
28
  }
2052
2053
33
  m_default_sample_info_size = range.read8();
2054
33
  m_num_samples = range.read32();
2055
2056
33
  if (limits && limits->max_sequence_frames > 0 && m_num_samples > limits->max_sequence_frames) {
2057
7
    return {
2058
7
      heif_error_Memory_allocation_error,
2059
7
      heif_suberror_Security_limit_exceeded,
2060
7
      "Number of 'saiz' samples exceeds the maximum number of sequence frames."
2061
7
    };
2062
7
  }
2063
2064
26
  if (m_default_sample_info_size == 0) {
2065
    // check required memory
2066
2067
23
    uint64_t mem_size = m_num_samples;
2068
23
    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'sample aux info sizes' (saiz) table")) {
2069
0
      return err;
2070
0
    }
2071
2072
    // read whole table at once
2073
2074
23
    m_sample_sizes.resize(m_num_samples);
2075
23
    range.read(m_sample_sizes.data(), m_num_samples);
2076
23
  }
2077
2078
26
  return range.get_error();
2079
26
}
2080
2081
2082
2083
void Box_saio::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
2084
0
{
2085
0
  m_aux_info_type = aux_info_type;
2086
0
  m_aux_info_type_parameter = aux_info_type_parameter;
2087
2088
0
  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
2089
0
  set_flags(nonnull ? 1 : 0);
2090
0
}
2091
2092
2093
void Box_saio::add_chunk_offset(uint64_t s)
2094
0
{
2095
0
  if (s > 0xFFFFFFFF) {
2096
0
    m_need_64bit = true;
2097
0
    set_version(1);
2098
0
  }
2099
2100
0
  m_chunk_offset.push_back(s);
2101
0
}
2102
2103
2104
uint64_t Box_saio::get_chunk_offset(uint32_t idx) const
2105
0
{
2106
0
  if (idx >= m_chunk_offset.size()) {
2107
0
    return 0;
2108
0
  }
2109
0
  else {
2110
0
    return m_chunk_offset[idx];
2111
0
  }
2112
0
}
2113
2114
2115
void Box_saio::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2116
0
{
2117
0
  FullBox::dump(sstr, indent, full_log);
2118
2119
0
  sstr << indent << "aux_info_type: ";
2120
0
  if (m_aux_info_type == 0) {
2121
0
    sstr << "0\n";
2122
0
  }
2123
0
  else {
2124
0
    sstr << fourcc_to_string(m_aux_info_type) << "\n";
2125
0
  }
2126
2127
0
  sstr << indent << "aux_info_type_parameter: ";
2128
0
  if (m_aux_info_type_parameter == 0) {
2129
0
    sstr << "0\n";
2130
0
  }
2131
0
  else {
2132
0
    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
2133
0
  }
2134
2135
0
  for (size_t i = 0; i < m_chunk_offset.size(); i++) {
2136
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_chunk_offset.size())) break;
2137
0
    sstr << indent << "[" << i << "] : 0x" << std::hex << m_chunk_offset[i] << "\n";
2138
0
  }
2139
2140
0
  return;
2141
0
}
2142
2143
2144
void Box_saio::patch_file_pointers(StreamWriter& writer, size_t offset)
2145
0
{
2146
0
  size_t oldPosition = writer.get_position();
2147
2148
0
  writer.set_position(m_offset_start_pos);
2149
2150
0
  for (uint64_t ptr : m_chunk_offset) {
2151
0
    if (get_version() == 0 && ptr + offset > std::numeric_limits<uint32_t>::max()) {
2152
0
      writer.write32(0); // TODO: error
2153
0
    } else if (get_version() == 0) {
2154
0
      writer.write32(static_cast<uint32_t>(ptr + offset));
2155
0
    } else {
2156
0
      writer.write64(ptr + offset);
2157
0
    }
2158
0
  }
2159
2160
0
  writer.set_position(oldPosition);
2161
0
}
2162
2163
2164
Error Box_saio::write(StreamWriter& writer) const
2165
0
{
2166
0
  size_t box_start = reserve_box_header_space(writer);
2167
2168
0
  if (get_flags() & 1) {
2169
0
    writer.write32(m_aux_info_type);
2170
0
    writer.write32(m_aux_info_type_parameter);
2171
0
  }
2172
2173
0
  if (m_chunk_offset.size() > std::numeric_limits<uint32_t>::max()) {
2174
0
    return Error{heif_error_Unsupported_feature,
2175
0
                 heif_suberror_Unspecified,
2176
0
                 "Maximum number of chunks exceeded"};
2177
0
  }
2178
0
  writer.write32(static_cast<uint32_t>(m_chunk_offset.size()));
2179
2180
0
  m_offset_start_pos = writer.get_position();
2181
2182
0
  for (uint64_t size : m_chunk_offset) {
2183
0
    if (m_need_64bit) {
2184
0
      writer.write64(size);
2185
0
    } else {
2186
0
      writer.write32(static_cast<uint32_t>(size));
2187
0
    }
2188
0
  }
2189
2190
0
  prepend_header(writer, box_start);
2191
2192
0
  return Error::Ok;
2193
0
}
2194
2195
2196
Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
2197
59
{
2198
59
  parse_full_box_header(range);
2199
2200
59
  if (get_flags() & 1) {
2201
10
    m_aux_info_type = range.read32();
2202
10
    m_aux_info_type_parameter = range.read32();
2203
10
  }
2204
2205
59
  uint32_t num_chunks = range.read32();
2206
2207
  // We have no explicit maximum on the number of chunks.
2208
  // Use the maximum number of frames as an upper limit.
2209
59
  if (limits && limits->max_sequence_frames > 0 && num_chunks > limits->max_sequence_frames) {
2210
3
    return {
2211
3
      heif_error_Memory_allocation_error,
2212
3
      heif_suberror_Security_limit_exceeded,
2213
3
      "Number of 'saio' chunks exceeds the maximum number of sequence frames."
2214
3
    };
2215
3
  }
2216
2217
56
  if (auto err = m_memory_handle.alloc(num_chunks, sizeof(uint64_t),
2218
56
                                       limits, "the 'saio' table")) {
2219
0
    return err;
2220
0
  }
2221
2222
56
  m_chunk_offset.resize(num_chunks);
2223
2224
428
  for (uint32_t i = 0; i < num_chunks; i++) {
2225
393
    uint64_t offset;
2226
393
    if (get_version() == 1) {
2227
66
      offset = range.read64();
2228
66
    }
2229
327
    else {
2230
327
      offset = range.read32();
2231
327
    }
2232
2233
393
    m_chunk_offset[i] = offset;
2234
2235
393
    if (range.error()) {
2236
21
      return range.get_error();
2237
21
    }
2238
393
  }
2239
2240
35
  return Error::Ok;
2241
56
}
2242
2243
2244
void Box_sdtp::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2245
0
{
2246
0
  FullBox::dump(sstr, indent, full_log);
2247
2248
0
  assert(m_sample_information.size() <= UINT32_MAX);
2249
2250
0
  for (uint32_t i = 0; i < static_cast<uint32_t>(m_sample_information.size()); i++) {
2251
2252
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_sample_information.size())) break;
2253
2254
0
    const char* spaces = "            ";
2255
0
    int nSpaces = 6;
2256
0
    int k = i;
2257
0
    while (k >= 10 && nSpaces < 12) {
2258
0
      k /= 10;
2259
0
      nSpaces++;
2260
0
    }
2261
2262
0
    spaces = spaces + 12 - nSpaces;
2263
2264
0
    sstr << indent << "[" << i << "] : is_leading=" << (int) get_is_leading(i) << "\n"
2265
0
        << indent << spaces << "depends_on=" << (int) get_depends_on(i) << "\n"
2266
0
        << indent << spaces << "is_depended_on=" << (int) get_is_depended_on(i) << "\n"
2267
0
        << indent << spaces << "has_redundancy=" << (int) get_has_redundancy(i) << "\n";
2268
0
  }
2269
2270
0
  return;
2271
0
}
2272
2273
2274
Error Box_sdtp::parse(BitstreamRange& range, const heif_security_limits* limits)
2275
2
{
2276
2
  parse_full_box_header(range);
2277
2278
  // We have no easy way to get the number of samples from 'saiz' or 'stz2' as specified
2279
  // in the standard. Instead, we read until the end of the box.
2280
2
  size_t nSamples = range.get_remaining_bytes();
2281
2282
2
  if (limits && limits->max_sequence_frames > 0 && nSamples > limits->max_sequence_frames) {
2283
0
    return {
2284
0
      heif_error_Memory_allocation_error,
2285
0
      heif_suberror_Security_limit_exceeded,
2286
0
      "Number of 'sdtp' samples exceeds the maximum number of sequence frames."
2287
0
    };
2288
0
  }
2289
2290
2
  if (auto err = m_memory_handle.alloc(nSamples, limits, "the 'sdtp' table")) {
2291
0
    return err;
2292
0
  }
2293
2294
2
  m_sample_information.resize(nSamples);
2295
2
  range.read(m_sample_information.data(), nSamples);
2296
2297
2
  return range.get_error();
2298
2
}
2299
2300
2301
Error Box_tref::parse(BitstreamRange& range, const heif_security_limits* limits)
2302
208
{
2303
427
  while (!range.eof()) {
2304
311
    BoxHeader header;
2305
311
    Error err = header.parse_header(range);
2306
311
    if (err != Error::Ok) {
2307
5
      return err;
2308
5
    }
2309
2310
306
    if (header.get_box_size() < header.get_header_size()) {
2311
2
      return {heif_error_Invalid_input,
2312
2
              heif_suberror_Unspecified,
2313
2
              "Invalid box size (smaller than header)"};
2314
2
    }
2315
2316
304
    uint64_t dataSize = (header.get_box_size() - header.get_header_size());
2317
2318
304
    if (dataSize % 4 != 0 || dataSize < 4) {
2319
16
      return {heif_error_Invalid_input,
2320
16
              heif_suberror_Unspecified,
2321
16
              "Input file has a 'tref' TrackReferenceTypeBox with invalid size."};
2322
16
    }
2323
2324
288
    uint64_t nRefs = dataSize / 4;
2325
2326
288
    if (limits->max_items && nRefs > limits->max_items) {
2327
59
      std::stringstream sstr;
2328
59
      sstr << "Number of references in tref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references.";
2329
2330
59
      return {heif_error_Invalid_input,
2331
59
              heif_suberror_Security_limit_exceeded,
2332
59
              sstr.str()};
2333
59
    }
2334
2335
229
    Reference ref;
2336
229
    ref.reference_type = header.get_short_type();
2337
2338
3.07k
    for (uint64_t i = 0; i < nRefs; i++) {
2339
2.85k
      if (range.eof()) {
2340
10
        std::stringstream sstr;
2341
10
        sstr << "tref box should contain " << nRefs << " references, but we can only read " << i << " references.";
2342
2343
10
        return {heif_error_Invalid_input,
2344
10
                heif_suberror_End_of_data,
2345
10
                sstr.str()};
2346
10
      }
2347
2348
2.84k
      ref.to_track_id.push_back(static_cast<uint32_t>(range.read32()));
2349
2.84k
    }
2350
2351
219
    m_references.push_back(ref);
2352
219
  }
2353
2354
  // Note: a track ID may appear several times within one reference type box.
2355
  // ISO/IEC 14496-12 does not forbid it and no consumer depends on uniqueness,
2356
  // so a redundant entry is tolerated instead of rejecting the whole file.
2357
2358
116
  return range.get_error();
2359
208
}
2360
2361
2362
Error Box_tref::write(StreamWriter& writer) const
2363
0
{
2364
0
  size_t box_start = reserve_box_header_space(writer);
2365
2366
0
  for (const auto& ref : m_references) {
2367
0
    uint32_t box_size = 8 + uint32_t(ref.to_track_id.size() * 4);
2368
2369
    // we write the BoxHeader ourselves since it is very simple
2370
0
    writer.write32(box_size);
2371
0
    writer.write32(ref.reference_type);
2372
2373
0
    for (uint32_t r : ref.to_track_id) {
2374
0
      writer.write32(r);
2375
0
    }
2376
0
  }
2377
2378
0
  prepend_header(writer, box_start);
2379
2380
0
  return Error::Ok;
2381
0
}
2382
2383
2384
void Box_tref::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2385
0
{
2386
0
  Box::dump(sstr, indent, full_log);
2387
2388
0
  for (const auto& ref : m_references) {
2389
0
    sstr << indent << "reference with type '" << fourcc_to_string(ref.reference_type) << "'"
2390
0
         << " to track IDs: ";
2391
0
    for (uint32_t id : ref.to_track_id) {
2392
0
      sstr << id << " ";
2393
0
    }
2394
0
    sstr << "\n";
2395
0
  }
2396
2397
0
  return;
2398
0
}
2399
2400
2401
std::vector<uint32_t> Box_tref::get_references(uint32_t ref_type) const
2402
0
{
2403
0
  for (const Reference& ref : m_references) {
2404
0
    if (ref.reference_type == ref_type) {
2405
0
      return ref.to_track_id;
2406
0
    }
2407
0
  }
2408
2409
0
  return {};
2410
0
}
2411
2412
2413
size_t Box_tref::get_number_of_references_of_type(uint32_t ref_type) const
2414
0
{
2415
0
  for (const Reference& ref : m_references) {
2416
0
    if (ref.reference_type == ref_type) {
2417
0
      return ref.to_track_id.size();
2418
0
    }
2419
0
  }
2420
2421
0
  return 0;
2422
0
}
2423
2424
2425
std::vector<uint32_t> Box_tref::get_reference_types() const
2426
0
{
2427
0
  std::vector<uint32_t> types;
2428
0
  types.reserve(m_references.size());
2429
0
  for (const auto& ref : m_references) {
2430
0
    types.push_back(ref.reference_type);
2431
0
  }
2432
2433
0
  return types;
2434
0
}
2435
2436
2437
void Box_tref::add_references(uint32_t to_track_id, uint32_t type)
2438
0
{
2439
0
  for (auto& ref : m_references) {
2440
0
    if (ref.reference_type == type) {
2441
0
      ref.to_track_id.push_back(to_track_id);
2442
0
      return;
2443
0
    }
2444
0
  }
2445
2446
0
  Reference ref;
2447
0
  ref.reference_type = type;
2448
0
  ref.to_track_id = {to_track_id};
2449
2450
0
  m_references.push_back(ref);
2451
0
}
2452
2453
2454
Error Box_elst::parse(BitstreamRange& range, const heif_security_limits* limits)
2455
24
{
2456
24
  Error err = parse_full_box_header(range);
2457
24
  if (err != Error::Ok) {
2458
1
    return err;
2459
1
  }
2460
2461
23
  if (get_version() > 1) {
2462
1
    return unsupported_version_error("edts");
2463
1
  }
2464
2465
22
  uint32_t nEntries = range.read32();
2466
22
  m_entries.clear();
2467
2468
551
  for (uint64_t i = 0; i < nEntries; i++) {
2469
549
    if (range.eof()) {
2470
20
      std::stringstream sstr;
2471
20
      sstr << "edts box should contain " << nEntries << " entries, but we can only read " << i << " entries.";
2472
2473
20
      return {heif_error_Invalid_input,
2474
20
              heif_suberror_End_of_data,
2475
20
              sstr.str()};
2476
20
    }
2477
2478
529
    Entry entry{};
2479
529
    if (get_version() == 1) {
2480
224
      entry.segment_duration = range.read64();
2481
224
      entry.media_time = range.read64s();
2482
224
    }
2483
305
    else {
2484
305
      entry.segment_duration = range.read32();
2485
305
      entry.media_time = range.read32s();
2486
305
    }
2487
2488
529
    entry.media_rate_integer = range.read16s();
2489
529
    entry.media_rate_fraction = range.read16s();
2490
2491
529
    m_entries.push_back(entry);
2492
529
  }
2493
2494
2
  return range.get_error();
2495
22
}
2496
2497
2498
Error Box_elst::write(StreamWriter& writer) const
2499
0
{
2500
0
  size_t box_start = reserve_box_header_space(writer);
2501
2502
0
  if (m_entries.size() > std::numeric_limits<uint32_t>::max()) {
2503
0
    return {heif_error_Usage_error,
2504
0
            heif_suberror_Invalid_parameter_value,
2505
0
            "Too many entries in edit list"};
2506
0
  }
2507
2508
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
2509
2510
2511
0
  for (const auto& entry : m_entries) {
2512
0
    if (get_version() == 1) {
2513
0
      writer.write64(entry.segment_duration);
2514
0
      writer.write64s(entry.media_time);
2515
0
    }
2516
0
    else {
2517
      // The cast is valid because we check in derive_box_version() whether everything
2518
      // fits into 32bit. If not, version 1 is used.
2519
2520
0
      writer.write32(static_cast<uint32_t>(entry.segment_duration));
2521
0
      writer.write32s(static_cast<int32_t>(entry.media_time));
2522
0
    }
2523
2524
0
    writer.write16s(entry.media_rate_integer);
2525
0
    writer.write16s(entry.media_rate_fraction);
2526
0
  }
2527
2528
0
  prepend_header(writer, box_start);
2529
2530
0
  return Error::Ok;
2531
0
}
2532
2533
2534
void Box_elst::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2535
0
{
2536
0
  FullBox::dump(sstr, indent, full_log);
2537
2538
0
  sstr << indent << "repeat list: " << ((get_flags() & Flags::Repeat_EditList) ? "yes" : "no") << "\n";
2539
2540
0
  for (size_t i = 0; i < m_entries.size(); i++) {
2541
0
    if (dump_reached_entry_limit(sstr, indent, full_log, i, m_entries.size())) break;
2542
0
    const auto& entry = m_entries[i];
2543
0
    sstr << indent << "segment duration: " << entry.segment_duration << "\n";
2544
0
    sstr << indent << "media time: " << entry.media_time << "\n";
2545
0
    sstr << indent << "media rate integer: " << entry.media_rate_integer << "\n";
2546
0
    sstr << indent << "media rate fraction: " << entry.media_rate_fraction << "\n";
2547
0
  }
2548
2549
0
  return;
2550
0
}
2551
2552
void Box_elst::derive_box_version()
2553
0
{
2554
  // check whether we need 64bit values
2555
2556
0
  bool need_64bit = std::any_of(m_entries.begin(),
2557
0
                                m_entries.end(),
2558
0
                                [](const Entry& entry) {
2559
0
                                  return (entry.segment_duration > std::numeric_limits<uint32_t>::max() ||
2560
0
                                          entry.media_time > std::numeric_limits<int32_t>::max());
2561
0
                                });
2562
2563
0
  if (need_64bit) {
2564
0
    set_version(1);
2565
0
  }
2566
0
  else {
2567
0
    set_version(0);
2568
0
  }
2569
0
}
2570
2571
2572
void Box_elst::enable_repeat_mode(bool enable)
2573
0
{
2574
0
  uint32_t flags = get_flags();
2575
0
  if (enable) {
2576
0
    flags |= Flags::Repeat_EditList;
2577
0
  }
2578
0
  else {
2579
0
    flags &= ~Flags::Repeat_EditList;
2580
0
  }
2581
2582
0
  set_flags(flags);
2583
0
}
2584
2585
2586
void Box_elst::add_entry(const Entry& entry)
2587
0
{
2588
0
  m_entries.push_back(entry);
2589
0
}