Coverage Report

Created: 2026-06-10 07:00

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