Coverage Report

Created: 2026-04-01 07:49

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
21.6k
{
31
21.6k
  return read_children(range, READ_CHILDREN_ALL, limits);
32
21.6k
}
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
924
{
57
924
  parse_full_box_header(range);
58
59
924
  if (get_version() > 1) {
60
8
    return unsupported_version_error("mvhd");
61
8
  }
62
63
916
  if (get_version() == 1) {
64
49
    m_creation_time = range.read64();
65
49
    m_modification_time = range.read64();
66
49
    m_timescale = range.read32();
67
49
    m_duration = range.read64();
68
49
  }
69
867
  else {
70
    // version==0
71
867
    m_creation_time = range.read32();
72
867
    m_modification_time = range.read32();
73
867
    m_timescale = range.read32();
74
867
    m_duration = range.read32();
75
867
  }
76
77
916
  m_rate = range.read32();
78
916
  m_volume = range.read16();
79
916
  range.skip(2);
80
916
  range.skip(8);
81
8.24k
  for (uint32_t& m : m_matrix) {
82
8.24k
    m = range.read32();
83
8.24k
  }
84
6.41k
  for (int i = 0; i < 6; i++) {
85
5.49k
    range.skip(4);
86
5.49k
  }
87
88
916
  m_next_track_ID = range.read32();
89
90
916
  return range.get_error();
91
924
}
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.12k
{
181
1.12k
  parse_full_box_header(range);
182
183
1.12k
  if (get_version() > 1) {
184
8
    return unsupported_version_error("tkhd");
185
8
  }
186
187
1.11k
  if (get_version() == 1) {
188
99
    m_creation_time = range.read64();
189
99
    m_modification_time = range.read64();
190
99
    m_track_id = range.read32();
191
99
    range.skip(4);
192
99
    m_duration = range.read64();
193
99
  }
194
1.01k
  else {
195
    // version==0
196
1.01k
    m_creation_time = range.read32();
197
1.01k
    m_modification_time = range.read32();
198
1.01k
    m_track_id = range.read32();
199
1.01k
    range.skip(4);
200
1.01k
    m_duration = range.read32();
201
1.01k
  }
202
203
1.11k
  range.skip(8);
204
1.11k
  m_layer = range.read16();
205
1.11k
  m_alternate_group = range.read16();
206
1.11k
  m_volume = range.read16();
207
1.11k
  range.skip(2);
208
10.0k
  for (uint32_t& m : m_matrix) {
209
10.0k
    m = range.read32();
210
10.0k
  }
211
212
1.11k
  m_width = range.read32();
213
1.11k
  m_height = range.read32();
214
215
1.11k
  return range.get_error();
216
1.12k
}
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
1.04k
{
303
1.04k
  parse_full_box_header(range);
304
305
1.04k
  if (get_version() > 1) {
306
7
    return unsupported_version_error("mdhd");
307
7
  }
308
309
1.03k
  if (get_version() == 1) {
310
93
    m_creation_time = range.read64();
311
93
    m_modification_time = range.read64();
312
93
    m_timescale = range.read32();
313
93
    m_duration = range.read64();
314
93
  }
315
942
  else {
316
    // version==0
317
942
    m_creation_time = range.read32();
318
942
    m_modification_time = range.read32();
319
942
    m_timescale = range.read32();
320
942
    m_duration = range.read32();
321
942
  }
322
323
1.03k
  uint16_t language_packed = range.read16();
324
1.03k
  m_language[0] = ((language_packed >> 10) & 0x1F) + 0x60;
325
1.03k
  m_language[1] = ((language_packed >> 5) & 0x1F) + 0x60;
326
1.03k
  m_language[2] = ((language_packed >> 0) & 0x1F) + 0x60;
327
1.03k
  m_language[3] = 0;
328
329
1.03k
  range.skip(2);
330
331
1.03k
  return range.get_error();
332
1.04k
}
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
486
{
395
486
  parse_full_box_header(range);
396
397
486
  if (get_version() > 0) {
398
9
    return unsupported_version_error("vmhd");
399
9
  }
400
401
477
  m_graphics_mode = range.read16();
402
1.43k
  for (uint16_t& c : m_op_color) {
403
1.43k
    c = range.read16();
404
1.43k
  }
405
406
477
  return range.get_error();
407
486
}
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
100
{
442
100
  parse_full_box_header(range);
443
444
100
  if (get_version() > 0) {
445
5
    return unsupported_version_error("nmhd");
446
5
  }
447
448
95
  return range.get_error();
449
100
}
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
1.00k
{
473
1.00k
  parse_full_box_header(range);
474
475
1.00k
  if (get_version() > 0) {
476
10
    return unsupported_version_error("stsd");
477
10
  }
478
479
995
  uint32_t entry_count = range.read32();
480
481
995
  if (limits->max_sample_description_box_entries &&
482
995
      entry_count > limits->max_sample_description_box_entries) {
483
53
    std::stringstream sstr;
484
53
    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample description items exceeds the security limit of "
485
53
         << limits->max_sample_description_box_entries << " items";
486
487
53
    return {heif_error_Memory_allocation_error,
488
53
            heif_suberror_Security_limit_exceeded,
489
53
            sstr.str()};
490
491
53
  }
492
493
2.16k
  for (uint32_t i = 0; i < entry_count; i++) {
494
1.33k
    std::shared_ptr<Box> entrybox;
495
1.33k
    Error err = Box::read(range, &entrybox, limits);
496
1.33k
    if (err) {
497
105
      return err;
498
105
    }
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
1.22k
    m_sample_entries.push_back(entrybox);
510
1.22k
  }
511
512
837
  return range.get_error();
513
942
}
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
1.06k
{
548
1.06k
  parse_full_box_header(range);
549
550
1.06k
  if (get_version() > 0) {
551
6
    return unsupported_version_error("stts");
552
6
  }
553
554
1.05k
  uint32_t entry_count = range.read32();
555
556
1.05k
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
557
33
    return {
558
33
      heif_error_Memory_allocation_error,
559
33
      heif_suberror_Security_limit_exceeded,
560
33
      "Security limit for maximum number of sequence frames exceeded"
561
33
    };
562
33
  }
563
564
1.02k
  if (auto err = m_memory_handle.alloc(entry_count * sizeof(TimeToSample),
565
1.02k
                                       limits, "the 'stts' table")) {
566
0
    return err;
567
0
  }
568
569
1.02k
  m_entries.resize(entry_count);
570
571
22.3k
  for (uint32_t i = 0; i < entry_count; i++) {
572
21.4k
    if (range.eof()) {
573
137
      std::stringstream sstr;
574
137
      sstr << "stts box should contain " << entry_count << " entries, but box only contained "
575
137
          << i << " entries";
576
577
137
      return {
578
137
        heif_error_Invalid_input,
579
137
        heif_suberror_End_of_data,
580
137
        sstr.str()
581
137
      };
582
137
    }
583
584
21.2k
    TimeToSample entry{};
585
21.2k
    entry.sample_count = range.read32();
586
21.2k
    entry.sample_delta = range.read32();
587
21.2k
    m_entries[i] = entry;
588
21.2k
  }
589
590
888
  return range.get_error();
591
1.02k
}
592
593
594
std::string Box_stts::dump(Indent& indent) const
595
0
{
596
0
  std::ostringstream sstr;
597
0
  sstr << FullBox::dump(indent);
598
0
  for (size_t i = 0; i < m_entries.size(); i++) {
599
0
    sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", delta=" << m_entries[i].sample_delta << "\n";
600
0
  }
601
602
0
  return sstr.str();
603
0
}
604
605
606
Error Box_stts::write(StreamWriter& writer) const
607
0
{
608
0
  size_t box_start = reserve_box_header_space(writer);
609
610
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
611
0
  for (const auto& sample : m_entries) {
612
0
    writer.write32(sample.sample_count);
613
0
    writer.write32(sample.sample_delta);
614
0
  }
615
616
0
  prepend_header(writer, box_start);
617
618
0
  return Error::Ok;
619
0
}
620
621
622
uint32_t Box_stts::get_sample_duration(uint32_t sample_idx)
623
24.3k
{
624
25.7k
  for (const auto& entry : m_entries) {
625
25.7k
    if (sample_idx < entry.sample_count) {
626
24.3k
      return entry.sample_delta;
627
24.3k
    }
628
1.45k
    sample_idx -= entry.sample_count;
629
1.45k
  }
630
631
0
  return 0;
632
24.3k
}
633
634
635
void Box_stts::append_sample_duration(uint32_t duration)
636
0
{
637
0
  if (m_entries.empty() || m_entries.back().sample_delta != duration) {
638
0
    TimeToSample entry{};
639
0
    entry.sample_delta = duration;
640
0
    entry.sample_count = 1;
641
0
    m_entries.push_back(entry);
642
0
    return;
643
0
  }
644
645
0
  m_entries.back().sample_count++;
646
0
}
647
648
649
uint64_t Box_stts::get_total_duration(bool include_last_frame_duration)
650
0
{
651
0
  uint64_t total = 0;
652
653
0
  for (const auto& entry : m_entries) {
654
0
    total += entry.sample_count * uint64_t(entry.sample_delta);
655
0
  }
656
657
0
  if (!include_last_frame_duration && !m_entries.empty()) {
658
0
    total -= m_entries.back().sample_delta;
659
0
  }
660
661
0
  return total;
662
0
}
663
664
665
uint32_t Box_stts::get_number_of_samples() const
666
190
{
667
190
  uint32_t total = 0;
668
220
  for (const auto& entry : m_entries) {
669
220
    total += entry.sample_count;
670
220
  }
671
672
190
  return total;
673
190
}
674
675
676
Error Box_ctts::parse(BitstreamRange& range, const heif_security_limits* limits)
677
466
{
678
466
  parse_full_box_header(range);
679
680
466
  uint8_t version = get_version();
681
682
466
  if (version > 1) {
683
5
    return unsupported_version_error("ctts");
684
5
  }
685
686
461
  uint32_t entry_count = range.read32();
687
688
461
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
689
21
    return {
690
21
      heif_error_Memory_allocation_error,
691
21
      heif_suberror_Security_limit_exceeded,
692
21
      "Security limit for maximum number of sequence frames exceeded"
693
21
    };
694
21
  }
695
696
440
  if (auto err = m_memory_handle.alloc(entry_count * sizeof(OffsetToSample),
697
440
                                       limits, "the 'ctts' table")) {
698
0
    return err;
699
0
  }
700
701
440
  m_entries.resize(entry_count);
702
703
53.0k
  for (uint32_t i = 0; i < entry_count; i++) {
704
52.7k
    if (range.eof()) {
705
99
      std::stringstream sstr;
706
99
      sstr << "ctts box should contain " << entry_count << " entries, but box only contained "
707
99
          << i << " entries";
708
709
99
      return {
710
99
        heif_error_Invalid_input,
711
99
        heif_suberror_End_of_data,
712
99
        sstr.str()
713
99
      };
714
99
    }
715
716
52.6k
    OffsetToSample entry{};
717
52.6k
    entry.sample_count = range.read32();
718
52.6k
    if (version == 0) {
719
52.1k
      uint32_t offset = range.read32();
720
#if 0
721
      // TODO: I disabled this because I found several files that seem to
722
      //       have wrong data. Since we are not using the 'ctts' data anyway,
723
      //       let's not care about it now.
724
725
      if (offset > INT32_MAX) {
726
        return {
727
          heif_error_Unsupported_feature,
728
          heif_suberror_Unsupported_parameter,
729
          "We don't support offsets > 0x7fff in 'ctts' box."
730
        };
731
      }
732
#endif
733
734
52.1k
      entry.sample_offset = static_cast<int32_t>(offset);
735
52.1k
    }
736
453
    else if (version == 1) {
737
453
      entry.sample_offset = range.read32s();
738
453
    }
739
0
    else {
740
0
      assert(false);
741
0
    }
742
743
52.6k
    m_entries[i] = entry;
744
52.6k
  }
745
746
341
  return range.get_error();
747
440
}
748
749
750
std::string Box_ctts::dump(Indent& indent) const
751
0
{
752
0
  std::ostringstream sstr;
753
0
  sstr << FullBox::dump(indent);
754
0
  for (size_t i = 0; i < m_entries.size(); i++) {
755
0
    sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", offset=" << m_entries[i].sample_offset << "\n";
756
0
  }
757
758
0
  return sstr.str();
759
0
}
760
761
762
int32_t Box_ctts::compute_min_offset() const
763
0
{
764
0
  int32_t min_offset = INT32_MAX;
765
0
  for (const auto& entry : m_entries) {
766
0
    min_offset = std::min(min_offset, entry.sample_offset);
767
0
  }
768
769
0
  return min_offset;
770
0
}
771
772
773
uint32_t Box_ctts::get_number_of_samples() const
774
0
{
775
0
  uint32_t total = 0;
776
0
  for (const auto& entry : m_entries) {
777
0
    total += entry.sample_count;
778
0
  }
779
780
0
  return total;
781
0
}
782
783
784
Error Box_ctts::write(StreamWriter& writer) const
785
0
{
786
0
  size_t box_start = reserve_box_header_space(writer);
787
788
0
  int32_t min_offset;
789
790
0
  if (get_version() == 0) {
791
    // shift such that all offsets are >= 0
792
0
    min_offset = compute_min_offset();
793
0
  }
794
0
  else {
795
    // do not modify offsets
796
0
    min_offset = 0;
797
0
  }
798
799
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
800
0
  for (const auto& sample : m_entries) {
801
0
    writer.write32(sample.sample_count);
802
0
    writer.write32s(sample.sample_offset - min_offset);
803
0
  }
804
805
0
  prepend_header(writer, box_start);
806
807
0
  return Error::Ok;
808
0
}
809
810
811
int32_t Box_ctts::get_sample_offset(uint32_t sample_idx)
812
0
{
813
0
  for (const auto& entry : m_entries) {
814
0
    if (sample_idx < entry.sample_count) {
815
0
      return entry.sample_offset;
816
0
    }
817
0
    sample_idx -= entry.sample_count;
818
0
  }
819
820
0
  return 0;
821
0
}
822
823
824
void Box_ctts::append_sample_offset(int32_t offset)
825
0
{
826
0
  if (m_entries.empty() || m_entries.back().sample_offset != offset) {
827
0
    OffsetToSample entry{};
828
0
    entry.sample_offset = offset;
829
0
    entry.sample_count = 1;
830
0
    m_entries.push_back(entry);
831
0
    return;
832
0
  }
833
834
0
  m_entries.back().sample_count++;
835
0
}
836
837
838
bool Box_ctts::is_constant_offset() const
839
0
{
840
0
  return m_entries.empty() || m_entries.size() == 1;
841
0
}
842
843
void Box_ctts::derive_box_version()
844
0
{
845
0
  set_version(1);
846
0
}
847
848
849
Error Box_stsc::parse(BitstreamRange& range, const heif_security_limits* limits)
850
851
{
851
851
  parse_full_box_header(range);
852
853
851
  if (get_version() > 0) {
854
8
    return unsupported_version_error("stsc");
855
8
  }
856
857
843
  uint32_t entry_count = range.read32();
858
843
  if (entry_count == 0) {
859
33
    return {
860
33
      heif_error_Invalid_input,
861
33
      heif_suberror_Unspecified,
862
33
      "'stsc' box with zero entries."};
863
33
  }
864
865
  // Note: test against maximum number of frames (upper limit) since we have no limit on maximum number of chunks
866
810
  if (limits->max_sequence_frames > 0 && entry_count > limits->max_sequence_frames) {
867
28
    return {
868
28
      heif_error_Invalid_input,
869
28
      heif_suberror_Unspecified,
870
28
      "Number of chunks in `stsc` box exceeds security limits of maximum number of frames."};
871
28
  }
872
873
874
782
  if (auto err = m_memory_handle.alloc(entry_count * sizeof(SampleToChunk),
875
782
                                       limits, "the 'stsc' table")) {
876
0
    return err;
877
0
  }
878
879
782
  m_entries.resize(entry_count);
880
881
3.03k
  for (uint32_t i = 0; i < entry_count; i++) {
882
2.47k
    SampleToChunk entry{};
883
2.47k
    entry.first_chunk = range.read32();
884
2.47k
    entry.samples_per_chunk = range.read32();
885
2.47k
    entry.sample_description_index = range.read32();
886
887
2.47k
    if (entry.samples_per_chunk == 0) {
888
45
      return {
889
45
        heif_error_Invalid_input,
890
45
        heif_suberror_Unspecified,
891
45
        "'stsc' box with zero samples per chunk entry."};
892
45
    }
893
894
2.42k
    if (entry.sample_description_index == 0) {
895
14
      return {
896
14
      heif_error_Invalid_input,
897
14
      heif_suberror_Unspecified,
898
14
      "'sample_description_index' in 'stsc' must not be 0."};
899
14
    }
900
901
2.41k
    if (limits->max_sequence_frames > 0 && entry.samples_per_chunk > limits->max_sequence_frames) {
902
158
      return {
903
158
        heif_error_Invalid_input,
904
158
        heif_suberror_Unspecified,
905
158
        "Number of chunk samples in `stsc` box exceeds security limits of maximum number of frames."};
906
158
    }
907
908
2.25k
    m_entries[i] = entry;
909
2.25k
  }
910
911
565
  return range.get_error();
912
782
}
913
914
915
std::string Box_stsc::dump(Indent& indent) const
916
0
{
917
0
  std::ostringstream sstr;
918
0
  sstr << FullBox::dump(indent);
919
0
  for (size_t i = 0; i < m_entries.size(); i++) {
920
0
    sstr << indent << "[" << i << "]\n"
921
0
        << indent << "  first chunk: " << m_entries[i].first_chunk << "\n"
922
0
        << indent << "  samples per chunk: " << m_entries[i].samples_per_chunk << "\n"
923
0
        << indent << "  sample description index: " << m_entries[i].sample_description_index << "\n";
924
0
  }
925
926
0
  return sstr.str();
927
0
}
928
929
930
Error Box_stsc::write(StreamWriter& writer) const
931
0
{
932
0
  size_t box_start = reserve_box_header_space(writer);
933
934
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
935
0
  for (const auto& sample : m_entries) {
936
0
    writer.write32(sample.first_chunk);
937
0
    writer.write32(sample.samples_per_chunk);
938
0
    writer.write32(sample.sample_description_index);
939
0
  }
940
941
0
  prepend_header(writer, box_start);
942
943
0
  return Error::Ok;
944
0
}
945
946
947
const Box_stsc::SampleToChunk* Box_stsc::get_chunk(uint32_t idx) const
948
7.28k
{
949
7.28k
  assert(idx>=1);
950
7.64k
  for (size_t i = 0 ; i < m_entries.size();i++) {
951
7.64k
    if (idx >= m_entries[i].first_chunk && (i==m_entries.size()-1 || idx < m_entries[i+1].first_chunk)) {
952
7.28k
      return &m_entries[i];
953
7.28k
    }
954
7.64k
  }
955
956
5
  return nullptr;
957
7.28k
}
958
959
960
void Box_stsc::add_chunk(uint32_t description_index)
961
0
{
962
0
  SampleToChunk entry{};
963
0
  entry.first_chunk = 1; // TODO
964
0
  entry.samples_per_chunk = 0;
965
0
  entry.sample_description_index = description_index;
966
0
  m_entries.push_back(entry);
967
0
}
968
969
970
void Box_stsc::increase_samples_in_chunk(uint32_t nFrames)
971
0
{
972
0
  assert(!m_entries.empty());
973
974
0
  m_entries.back().samples_per_chunk += nFrames;
975
0
}
976
977
978
Error Box_stco::parse(BitstreamRange& range, const heif_security_limits* limits)
979
1.60k
{
980
1.60k
  parse_full_box_header(range);
981
982
1.60k
  if (get_version() > 0) {
983
9
    return unsupported_version_error("stco");
984
9
  }
985
986
1.59k
  uint32_t entry_count = range.read32();
987
988
  // check required memory
989
990
1.59k
  uint64_t mem_size = entry_count * sizeof(uint32_t);
991
1.59k
  if (auto err = m_memory_handle.alloc(mem_size,
992
1.59k
                                       limits, "the 'stco' table")) {
993
17
    return err;
994
17
  }
995
996
32.5k
  for (uint32_t i = 0; i < entry_count; i++) {
997
31.0k
    m_offsets.push_back(range.read32());
998
999
31.0k
    if (range.error()) {
1000
56
      return range.get_error();
1001
56
    }
1002
31.0k
  }
1003
1004
1.52k
  return range.get_error();
1005
1.57k
}
1006
1007
1008
std::string Box_stco::dump(Indent& indent) const
1009
0
{
1010
0
  std::ostringstream sstr;
1011
0
  sstr << FullBox::dump(indent);
1012
0
  for (size_t i = 0; i < m_offsets.size(); i++) {
1013
0
    sstr << indent << "[" << i << "] : 0x" << std::hex << m_offsets[i] << std::dec << "\n";
1014
0
  }
1015
1016
0
  return sstr.str();
1017
0
}
1018
1019
1020
Error Box_stco::write(StreamWriter& writer) const
1021
0
{
1022
0
  size_t box_start = reserve_box_header_space(writer);
1023
1024
0
  writer.write32(static_cast<uint32_t>(m_offsets.size()));
1025
1026
0
  m_offset_start_pos = writer.get_position();
1027
1028
0
  for (uint32_t offset : m_offsets) {
1029
0
    writer.write32(offset);
1030
0
  }
1031
1032
0
  prepend_header(writer, box_start);
1033
1034
0
  return Error::Ok;
1035
0
}
1036
1037
1038
void Box_stco::patch_file_pointers(StreamWriter& writer, size_t offset)
1039
0
{
1040
0
  size_t oldPosition = writer.get_position();
1041
1042
0
  writer.set_position(m_offset_start_pos);
1043
1044
0
  for (uint32_t chunk_offset : m_offsets) {
1045
0
    if (chunk_offset + offset > std::numeric_limits<uint32_t>::max()) {
1046
0
      writer.write32(0); // TODO: error
1047
0
    }
1048
0
    else {
1049
0
      writer.write32(static_cast<uint32_t>(chunk_offset + offset));
1050
0
    }
1051
0
  }
1052
1053
0
  writer.set_position(oldPosition);
1054
0
}
1055
1056
1057
1058
Error Box_stsz::parse(BitstreamRange& range, const heif_security_limits* limits)
1059
844
{
1060
844
  parse_full_box_header(range);
1061
1062
844
  if (get_version() > 0) {
1063
7
    return unsupported_version_error("stsz");
1064
7
  }
1065
1066
837
  m_fixed_sample_size = range.read32();
1067
837
  m_sample_count = range.read32();
1068
1069
837
  if (m_fixed_sample_size == 0) {
1070
    // check required memory
1071
1072
597
    if (limits->max_sequence_frames > 0 && m_sample_count > limits->max_sequence_frames) {
1073
30
      return {
1074
30
        heif_error_Memory_allocation_error,
1075
30
        heif_suberror_Security_limit_exceeded,
1076
30
        "Security limit for maximum number of sequence frames exceeded"
1077
30
      };
1078
30
    }
1079
1080
567
    uint64_t mem_size = m_sample_count * sizeof(uint32_t);
1081
567
    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stsz' table")) {
1082
0
      return err;
1083
0
    }
1084
1085
85.5k
    for (uint32_t i = 0; i < m_sample_count; i++) {
1086
85.1k
      if (range.eof()) {
1087
34
        std::stringstream sstr;
1088
34
        sstr << "stsz box should contain " << m_sample_count << " entries, but box only contained "
1089
34
            << i << " entries";
1090
1091
34
        return {
1092
34
          heif_error_Invalid_input,
1093
34
          heif_suberror_End_of_data,
1094
34
          sstr.str()
1095
34
        };
1096
34
      }
1097
1098
85.0k
      m_sample_sizes.push_back(range.read32());
1099
1100
85.0k
      if (range.error()) {
1101
40
        return range.get_error();
1102
40
      }
1103
85.0k
    }
1104
567
  }
1105
1106
733
  return range.get_error();
1107
837
}
1108
1109
1110
std::string Box_stsz::dump(Indent& indent) const
1111
0
{
1112
0
  std::ostringstream sstr;
1113
0
  sstr << FullBox::dump(indent);
1114
0
  sstr << indent << "sample count: " << m_sample_count << "\n";
1115
0
  if (m_fixed_sample_size == 0) {
1116
0
    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
1117
0
      sstr << indent << "[" << i << "] : " << m_sample_sizes[i] << "\n";
1118
0
    }
1119
0
  }
1120
0
  else {
1121
0
    sstr << indent << "fixed sample size: " << m_fixed_sample_size << "\n";
1122
0
  }
1123
1124
0
  return sstr.str();
1125
0
}
1126
1127
1128
Error Box_stsz::write(StreamWriter& writer) const
1129
0
{
1130
0
  size_t box_start = reserve_box_header_space(writer);
1131
1132
0
  writer.write32(m_fixed_sample_size);
1133
0
  writer.write32(m_sample_count);
1134
0
  if (m_fixed_sample_size == 0) {
1135
0
    assert(m_sample_count == m_sample_sizes.size());
1136
1137
0
    for (uint32_t size : m_sample_sizes) {
1138
0
      writer.write32(size);
1139
0
    }
1140
0
  }
1141
1142
0
  prepend_header(writer, box_start);
1143
1144
0
  return Error::Ok;
1145
0
}
1146
1147
1148
void Box_stsz::append_sample_size(uint32_t size)
1149
0
{
1150
0
  if (m_sample_count == 0 && size != 0) {
1151
0
    m_fixed_sample_size = size;
1152
0
    m_sample_count = 1;
1153
0
    return;
1154
0
  }
1155
1156
0
  if (m_fixed_sample_size == size && size != 0) {
1157
0
    m_sample_count++;
1158
0
    return;
1159
0
  }
1160
1161
0
  if (m_fixed_sample_size != 0) {
1162
0
    for (uint32_t i = 0; i < m_sample_count; i++) {
1163
0
      m_sample_sizes.push_back(m_fixed_sample_size);
1164
0
    }
1165
1166
0
    m_fixed_sample_size = 0;
1167
0
  }
1168
1169
0
  m_sample_sizes.push_back(size);
1170
0
  m_sample_count++;
1171
1172
0
  assert(m_sample_count == m_sample_sizes.size());
1173
0
}
1174
1175
1176
Error Box_stss::parse(BitstreamRange& range, const heif_security_limits* limits)
1177
718
{
1178
718
  parse_full_box_header(range);
1179
1180
718
  if (get_version() > 0) {
1181
10
    return unsupported_version_error("stss");
1182
10
  }
1183
1184
708
  uint32_t sample_count = range.read32();
1185
1186
  // check required memory
1187
1188
708
  uint64_t mem_size = sample_count * sizeof(uint32_t);
1189
708
  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stss' table")) {
1190
14
    return err;
1191
14
  }
1192
1193
4.56k
  for (uint32_t i = 0; i < sample_count; i++) {
1194
3.93k
    m_sync_samples.push_back(range.read32());
1195
1196
3.93k
    if (range.error()) {
1197
64
      return range.get_error();
1198
64
    }
1199
3.93k
  }
1200
1201
630
  return range.get_error();
1202
694
}
1203
1204
1205
std::string Box_stss::dump(Indent& indent) const
1206
0
{
1207
0
  std::ostringstream sstr;
1208
0
  sstr << FullBox::dump(indent);
1209
0
  for (size_t i = 0; i < m_sync_samples.size(); i++) {
1210
0
    sstr << indent << "[" << i << "] : " << m_sync_samples[i] << "\n";
1211
0
  }
1212
1213
0
  return sstr.str();
1214
0
}
1215
1216
1217
void Box_stss::set_total_number_of_samples(uint32_t num_samples)
1218
0
{
1219
0
  m_all_samples_are_sync_samples = (m_sync_samples.size() == num_samples);
1220
0
}
1221
1222
1223
Error Box_stss::write(StreamWriter& writer) const
1224
0
{
1225
  // If we don't need this box, skip it.
1226
0
  if (m_all_samples_are_sync_samples) {
1227
0
    return Error::Ok;
1228
0
  }
1229
1230
0
  size_t box_start = reserve_box_header_space(writer);
1231
1232
0
  writer.write32(static_cast<uint32_t>(m_sync_samples.size()));
1233
0
  for (uint32_t sample : m_sync_samples) {
1234
0
    writer.write32(sample);
1235
0
  }
1236
1237
0
  prepend_header(writer, box_start);
1238
1239
0
  return Error::Ok;
1240
0
}
1241
1242
1243
Error VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1244
1.15k
{
1245
1.15k
  (void)limits;
1246
1247
1.15k
  range.skip(6);
1248
1.15k
  data_reference_index = range.read16();
1249
1250
1.15k
  pre_defined = range.read16();
1251
1.15k
  range.skip(2);
1252
3.46k
  for (uint32_t& p : pre_defined2) {
1253
3.46k
    p = range.read32();
1254
3.46k
  }
1255
1.15k
  width = range.read16();
1256
1.15k
  height = range.read16();
1257
1.15k
  horizresolution = range.read32();
1258
1.15k
  vertresolution = range.read32();
1259
1.15k
  range.skip(4);
1260
1.15k
  frame_count = range.read16();
1261
1.15k
  compressorname = range.read_fixed_string(32);
1262
1.15k
  depth = range.read16();
1263
1.15k
  pre_defined3 = range.read16s();
1264
1265
  // other boxes from derived specifications
1266
  //std::shared_ptr<Box_clap> clap; // optional // TODO
1267
  //std::shared_ptr<Box_pixi> pixi; // optional // TODO
1268
1269
1.15k
  return Error::Ok;
1270
1.15k
}
1271
1272
1273
Error VisualSampleEntry::write(StreamWriter& writer) const
1274
0
{
1275
0
  writer.write32(0);
1276
0
  writer.write16(0);
1277
0
  writer.write16(data_reference_index);
1278
1279
0
  writer.write16(pre_defined);
1280
0
  writer.write16(0);
1281
0
  for (uint32_t p : pre_defined2) {
1282
0
    writer.write32(p);
1283
0
  }
1284
1285
0
  writer.write16(width);
1286
0
  writer.write16(height);
1287
0
  writer.write32(horizresolution);
1288
0
  writer.write32(vertresolution);
1289
0
  writer.write32(0);
1290
0
  writer.write16(frame_count);
1291
0
  writer.write_fixed_string(compressorname, 32);
1292
0
  writer.write16(depth);
1293
0
  writer.write16(pre_defined3);
1294
1295
0
  return Error::Ok;
1296
0
}
1297
1298
1299
std::string VisualSampleEntry::dump(Indent& indent) const
1300
0
{
1301
0
  std::stringstream sstr;
1302
0
  sstr << indent << "data reference index: " << data_reference_index << "\n"
1303
0
       << indent << "width: " << width << "\n"
1304
0
       << indent << "height: " << height << "\n"
1305
0
       << indent << "horiz. resolution: " << get_horizontal_resolution() << "\n"
1306
0
       << indent << "vert. resolution: " << get_vertical_resolution() << "\n"
1307
0
       << indent << "frame count: " << frame_count << "\n"
1308
0
       << indent << "compressorname: " << compressorname << "\n"
1309
0
       << indent << "depth: " << depth << "\n";
1310
1311
0
  return sstr.str();
1312
0
}
1313
1314
1315
Error Box_URIMetaSampleEntry::write(StreamWriter& writer) const
1316
0
{
1317
0
  size_t box_start = reserve_box_header_space(writer);
1318
1319
0
  writer.write32(0);
1320
0
  writer.write16(0);
1321
0
  writer.write16(data_reference_index);
1322
1323
0
  write_children(writer);
1324
1325
0
  prepend_header(writer, box_start);
1326
1327
0
  return Error::Ok;
1328
0
}
1329
1330
1331
std::string Box_URIMetaSampleEntry::dump(Indent& indent) const
1332
0
{
1333
0
  std::stringstream sstr;
1334
0
  sstr << Box::dump(indent);
1335
0
  sstr << indent << "data reference index: " << data_reference_index << "\n";
1336
0
  sstr << dump_children(indent);
1337
0
  return sstr.str();
1338
0
}
1339
1340
1341
Error Box_URIMetaSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1342
403
{
1343
403
  range.skip(6);
1344
403
  data_reference_index = range.read16();
1345
1346
403
  Error err = read_children(range, READ_CHILDREN_ALL, limits);
1347
403
  if (err) {
1348
52
    return err;
1349
52
  }
1350
1351
351
  return Error::Ok;
1352
403
}
1353
1354
1355
Error Box_uri::parse(BitstreamRange& range, const heif_security_limits* limits)
1356
86
{
1357
86
  parse_full_box_header(range);
1358
1359
86
  if (get_version() > 0) {
1360
6
    return unsupported_version_error("uri ");
1361
6
  }
1362
1363
80
  m_uri = range.read_string();
1364
1365
80
  return range.get_error();
1366
86
}
1367
1368
1369
std::string Box_uri::dump(Indent& indent) const
1370
0
{
1371
0
  std::ostringstream sstr;
1372
0
  sstr << FullBox::dump(indent);
1373
0
  sstr << indent << "uri: " << m_uri << "\n";
1374
1375
0
  return sstr.str();
1376
0
}
1377
1378
1379
Error Box_uri::write(StreamWriter& writer) const
1380
0
{
1381
0
  size_t box_start = reserve_box_header_space(writer);
1382
1383
0
  writer.write(m_uri);
1384
1385
0
  prepend_header(writer, box_start);
1386
1387
0
  return Error::Ok;
1388
0
}
1389
1390
1391
1392
Error Box_ccst::parse(BitstreamRange& range, const heif_security_limits* limits)
1393
166
{
1394
166
  parse_full_box_header(range);
1395
1396
166
  if (get_version() > 0) {
1397
7
    return unsupported_version_error("ccst");
1398
7
  }
1399
1400
159
  uint32_t bits = range.read32();
1401
1402
159
  auto& constraints = m_codingConstraints;
1403
1404
159
  constraints.all_ref_pics_intra = (bits & 0x80000000) != 0;
1405
159
  constraints.intra_pred_used = (bits & 0x40000000) != 0;
1406
159
  constraints.max_ref_per_pic = (bits >> 26) & 0x0F;
1407
1408
159
  return range.get_error();
1409
166
}
1410
1411
1412
std::string Box_ccst::dump(Indent& indent) const
1413
0
{
1414
0
  const auto& constraints = m_codingConstraints;
1415
1416
0
  std::ostringstream sstr;
1417
0
  sstr << FullBox::dump(indent);
1418
0
  sstr << indent << "all ref pics intra: " << std::boolalpha <<constraints.all_ref_pics_intra << "\n"
1419
0
       << indent << "intra pred used: " << constraints.intra_pred_used << "\n"
1420
0
       << indent << "max ref per pic: " << ((int) constraints.max_ref_per_pic) << "\n";
1421
1422
0
  return sstr.str();
1423
0
}
1424
1425
1426
Error Box_ccst::write(StreamWriter& writer) const
1427
0
{
1428
0
  const auto& constraints = m_codingConstraints;
1429
1430
0
  size_t box_start = reserve_box_header_space(writer);
1431
1432
0
  uint32_t bits = 0;
1433
1434
0
  if (constraints.all_ref_pics_intra) {
1435
0
    bits |= 0x80000000;
1436
0
  }
1437
1438
0
  if (constraints.intra_pred_used) {
1439
0
    bits |= 0x40000000;
1440
0
  }
1441
1442
0
  bits |= constraints.max_ref_per_pic << 26;
1443
1444
0
  writer.write32(bits);
1445
1446
0
  prepend_header(writer, box_start);
1447
1448
0
  return Error::Ok;
1449
0
}
1450
1451
1452
Error Box_auxi::parse(BitstreamRange& range, const heif_security_limits* limits)
1453
67
{
1454
67
  parse_full_box_header(range);
1455
1456
67
  if (get_version() > 0) {
1457
6
    return unsupported_version_error("auxi");
1458
6
  }
1459
1460
61
  m_aux_track_type = range.read_string();
1461
1462
61
  return range.get_error();
1463
67
}
1464
1465
1466
std::string Box_auxi::dump(Indent& indent) const
1467
0
{
1468
0
  std::ostringstream sstr;
1469
0
  sstr << FullBox::dump(indent);
1470
0
  sstr << indent << "aux track info type: " << m_aux_track_type << "\n";
1471
1472
0
  return sstr.str();
1473
0
}
1474
1475
1476
Error Box_auxi::write(StreamWriter& writer) const
1477
0
{
1478
0
  size_t box_start = reserve_box_header_space(writer);
1479
1480
0
  writer.write(m_aux_track_type);
1481
1482
0
  prepend_header(writer, box_start);
1483
1484
0
  return Error::Ok;
1485
0
}
1486
1487
1488
Error Box_VisualSampleEntry::write(StreamWriter& writer) const
1489
0
{
1490
0
  size_t box_start = reserve_box_header_space(writer);
1491
1492
0
  Error err = get_VisualSampleEntry_const().write(writer);
1493
0
  if (err) {
1494
0
    return err;
1495
0
  }
1496
1497
0
  write_children(writer);
1498
1499
0
  prepend_header(writer, box_start);
1500
1501
0
  return Error::Ok;
1502
0
}
1503
1504
1505
std::string Box_VisualSampleEntry::dump(Indent& indent) const
1506
0
{
1507
0
  std::stringstream sstr;
1508
0
  sstr << Box::dump(indent);
1509
0
  sstr << m_visualSampleEntry.dump(indent);
1510
0
  sstr << dump_children(indent);
1511
0
  return sstr.str();
1512
0
}
1513
1514
1515
Error Box_VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
1516
1.15k
{
1517
1.15k
  auto err = m_visualSampleEntry.parse(range, limits);
1518
1.15k
  if (err) {
1519
0
    return err;
1520
0
  }
1521
1522
1.15k
  err = read_children(range, READ_CHILDREN_ALL, limits);
1523
1.15k
  if (err) {
1524
255
    return err;
1525
255
  }
1526
1527
900
  return Error::Ok;
1528
1.15k
}
1529
1530
1531
std::string Box_sbgp::dump(Indent& indent) const
1532
0
{
1533
0
  std::stringstream sstr;
1534
0
  sstr << FullBox::dump(indent);
1535
0
  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
1536
1537
0
  if (m_grouping_type_parameter) {
1538
0
    sstr << indent << "grouping_type_parameter: " << *m_grouping_type_parameter << "\n";
1539
0
  }
1540
1541
0
  uint32_t total_samples = 0;
1542
0
  for (size_t i = 0; i < m_entries.size(); i++) {
1543
0
    sstr << indent << "[" << std::setw(2) << (i + 1) << "] : " << std::setw(3) << m_entries[i].sample_count << "x " << m_entries[i].group_description_index << "\n";
1544
0
    total_samples += m_entries[i].sample_count;
1545
0
  }
1546
0
  sstr << indent << "total samples: " << total_samples << "\n";
1547
1548
0
  return sstr.str();
1549
0
}
1550
1551
1552
void Box_sbgp::derive_box_version()
1553
0
{
1554
0
  if (m_grouping_type_parameter) {
1555
0
    set_version(1);
1556
0
  }
1557
0
  else {
1558
0
    set_version(0);
1559
0
  }
1560
0
}
1561
1562
1563
Error Box_sbgp::write(StreamWriter& writer) const
1564
0
{
1565
0
  size_t box_start = reserve_box_header_space(writer);
1566
1567
0
  writer.write32(m_grouping_type);
1568
0
  if (m_grouping_type_parameter) {
1569
0
    writer.write32(*m_grouping_type_parameter);
1570
0
  }
1571
1572
0
  if (m_entries.size() > 0xFFFFFFFF) {
1573
0
    return {heif_error_Usage_error,
1574
0
            heif_suberror_Invalid_parameter_value,
1575
0
            "Too many sbgp entries."};
1576
0
  }
1577
1578
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
1579
0
  for (const auto& entry : m_entries) {
1580
0
    writer.write32(entry.sample_count);
1581
0
    writer.write32(entry.group_description_index);
1582
0
  }
1583
1584
0
  prepend_header(writer, box_start);
1585
1586
0
  return Error::Ok;
1587
0
}
1588
1589
1590
Error Box_sbgp::parse(BitstreamRange& range, const heif_security_limits* limits)
1591
254
{
1592
254
  parse_full_box_header(range);
1593
1594
254
  if (get_version() > 1) {
1595
7
    return unsupported_version_error("sbgp");
1596
7
  }
1597
1598
247
  m_grouping_type = range.read32();
1599
1600
247
  if (get_version() == 1) {
1601
17
    m_grouping_type_parameter = range.read32();
1602
17
  }
1603
1604
247
  uint32_t count = range.read32();
1605
247
  if (auto err = m_memory_handle.alloc(count * sizeof(Entry),
1606
247
                                       limits, "the 'sample to group' table")) {
1607
17
    return err;
1608
17
  }
1609
1610
1.44k
  for (uint32_t i = 0; i < count; i++) {
1611
1.26k
    Entry e{};
1612
1.26k
    e.sample_count = range.read32();
1613
1.26k
    e.group_description_index = range.read32();
1614
1.26k
    m_entries.push_back(e);
1615
1.26k
    if (range.error()) {
1616
50
      return range.get_error();
1617
50
    }
1618
1.26k
  }
1619
1620
180
  return range.get_error();
1621
230
}
1622
1623
1624
std::string SampleGroupEntry_refs::dump() const
1625
0
{
1626
0
  std::stringstream sstr;
1627
0
  if (m_sample_id==0) {
1628
0
    sstr << "0 (non-ref) refs =";
1629
0
  }
1630
0
  else {
1631
0
    sstr << m_sample_id << " refs =";
1632
0
  }
1633
0
  for (uint32_t ref : m_direct_reference_sample_id) {
1634
0
    sstr << ' ' << ref;
1635
0
  }
1636
1637
0
  return sstr.str();
1638
0
}
1639
1640
Error SampleGroupEntry_refs::write(StreamWriter& writer) const
1641
0
{
1642
0
  return {};
1643
0
}
1644
1645
Error SampleGroupEntry_refs::parse(BitstreamRange& range, const heif_security_limits*)
1646
16.3k
{
1647
16.3k
  m_sample_id = range.read32();
1648
16.3k
  uint8_t cnt = range.read8();
1649
32.9k
  for (uint8_t i = 0; i < cnt; i++) {
1650
16.5k
    m_direct_reference_sample_id.push_back(range.read32());
1651
16.5k
  }
1652
1653
16.3k
  return Error::Ok;
1654
16.3k
}
1655
1656
1657
void Box_sgpd::derive_box_version()
1658
0
{
1659
0
  if (m_default_length) {
1660
0
    set_version(1);
1661
0
    assert(!m_default_sample_description_index);
1662
0
    return;
1663
0
  }
1664
1665
0
  if (m_default_sample_description_index) {
1666
0
    set_version(2);
1667
0
    return;
1668
0
  }
1669
1670
0
  set_version(0);
1671
0
}
1672
1673
1674
std::string Box_sgpd::dump(Indent& indent) const
1675
0
{
1676
0
  std::stringstream sstr;
1677
0
  sstr << FullBox::dump(indent);
1678
1679
0
  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
1680
0
  if (m_default_length) {
1681
0
    sstr << indent << "default_length: " << *m_default_length << "\n";
1682
0
  }
1683
0
  if (m_default_sample_description_index) {
1684
0
    sstr << indent << "default_sample_description_index: " << *m_default_sample_description_index << "\n";
1685
0
  }
1686
1687
0
  for (size_t i=0; i<m_entries.size(); i++) {
1688
0
    sstr << indent << "[" << (i+1) << "] : ";
1689
0
    if (m_entries[i].sample_group_entry) {
1690
0
      sstr << m_entries[i].sample_group_entry->dump() << "\n";
1691
0
    }
1692
0
    else {
1693
0
      sstr << "empty (description_length=" << m_entries[i].description_length << ")\n";
1694
0
    }
1695
0
  }
1696
1697
0
  return sstr.str();
1698
0
}
1699
1700
1701
Error Box_sgpd::write(StreamWriter& writer) const
1702
0
{
1703
0
return {};
1704
0
}
1705
1706
1707
Error Box_sgpd::parse(BitstreamRange& range, const heif_security_limits* limits)
1708
1.18k
{
1709
1.18k
  parse_full_box_header(range);
1710
1711
1.18k
  m_grouping_type = range.read32();
1712
1713
1.18k
  if (get_version() == 1) {
1714
485
    m_default_length = range.read32();
1715
485
  }
1716
1717
1.18k
  if (get_version() >= 2) {
1718
278
    m_default_sample_description_index = range.read32();
1719
278
  }
1720
1721
1.18k
  uint32_t entry_count = range.read32();
1722
1723
1.18k
  if (limits->max_sample_group_description_box_entries &&
1724
1.18k
      entry_count > limits->max_sample_group_description_box_entries) {
1725
65
    std::stringstream sstr;
1726
65
    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample group description items exceeds the security limit of "
1727
65
         << limits->max_sample_group_description_box_entries << " items";
1728
1729
65
    return {heif_error_Memory_allocation_error,
1730
65
            heif_suberror_Security_limit_exceeded,
1731
65
            sstr.str()};
1732
1733
65
  }
1734
1735
89.5k
  for (uint32_t i = 0; i < entry_count; i++) {
1736
88.4k
    Entry entry;
1737
1738
88.4k
    if (get_version() == 1) {
1739
52.9k
      if (*m_default_length == 0) {
1740
3.41k
        entry.description_length = range.read32();
1741
3.41k
      }
1742
52.9k
    }
1743
1744
88.4k
    switch (m_grouping_type) {
1745
16.3k
      case fourcc("refs"): {
1746
16.3k
        entry.sample_group_entry = std::make_shared<SampleGroupEntry_refs>();
1747
16.3k
        Error err = entry.sample_group_entry->parse(range, limits);
1748
16.3k
        if (err) {
1749
0
          return err;
1750
0
        }
1751
1752
16.3k
        break;
1753
16.3k
      }
1754
1755
72.0k
      default:
1756
72.0k
        break;
1757
88.4k
    }
1758
1759
88.4k
    m_entries.emplace_back(std::move(entry));
1760
88.4k
  }
1761
1762
1.12k
  return Error::Ok;
1763
1.12k
}
1764
1765
1766
std::string Box_btrt::dump(Indent& indent) const
1767
0
{
1768
0
  std::stringstream sstr;
1769
0
  sstr << FullBox::dump(indent);
1770
1771
0
  sstr << indent << "bufferSizeDB: " << m_bufferSizeDB << " bytes\n";
1772
0
  sstr << indent << "max bitrate: " << m_maxBitrate << " bits/sec\n";
1773
0
  sstr << indent << "avg bitrate: " << m_avgBitrate << " bits/sec\n";
1774
1775
0
  return sstr.str();
1776
0
}
1777
1778
1779
Error Box_btrt::write(StreamWriter& writer) const
1780
0
{
1781
0
  size_t box_start = reserve_box_header_space(writer);
1782
1783
0
  writer.write32(m_bufferSizeDB);
1784
0
  writer.write32(m_maxBitrate);
1785
0
  writer.write32(m_avgBitrate);
1786
1787
0
  prepend_header(writer, box_start);
1788
1789
0
  return Error::Ok;
1790
0
}
1791
1792
1793
Error Box_btrt::parse(BitstreamRange& range, const heif_security_limits*)
1794
130
{
1795
130
  m_bufferSizeDB = range.read32();
1796
130
  m_maxBitrate = range.read32();
1797
130
  m_avgBitrate = range.read32();
1798
1799
130
  return Error::Ok;
1800
130
}
1801
1802
1803
1804
void Box_saiz::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
1805
0
{
1806
0
  m_aux_info_type = aux_info_type;
1807
0
  m_aux_info_type_parameter = aux_info_type_parameter;
1808
1809
0
  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
1810
0
  set_flags(nonnull ? 1 : 0);
1811
0
}
1812
1813
1814
void Box_saiz::add_sample_size(uint8_t s)
1815
0
{
1816
  // --- it is the first sample -> put into default size (except if it is a zero size = no sample aux info)
1817
1818
0
  if (s != 0 && m_num_samples == 0) {
1819
0
    m_default_sample_info_size = s;
1820
0
    m_num_samples = 1;
1821
0
    return;
1822
0
  }
1823
1824
  // --- if it's the default size, just add more to the number of default sizes
1825
1826
0
  if (s != 0 && s == m_default_sample_info_size) {
1827
0
    m_num_samples++;
1828
0
    return;
1829
0
  }
1830
1831
  // --- it is different from the default size -> add the list
1832
1833
  // first copy samples with the default size into the list
1834
1835
0
  if (m_default_sample_info_size != 0) {
1836
0
    for (uint32_t i = 0; i < m_num_samples; i++) {
1837
0
      m_sample_sizes.push_back(m_default_sample_info_size);
1838
0
    }
1839
1840
0
    m_default_sample_info_size = 0;
1841
0
  }
1842
1843
  // add the new sample size
1844
1845
0
  m_num_samples++;
1846
0
  m_sample_sizes.push_back(s);
1847
0
}
1848
1849
1850
uint8_t Box_saiz::get_sample_size(uint32_t idx)
1851
0
{
1852
0
  if (m_default_sample_info_size != 0) {
1853
0
    return m_default_sample_info_size;
1854
0
  }
1855
1856
0
  if (idx >= m_sample_sizes.size()) {
1857
0
    return 0;
1858
0
  }
1859
1860
0
  return m_sample_sizes[idx];
1861
0
}
1862
1863
1864
std::string Box_saiz::dump(Indent& indent) const
1865
0
{
1866
0
  std::stringstream sstr;
1867
0
  sstr << FullBox::dump(indent);
1868
1869
0
  sstr << indent << "aux_info_type: ";
1870
0
  if (m_aux_info_type == 0) {
1871
0
    sstr << "0\n";
1872
0
  }
1873
0
  else {
1874
0
    sstr << fourcc_to_string(m_aux_info_type) << "\n";
1875
0
  }
1876
1877
0
  sstr << indent << "aux_info_type_parameter: ";
1878
0
  if (m_aux_info_type_parameter == 0) {
1879
0
    sstr << "0\n";
1880
0
  }
1881
0
  else {
1882
0
    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
1883
0
  }
1884
1885
0
  sstr << indent << "default sample size: ";
1886
0
  if (m_default_sample_info_size == 0) {
1887
0
    sstr << "0 (variable)\n";
1888
0
  }
1889
0
  else {
1890
0
    sstr << ((int)m_default_sample_info_size) << "\n";
1891
0
  }
1892
1893
0
  if (m_default_sample_info_size == 0) {
1894
0
    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
1895
0
      sstr << indent << "[" << i << "] : " << ((int) m_sample_sizes[i]) << "\n";
1896
0
    }
1897
0
  }
1898
1899
0
  return sstr.str();
1900
0
}
1901
1902
1903
Error Box_saiz::write(StreamWriter& writer) const
1904
0
{
1905
0
  size_t box_start = reserve_box_header_space(writer);
1906
1907
0
  if (get_flags() & 1) {
1908
0
    writer.write32(m_aux_info_type);
1909
0
    writer.write32(m_aux_info_type_parameter);
1910
0
  }
1911
1912
0
  writer.write8(m_default_sample_info_size);
1913
1914
0
  if (m_default_sample_info_size == 0) {
1915
0
    assert(m_num_samples == m_sample_sizes.size());
1916
1917
0
    uint32_t num_nonnull_samples = static_cast<uint32_t>(m_sample_sizes.size());
1918
0
    while (num_nonnull_samples > 0 && m_sample_sizes[num_nonnull_samples-1] == 0) {
1919
0
      num_nonnull_samples--;
1920
0
    }
1921
1922
0
    writer.write32(num_nonnull_samples);
1923
1924
0
    for (size_t i = 0; i < num_nonnull_samples; i++) {
1925
0
      writer.write8(m_sample_sizes[i]);
1926
0
    }
1927
0
  }
1928
0
  else {
1929
0
    writer.write32(m_num_samples);
1930
0
  }
1931
1932
0
  prepend_header(writer, box_start);
1933
1934
0
  return Error::Ok;
1935
0
}
1936
1937
1938
Error Box_saiz::parse(BitstreamRange& range, const heif_security_limits* limits)
1939
142
{
1940
142
  parse_full_box_header(range);
1941
1942
142
  if (get_flags() & 1) {
1943
71
    m_aux_info_type = range.read32();
1944
71
    m_aux_info_type_parameter = range.read32();
1945
71
  }
1946
1947
142
  m_default_sample_info_size = range.read8();
1948
142
  m_num_samples = range.read32();
1949
1950
142
  if (limits && limits->max_sequence_frames > 0 && m_num_samples > limits->max_sequence_frames) {
1951
28
    return {
1952
28
      heif_error_Memory_allocation_error,
1953
28
      heif_suberror_Security_limit_exceeded,
1954
28
      "Number of 'saiz' samples exceeds the maximum number of sequence frames."
1955
28
    };
1956
28
  }
1957
1958
114
  if (m_default_sample_info_size == 0) {
1959
    // check required memory
1960
1961
85
    uint64_t mem_size = m_num_samples;
1962
85
    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'sample aux info sizes' (saiz) table")) {
1963
0
      return err;
1964
0
    }
1965
1966
    // read whole table at once
1967
1968
85
    m_sample_sizes.resize(m_num_samples);
1969
85
    range.read(m_sample_sizes.data(), m_num_samples);
1970
85
  }
1971
1972
114
  return range.get_error();
1973
114
}
1974
1975
1976
1977
void Box_saio::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
1978
0
{
1979
0
  m_aux_info_type = aux_info_type;
1980
0
  m_aux_info_type_parameter = aux_info_type_parameter;
1981
1982
0
  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
1983
0
  set_flags(nonnull ? 1 : 0);
1984
0
}
1985
1986
1987
void Box_saio::add_chunk_offset(uint64_t s)
1988
0
{
1989
0
  if (s > 0xFFFFFFFF) {
1990
0
    m_need_64bit = true;
1991
0
    set_version(1);
1992
0
  }
1993
1994
0
  m_chunk_offset.push_back(s);
1995
0
}
1996
1997
1998
uint64_t Box_saio::get_chunk_offset(uint32_t idx) const
1999
0
{
2000
0
  if (idx >= m_chunk_offset.size()) {
2001
0
    return 0;
2002
0
  }
2003
0
  else {
2004
0
    return m_chunk_offset[idx];
2005
0
  }
2006
0
}
2007
2008
2009
std::string Box_saio::dump(Indent& indent) const
2010
0
{
2011
0
  std::stringstream sstr;
2012
0
  sstr << FullBox::dump(indent);
2013
2014
0
  sstr << indent << "aux_info_type: ";
2015
0
  if (m_aux_info_type == 0) {
2016
0
    sstr << "0\n";
2017
0
  }
2018
0
  else {
2019
0
    sstr << fourcc_to_string(m_aux_info_type) << "\n";
2020
0
  }
2021
2022
0
  sstr << indent << "aux_info_type_parameter: ";
2023
0
  if (m_aux_info_type_parameter == 0) {
2024
0
    sstr << "0\n";
2025
0
  }
2026
0
  else {
2027
0
    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
2028
0
  }
2029
2030
0
  for (size_t i = 0; i < m_chunk_offset.size(); i++) {
2031
0
    sstr << indent << "[" << i << "] : 0x" << std::hex << m_chunk_offset[i] << "\n";
2032
0
  }
2033
2034
0
  return sstr.str();
2035
0
}
2036
2037
2038
void Box_saio::patch_file_pointers(StreamWriter& writer, size_t offset)
2039
0
{
2040
0
  size_t oldPosition = writer.get_position();
2041
2042
0
  writer.set_position(m_offset_start_pos);
2043
2044
0
  for (uint64_t ptr : m_chunk_offset) {
2045
0
    if (get_version() == 0 && ptr + offset > std::numeric_limits<uint32_t>::max()) {
2046
0
      writer.write32(0); // TODO: error
2047
0
    } else if (get_version() == 0) {
2048
0
      writer.write32(static_cast<uint32_t>(ptr + offset));
2049
0
    } else {
2050
0
      writer.write64(ptr + offset);
2051
0
    }
2052
0
  }
2053
2054
0
  writer.set_position(oldPosition);
2055
0
}
2056
2057
2058
Error Box_saio::write(StreamWriter& writer) const
2059
0
{
2060
0
  size_t box_start = reserve_box_header_space(writer);
2061
2062
0
  if (get_flags() & 1) {
2063
0
    writer.write32(m_aux_info_type);
2064
0
    writer.write32(m_aux_info_type_parameter);
2065
0
  }
2066
2067
0
  if (m_chunk_offset.size() > std::numeric_limits<uint32_t>::max()) {
2068
0
    return Error{heif_error_Unsupported_feature,
2069
0
                 heif_suberror_Unspecified,
2070
0
                 "Maximum number of chunks exceeded"};
2071
0
  }
2072
0
  writer.write32(static_cast<uint32_t>(m_chunk_offset.size()));
2073
2074
0
  m_offset_start_pos = writer.get_position();
2075
2076
0
  for (uint64_t size : m_chunk_offset) {
2077
0
    if (m_need_64bit) {
2078
0
      writer.write64(size);
2079
0
    } else {
2080
0
      writer.write32(static_cast<uint32_t>(size));
2081
0
    }
2082
0
  }
2083
2084
0
  prepend_header(writer, box_start);
2085
2086
0
  return Error::Ok;
2087
0
}
2088
2089
2090
Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
2091
368
{
2092
368
  parse_full_box_header(range);
2093
2094
368
  if (get_flags() & 1) {
2095
58
    m_aux_info_type = range.read32();
2096
58
    m_aux_info_type_parameter = range.read32();
2097
58
  }
2098
2099
368
  uint32_t num_chunks = range.read32();
2100
2101
  // We have no explicit maximum on the number of chunks.
2102
  // Use the maximum number of frames as an upper limit.
2103
368
  if (limits && limits->max_sequence_frames > 0 && num_chunks > limits->max_sequence_frames) {
2104
28
    return {
2105
28
      heif_error_Memory_allocation_error,
2106
28
      heif_suberror_Security_limit_exceeded,
2107
28
      "Number of 'saio' chunks exceeds the maximum number of sequence frames."
2108
28
    };
2109
28
  }
2110
2111
  // check required memory
2112
340
  uint64_t mem_size = num_chunks * sizeof(uint64_t);
2113
2114
340
  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'saio' table")) {
2115
0
    return err;
2116
0
  }
2117
2118
340
  m_chunk_offset.resize(num_chunks);
2119
2120
1.67k
  for (uint32_t i = 0; i < num_chunks; i++) {
2121
1.44k
    uint64_t offset;
2122
1.44k
    if (get_version() == 1) {
2123
388
      offset = range.read64();
2124
388
    }
2125
1.06k
    else {
2126
1.06k
      offset = range.read32();
2127
1.06k
    }
2128
2129
1.44k
    m_chunk_offset[i] = offset;
2130
2131
1.44k
    if (range.error()) {
2132
115
      return range.get_error();
2133
115
    }
2134
1.44k
  }
2135
2136
225
  return Error::Ok;
2137
340
}
2138
2139
2140
std::string Box_sdtp::dump(Indent& indent) const
2141
0
{
2142
0
  std::stringstream sstr;
2143
0
  sstr << FullBox::dump(indent);
2144
2145
0
  assert(m_sample_information.size() <= UINT32_MAX);
2146
2147
0
  for (uint32_t i = 0; i < static_cast<uint32_t>(m_sample_information.size()); i++) {
2148
0
    const char* spaces = "            ";
2149
0
    int nSpaces = 6;
2150
0
    int k = i;
2151
0
    while (k >= 10 && nSpaces < 12) {
2152
0
      k /= 10;
2153
0
      nSpaces++;
2154
0
    }
2155
2156
0
    spaces = spaces + 12 - nSpaces;
2157
2158
0
    sstr << indent << "[" << i << "] : is_leading=" << (int) get_is_leading(i) << "\n"
2159
0
        << indent << spaces << "depends_on=" << (int) get_depends_on(i) << "\n"
2160
0
        << indent << spaces << "is_depended_on=" << (int) get_is_depended_on(i) << "\n"
2161
0
        << indent << spaces << "has_redundancy=" << (int) get_has_redundancy(i) << "\n";
2162
0
  }
2163
2164
0
  return sstr.str();
2165
0
}
2166
2167
2168
Error Box_sdtp::parse(BitstreamRange& range, const heif_security_limits* limits)
2169
227
{
2170
227
  parse_full_box_header(range);
2171
2172
  // We have no easy way to get the number of samples from 'saiz' or 'stz2' as specified
2173
  // in the standard. Instead, we read until the end of the box.
2174
227
  size_t nSamples = range.get_remaining_bytes();
2175
2176
227
  m_sample_information.resize(nSamples);
2177
227
  range.read(m_sample_information.data(), nSamples);
2178
2179
227
  return Error::Ok;
2180
227
}
2181
2182
2183
Error Box_tref::parse(BitstreamRange& range, const heif_security_limits* limits)
2184
1.99k
{
2185
4.88k
  while (!range.eof()) {
2186
3.43k
    BoxHeader header;
2187
3.43k
    Error err = header.parse_header(range);
2188
3.43k
    if (err != Error::Ok) {
2189
16
      return err;
2190
16
    }
2191
2192
3.41k
    if (header.get_box_size() < header.get_header_size()) {
2193
12
      return {heif_error_Invalid_input,
2194
12
              heif_suberror_Unspecified,
2195
12
              "Invalid box size (smaller than header)"};
2196
12
    }
2197
2198
3.40k
    uint64_t dataSize = (header.get_box_size() - header.get_header_size());
2199
2200
3.40k
    if (dataSize % 4 != 0 || dataSize < 4) {
2201
117
      return {heif_error_Invalid_input,
2202
117
              heif_suberror_Unspecified,
2203
117
              "Input file has a 'tref' TrackReferenceTypeBox with invalid size."};
2204
117
    }
2205
2206
3.28k
    uint64_t nRefs = dataSize / 4;
2207
2208
3.28k
    if (limits->max_items && nRefs > limits->max_items) {
2209
344
      std::stringstream sstr;
2210
344
      sstr << "Number of references in tref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references.";
2211
2212
344
      return {heif_error_Invalid_input,
2213
344
              heif_suberror_Security_limit_exceeded,
2214
344
              sstr.str()};
2215
344
    }
2216
2217
2.94k
    Reference ref;
2218
2.94k
    ref.reference_type = header.get_short_type();
2219
2220
34.9k
    for (uint64_t i = 0; i < nRefs; i++) {
2221
32.0k
      if (range.eof()) {
2222
53
        std::stringstream sstr;
2223
53
        sstr << "tref box should contain " << nRefs << " references, but we can only read " << i << " references.";
2224
2225
53
        return {heif_error_Invalid_input,
2226
53
                heif_suberror_End_of_data,
2227
53
                sstr.str()};
2228
53
      }
2229
2230
31.9k
      ref.to_track_id.push_back(static_cast<uint32_t>(range.read32()));
2231
31.9k
    }
2232
2233
2.89k
    m_references.push_back(ref);
2234
2.89k
  }
2235
2236
2237
  // --- check for duplicate references
2238
2239
1.45k
  if (auto error = check_for_double_references()) {
2240
159
    return error;
2241
159
  }
2242
2243
1.29k
  return range.get_error();
2244
1.45k
}
2245
2246
2247
Error Box_tref::check_for_double_references() const
2248
1.45k
{
2249
2.45k
  for (const auto& ref : m_references) {
2250
2.45k
    std::set<uint32_t> to_ids;
2251
26.0k
    for (const auto to_id : ref.to_track_id) {
2252
26.0k
      if (to_ids.find(to_id) == to_ids.end()) {
2253
25.9k
        to_ids.insert(to_id);
2254
25.9k
      }
2255
159
      else {
2256
159
        return {heif_error_Invalid_input,
2257
159
                heif_suberror_Unspecified,
2258
159
                "'tref' has double references"};
2259
159
      }
2260
26.0k
    }
2261
2.45k
  }
2262
2263
1.29k
  return Error::Ok;
2264
1.45k
}
2265
2266
2267
Error Box_tref::write(StreamWriter& writer) const
2268
0
{
2269
0
  if (auto error = check_for_double_references()) {
2270
0
    return error;
2271
0
  }
2272
2273
0
  size_t box_start = reserve_box_header_space(writer);
2274
2275
0
  for (const auto& ref : m_references) {
2276
0
    uint32_t box_size = 8 + uint32_t(ref.to_track_id.size() * 4);
2277
2278
    // we write the BoxHeader ourselves since it is very simple
2279
0
    writer.write32(box_size);
2280
0
    writer.write32(ref.reference_type);
2281
2282
0
    for (uint32_t r : ref.to_track_id) {
2283
0
      writer.write32(r);
2284
0
    }
2285
0
  }
2286
2287
0
  prepend_header(writer, box_start);
2288
2289
0
  return Error::Ok;
2290
0
}
2291
2292
2293
std::string Box_tref::dump(Indent& indent) const
2294
0
{
2295
0
  std::ostringstream sstr;
2296
0
  sstr << Box::dump(indent);
2297
2298
0
  for (const auto& ref : m_references) {
2299
0
    sstr << indent << "reference with type '" << fourcc_to_string(ref.reference_type) << "'"
2300
0
         << " to track IDs: ";
2301
0
    for (uint32_t id : ref.to_track_id) {
2302
0
      sstr << id << " ";
2303
0
    }
2304
0
    sstr << "\n";
2305
0
  }
2306
2307
0
  return sstr.str();
2308
0
}
2309
2310
2311
std::vector<uint32_t> Box_tref::get_references(uint32_t ref_type) const
2312
1
{
2313
1
  for (const Reference& ref : m_references) {
2314
1
    if (ref.reference_type == ref_type) {
2315
1
      return ref.to_track_id;
2316
1
    }
2317
1
  }
2318
2319
0
  return {};
2320
1
}
2321
2322
2323
size_t Box_tref::get_number_of_references_of_type(uint32_t ref_type) const
2324
0
{
2325
0
  for (const Reference& ref : m_references) {
2326
0
    if (ref.reference_type == ref_type) {
2327
0
      return ref.to_track_id.size();
2328
0
    }
2329
0
  }
2330
2331
0
  return 0;
2332
0
}
2333
2334
2335
std::vector<uint32_t> Box_tref::get_reference_types() const
2336
0
{
2337
0
  std::vector<uint32_t> types;
2338
0
  types.reserve(m_references.size());
2339
0
  for (const auto& ref : m_references) {
2340
0
    types.push_back(ref.reference_type);
2341
0
  }
2342
2343
0
  return types;
2344
0
}
2345
2346
2347
void Box_tref::add_references(uint32_t to_track_id, uint32_t type)
2348
0
{
2349
0
  for (auto& ref : m_references) {
2350
0
    if (ref.reference_type == type) {
2351
0
      ref.to_track_id.push_back(to_track_id);
2352
0
      return;
2353
0
    }
2354
0
  }
2355
2356
0
  Reference ref;
2357
0
  ref.reference_type = type;
2358
0
  ref.to_track_id = {to_track_id};
2359
2360
0
  m_references.push_back(ref);
2361
0
}
2362
2363
2364
Error Box_elst::parse(BitstreamRange& range, const heif_security_limits* limits)
2365
769
{
2366
769
  Error err = parse_full_box_header(range);
2367
769
  if (err != Error::Ok) {
2368
7
    return err;
2369
7
  }
2370
2371
762
  if (get_version() > 1) {
2372
9
    return unsupported_version_error("edts");
2373
9
  }
2374
2375
753
  uint32_t nEntries = range.read32();
2376
753
  m_entries.clear();
2377
2378
3.59k
  for (uint64_t i = 0; i < nEntries; i++) {
2379
3.00k
    if (range.eof()) {
2380
155
      std::stringstream sstr;
2381
155
      sstr << "edts box should contain " << nEntries << " entries, but we can only read " << i << " entries.";
2382
2383
155
      return {heif_error_Invalid_input,
2384
155
              heif_suberror_End_of_data,
2385
155
              sstr.str()};
2386
155
    }
2387
2388
2.84k
    Entry entry{};
2389
2.84k
    if (get_version() == 1) {
2390
352
      entry.segment_duration = range.read64();
2391
352
      entry.media_time = range.read64s();
2392
352
    }
2393
2.49k
    else {
2394
2.49k
      entry.segment_duration = range.read32();
2395
2.49k
      entry.media_time = range.read32s();
2396
2.49k
    }
2397
2398
2.84k
    entry.media_rate_integer = range.read16s();
2399
2.84k
    entry.media_rate_fraction = range.read16s();
2400
2401
2.84k
    m_entries.push_back(entry);
2402
2.84k
  }
2403
2404
598
  return range.get_error();
2405
753
}
2406
2407
2408
Error Box_elst::write(StreamWriter& writer) const
2409
0
{
2410
0
  size_t box_start = reserve_box_header_space(writer);
2411
2412
0
  if (m_entries.size() > std::numeric_limits<uint32_t>::max()) {
2413
0
    return {heif_error_Usage_error,
2414
0
            heif_suberror_Invalid_parameter_value,
2415
0
            "Too many entries in edit list"};
2416
0
  }
2417
2418
0
  writer.write32(static_cast<uint32_t>(m_entries.size()));
2419
2420
2421
0
  for (const auto& entry : m_entries) {
2422
0
    if (get_version() == 1) {
2423
0
      writer.write64(entry.segment_duration);
2424
0
      writer.write64s(entry.media_time);
2425
0
    }
2426
0
    else {
2427
      // The cast is valid because we check in derive_box_version() whether everything
2428
      // fits into 32bit. If not, version 1 is used.
2429
2430
0
      writer.write32(static_cast<uint32_t>(entry.segment_duration));
2431
0
      writer.write32s(static_cast<int32_t>(entry.media_time));
2432
0
    }
2433
2434
0
    writer.write16s(entry.media_rate_integer);
2435
0
    writer.write16s(entry.media_rate_fraction);
2436
0
  }
2437
2438
0
  prepend_header(writer, box_start);
2439
2440
0
  return Error::Ok;
2441
0
}
2442
2443
2444
std::string Box_elst::dump(Indent& indent) const
2445
0
{
2446
0
  std::ostringstream sstr;
2447
0
  sstr << FullBox::dump(indent);
2448
2449
0
  sstr << indent << "repeat list: " << ((get_flags() & Flags::Repeat_EditList) ? "yes" : "no") << "\n";
2450
2451
0
  for (const auto& entry : m_entries) {
2452
0
    sstr << indent << "segment duration: " << entry.segment_duration << "\n";
2453
0
    sstr << indent << "media time: " << entry.media_time << "\n";
2454
0
    sstr << indent << "media rate integer: " << entry.media_rate_integer << "\n";
2455
0
    sstr << indent << "media rate fraction: " << entry.media_rate_fraction << "\n";
2456
0
  }
2457
2458
0
  return sstr.str();
2459
0
}
2460
2461
void Box_elst::derive_box_version()
2462
0
{
2463
  // check whether we need 64bit values
2464
2465
0
  bool need_64bit = std::any_of(m_entries.begin(),
2466
0
                                m_entries.end(),
2467
0
                                [](const Entry& entry) {
2468
0
                                  return (entry.segment_duration > std::numeric_limits<uint32_t>::max() ||
2469
0
                                          entry.media_time > std::numeric_limits<int32_t>::max());
2470
0
                                });
2471
2472
0
  if (need_64bit) {
2473
0
    set_version(1);
2474
0
  }
2475
0
  else {
2476
0
    set_version(0);
2477
0
  }
2478
0
}
2479
2480
2481
void Box_elst::enable_repeat_mode(bool enable)
2482
0
{
2483
0
  uint32_t flags = get_flags();
2484
0
  if (enable) {
2485
0
    flags |= Flags::Repeat_EditList;
2486
0
  }
2487
0
  else {
2488
0
    flags &= ~Flags::Repeat_EditList;
2489
0
  }
2490
2491
0
  set_flags(flags);
2492
0
}
2493
2494
2495
void Box_elst::add_entry(const Entry& entry)
2496
0
{
2497
0
  m_entries.push_back(entry);
2498
0
}