Coverage Report

Created: 2026-09-14 07:37

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